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
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
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
+
+
+
+
+
+