diff --git a/account_chart_update/__init__.py b/account_chart_update/__init__.py new file mode 100644 index 00000000..36d5407d --- /dev/null +++ b/account_chart_update/__init__.py @@ -0,0 +1,25 @@ +# -*- coding: utf-8 -*- +############################################################################## +# +# Copyright (c) 2010 Zikzakmedia S.L. (http://www.zikzakmedia.com) +# Copyright (c) 2010 Pexego Sistemas Informáticos S.L. (http://www.pexego.es) +# @authors: Jordi Esteve (Zikzakmedia), Borja López Soilán (Pexego) +# +# 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 . +# +############################################################################## +""" +Account Chart Update Wizard +""" +import wizard diff --git a/account_chart_update/__openerp__.py b/account_chart_update/__openerp__.py new file mode 100644 index 00000000..79b16c08 --- /dev/null +++ b/account_chart_update/__openerp__.py @@ -0,0 +1,69 @@ +# -*- coding: utf-8 -*- +############################################################################## +# +# OpenERP, Open Source Management Solution +# Copyright (c) 2010 Zikzakmedia S.L. (http://www.zikzakmedia.com) +# Copyright (c) 2010 Pexego Sistemas Informáticos S.L. (http://www.pexego.es) +# Copyright (c) 2013 Joaquin Gutierrez (http://www.gutierrezweb.es) +# Pedro Manuel Baeza +# $Id$ +# 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 . +# +# 2013/09/08 - Joaquín Gutierrez: Adaptación a la versión +# +############################################################################## + +{ + 'name': "Detect changes and update the Account Chart from a template", + 'version': "1.0", + 'author': "Zikzakmedia SL", + 'website': "www.zikzakmedia.com", + 'depends': ["account"], + 'category': "Generic Modules/Accounting", + 'contributors': ['Joaquin Gutierrez', 'Pedro M. Baeza'], + 'description': """ +Adds a wizard to update a company account chart from a chart template. +====================================================================== + +This is a pretty useful tool to update OpenERP instalations after tax reforms +on the oficial charts of accounts, or to apply fixes performed on the chart +template. + +The wizard: +- Allows the user to compare a chart and a template showing differences + on accounts, taxes, tax codes and fiscal positions. +- It may create the new account, taxes, tax codes and fiscal positions detected + on the template. +- It can also update (overwrite) the accounts, taxes, tax codes and fiscal + positions that got modified on the template. + +The wizard lets the user select what kind of objects must be checked/updated, +and whether old records must be checked for changes and updated. +It will display all the accounts to be created / updated with some information +about the detected differences, and allow the user to exclude records +individually. +Any problem found while updating will be shown on the last step. +""", + 'license': "AGPL-3", + "depends": [ + "account", + "base", + ], + "demo" : [], + "data": [ + 'wizard/wizard_chart_update_view.xml', + ], + "active": False, + "installable": True +} diff --git a/account_chart_update/wizard/__init__.py b/account_chart_update/wizard/__init__.py new file mode 100644 index 00000000..8d0cfc9e --- /dev/null +++ b/account_chart_update/wizard/__init__.py @@ -0,0 +1,25 @@ +# -*- coding: utf-8 -*- +############################################################################## +# +# Copyright (c) 2010 Zikzakmedia S.L. (http://www.zikzakmedia.com) +# Copyright (c) 2010 Pexego Sistemas Informáticos S.L. (http://www.pexego.es) +# @authors: Jordi Esteve (Zikzakmedia), Borja López Soilán (Pexego) +# +# 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 . +# +############################################################################## +""" +Account Chart Update Wizard +""" +import wizard_chart_update diff --git a/account_chart_update/wizard/wizard_chart_update.py b/account_chart_update/wizard/wizard_chart_update.py new file mode 100644 index 00000000..e4801373 --- /dev/null +++ b/account_chart_update/wizard/wizard_chart_update.py @@ -0,0 +1,1351 @@ +# -*- coding: utf-8 -*- +############################################################################## +# +# Copyright (c) 2010 Zikzakmedia S.L. (http://www.zikzakmedia.com) +# Copyright (c) 2010 Pexego Sistemas Informáticos S.L. (http://www.pexego.es) +# @authors: Jordi Esteve (Zikzakmedia), Borja López Soilán (Pexego) +# +# 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 . +# +############################################################################## +""" +Account Chart Update Wizard +""" + +from openerp.osv import fields, orm +from openerp.tools.translate import _ +import logging + + +def _reopen(self, res_id, model): + return { + 'type': 'ir.actions.act_window', + 'view_mode': 'form', + 'view_type': 'form', + 'res_id': res_id, + 'res_model': self._name, + 'target': 'new', + # save original model in context, because selecting the list of available + # templates requires a model in context + 'context': { + 'default_model': model, + }, + } + + +class WizardLog: + """ + ******************************************************************* + Small helper class to store the messages and errors on the wizard. + ******************************************************************* + """ + def __init__(self): + self.messages = [] + self.errors = [] + + """ + Adds a message to the log. + """ + def add(self, message, is_error=False): + logger = logging.getLogger("account_chart_update") + if is_error: + logger.warning(u"Log line: %s" % message) + self.errors.append(message) + else: + logger.debug(u"Log line: %s" % message) + self.messages.append(message) + + """ + Returns whether errors where logged. + """ + def has_errors(self): + return self.errors + + def __call__(self): + return "".join(self.messages) + + def __str__(self): + return "".join(self.messages) + + def get_errors_str(self): + return "".join(self.errors) + + +class wizard_update_charts_accounts(orm.TransientModel): + _name = 'wizard.update.charts.accounts' + + """ + Gets the available languages for the selection. + """ + def _get_lang_selection_options(self, cr, uid, context={}): + obj = self.pool.get('res.lang') + ids = obj.search(cr, uid, [], context=context) + res = obj.read(cr, uid, ids, ['code', 'name'], context) + return [(r['code'], r['name']) for r in res] + [('', '')] + + _columns = { + 'state': fields.selection([ + ('init', 'Step 1'), + ('ready', 'Step 2'), + ('done', 'Wizard Complete') + ], 'Status', readonly=True), + 'company_id': fields.many2one('res.company', 'Company', required=True, ondelete='set null'), + 'chart_template_id': fields.many2one('account.chart.template', 'Chart Template', required=True, ondelete='set null'), + 'code_digits': fields.integer('# of Digits', required=True, help="No. of Digits to use for account code. Make sure it is the same number as existing accounts."), + 'lang': fields.selection(_get_lang_selection_options, 'Language', size=5, help="For records searched by name (taxes, tax codes, fiscal positions), the template name will be matched against the record name on this language."), + 'update_tax_code': fields.boolean('Update tax codes', help="Existing tax codes are updated. Tax codes are searched by name."), + 'update_tax': fields.boolean('Update taxes', help="Existing taxes are updated. Taxes are searched by name."), + 'update_account': fields.boolean('Update accounts', help="Existing accounts are updated. Accounts are searched by code."), + 'update_fiscal_position': fields.boolean('Update fiscal positions', help="Existing fiscal positions are updated. Fiscal positions are searched by name."), + 'update_children_accounts_parent': fields.boolean("Update children accounts parent", + help="Update the parent of accounts that seem (based on the code) to be children of the newly created ones. If you had an account 430 with a child 4300000, and a 4300 account is created, the 4300000 parent will be set to 4300."), + 'continue_on_errors': fields.boolean("Continue on errors", help="If set, the wizard will continue to the next step even if there are minor errors (for example the parent account of a new account couldn't be set)."), + 'tax_code_ids': fields.one2many('wizard.update.charts.accounts.tax.code', 'update_chart_wizard_id', 'Tax codes', ondelete='cascade'), + 'tax_ids': fields.one2many('wizard.update.charts.accounts.tax', 'update_chart_wizard_id', 'Taxes', ondelete='cascade'), + 'account_ids': fields.one2many('wizard.update.charts.accounts.account', 'update_chart_wizard_id', 'Accounts', ondelete='cascade'), + 'fiscal_position_ids': fields.one2many('wizard.update.charts.accounts.fiscal.position', 'update_chart_wizard_id', 'Fiscal positions', ondelete='cascade'), + 'new_tax_codes': fields.integer('New tax codes', readonly=True), + 'new_taxes': fields.integer('New taxes', readonly=True), + 'new_accounts': fields.integer('New accounts', readonly=True), + 'new_fps': fields.integer('New fiscal positions', readonly=True), + 'updated_tax_codes': fields.integer('Updated tax codes', readonly=True), + 'updated_taxes': fields.integer('Updated taxes', readonly=True), + 'updated_accounts': fields.integer('Updated accounts', readonly=True), + 'updated_fps': fields.integer('Updated fiscal positions', readonly=True), + 'log': fields.text('Messages and Errors', readonly=True) + } + """ + Redefine the search to search by company name. + """ + def name_search(self, cr, user, name, + args=None, operator='ilike', context=None, limit=80): + if not name: + name = '%' + if not args: + args = [] + if not context: + context = {} + args = args[:] + ids = [] + ids = self.search( + cr, user, [('company_id', operator, name)] + args, limit=limit) + return self.name_get(cr, user, ids, context=context) + + """ + Use the company name and template as name. + """ + def name_get(self, cr, uid, ids, context=None): + if context is None: + context = {} + if not len(ids): + return [] + records = self.browse(cr, uid, ids, context) + res = [] + for record in records: + res.append((record.id, record.company_id.name + + ' - ' + record.chart_template_id.name)) + return res + + """ + Returns the default chart template. + """ + def _get_chart(self, cr, uid, context=None): + if context is None: + context = {} + ids = self.pool.get( + 'account.chart.template').search(cr, uid, [], context=context) + if ids: + return ids[0] + return False + + """ + Returns the default code size for the accounts. + To figure out the number of digits of the accounts it look at the + code size of the default receivable account of the company + (or user's company if any company is given). + """ + def _get_code_digits(self, cr, uid, context=None, company_id=None): + if context is None: + context = {} + property_obj = self.pool.get('ir.property') + account_obj = self.pool.get('account.account') + if not company_id: + user = self.pool.get('res.users').browse(cr, uid, uid, context) + company_id = user.company_id.id + property_ids = property_obj.search( + cr, + uid, + [('name', '=', 'property_account_receivable'), + ('company_id', '=', company_id), + ('res_id', '=', False), + ('value_reference', '!=', False) + ]) + if not property_ids: + # Try to get a generic (no-company) property + property_ids = property_obj.search( + cr, + uid, + [('name', '=', 'property_account_receivable'), + ('res_id', '=', False), + ('value_reference', '!=', False) + ]) + number_digits = 6 + if property_ids: + prop = property_obj.browse( + cr, uid, property_ids[0], context=context) + account_id = prop.value_reference.id + + if account_id: + code = account_obj.read( + cr, uid, account_id, ['code'], context)['code'] + number_digits = len(code) + return number_digits + + _defaults = { + 'state': 'init', + 'company_id': lambda self, cr, uid, context: self.pool.get('res.users').browse(cr, uid, [uid], context)[0].company_id.id, + 'chart_template_id': _get_chart, + 'update_tax_code': True, + 'update_tax': True, + 'update_account': True, + 'update_fiscal_position': True, + 'update_children_accounts_parent': True, + 'continue_on_errors': False, + 'lang': lambda self, cr, uid, context: context and context.get('lang') or None, + } + + """ + Update the code size when the company changes + """ + def onchange_company_id(self, cr, uid, ids, company_id, context=None): + res = { + 'value': { + 'code_digits': self._get_code_digits( + cr, + uid, + context=context, + company_id=company_id), + } + } + return res + + """ + Initial action that sets the initial state. + """ + def action_init(self, cr, uid, ids, context=None): + if context is None: + context = {} + wizard = self.browse(cr, uid, ids[0], context=context) + self.write(cr, uid, ids, {'state': 'init'}, context) + view_wizard = self.pool.get('ir.model.data').get_object_reference(cr, uid, 'account_chart_update', 'view_update_multi_chart') + view_wizard_id = view_wizard and view_wizard[1] or False, + res = { + 'type': 'ir.actions.act_window', + 'name': _("Update Chart of Accounts from a Chart Template "), + 'res_model': 'wizard.update.charts.accounts', + 'view_type': 'form', + 'view_mode': 'form', + 'res_id': wizard.id, + 'view_id': view_wizard_id, + 'context': context, + 'target': 'new', + } + return res + + return True + + ########################################################################## + # Helper methods + ########################################################################## + """ + Adds a tax template -> tax id to the mapping. + """ + def _map_tax_template(self, cr, uid, wizard, tax_template_mapping, tax_template, context=None): + if tax_template and not tax_template_mapping.get(tax_template.id): + taxes = self.pool.get('account.tax') + tax_ids = taxes.search(cr, uid, [ + ('name', '=', tax_template.name), + ('company_id', '=', wizard.company_id.id) + ], context=context) + if tax_ids: + tax_template_mapping[tax_template.id] = tax_ids[0] + + """ + Adds a tax code template -> tax code id to the mapping. + """ + def _map_tax_code_template(self, cr, uid, wizard, tax_code_template_mapping, tax_code_template, context=None): + if tax_code_template and not tax_code_template_mapping.get(tax_code_template.id): + tax_codes = self.pool.get('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_codes.search(cr, uid, [ + ('name', '=', tax_code_name), + ('company_id', '=', wizard.company_id.id) + ]) + if tax_code_ids: + tax_code_template_mapping[ + tax_code_template.id] = tax_code_ids[0] + + """ + Adds an account template -> account id to the mapping + """ + def _map_account_template(self, cr, uid, wizard, account_template_mapping, account_template, context=None): + if account_template and not account_template_mapping.get(account_template.id): + accounts = self.pool.get('account.account') + code = account_template.code or '' + if account_template.type != 'view': + if len(code) > 0 and len(code) <= wizard.code_digits: + code = '%s%s' % ( + code, '0' * (wizard.code_digits - len(code))) + account_ids = accounts.search(cr, uid, [ + ('code', '=', code), + ('company_id', '=', wizard.company_id.id) + ], context=context) + if account_ids: + account_template_mapping[account_template.id] = account_ids[0] + """ + Adds a fiscal position template -> fiscal position id to the mapping. + """ + def _map_fp_template(self, cr, uid, wizard, fp_template_mapping, fp_template, context=None): + if fp_template and not fp_template_mapping.get(fp_template.id): + fiscalpositions = self.pool.get('account.fiscal.position') + fp_ids = fiscalpositions.search(cr, uid, [ + ('name', '=', fp_template.name), + ('company_id', '=', wizard.company_id.id) + ], context=context) + if fp_ids: + fp_template_mapping[fp_template.id] = fp_ids[0] + + """ + Search for, and load, tax code templates to create/update. + """ + def _find_tax_codes(self, cr, uid, wizard, context=None): + new_tax_codes = 0 + updated_tax_codes = 0 + tax_code_template_mapping = {} + + taxes_codes_templates = self.pool.get('account.tax.code.template') + taxcodes = self.pool.get('account.tax.code') + wiz_taxcodes = self.pool.get('wizard.update.charts.accounts.tax.code') + + # Remove previous tax codes + wiz_taxcodes.unlink( + cr, uid, wiz_taxcodes.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 = taxes_codes_templates.search(cr, uid, [( + 'parent_id', 'child_of', [root_tax_code_id])], order='id') + for tax_code_template in taxes_codes_templates.browse(cr, uid, children_tax_code_template): + # Ensure the tax code template is on the map (search for the mapped + # tax code id). + self._map_tax_code_template(cr, uid, wizard, tax_code_template_mapping, tax_code_template, context) + + tax_code_id = tax_code_template_mapping.get(tax_code_template.id) + if not tax_code_id: + new_tax_codes += 1 + wiz_taxcodes.create(cr, uid, { + 'tax_code_id': tax_code_template.id, + 'update_chart_wizard_id': wizard.id, + 'type': 'new', + }, context) + elif wizard.update_tax_code: + # + # Check the tax code for changes. + # + modified = False + notes = "" + tax_code = taxcodes.browse( + cr, uid, tax_code_id, context=context) + + if tax_code.code != tax_code_template.code: + notes += _("The code field is different.\n") + modified = True + if tax_code.info != tax_code_template.info: + notes += _("The info field is different.\n") + modified = True + 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 modified: + # + # Tax code to update. + # + updated_tax_codes += 1 + wiz_taxcodes.create(cr, uid, { + 'tax_code_id': tax_code_template.id, + 'update_chart_wizard_id': wizard.id, + 'type': 'updated', + 'update_tax_code_id': tax_code_id, + 'notes': notes, + }, context) + + return { + 'new': new_tax_codes, + 'updated': updated_tax_codes, + 'mapping': tax_code_template_mapping + } + """ + Search for, and load, tax templates to create/update. + """ + def _find_taxes(self, cr, uid, wizard, context=None): + new_taxes = 0 + updated_taxes = 0 + tax_template_mapping = {} + + taxes = self.pool.get('account.tax') + wiz_taxes = self.pool.get('wizard.update.charts.accounts.tax') + + delay_wiz_tax = [] + # Remove previous taxes + wiz_taxes.unlink(cr, uid, wiz_taxes.search(cr, uid, [])) + + # + # Search for new / updated taxes + # + for tax_template in wizard.chart_template_id.tax_template_ids: + # Ensure the tax template is on the map (search for the mapped tax + # id). + self._map_tax_template( + cr, uid, wizard, tax_template_mapping, tax_template, context) + + tax_id = tax_template_mapping.get(tax_template.id) + if not tax_id: + new_taxes += 1 + vals_wiz = { + 'tax_id': tax_template.id, + 'update_chart_wizard_id': wizard.id, + 'type': 'new', + } + if not tax_template.parent_id: + wiz_taxes.create(cr, uid, vals_wiz, context) + else: + delay_wiz_tax.append(vals_wiz) + elif wizard.update_tax: + # + # Check the tax for changes. + # + modified = False + notes = "" + tax = taxes.browse(cr, uid, tax_id, context=context) + if tax.sequence != tax_template.sequence: + notes += _("The sequence field is different.\n") + modified = True + if tax.amount != tax_template.amount: + notes += _("The amount field is different.\n") + modified = True + if tax.type != tax_template.type: + notes += _("The type field is different.\n") + modified = True + if tax.applicable_type != tax_template.applicable_type: + notes += _("The applicable type field is different.\n") + modified = True + if tax.domain != tax_template.domain: + notes += _("The domain field is different.\n") + modified = True + if tax.child_depend != tax_template.child_depend: + notes += _("The child depend field is different.\n") + modified = True + if tax.python_compute != tax_template.python_compute: + notes += _("The python compute field is different.\n") + modified = True + # if tax.tax_group != tax_template.tax_group: + # notes += _("The tax group field is different.\n") + # modified = True + if tax.base_sign != tax_template.base_sign: + notes += _("The base sign field is different.\n") + modified = True + if tax.tax_sign != tax_template.tax_sign: + notes += _("The tax sign field is different.\n") + modified = True + if tax.include_base_amount != tax_template.include_base_amount: + notes += _("The include base amount field is different.\n") + modified = True + if tax.type_tax_use != tax_template.type_tax_use: + notes += _("The type tax use field is different.\n") + modified = True + # TODO: We could check other tax fields for changes... + + if modified: + # + # Tax code to update. + # + updated_taxes += 1 + wiz_taxes.create(cr, uid, { + 'tax_id': tax_template.id, + 'update_chart_wizard_id': wizard.id, + 'type': 'updated', + 'update_tax_id': tax_id, + 'notes': notes, + }, context) + + for delay_vals_wiz in delay_wiz_tax: + wiz_taxes.create(cr, uid, delay_vals_wiz, context) + + return {'new': new_taxes, 'updated': updated_taxes, 'mapping': tax_template_mapping} + + """ + Search for, and load, account templates to create/update. + """ + def _find_accounts(self, cr, uid, wizard, context=None): + new_accounts = 0 + updated_accounts = 0 + account_template_mapping = {} + + accounts = self.pool.get('account.account') + accounts_template = self.pool.get('account.account.template') + wiz_accounts = self.pool.get( + 'wizard.update.charts.accounts.account') + + # Remove previous accounts + wiz_accounts.unlink( + cr, uid, wiz_accounts.search(cr, uid, [])) + + # + # Search for new / updated accounts + # + root_account_id = wizard.chart_template_id.account_root_id.id + children_acc_template = accounts_template.search(cr, uid, [( + 'parent_id', 'child_of', [root_account_id])], context=context) + children_acc_template.sort() + for account_template in accounts_template.browse(cr, uid, children_acc_template, context=context): + # Ensure the account template is on the map (search for the mapped + # account id). + self._map_account_template(cr, uid, wizard, account_template_mapping, account_template, context) + + account_id = account_template_mapping.get(account_template.id) + if not account_id: + new_accounts += 1 + wiz_accounts.create(cr, uid, { + 'account_id': account_template.id, + 'update_chart_wizard_id': wizard.id, + 'type': 'new', + }, context) + elif wizard.update_account: + # + # Check the account for changes. + # + modified = False + notes = "" + account = accounts.browse( + cr, uid, account_id, context=context) + + if account.name != account_template.name and account.name != wizard.company_id.name: + notes += _("The name is different.\n") + modified = True + if account.type != account_template.type: + notes += _("The type is different.\n") + modified = True + if account.user_type != account_template.user_type: + notes += _("The user type is different.\n") + modified = True + if account.reconcile != account_template.reconcile: + notes += _("The reconcile is different.\n") + modified = True + + # TODO: We could check other account fields for changes... + + if modified: + # + # Account to update. + # + updated_accounts += 1 + wiz_accounts.create(cr, uid, { + 'account_id': account_template.id, + 'update_chart_wizard_id': wizard.id, + 'type': 'updated', + 'update_account_id': account_id, + 'notes': notes, + }, context) + + return {'new': new_accounts, 'updated': updated_accounts, 'mapping': account_template_mapping} + + """ + Search for, and load, fiscal position templates to create/update. + """ + def _find_fiscal_positions(self, cr, uid, wizard, context=None): + new_fps = 0 + updated_fps = 0 + fp_template_mapping = {} + + fiscalpostitions_template = self.pool.get('account.fiscal.position.template') + fiscalpositions = self.pool.get('account.fiscal.position') + wiz_fiscalpositions = self.pool.get( + 'wizard.update.charts.accounts.fiscal.position') + + # Remove previous fiscal positions + wiz_fiscalpositions.unlink(cr, uid, wiz_fiscalpositions.search(cr, uid, [])) + + # + # Search for new / updated fiscal positions + # + fp_template_ids = fiscalpostitions_template.search(cr, uid, [('chart_template_id', '=', wizard.chart_template_id.id)], context=context) + for fp_template in fiscalpostitions_template.browse(cr, uid, fp_template_ids, context=context): + # Ensure the fiscal position template is on the map (search for the + # mapped fiscal position id). + self._map_fp_template( + cr, uid, wizard, fp_template_mapping, fp_template, context) + + fp_id = fp_template_mapping.get(fp_template.id) + if not fp_id: + # + # New fiscal position template. + # + new_fps += 1 + wiz_fiscalpositions.create(cr, uid, { + 'fiscal_position_id': fp_template.id, + 'update_chart_wizard_id': wizard.id, + 'type': 'new', + }, context) + elif wizard.update_fiscal_position: + # + # Check the fiscal position for changes. + # + modified = False + notes = "" + fp = fiscalpositions.browse(cr, uid, fp_id, context=context) + + # + # Check fiscal position taxes for changes. + # + if fp_template.tax_ids and fp.tax_ids: + for fp_tax_template in fp_template.tax_ids: + found = False + for fp_tax in fp.tax_ids: + if fp_tax.tax_src_id.name == fp_tax_template.tax_src_id.name: + if fp_tax_template.tax_dest_id and fp_tax.tax_dest_id: + if fp_tax.tax_dest_id.name == fp_tax_template.tax_dest_id.name: + found = True + break + elif not fp_tax_template.tax_dest_id and not fp_tax.tax_dest_id: + found = True + break + if not found: + if fp_tax_template.tax_dest_id: + notes += _("Tax mapping not found on the fiscal position instance: %s -> %s.\n") % (fp_tax_template.tax_src_id.name, fp_tax_template.tax_dest_id.name) + else: + notes += _("Tax mapping not found on the fiscal position instance: %s -> None.\n") % fp_tax_template.tax_src_id.name + modified = True + elif fp_template.tax_ids and not fp.tax_ids: + notes += _("The template has taxes the fiscal position instance does not.\n") + modified = True + + # + # Check fiscal position accounts for changes. + # + if fp_template.account_ids and fp.account_ids: + for fp_account_template in fp_template.account_ids: + found = False + for fp_account in fp.account_ids: + if fp_account.account_src_id.name == fp_account_template.account_src_id.name: + if fp_account.account_dest_id.name == fp_account_template.account_dest_id.name: + found = True + break + if not found: + notes += _("Account mapping not found on the fiscal position instance: %s -> %s.\n") % (fp_account_template.account_src_id.name, fp_account_template.account_dest_id.name) + modified = True + elif fp_template.account_ids and not fp.account_ids: + notes += _("The template has accounts the fiscal position instance does not.\n") + modified = True + + if modified: + # + # Fiscal position template to update. + # + updated_fps += 1 + wiz_fiscalpositions.create(cr, uid, { + 'fiscal_position_id': fp_template.id, + 'update_chart_wizard_id': wizard.id, + 'type': 'updated', + 'update_fiscal_position_id': fp_id, + 'notes': notes, + }, context) + + return {'new': new_fps, 'updated': updated_fps, 'mapping': fp_template_mapping} + + """ + Searchs for records to update/create and shows them + """ + def action_find_records(self, cr, uid, ids, context=None): + if context is None: + context = {} + wizard = self.browse(cr, uid, ids[0], context=context) + + if wizard.lang: + context['lang'] = wizard.lang + elif context.get('lang'): + del context['lang'] + + # + # Search for, and load, the records to create/update. + # + tax_codes_res = self._find_tax_codes(cr, uid, wizard, context=context) + taxes_res = self._find_taxes(cr, uid, wizard, context=context) + accounts_res = self._find_accounts(cr, uid, wizard, context=context) + fps_res = self._find_fiscal_positions(cr, uid, wizard, context=context) + + # + # Write the results, and go to the next step. + # + self.write(cr, uid, [wizard.id], { + 'state': 'ready', + 'new_tax_codes': tax_codes_res.get('new', 0), + 'new_taxes': taxes_res.get('new', 0), + 'new_accounts': accounts_res.get('new', 0), + 'new_fps': fps_res.get('new', 0), + 'updated_tax_codes': tax_codes_res.get('updated', 0), + 'updated_taxes': taxes_res.get('updated', 0), + 'updated_accounts': accounts_res.get('updated', 0), + 'updated_fps': fps_res.get('updated', 0), + }, context) + + return _reopen(self, wizard.id, 'wizard.update.chart.accounts') + """ + Search for, and load, tax code templates to create/update. + """ + def _update_tax_codes(self, cr, uid, wizard, log, context=None): + taxcodes = self.pool.get('account.tax.code') + root_tax_code_id = wizard.chart_template_id.tax_code_root_id.id + new_tax_codes = 0 + updated_tax_codes = 0 + tax_code_template_mapping = {} + + for wiz_tax_code in wizard.tax_code_ids: + 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 + + # Ensure the parent tax code template is on the map. + self._map_tax_code_template(cr, uid, wizard, tax_code_template_mapping, tax_code_template.parent_id, context) + + # + # Values + # + vals = { + 'name': tax_code_name, + 'code': tax_code_template.code, + 'info': tax_code_template.info, + 'parent_id': tax_code_template.parent_id and tax_code_template_mapping.get(tax_code_template.parent_id.id), + 'company_id': wizard.company_id.id, + 'sign': tax_code_template.sign, + } + + tax_code_id = None + modified = False + + if wiz_tax_code.type == 'new': + # + # Create the tax code + # + tax_code_id = taxcodes.create(cr, uid, vals) + log.add(_("Created tax code %s.\n") % tax_code_name) + new_tax_codes += 1 + modified = True + elif wizard.update_tax_code and wiz_tax_code.update_tax_code_id: + # + # Update the tax code + # + tax_code_id = wiz_tax_code.update_tax_code_id.id + taxcodes.write(cr, uid, [tax_code_id], vals) + log.add(_("Updated tax code %s.\n") % tax_code_name) + updated_tax_codes += 1 + modified = True + else: + tax_code_id = wiz_tax_code.update_tax_code_id and wiz_tax_code.update_tax_code_id.id + modified = False + + # Store the tax codes on the map + tax_code_template_mapping[tax_code_template.id] = tax_code_id + + if modified: + # + # Detect errors + # + if tax_code_template.parent_id and not tax_code_template_mapping.get(tax_code_template.parent_id.id): + log.add(_("Tax code %s: The parent tax code %s can not be set.\n") % (tax_code_name, tax_code_template.parent_id.name), True) + + return { + 'new': new_tax_codes, + 'updated': updated_tax_codes, + 'mapping': tax_code_template_mapping + } + + """ + Search for, and load, tax templates to create/update. + """ + def _update_taxes(self, cr, uid, wizard, log, tax_code_template_mapping, context=None): + taxes = self.pool.get('account.tax') + + new_taxes = 0 + updated_taxes = 0 + tax_template_mapping = {} + taxes_pending_for_accounts = {} + + for wiz_tax in wizard.tax_ids: + 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, + tax_template.parent_id, context) + + # + # Ensure the referenced tax codes are on the map. + # + tax_code_templates_to_find = [ + tax_template.base_code_id, + tax_template.tax_code_id, + tax_template.ref_base_code_id, + tax_template.ref_tax_code_id + ] + for tax_code_template in [tmpl for tmpl in tax_code_templates_to_find if tmpl]: + self._map_tax_code_template(cr, uid, wizard, tax_code_template_mapping, tax_code_template) + + # + # Values + # + vals_tax = { + 'name': tax_template.name, + 'sequence': tax_template.sequence, + 'amount': tax_template.amount, + 'type': tax_template.type, + 'applicable_type': tax_template.applicable_type, + 'domain': tax_template.domain, + 'parent_id': tax_template.parent_id and tax_template_mapping.get(tax_template.parent_id.id), + 'child_depend': tax_template.child_depend, + 'python_compute': tax_template.python_compute, + 'python_compute_inv': tax_template.python_compute_inv, + 'python_applicable': tax_template.python_applicable, + #'tax_group': tax_template.tax_group, + 'base_code_id': tax_template.base_code_id and tax_code_template_mapping.get(tax_template.base_code_id.id), + 'tax_code_id': tax_template.tax_code_id and tax_code_template_mapping.get(tax_template.tax_code_id.id), + 'base_sign': tax_template.base_sign, + 'tax_sign': tax_template.tax_sign, + 'ref_base_code_id': tax_template.ref_base_code_id and tax_code_template_mapping.get(tax_template.ref_base_code_id.id), + 'ref_tax_code_id': tax_template.ref_tax_code_id and tax_code_template_mapping.get(tax_template.ref_tax_code_id.id), + 'ref_base_sign': tax_template.ref_base_sign, + 'ref_tax_sign': tax_template.ref_tax_sign, + 'include_base_amount': tax_template.include_base_amount, + 'description': tax_template.description, + 'company_id': wizard.company_id.id, + 'type_tax_use': tax_template.type_tax_use + } + + tax_id = None + modified = False + + if wiz_tax.type == 'new': + # + # Create a new tax. + # + tax_id = taxes.create(cr, uid, vals_tax) + log.add(_("Created tax %s.\n") % tax_template.name) + new_taxes += 1 + modified = True + elif wizard.update_tax and wiz_tax.update_tax_id: + # + # Update a tax. + # + tax_id = wiz_tax.update_tax_id.id + taxes.write(cr, uid, [tax_id], vals_tax) + log.add(_("Updated tax %s.\n") % tax_template.name) + updated_taxes += 1 + modified = True + else: + tax_id = wiz_tax.update_tax_id and wiz_tax.update_tax_id.id + + # Update the tax template map + tax_template_mapping[tax_template.id] = tax_id + + if modified: + # + # Add to the dict of taxes waiting for accounts. + # + taxes_pending_for_accounts[tax_id] = { + 'account_collected_id': tax_template.account_collected_id and tax_template.account_collected_id.id or False, + 'account_paid_id': tax_template.account_paid_id and tax_template.account_paid_id.id or False, + } + + # + # Detect errors + # + if tax_template.parent_id and not tax_template_mapping.get(tax_template.parent_id.id): + log.add(_("Tax %s: The parent tax %s can not be set.\n") % (tax_template.name, tax_template.parent_id.name), True) + if tax_template.base_code_id and not tax_code_template_mapping.get(tax_template.base_code_id.id): + log.add(_("Tax %s: The tax code for the base %s can not be set.\n") % (tax_template.name, tax_template.base_code_id.name), True) + if tax_template.tax_code_id and not tax_code_template_mapping.get(tax_template.tax_code_id.id): + log.add(_("Tax %s: The tax code for the tax %s can not be set.\n") % (tax_template.name, tax_template.tax_code_id.name), True) + if tax_template.ref_base_code_id and not tax_code_template_mapping.get(tax_template.ref_base_code_id.id): + log.add(_("Tax %s: The tax code for the base refund %s can not be set.\n") % (tax_template.name, tax_template.ref_base_code_id.name), True) + if tax_template.ref_tax_code_id and not tax_code_template_mapping.get(tax_template.ref_tax_code_id.id): + log.add(_("Tax %s: The tax code for the tax refund %s can not be set.\n") % (tax_template.name, tax_template.ref_tax_code_id.name), True) + + return { + 'new': new_taxes, + 'updated': updated_taxes, + 'mapping': tax_template_mapping, + 'pending': taxes_pending_for_accounts + } + + """ + Updates the parent_id of accounts that seem to be children of the + given account (accounts that start with the same code and are brothers + of the first account). + """ + def _update_children_accounts_parent(self, cr, uid, wizard, log, parent_account_id, context=None): + account_account = self.pool.get('account.account') + parent_account = account_account.browse( + cr, uid, parent_account_id, context=context) + + if not parent_account.parent_id or not parent_account.code: + return False + + children_ids = account_account.search(cr, uid, [ + ('company_id', '=', + parent_account.company_id and parent_account.company_id.id), + ('parent_id', '=', parent_account.parent_id.id), + ('code', '=like', "%s%%" % parent_account.code), + ('id', '!=', parent_account.id), + ], context=context) + + if children_ids: + try: + account_account.write(cr, uid, children_ids, {'parent_id': + parent_account.id}, context=context) + except orm.except_orm, ex: + log.add(_("Exception setting the parent of account %s children: %s - %s.\n") % (parent_account.code, ex.name, ex.value), True) + + return True + + """ + Search for, and load, account templates to create/update. + """ + def _update_accounts(self, cr, uid, wizard, log, tax_template_mapping, context=None): + accounts = self.pool.get('account.account') + root_account_id = wizard.chart_template_id.account_root_id.id + + # Disable the parent_store computing on account_account during the batch + # processing, we will force _parent_store_compute afterwards. + self.pool._init = True + new_accounts = 0 + updated_accounts = 0 + account_template_mapping = {} + + for wiz_account in wizard.account_ids: + account_template = wiz_account.account_id + + # Ensure the parent account template is on the map. + self._map_account_template(cr, uid, wizard, account_template_mapping, account_template.parent_id, context) + + # + # Ensure the related tax templates are on the map. + # + for tax_template in account_template.tax_ids: + self._map_tax_template(cr, uid, wizard, tax_template_mapping, + tax_template, context) + + # Get the tax ids + tax_ids = [tax_template_mapping[tax_template.id] for tax_template in account_template.tax_ids if tax_template_mapping[tax_template.id]] + + # + # Calculate the account code (we need to add zeros to non-view + # account codes) + # + code = account_template.code or '' + if account_template.type != 'view': + if len(code) > 0 and len(code) <= wizard.code_digits: + code = '%s%s' % ( + code, '0' * (wizard.code_digits - len(code))) + + # + # Values + # + vals = { + 'name': (root_account_id == account_template.id) and wizard.company_id.name or account_template.name, + #'sign': account_template.sign, + 'currency_id': account_template.currency_id and account_template.currency_id.id or False, + 'code': code, + 'type': account_template.type, + 'user_type': account_template.user_type and account_template.user_type.id or False, + 'reconcile': account_template.reconcile, + 'shortcut': account_template.shortcut, + 'note': account_template.note, + 'parent_id': account_template.parent_id and account_template_mapping.get(account_template.parent_id.id) or False, + 'tax_ids': [(6, 0, tax_ids)], + 'company_id': wizard.company_id.id, + } + + account_id = None + modified = False + + if wiz_account.type == 'new': + # + # Create the account + # + try: + account_id = accounts.create(cr, uid, vals) + log.add(_("Created account %s.\n") % code) + new_accounts += 1 + modified = True + except orm.except_orm, ex: + log.add(_("Exception creating account %s: %s - %s.\n") + % (code, ex.name, ex.value), True) + elif wizard.update_account and wiz_account.update_account_id: + # + # Update the account + # + account_id = wiz_account.update_account_id.id + try: + accounts.write(cr, uid, [account_id], vals) + log.add(_("Updated account %s.\n") % code) + updated_accounts += 1 + modified = True + except orm.except_orm, ex: + log.add(_("Exception writing account %s: %s - %s.\n") + % (code, ex.name, ex.value), True) + else: + account_id = wiz_account.update_account_id and wiz_account.update_account_id.id + + # Store the account on the map + account_template_mapping[account_template.id] = account_id + + if modified: + # + # Detect errors + # + if account_template.parent_id and not account_template_mapping.get(account_template.parent_id.id): + log.add(_("Account %s: The parent account %s can not be set.\n") % (code, account_template.parent_id.code), True) + + # + # Set this account as the parent of the accounts that seem to + # be its children (brothers starting with the same code). + # + if wizard.update_children_accounts_parent: + self._update_children_accounts_parent( + cr, uid, wizard, log, account_id, context=context) + + # + # Reenable the parent_store computing on account_account + # and force the recomputation. + # + self.pool._init = False + self.pool.get('account.account')._parent_store_compute(cr) + + return { + 'new': new_accounts, + 'updated': updated_accounts, + 'mapping': account_template_mapping + } + + """ + Updates the taxes (created or updated on previous steps) to set + the references to the accounts (the taxes where created/updated first, + when the referenced accounts where still not available). + """ + def _update_taxes_pending_for_accounts(self, cr, uid, wizard, log, taxes_pending_for_accounts, account_template_mapping, context=None): + taxes = self.pool.get('account.tax') + accounts_template = self.pool.get('account.account.template') + + for key, value in taxes_pending_for_accounts.items(): + # + # Ensure the related account templates are on the map. + # + if value['account_collected_id']: + account_template = accounts_template.browse( + cr, uid, value['account_collected_id'], context=context) + self._map_account_template(cr, uid, wizard, account_template_mapping, account_template, context) + if value['account_paid_id']: + account_template = accounts_template.browse( + cr, uid, value['account_paid_id'], context=context) + self._map_account_template(cr, uid, wizard, account_template_mapping, account_template, context) + + if value['account_collected_id'] or value['account_paid_id']: + if account_template_mapping.get(value['account_collected_id']) and account_template_mapping.get(value['account_paid_id']): + vals = { + 'account_collected_id': account_template_mapping[value['account_collected_id']], + 'account_paid_id': account_template_mapping[value['account_paid_id']], + } + taxes.write(cr, uid, [key], vals) + else: + tax = taxes.browse(cr, uid, key) + if not account_template_mapping.get(value['account_collected_id']): + log.add(_("Tax %s: The collected account can not be set.\n") % (tax.name), True) + if not account_template_mapping.get(value['account_paid_id']): + log.add(_("Tax %s: The paid account can not be set.\n") + % (tax.name), True) + + """ + Search for, and load, fiscal position templates to create/update. + """ + def _update_fiscal_positions(self, cr, uid, wizard, log, tax_template_mapping, account_template_mapping, context=None): + fiscalpositions = self.pool.get('account.fiscal.position') + fiscalpositions_taxes = self.pool.get('account.fiscal.position.tax') + fiscalpositions_account = self.pool.get('account.fiscal.position.account') + + new_fps = 0 + updated_fps = 0 + + for wiz_fp in wizard.fiscal_position_ids: + fp_template = wiz_fp.fiscal_position_id + fp_id = None + modified = False + if wiz_fp.type == 'new': + # + # Create a new fiscal position + # + vals_fp = { + 'company_id': wizard.company_id.id, + 'name': fp_template.name, + } + fp_id = fiscalpositions.create(cr, uid, vals_fp) + new_fps += 1 + modified = True + elif wizard.update_fiscal_position and wiz_fp.update_fiscal_position_id: + # + # Update the given fiscal position (remove the tax and account + # mappings, that will be regenerated later) + # + fp_id = wiz_fp.update_fiscal_position_id.id + updated_fps += 1 + modified = True + # Remove the tax mappings + fp_tax_ids = fiscalpositions_taxes.search( + cr, uid, [('position_id', '=', fp_id)]) + fiscalpositions_taxes.unlink(cr, uid, fp_tax_ids) + # Remove the account mappings + fp_account_ids = fiscalpositions_account.search( + cr, uid, [('position_id', '=', fp_id)]) + fiscalpositions_account.unlink(cr, uid, fp_account_ids) + else: + fp_id = wiz_fp.update_fiscal_position_id and wiz_fp.update_fiscal_position_id.id + + if modified: + # + # (Re)create the tax mappings + # + for fp_tax in fp_template.tax_ids: + # + # Ensure the related tax templates are on the map. + # + self._map_tax_template(cr, uid, wizard, tax_template_mapping, fp_tax.tax_src_id, context) + if fp_tax.tax_dest_id: + self._map_tax_template(cr, uid, wizard, tax_template_mapping, fp_tax.tax_dest_id, context) + + # + # Create the fp tax mapping + # + vals_tax = { + 'tax_src_id': tax_template_mapping.get(fp_tax.tax_src_id.id), + 'tax_dest_id': fp_tax.tax_dest_id and tax_template_mapping.get(fp_tax.tax_dest_id.id), + 'position_id': fp_id, + } + fiscalpositions_taxes.create(cr, uid, vals_tax) + + # + # Check for errors + # + if not tax_template_mapping.get(fp_tax.tax_src_id.id): + log.add(_("Fiscal position %s: The source tax %s can not be set.\n") % (fp_template.name, fp_tax.tax_src_id.code), True) + if fp_tax.tax_dest_id and not tax_template_mapping.get(fp_tax.tax_dest_id.id): + log.add(_("Fiscal position %s: The destination tax %s can not be set.\n") % (fp_template.name, fp_tax.tax_dest_id.name), True) + # + # (Re)create the account mappings + # + for fp_account in fp_template.account_ids: + # + # Ensure the related account templates are on the map. + # + self._map_account_template(cr, uid, wizard, account_template_mapping, fp_account.account_src_id, context) + if fp_account.account_dest_id: + self._map_account_template(cr, uid, wizard, account_template_mapping, fp_account.account_dest_id, context) + + # + # Create the fp account mapping + # + vals_account = { + 'account_src_id': account_template_mapping.get(fp_account.account_src_id.id), + 'account_dest_id': fp_account.account_dest_id and account_template_mapping.get(fp_account.account_dest_id.id), + 'position_id': fp_id, + } + fiscalpositions_account.create(cr, uid, vals_account) + + # + # Check for errors + # + if not account_template_mapping.get(fp_account.account_src_id.id): + log.add(_("Fiscal position %s: The source account %s can not be set.\n") % (fp_template.name, fp_account.account_src_id.code), True) + if fp_account.account_dest_id and not account_template_mapping.get(fp_account.account_dest_id.id): + log.add(_("Fiscal position %s: The destination account %s can not be set.\n") % (fp_template.name, fp_account.account_dest_id.code), True) + + log.add(_("Created or updated fiscal position %s.\n") + % fp_template.name) + return {'new': new_fps, 'updated': updated_fps} + + """ + Action that creates/updates the selected elements. + """ + def action_update_records(self, cr, uid, ids, context=None): + if context is None: + context = {} + wizard = self.browse(cr, uid, ids[0], context=context) + + if wizard.lang: + context['lang'] = wizard.lang + elif context.get('lang'): + del context['lang'] + + log = WizardLog() + + # + # Create or update the records. + # + tax_codes_res = self._update_tax_codes( + cr, uid, wizard, log, context=context) + taxes_res = self._update_taxes( + cr, uid, wizard, log, tax_codes_res['mapping'], context=context) + accounts_res = self._update_accounts( + cr, uid, wizard, log, taxes_res['pending'], context=context) + self._update_taxes_pending_for_accounts(cr, uid, wizard, log, taxes_res['pending'], accounts_res['mapping'], context=context) + fps_res = self._update_fiscal_positions(cr, uid, wizard, log, taxes_res['mapping'], accounts_res['mapping'], context=context) + + # + # Check if errors where detected and wether we should stop. + # + if log.has_errors() and not wizard.continue_on_errors: + raise orm.except_orm(_('Error'), _( + "One or more errors detected!\n\n%s") % log.get_errors_str()) + + # + # Store the data and go to the next step. + # + self.write(cr, uid, [wizard.id], { + 'state': 'done', + 'new_tax_codes': tax_codes_res.get('new', 0), + 'new_taxes': taxes_res.get('new', 0), + 'new_accounts': accounts_res .get('new', 0), + 'new_fps': fps_res.get('new', 0), + 'updated_tax_codes': tax_codes_res.get('updated', 0), + 'updated_taxes': taxes_res.get('updated', 0), + 'updated_accounts': accounts_res.get('updated', 0), + 'updated_fps': fps_res.get('updated', 0), + 'log': log(), + }, context) + + return _reopen(self, wizard.id, 'wizard.update.chart.accounts') + +wizard_update_charts_accounts() + + +class wizard_update_charts_accounts_tax_code(orm.TransientModel): + """ + ****************************************************************** + Tax code that needs to be updated (new or updated in the template). + ****************************************************************** + """ + _name = 'wizard.update.charts.accounts.tax.code' + _columns = { + 'tax_code_id': fields.many2one('account.tax.code.template', 'Tax code template', required=True, ondelete='set null'), + 'update_chart_wizard_id': fields.many2one('wizard.update.charts.accounts', 'Update chart wizard', required=True, ondelete='cascade'), + 'type': fields.selection([ + ('new', 'New template'), + ('updated', 'Updated template'), + ], 'Type'), + 'update_tax_code_id': fields.many2one('account.tax.code', 'Tax code to update', required=False, ondelete='set null'), + 'notes': fields.text('Notes'), + } + _defaults = { + } + +wizard_update_charts_accounts_tax_code() + + +class wizard_update_charts_accounts_tax(orm.TransientModel): + """ + ************************************************************** + Tax that needs to be updated (new or updated in the template). + ************************************************************** + """ + _name = 'wizard.update.charts.accounts.tax' + _columns = { + 'tax_id': fields.many2one('account.tax.template', 'Tax template', required=True, ondelete='set null'), + 'update_chart_wizard_id': fields.many2one('wizard.update.charts.accounts', 'Update chart wizard', required=True, ondelete='cascade'), + 'type': fields.selection([ + ('new', 'New template'), + ('updated', 'Updated template'), + ], 'Type'), + 'update_tax_id': fields.many2one('account.tax', 'Tax to update', required=False, ondelete='set null'), + 'notes': fields.text('Notes'), + } + + _defaults = { + } + +wizard_update_charts_accounts_tax() + + +class wizard_update_charts_accounts_account(orm.TransientModel): + """ + ****************************************************************** + Account that needs to be updated (new or updated in the template). + ******************************************************************* + """ + _name = 'wizard.update.charts.accounts.account' + # The chart of accounts can have a lot of accounts, so we need an higher + # limit for the objects in memory to let the wizard create all the items + # at once. + _max_count = 4096 + _columns = { + 'account_id': fields.many2one('account.account.template', 'Account template', required=True, ondelete='set null'), + 'update_chart_wizard_id': fields.many2one('wizard.update.charts.accounts', 'Update chart wizard', required=True, ondelete='cascade'), + 'type': fields.selection([ + ('new', 'New template'), + ('updated', 'Updated template'), + ], 'Type'), + 'update_account_id': fields.many2one('account.account', 'Account to update', required=False, ondelete='set null'), + 'notes': fields.text('Notes'), + } + + _defaults = { + } + +wizard_update_charts_accounts_account() + + +class wizard_update_charts_accounts_fiscal_position(orm.TransientModel): + """ + ************************************************************************** + Fiscal position that needs to be updated (new or updated in the template). + ************************************************************************** + """ + _name = 'wizard.update.charts.accounts.fiscal.position' + _columns = { + 'fiscal_position_id': fields.many2one('account.fiscal.position.template', 'Fiscal position template', required=True, ondelete='set null'), + 'update_chart_wizard_id': fields.many2one('wizard.update.charts.accounts', 'Update chart wizard', required=True, ondelete='cascade'), + 'type': fields.selection([ + ('new', 'New template'), + ('updated', 'Updated template'), + ], 'Type'), + 'update_fiscal_position_id': fields.many2one('account.fiscal.position', 'Fiscal position to update', required=False, ondelete='set null'), + 'notes': fields.text('Notes'), + } + _defaults = { + } + + +wizard_update_charts_accounts_fiscal_position() + +# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4: diff --git a/account_chart_update/wizard/wizard_chart_update_view.xml b/account_chart_update/wizard/wizard_chart_update_view.xml new file mode 100644 index 00000000..66e76dbc --- /dev/null +++ b/account_chart_update/wizard/wizard_chart_update_view.xml @@ -0,0 +1,176 @@ + + + + + + + + Update Chart of Accounts from a Chart Template + + wizard.update.charts.accounts + + +
+
+ +
+ +

+

This wizard will update your accounts, taxes and fiscal positions according to the selected chart template

+ +

+
+ + + + + + + + + + + + + + + + + + + + +
+

If you leave these options set, the wizard will not just create new records, but also update records with changes (i.e. different tax amount)

+

Note:Not all the fields are tested for changes, just the main ones

+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + + + +
+
+
+ + + + + + + +
+ + + + + + +
+
+
+ + + + + + + +
+ + + + + + +
+
+
+ +
+
+ + + + + + + + + + + + + + + + + + + + +
+
+
+
+
+
+ +
+
+ + + Update Chart of Accounts from a Chart Template + + ir.actions.act_window + wizard.update.charts.accounts + form + form + new + + + + +
+