diff --git a/account_chart_update/__init__.py b/account_chart_update/__init__.py index d04374b7..4319b421 100644 --- a/account_chart_update/__init__.py +++ b/account_chart_update/__init__.py @@ -23,4 +23,6 @@ """ Account Chart Update Wizard """ + +from . import model from . import wizard diff --git a/account_chart_update/__openerp__.py b/account_chart_update/__openerp__.py index e1f10c21..80afbf9f 100644 --- a/account_chart_update/__openerp__.py +++ b/account_chart_update/__openerp__.py @@ -27,7 +27,7 @@ { 'name': "Detect changes and update the Account Chart from a template", 'version': "1.2", - 'author': "Zikzakmedia SL", + 'author': "Zikzakmedia SL,Odoo Community Association (OCA)", 'website': "www.zikzakmedia.com", 'depends': ["account"], 'category': "Generic Modules/Accounting", diff --git a/account_chart_update/model/__init__.py b/account_chart_update/model/__init__.py new file mode 100644 index 00000000..a7362088 --- /dev/null +++ b/account_chart_update/model/__init__.py @@ -0,0 +1 @@ +from . import account_tax_code diff --git a/account_chart_update/model/account_tax_code.py b/account_chart_update/model/account_tax_code.py new file mode 100644 index 00000000..ae68fd81 --- /dev/null +++ b/account_chart_update/model/account_tax_code.py @@ -0,0 +1,27 @@ +# -*- coding: utf-8 -*- +############################################################################## +# +# OpenERP, Open Source Management Solution +# Copyright (c) 2014 ACSONE SA/NV (http://acsone.eu) +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU Affero General Public License as published +# by the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Affero General Public License for more details. +# +# You should have received a copy of the GNU Affero General Public License +# along with this program. If not, see . +# +############################################################################## + +from openerp import models, fields + + +class AccountTaxCode(models.Model): + _inherit = 'account.tax.code' + + active = fields.Boolean('Active', default=True) diff --git a/account_chart_update/wizard/wizard_chart_update.py b/account_chart_update/wizard/wizard_chart_update.py index 891faf0d..14906044 100644 --- a/account_chart_update/wizard/wizard_chart_update.py +++ b/account_chart_update/wizard/wizard_chart_update.py @@ -194,6 +194,14 @@ class wizard_update_charts_accounts(orm.TransientModel): 'Updated fiscal positions', readonly=True ), + 'deleted_tax_codes': fields.integer( + 'Deactivated tax codes', + readonly=True + ), + 'deleted_taxes': fields.integer( + 'Deactivated taxes', + readonly=True + ), 'log': fields.text('Messages and Errors', readonly=True) } @@ -324,6 +332,8 @@ class wizard_update_charts_accounts(orm.TransientModel): """ Adds a tax template -> tax id to the mapping. """ + if context is None: + context = {} if not tax_templ: return False if tax_templ_mapping.get(tax_templ.id): @@ -337,7 +347,11 @@ class wizard_update_charts_accounts(orm.TransientModel): criteria = (['|', '|'] + criteria + [('description', '=', tax_templ.description), ('name', '=', tax_templ.description)]) - tax_ids = tax_obj.search(cr, uid, criteria, context=context) + criteria += [('company_id', '=', wizard.company_id.id)] + # search inactive taxes too, to avoid re-creating + # taxes that have been deactivated before + search_context = dict(context, active_test=False) + tax_ids = tax_obj.search(cr, uid, criteria, context=search_context) tax_templ_mapping[tax_templ.id] = tax_ids and tax_ids[0] or False return tax_templ_mapping[tax_templ.id] @@ -353,24 +367,25 @@ class wizard_update_charts_accounts(orm.TransientModel): return False if tax_code_templ_mapping.get(tax_code_template.id): return tax_code_templ_mapping[tax_code_template.id] - # In other case + # prepare a search context in order to + # search inactive tax codes too, to avoid re-creating + # tax codes that have been deactivated before + search_context = dict(context, active_test=False) tax_code_obj = self.pool['account.tax.code'] root_tax_code_id = wizard.chart_template_id.tax_code_root_id.id - tax_code_name = ((tax_code_template.id == root_tax_code_id) and - wizard.company_id.name or tax_code_template.name) - tax_code_ids = tax_code_obj.search(cr, uid, [ - ('name', '=', tax_code_name), - ('company_id', '=', wizard.company_id.id) - ], context=context) - if not tax_code_ids: - # if we could not match no tax code template name, - # try to match on tax code template code, if any - tax_code_code = tax_code_template.code - if tax_code_code: - tax_code_ids = tax_code_obj.search(cr, uid, [ - ('code', '=', tax_code_code), - ('company_id', '=', wizard.company_id.id) - ], context=context) + tax_code_code = tax_code_template.code + if tax_code_code: + tax_code_ids = tax_code_obj.search(cr, uid, [ + ('code', '=', tax_code_code), + ('company_id', '=', wizard.company_id.id) + ], context=search_context) + if not tax_code_code or not tax_code_ids: + tax_code_name = ((tax_code_template.id == root_tax_code_id) and + wizard.company_id.name or tax_code_template.name) + tax_code_ids = tax_code_obj.search(cr, uid, [ + ('name', '=', tax_code_name), + ('company_id', '=', wizard.company_id.id) + ], context=search_context) tax_code_templ_mapping[tax_code_template.id] = (tax_code_ids and tax_code_ids[0] or False) @@ -416,6 +431,21 @@ class wizard_update_charts_accounts(orm.TransientModel): fp_templ_mapping[fp_template.id] = fp_ids and fp_ids[0] or False return fp_templ_mapping[fp_template.id] + def _get_depth_first_tax_code_template_ids(self, cr, uid, root_tax_code_id, + context=None): + tax_code_templ_obj = self.pool['account.tax.code.template'] + + def get_children(tct): + for child in tct.child_ids: + res.append(child.id) + get_children(child) + + tct = tax_code_templ_obj.browse(cr, uid, root_tax_code_id, + context=context) + res = [tct.id] + get_children(tct) + return res + def _find_tax_codes(self, cr, uid, wizard, chart_template_ids, context=None): """ @@ -436,9 +466,9 @@ class wizard_update_charts_accounts(orm.TransientModel): wiz_tax_code_obj.unlink(cr, uid, wiz_tax_code_obj.search(cr, uid, [])) # Search for new / updated tax codes root_tax_code_id = wizard.chart_template_id.tax_code_root_id.id - children_tax_code_template = tax_code_templ_obj.search(cr, uid, [( - 'parent_id', 'child_of', [root_tax_code_id])], order='id', - context=context) + children_tax_code_template = \ + self._get_depth_first_tax_code_template_ids( + cr, uid, root_tax_code_id, context=context) for tax_code_template in tax_code_templ_obj.browse( cr, uid, children_tax_code_template, context=context): @@ -463,6 +493,9 @@ class wizard_update_charts_accounts(orm.TransientModel): notes = "" tax_code = tax_code_obj.browse( cr, uid, tax_code_id, context=context) + if tax_code.name != tax_code_template.name: + notes += _("The name field is different.\n") + modified = True if tax_code.code != tax_code_template.code: notes += _("The code field is different.\n") modified = True @@ -472,7 +505,18 @@ class wizard_update_charts_accounts(orm.TransientModel): if tax_code.sign != tax_code_template.sign: notes += _("The sign field is different.\n") modified = True - # TODO: We could check other account fields for changes... + if tax_code.notprintable != tax_code_template.notprintable: + notes += _("The notprintable field is different.\n") + modified = True + if tax_code.sequence != tax_code_template.sequence: + notes += _("The sequence field is different.\n") + modified = True + if tax_code.parent_id.id != self._map_tax_code_template( + cr, uid, wizard, + tax_code_template_mapping, + tax_code_template.parent_id, context=context): + notes += _("The parent field is different.\n") + modified = True if modified: # Tax code to update. updated_tax_codes += 1 @@ -483,6 +527,24 @@ class wizard_update_charts_accounts(orm.TransientModel): 'update_tax_code_id': tax_code_id, 'notes': notes, }, context) + # search for tax codes not in the template + # and propose them for deletion + tax_code_ids = tax_code_obj.\ + search(cr, uid, [('company_id', '=', wizard.company_id.id)], + context=context) + tax_code_ids = set(tax_code_ids) + template_tax_code_ids = set(tax_code_template_mapping.values()) + tax_code_ids_to_delete = tax_code_ids - template_tax_code_ids + for tax_code_id in tax_code_ids_to_delete: + updated_tax_codes += 1 + wiz_tax_code_obj.create(cr, uid, { + 'tax_code_id': False, + 'update_chart_wizard_id': wizard.id, + 'type': 'deleted', + 'update_tax_code_id': tax_code_id, + 'notes': "To deactivate: not in the template", + }, context) + return { 'new': new_tax_codes, 'updated': updated_tax_codes, @@ -499,6 +561,8 @@ class wizard_update_charts_accounts(orm.TransientModel): new_taxes = 0 updated_taxes = 0 tax_templ_mapping = {} + tax_code_template_mapping = {} + acc_templ_mapping = {} tax_obj = self.pool['account.tax'] tax_templ_obj = self.pool['account.tax.template'] wiz_taxes_obj = self.pool['wizard.update.charts.accounts.tax'] @@ -569,6 +633,46 @@ class wizard_update_charts_accounts(orm.TransientModel): if tax.type_tax_use != tax_templ.type_tax_use: notes += _("The type tax use field is different.\n") modified = True + # compare tax code fields + if tax.base_code_id.id != self._map_tax_code_template( + cr, uid, wizard, + tax_code_template_mapping, + tax_templ.base_code_id, context=context): + notes += _("The base_code_id field is different.\n") + modified = True + if tax.tax_code_id.id != self._map_tax_code_template( + cr, uid, wizard, + tax_code_template_mapping, + tax_templ.tax_code_id, context=context): + notes += _("The tax_code_id field is different.\n") + modified = True + if tax.ref_base_code_id.id != self._map_tax_code_template( + cr, uid, wizard, + tax_code_template_mapping, + tax_templ.ref_base_code_id, context=context): + notes += _("The ref_base_code_id field is different.\n") + modified = True + if tax.ref_tax_code_id.id != self._map_tax_code_template( + cr, uid, wizard, + tax_code_template_mapping, + tax_templ.ref_tax_code_id, context=context): + notes += _("The ref_tax_code_id field is different.\n") + modified = True + # compare tax account fields + if tax.account_paid_id.id != self._map_account_template( + cr, uid, wizard, + acc_templ_mapping, + tax_templ.account_paid_id, + context=context): + notes += _("The account_paid field is different.\n") + modified = True + if tax.account_collected_id.id != self._map_account_template( + cr, uid, wizard, + acc_templ_mapping, + tax_templ.account_collected_id, + context=context): + notes += _("The account_collected field is different.\n") + modified = True # TODO: We could check other tax fields for changes... if modified: # Tax code to update. @@ -582,6 +686,23 @@ class wizard_update_charts_accounts(orm.TransientModel): }, context) for delay_vals_wiz in delay_wiz_tax: wiz_taxes_obj.create(cr, uid, delay_vals_wiz, context) + # search for taxes not in the template + # and propose them for deletion + tax_ids = tax_obj.\ + search(cr, uid, [('company_id', '=', wizard.company_id.id)], + context=context) + tax_ids = set(tax_ids) + template_tax_ids = set(tax_templ_mapping.values()) + tax_ids_to_delete = tax_ids - template_tax_ids + for tax_id in tax_ids_to_delete: + updated_taxes += 1 + wiz_taxes_obj.create(cr, uid, { + 'tax_id': False, + 'update_chart_wizard_id': wizard.id, + 'type': 'deleted', + 'update_tax_id': tax_id, + 'notes': "To deactivate: not in the template", + }, context) return {'new': new_taxes, 'updated': updated_taxes, @@ -848,7 +969,10 @@ class wizard_update_charts_accounts(orm.TransientModel): new_tax_codes = 0 updated_tax_codes = 0 tax_code_template_mapping = {} + # process new/updated for wiz_tax_code in wizard.tax_code_ids: + if wiz_tax_code.type == 'deleted': + continue tax_code_template = wiz_tax_code.tax_code_id tax_code_name = ((root_tax_code_id == tax_code_template.id) and wizard.company_id.name or tax_code_template.name) @@ -867,6 +991,8 @@ class wizard_update_charts_accounts(orm.TransientModel): tax_code_template_mapping.get(p_id)), 'company_id': wizard.company_id.id, 'sign': tax_code_template.sign, + 'notprintable': tax_code_template.notprintable, + 'sequence': tax_code_template.sequence, } tax_code_id = None modified = False @@ -900,9 +1026,19 @@ class wizard_update_charts_accounts(orm.TransientModel): tax_code_name, tax_code_template.parent_id.name), True ) + # process deleted + tax_code_ids_to_delete = [wtc.update_tax_code_id.id + for wtc in wizard.tax_code_ids + if wtc.type == 'deleted'] + taxcodes.write(cr, uid, tax_code_ids_to_delete, + {'active': False}, + context=context) + log.add(_("Deactivated %d tax codes\n" % len(tax_code_ids_to_delete))) + deleted_tax_codes = len(tax_code_ids_to_delete) return { 'new': new_tax_codes, 'updated': updated_tax_codes, + 'deleted': deleted_tax_codes, 'mapping': tax_code_template_mapping } @@ -917,6 +1053,8 @@ class wizard_update_charts_accounts(orm.TransientModel): tax_template_mapping = {} taxes_pending_for_accounts = {} for wiz_tax in wizard.tax_ids: + if wiz_tax.type == 'deleted': + continue tax_template = wiz_tax.tax_id # Ensure the parent tax template is on the map. self._map_tax_template(cr, uid, wizard, tax_template_mapping, @@ -1058,9 +1196,19 @@ class wizard_update_charts_accounts(orm.TransientModel): ), True ) + # process deleted + tax_ids_to_delete = [wtc.update_tax_id.id + for wtc in wizard.tax_ids + if wtc.type == 'deleted'] + taxes.write(cr, uid, tax_ids_to_delete, + {'active': False}, + context=context) + log.add(_("Deactivated %d taxes\n" % len(tax_ids_to_delete))) + deleted_taxes = len(tax_ids_to_delete) return { 'new': new_taxes, 'updated': updated_taxes, + 'deleted': deleted_taxes, 'mapping': tax_template_mapping, 'pending': taxes_pending_for_accounts } @@ -1157,9 +1305,9 @@ class wizard_update_charts_accounts(orm.TransientModel): 'shortcut': account_template.shortcut, 'note': account_template.note, 'parent_id': ( - account_template.parent_id - and account_template_mapping.get(p_id) or - False + account_template_mapping.get(p_id) + if account_template.parent_id + else False ), 'tax_ids': [(6, 0, tax_ids)], 'company_id': wizard.company_id.id, @@ -1436,7 +1584,7 @@ class wizard_update_charts_accounts(orm.TransientModel): accounts_res = self._update_accounts(cr, uid, wizard, log, taxes_res['mapping'], context=context) - if wizard.update_tax and wizard.update_account: + if wizard.update_tax: self._update_taxes_pending_for_accounts(cr, uid, wizard, log, taxes_res['pending'], accounts_res['mapping'], @@ -1461,6 +1609,8 @@ class wizard_update_charts_accounts(orm.TransientModel): 'updated_taxes': taxes_res.get('updated', 0), 'updated_accounts': accounts_res.get('updated', 0), 'updated_fps': fps_res.get('updated', 0), + 'deleted_tax_codes': tax_codes_res.get('deleted', 0), + 'deleted_taxes': taxes_res.get('deleted', 0), 'log': log(), }, context=context) return _reopen(self, wizard.id, 'wizard.update.chart.accounts') @@ -1477,7 +1627,6 @@ class wizard_update_charts_accounts_tax_code(orm.TransientModel): 'tax_code_id': fields.many2one( 'account.tax.code.template', 'Tax code template', - required=True, ondelete='set null' ), 'update_chart_wizard_id': fields.many2one( @@ -1487,8 +1636,9 @@ class wizard_update_charts_accounts_tax_code(orm.TransientModel): ondelete='cascade' ), 'type': fields.selection([ - ('new', 'New template'), - ('updated', 'Updated template'), + ('new', 'New tax code'), + ('updated', 'Updated tax code'), + ('deleted', 'Tax code to deactivate'), ], 'Type'), 'update_tax_code_id': fields.many2one( 'account.tax.code', @@ -1513,7 +1663,6 @@ class wizard_update_charts_accounts_tax(orm.TransientModel): 'tax_id': fields.many2one( 'account.tax.template', 'Tax template', - required=True, ondelete='set null' ), 'update_chart_wizard_id': fields.many2one( @@ -1525,6 +1674,7 @@ class wizard_update_charts_accounts_tax(orm.TransientModel): 'type': fields.selection([ ('new', 'New template'), ('updated', 'Updated template'), + ('deleted', 'Tax to deactivate'), ], 'Type'), 'update_tax_id': fields.many2one( 'account.tax', diff --git a/account_chart_update/wizard/wizard_chart_update_view.xml b/account_chart_update/wizard/wizard_chart_update_view.xml index 91c7927d..f4f1586e 100644 --- a/account_chart_update/wizard/wizard_chart_update_view.xml +++ b/account_chart_update/wizard/wizard_chart_update_view.xml @@ -113,6 +113,10 @@ + + + +