From 1eeb127f913159254d16e16b6cf22d6859bb2ace Mon Sep 17 00:00:00 2001 From: "Pedro M. Baeza" Date: Tue, 18 Sep 2018 14:59:54 +0200 Subject: [PATCH 1/2] [FIX] account_chart_update: Don't cache recordsets When marking methods as ormcache, you can't return recordsets, as they keep the cursor information, and that cursor can be closed (worker spawn for example) when you need to access again data. This is fixed returning IDs instead, and browsing when needed --- .../wizard/wizard_chart_update.py | 85 ++++++++++--------- 1 file changed, 43 insertions(+), 42 deletions(-) diff --git a/account_chart_update/wizard/wizard_chart_update.py b/account_chart_update/wizard/wizard_chart_update.py index 8120ec35f..492452488 100644 --- a/account_chart_update/wizard/wizard_chart_update.py +++ b/account_chart_update/wizard/wizard_chart_update.py @@ -262,7 +262,7 @@ class WizardUpdateChartsAccounts(models.TransientModel): ("type_tax_use", "=", template.type_tax_use)], limit=1) result |= single - return result + return result[:1].id @api.model @tools.ormcache("code") @@ -276,7 +276,8 @@ class WizardUpdateChartsAccounts(models.TransientModel): """Find an account that matches the template.""" return self.env['account.account'].search( [('code', 'in', map(self.padded_code, templates.mapped("code"))), - ('company_id', '=', self.company_id.id)]) + ('company_id', '=', self.company_id.id)], + ).id @api.multi @tools.ormcache("templates") @@ -284,27 +285,27 @@ class WizardUpdateChartsAccounts(models.TransientModel): """Find a real fiscal position from a template.""" return self.env['account.fiscal.position'].search( [('name', 'in', templates.mapped("name")), - ('company_id', '=', self.company_id.id)], limit=1) + ('company_id', '=', self.company_id.id)], limit=1).id @api.multi @tools.ormcache("templates", "current_fp_accounts") def find_fp_account_by_templates(self, templates, current_fp_accounts): result = [] for tpl in templates: - pos = self.find_fp_by_templates(tpl.position_id) - src = self.find_account_by_templates(tpl.account_src_id) - dest = self.find_account_by_templates(tpl.account_dest_id) + pos_id = self.find_fp_by_templates(tpl.position_id) + src_id = self.find_account_by_templates(tpl.account_src_id) + dest_id = self.find_account_by_templates(tpl.account_dest_id) mappings = self.env["account.fiscal.position.account"].search([ - ("position_id", "=", pos.id), - ("account_src_id", "=", src.id), + ("position_id", "=", pos_id), + ("account_src_id", "=", src_id), ]) existing = mappings.filtered(lambda x: x.account_dest_id == dest) if not existing: # create a new mapping result.append((0, 0, { - 'position_id': pos.id, - 'account_src_id': src.id, - 'account_dest_id': dest.id, + 'position_id': pos_id, + 'account_src_id': src_id, + 'account_dest_id': dest_id, })) else: current_fp_accounts -= existing @@ -318,20 +319,20 @@ class WizardUpdateChartsAccounts(models.TransientModel): def find_fp_tax_by_templates(self, templates, current_fp_taxes): result = [] for tpl in templates: - pos = self.find_fp_by_templates(tpl.position_id) - src = self.find_tax_by_templates(tpl.tax_src_id) - dest = self.find_tax_by_templates(tpl.tax_dest_id) + pos_id = self.find_fp_by_templates(tpl.position_id) + src_id = self.find_tax_by_templates(tpl.tax_src_id) + dest_id = self.find_tax_by_templates(tpl.tax_dest_id) mappings = self.env["account.fiscal.position.tax"].search([ - ("position_id", "=", pos.id), - ("tax_src_id", "=", src.id), + ("position_id", "=", pos_id), + ("tax_src_id", "=", src_id), ]) - existing = mappings.filtered(lambda x: x.tax_dest_id == dest) + existing = mappings.filtered(lambda x: x.tax_dest_id.id == dest_id) if not existing: # create a new mapping result.append((0, 0, { - 'position_id': pos.id, - 'tax_src_id': src.id, - 'tax_dest_id': dest.id, + 'position_id': pos_id, + 'tax_src_id': src_id, + 'tax_dest_id': dest_id, })) else: current_fp_taxes -= existing @@ -461,15 +462,15 @@ class WizardUpdateChartsAccounts(models.TransientModel): @api.multi def _find_taxes(self): """Search for, and load, tax templates to create/update/delete.""" - found_taxes = self.env["account.tax"] + found_taxes_ids = [] self.tax_ids.unlink() # Search for changes between template and real tax for template in self.chart_template_ids.mapped("tax_template_ids"): # Check if the template matches a real tax - tax = self.find_tax_by_templates(template) + tax_id = self.find_tax_by_templates(template) - if not tax: + if not tax_id: # Tax to be created self.tax_ids.create({ 'tax_id': template.id, @@ -478,9 +479,10 @@ class WizardUpdateChartsAccounts(models.TransientModel): 'notes': _('Name or description not found.'), }) else: - found_taxes |= tax + found_taxes_ids.append(tax_id) # Check the tax for changes + tax = self.env['account.tax'].browse(tax_id) notes = self.diff_notes(template, tax) if notes: # Tax to be updated @@ -488,7 +490,7 @@ class WizardUpdateChartsAccounts(models.TransientModel): 'tax_id': template.id, 'update_chart_wizard_id': self.id, 'type': 'updated', - 'update_tax_id': tax.id, + 'update_tax_id': tax_id, 'notes': notes, }) @@ -496,7 +498,7 @@ class WizardUpdateChartsAccounts(models.TransientModel): # deactivation taxes_to_delete = self.env['account.tax'].search( [('company_id', '=', self.company_id.id), - ("id", "not in", found_taxes.ids), + ("id", "not in", found_taxes_ids), ("active", "=", True)]) for tax in taxes_to_delete: self.tax_ids.create({ @@ -513,9 +515,9 @@ class WizardUpdateChartsAccounts(models.TransientModel): for template in self.chart_template_ids.mapped("account_ids"): # Search for a real account that matches the template - account = self.find_account_by_templates(template) + account_id = self.find_account_by_templates(template) - if not account: + if not account_id: # Account to be created self.account_ids.create({ 'account_id': template.id, @@ -525,6 +527,7 @@ class WizardUpdateChartsAccounts(models.TransientModel): }) else: # Check the account for changes + account = self.env['account.account'].browse(account_id) notes = self.diff_notes(template, account) if notes: # Account to be updated @@ -532,7 +535,7 @@ class WizardUpdateChartsAccounts(models.TransientModel): 'account_id': template.id, 'update_chart_wizard_id': self.id, 'type': 'updated', - 'update_account_id': account.id, + 'update_account_id': account_id, 'notes': notes, }) @@ -547,8 +550,8 @@ class WizardUpdateChartsAccounts(models.TransientModel): [('chart_template_id', 'in', self.chart_template_ids.ids)]) for template in templates: # Search for a real fiscal position that matches the template - fp = self.find_fp_by_templates(template) - if not fp: + fp_id = self.find_fp_by_templates(template) + if not fp_id: # Fiscal position to be created wiz_fp.create({ 'fiscal_position_id': template.id, @@ -558,6 +561,7 @@ class WizardUpdateChartsAccounts(models.TransientModel): }) else: # Check the fiscal position for changes + fp = self.env['account.fiscal.position'].browse(fp_id) notes = self.diff_notes(template, fp) if notes: # Fiscal position template to be updated @@ -565,7 +569,7 @@ class WizardUpdateChartsAccounts(models.TransientModel): 'fiscal_position_id': template.id, 'update_chart_wizard_id': self.id, 'type': 'updated', - 'update_fiscal_position_id': fp.id, + 'update_fiscal_position_id': fp_id, 'notes': notes, }) @@ -602,8 +606,7 @@ class WizardUpdateChartsAccounts(models.TransientModel): 'reconcile': account_template.reconcile, 'note': account_template.note, 'tax_ids': [ - (6, 0, - self.find_tax_by_templates(account_template.tax_ids).ids), + (6, 0, [self.find_tax_by_templates(account_template.tax_ids)]), ], 'company_id': self.company_id.id, }) @@ -665,10 +668,8 @@ class WizardUpdateChartsAccounts(models.TransientModel): for fp_tax in fp_template.tax_ids: # Create the fp tax mapping tax_mapping.append({ - 'tax_src_id': self.find_tax_by_templates( - fp_tax.tax_src_id).id, - 'tax_dest_id': self.find_tax_by_templates( - fp_tax.tax_dest_id).id, + 'tax_src_id': self.find_tax_by_templates(fp_tax.tax_src_id), + 'tax_dest_id': self.find_tax_by_templates(fp_tax.tax_dest_id), }) # Account mappings account_mapping = [] @@ -676,11 +677,11 @@ class WizardUpdateChartsAccounts(models.TransientModel): # Create the fp account mapping account_mapping.append({ 'account_src_id': ( - self.find_account_by_templates( - fp_account.account_src_id).id), + self.find_account_by_templates(fp_account.account_src_id) + ), 'account_dest_id': ( - self.find_account_by_templates( - fp_account.account_dest_id).id), + self.find_account_by_templates(fp_account.account_dest_id) + ), }) return { 'company_id': self.company_id.id, From 949ca0a235df7b878b4fd9a842408a624615aeae Mon Sep 17 00:00:00 2001 From: "Pedro M. Baeza" Date: Tue, 18 Sep 2018 14:41:19 +0200 Subject: [PATCH 2/2] [FIX+IMP] account_chart_update: Major refactoring * Compare all fields from template Not all fields were properly compared with previous code. This means also to exclude some of them, but now we have a full and extensible system. * Optimize cached method * Generate accounts and fiscal positions using Odoo methods (tax already did) * README by fragments * Add tests up to full coverage --- account_chart_update/README.rst | 87 +++- account_chart_update/__manifest__.py | 14 +- account_chart_update/i18n/es.po | 266 +++++----- account_chart_update/readme/CONTRIBUTORS.rst | 6 + account_chart_update/readme/DESCRIPTION.rst | 12 + account_chart_update/readme/ROADMAP.rst | 5 + account_chart_update/readme/USAGE.rst | 7 + .../static/description/index.html | 457 ++++++++++++++++++ account_chart_update/tests/__init__.py | 4 + .../tests/test_account_chart_update.py | 306 ++++++++++++ account_chart_update/wizard/__init__.py | 2 - .../wizard/wizard_chart_update.py | 263 +++++----- 12 files changed, 1129 insertions(+), 300 deletions(-) create mode 100644 account_chart_update/readme/CONTRIBUTORS.rst create mode 100644 account_chart_update/readme/DESCRIPTION.rst create mode 100644 account_chart_update/readme/ROADMAP.rst create mode 100644 account_chart_update/readme/USAGE.rst create mode 100644 account_chart_update/static/description/index.html create mode 100644 account_chart_update/tests/__init__.py create mode 100644 account_chart_update/tests/test_account_chart_update.py diff --git a/account_chart_update/README.rst b/account_chart_update/README.rst index a1d133ab0..e4e6541b1 100644 --- a/account_chart_update/README.rst +++ b/account_chart_update/README.rst @@ -1,11 +1,30 @@ -.. image:: https://img.shields.io/badge/licence-AGPL--3-blue.svg - :target: http://www.gnu.org/licenses/agpl-3.0-standalone.html - :alt: License: AGPL-3 - =========================================================== Detect changes and update the Account Chart from a template =========================================================== +.. !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + !! This file is generated by oca-gen-addon-readme !! + !! changes will be overwritten. !! + !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + +.. |badge1| image:: https://img.shields.io/badge/maturity-Mature-brightgreen.png + :target: https://odoo-community.org/page/development-status + :alt: Mature +.. |badge2| image:: https://img.shields.io/badge/licence-AGPL--3-blue.png + :target: http://www.gnu.org/licenses/agpl-3.0-standalone.html + :alt: License: AGPL-3 +.. |badge3| image:: https://img.shields.io/badge/github-OCA%2Faccount--financial--tools-lightgray.png?logo=github + :target: https://github.com/OCA/account-financial-tools/tree/10.0/account_chart_update + :alt: OCA/account-financial-tools +.. |badge4| image:: https://img.shields.io/badge/weblate-Translate%20me-F47D42.png + :target: https://translation.odoo-community.org/projects/account-financial-tools-10-0/account-financial-tools-10-0-account_chart_update + :alt: Translate me on Weblate +.. |badge5| image:: https://img.shields.io/badge/runbot-Try%20me-875A7B.png + :target: https://runbot.odoo-community.org/runbot/92/10.0 + :alt: Try me on Runbot + +|badge1| |badge2| |badge3| |badge4| |badge5| + This is a pretty useful tool to update Odoo installations after tax reforms on the official charts of accounts, or to apply fixes performed on the chart template. @@ -19,40 +38,53 @@ The wizard: * It can also update (overwrite) the accounts, taxes, tax codes and fiscal positions that got modified on the template. +**Table of contents** + +.. contents:: + :local: + Usage ===== -The wizard, accesible from *Accounting > Configuration > Settings > Chart of -Accounts > Update chart of accounts*, lets the user select what kind of objects -must be checked/updated, and whether old records must be checked for changes -and updates. +The wizard, accesible from *Accounting > Settings > Update chart of accounts*, +lets the user select what kind of objects must be checked/updated, and whether +old records must be checked for changes and updates. -It will display all the objects to be created / updated with some information -about the detected differences, and allow the user to exclude records -individually. - -.. image:: https://odoo-community.org/website/image/ir.attachment/5784_f2813bd/datas - :alt: Try me on Runbot - :target: https://runbot.odoo-community.org/runbot/92/9.0 +It will display all the objects to be created / updated / deactivated with some +information about the detected differences, and allow the user to exclude +records individually. Known issues / Roadmap ====================== -* Add tests. +* Generate and update account reconcile models. +* Generate XML-ID for fiscal position tax and account mapping lines. +* Allow to select independently operations to perform (create, update, + deactivate). +* Detect fiscal positions to deactivate? Bug Tracker =========== -Bugs are tracked on `GitHub Issues -`_. In case of trouble, -please check there if your issue has already been reported. If you spotted it -first, help us smashing it by providing a detailed and welcomed feedback. +Bugs are tracked on `GitHub Issues `_. +In case of trouble, please check there if your issue has already been reported. +If you spotted it first, help us smashing it by providing a detailed and welcomed +`feedback `_. + +Do not contact contributors directly about support or help with technical issues. Credits ======= +Authors +~~~~~~~ + +* Tecnativa +* BCIM +* Okia + Contributors ------------- +~~~~~~~~~~~~ * Pedro M. Baeza * Jairo Llopis @@ -60,17 +92,20 @@ Contributors * Sylvain Van Hoof * Nacho Muñoz -Maintainer ----------- + +Maintainers +~~~~~~~~~~~ + +This module is maintained by the OCA. .. image:: https://odoo-community.org/logo.png :alt: Odoo Community Association :target: https://odoo-community.org -This module is maintained by the OCA. - OCA, or the Odoo Community Association, is a nonprofit organization whose mission is to support the collaborative development of Odoo features and promote its widespread use. -To contribute to this module, please visit https://odoo-community.org. +This module is part of the `OCA/account-financial-tools `_ project on GitHub. + +You are welcome to contribute. To learn how please visit https://odoo-community.org/page/Contribute. diff --git a/account_chart_update/__manifest__.py b/account_chart_update/__manifest__.py index 2a1cf15d1..872b71007 100644 --- a/account_chart_update/__manifest__.py +++ b/account_chart_update/__manifest__.py @@ -1,28 +1,22 @@ # -*- coding: utf-8 -*- -# Copyright 2015-2017 Pedro Manuel Baeza # Copyright 2016 Jairo Llopis # Copyright 2016 Jacques-Etienne Baudoux # Copyright 2016 Sylvain Van Hoof +# Copyright 2015-2018 Tecnativa - Pedro M. Baeza # License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). { 'name': "Detect changes and update the Account Chart from a template", "summary": "Wizard to update a company's account chart from a template", - 'version': "10.0.1.0.2", + 'version': "10.0.2.0.0", 'author': "Tecnativa, " "BCIM, " "Okia, " "Odoo Community Association (OCA)", 'website': "http://odoo-community.org", 'depends': ["account"], - 'category': "Accounting & Finance", - 'contributors': [ - 'Pedro M. Baeza', - 'Jairo Llopis', - 'Jacques-Etienne Baudoux', - 'Sylvain Van Hoof' - 'Nacho Muñoz', - ], + 'development_status': 'Mature', + 'category': "Accounting", 'license': "AGPL-3", "data": [ 'wizard/wizard_chart_update_view.xml', diff --git a/account_chart_update/i18n/es.po b/account_chart_update/i18n/es.po index aa7de1162..523559d12 100644 --- a/account_chart_update/i18n/es.po +++ b/account_chart_update/i18n/es.po @@ -1,73 +1,70 @@ # Translation of Odoo Server. # This file contains the translation of the following modules: -# * account_chart_update +# * account_chart_update # -# Translators: -# OCA Transbot , 2017 msgid "" msgstr "" "Project-Id-Version: Odoo Server 10.0\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2018-02-01 03:44+0000\n" -"PO-Revision-Date: 2018-02-01 03:44+0000\n" -"Last-Translator: OCA Transbot , 2017\n" -"Language-Team: Spanish (https://www.transifex.com/oca/teams/23907/es/)\n" -"Language: es\n" +"POT-Creation-Date: 2018-09-21 00:20+0000\n" +"PO-Revision-Date: 2018-09-21 00:20+0000\n" +"Last-Translator: <>\n" +"Language-Team: \n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: \n" -"Plural-Forms: nplurals=2; plural=(n != 1);\n" +"Plural-Forms: \n" #. module: account_chart_update #: model:ir.model.fields,field_description:account_chart_update.field_wizard_update_charts_accounts_code_digits msgid "# of digits" -msgstr "" +msgstr "Nº de dígitos" #. module: account_chart_update #: model:ir.ui.view,arch_db:account_chart_update.view_update_multi_chart msgid " or " -msgstr "" +msgstr " o " #. module: account_chart_update #: model:ir.model.fields,field_description:account_chart_update.field_wizard_update_charts_accounts_account_account_id msgid "Account template" -msgstr "" +msgstr "Plantilla de cuenta" #. module: account_chart_update #: model:ir.model,name:account_chart_update.model_wizard_update_charts_accounts_account msgid "Account that needs to be updated (new or updated in the template)." -msgstr "" +msgstr "Cuenta que necesita ser actualizada (nueva o cambiada en la plantilla)." #. module: account_chart_update #: model:ir.model.fields,field_description:account_chart_update.field_wizard_update_charts_accounts_account_update_account_id msgid "Account to update" -msgstr "" +msgstr "Cuenta a actualizar" #. module: account_chart_update #: model:ir.model.fields,field_description:account_chart_update.field_wizard_update_charts_accounts_account_ids #: model:ir.ui.view,arch_db:account_chart_update.view_update_multi_chart msgid "Accounts" -msgstr "" +msgstr "Cuentas" #. module: account_chart_update #: model:ir.model.fields,field_description:account_chart_update.field_wizard_update_charts_accounts_chart_template_id msgid "Chart Template" -msgstr "" +msgstr "Plan contable" #. module: account_chart_update #: model:ir.model.fields,field_description:account_chart_update.field_wizard_update_charts_accounts_chart_template_ids msgid "Chart Templates" -msgstr "" +msgstr "Planes contables" #. module: account_chart_update #: model:ir.ui.view,arch_db:account_chart_update.view_update_multi_chart msgid "Chart of Accounts" -msgstr "" +msgstr "Plan contable" #. module: account_chart_update #: model:ir.ui.view,arch_db:account_chart_update.view_update_multi_chart msgid "Close" -msgstr "" +msgstr "Cerrar" #. module: account_chart_update #: model:ir.model.fields,field_description:account_chart_update.field_wizard_update_charts_accounts_company_id @@ -77,23 +74,23 @@ msgstr "Compañía" #. module: account_chart_update #: selection:wizard.update.charts.accounts,state:0 msgid "Configuration" -msgstr "" +msgstr "Configuración" #. module: account_chart_update #: model:ir.model.fields,field_description:account_chart_update.field_wizard_update_charts_accounts_continue_on_errors msgid "Continue on errors" -msgstr "" +msgstr "Continuar en errores" #. module: account_chart_update #: model:ir.ui.view,arch_db:account_chart_update.view_update_multi_chart msgid "Create/Update" -msgstr "" +msgstr "Crear/Actualizar" #. module: account_chart_update -#: code:addons/account_chart_update/wizard/wizard_chart_update.py:625 +#: code:addons/account_chart_update/wizard/wizard_chart_update.py:613 #, python-format msgid "Created account %s." -msgstr "" +msgstr "Creada cuenta %s." #. module: account_chart_update #: model:ir.model.fields,field_description:account_chart_update.field_wizard_update_charts_accounts_account_create_uid @@ -109,43 +106,41 @@ msgstr "Creado por" #: model:ir.model.fields,field_description:account_chart_update.field_wizard_update_charts_accounts_fiscal_position_create_date #: model:ir.model.fields,field_description:account_chart_update.field_wizard_update_charts_accounts_tax_create_date msgid "Created on" -msgstr "Creado en" +msgstr "Creado el" #. module: account_chart_update -#: code:addons/account_chart_update/wizard/wizard_chart_update.py:708 +#: code:addons/account_chart_update/wizard/wizard_chart_update.py:714 #, python-format msgid "Created or updated fiscal position %s." -msgstr "" - -#. module: account_chart_update -#: code:addons/account_chart_update/wizard/wizard_chart_update.py:587 -#, python-format -msgid "Created tax %s." -msgstr "" +msgstr "Creada o actualizada posición fiscal %s." #. module: account_chart_update #: code:addons/account_chart_update/wizard/wizard_chart_update.py:580 #, python-format +msgid "Created tax %s." +msgstr "Creado impuesto %s." + +#. module: account_chart_update +#: code:addons/account_chart_update/wizard/wizard_chart_update.py:575 +#, python-format msgid "Deactivated tax %s." -msgstr "" +msgstr "Deactivado impuesto %s." #. module: account_chart_update #: model:ir.model.fields,field_description:account_chart_update.field_wizard_update_charts_accounts_deleted_taxes msgid "Deactivated taxes" -msgstr "" +msgstr "Impuestos desactivados" #. module: account_chart_update #: model:ir.model.fields,help:account_chart_update.field_wizard_update_charts_accounts_tax_type_tax_use -msgid "" -"Determines where the tax is selectable. Note : 'None' means a tax can't be " -"used by itself, however it can still be used in a group." -msgstr "" +msgid "Determines where the tax is selectable. Note : 'None' means a tax can't be used by itself, however it can still be used in a group." +msgstr "Determina dónde puede seleccionarse un impuesto. Nota : 'Ninguno' significa que un impuesto no puede ser usado por si mismo; aun así, puede utilizarse en un grupo." #. module: account_chart_update -#: code:addons/account_chart_update/wizard/wizard_chart_update.py:453 +#: code:addons/account_chart_update/wizard/wizard_chart_update.py:451 #, python-format msgid "Differences in these fields: %s." -msgstr "" +msgstr "Diferencias en estos campos: %s." #. module: account_chart_update #: model:ir.model.fields,field_description:account_chart_update.field_wizard_update_charts_accounts_account_display_name @@ -153,64 +148,60 @@ msgstr "" #: model:ir.model.fields,field_description:account_chart_update.field_wizard_update_charts_accounts_fiscal_position_display_name #: model:ir.model.fields,field_description:account_chart_update.field_wizard_update_charts_accounts_tax_display_name msgid "Display Name" -msgstr "Nombre mostrado" +msgstr "Nombre a mostrar" #. module: account_chart_update -#: code:addons/account_chart_update/wizard/wizard_chart_update.py:629 +#: code:addons/account_chart_update/wizard/wizard_chart_update.py:622 #, python-format msgid "Exception creating account %s." -msgstr "" +msgstr "Excepción creando cuenta %s." #. module: account_chart_update -#: code:addons/account_chart_update/wizard/wizard_chart_update.py:641 +#: code:addons/account_chart_update/wizard/wizard_chart_update.py:644 #, python-format msgid "Exception writing account %s." -msgstr "" +msgstr "Excepción escribiendo cuenta %s." #. module: account_chart_update #: model:ir.model.fields,help:account_chart_update.field_wizard_update_charts_accounts_update_account msgid "Existing accounts are updated. Accounts are searched by code." -msgstr "" +msgstr "Las cuentas existentes serán actualizadas. Las cuentas se buscan por código." #. module: account_chart_update #: model:ir.model.fields,help:account_chart_update.field_wizard_update_charts_accounts_update_fiscal_position -msgid "" -"Existing fiscal positions are updated. Fiscal positions are searched by name." -msgstr "" +msgid "Existing fiscal positions are updated. Fiscal positions are searched by name." +msgstr "Las posiciones fiscales existentes serán actualizadas. Las posiciones fiscales se buscan por nombre." #. module: account_chart_update #: model:ir.model.fields,help:account_chart_update.field_wizard_update_charts_accounts_update_tax msgid "Existing taxes are updated. Taxes are searched by name." -msgstr "" +msgstr "Los impuestos existentes serán actualizados. Los impuestos se buscan por nombre." #. module: account_chart_update #: model:ir.model.fields,field_description:account_chart_update.field_wizard_update_charts_accounts_fiscal_position_fiscal_position_id msgid "Fiscal position template" -msgstr "" +msgstr "Plantilla posición fiscal" #. module: account_chart_update #: model:ir.model,name:account_chart_update.model_wizard_update_charts_accounts_fiscal_position -msgid "" -"Fiscal position that needs to be updated (new or updated in the template)." -msgstr "" +msgid "Fiscal position that needs to be updated (new or updated in the template)." +msgstr "Posición fiscal que necesita ser actualizada (nuevo o modificada en la plantilla)" #. module: account_chart_update #: model:ir.model.fields,field_description:account_chart_update.field_wizard_update_charts_accounts_fiscal_position_update_fiscal_position_id msgid "Fiscal position to update" -msgstr "" +msgstr "Posición fiscal a actualizar" #. module: account_chart_update #: model:ir.model.fields,field_description:account_chart_update.field_wizard_update_charts_accounts_fiscal_position_ids #: model:ir.ui.view,arch_db:account_chart_update.view_update_multi_chart msgid "Fiscal positions" -msgstr "" +msgstr "Posiciones fiscales" #. module: account_chart_update #: model:ir.model.fields,help:account_chart_update.field_wizard_update_charts_accounts_lang -msgid "" -"For records searched by name (taxes, fiscal positions), the template name " -"will be matched against the record name on this language." -msgstr "" +msgid "For records searched by name (taxes, fiscal positions), the template name will be matched against the record name on this language." +msgstr "Para registros buscados por nombre (impuestos, posiciones fiscales), el nombre de la plantilla será casado contra el nombre del registro en este idioma." #. module: account_chart_update #: model:ir.model.fields,field_description:account_chart_update.field_wizard_update_charts_accounts_account_id @@ -222,27 +213,23 @@ msgstr "ID" #. module: account_chart_update #: model:ir.model.fields,help:account_chart_update.field_wizard_update_charts_accounts_continue_on_errors -msgid "" -"If set, the wizard will continue to the next step even if there are minor " -"errors." -msgstr "" +msgid "If set, the wizard will continue to the next step even if there are minor errors." +msgstr "Si está establecido, el asistente continuará al siguiente paso aunque haya errores." #. module: account_chart_update #: model:ir.ui.view,arch_db:account_chart_update.view_update_multi_chart -msgid "" -"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)" -msgstr "" +msgid "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)" +msgstr "Si deja estas opciones establecidas, el asistente no solo crearé nuevos registros, si no que también actualizará registros con cambios (por ejemplo, diferente importe de impuesto)" #. module: account_chart_update #: model:ir.model.fields,help:account_chart_update.field_wizard_update_charts_accounts_chart_template_ids msgid "Includes all chart templates." -msgstr "" +msgstr "Incluye todos los planes contables." #. module: account_chart_update #: model:ir.model.fields,field_description:account_chart_update.field_wizard_update_charts_accounts_lang msgid "Language" -msgstr "" +msgstr "Idioma" #. module: account_chart_update #: model:ir.model.fields,field_description:account_chart_update.field_wizard_update_charts_accounts___last_update @@ -250,7 +237,7 @@ msgstr "" #: model:ir.model.fields,field_description:account_chart_update.field_wizard_update_charts_accounts_fiscal_position___last_update #: model:ir.model.fields,field_description:account_chart_update.field_wizard_update_charts_accounts_tax___last_update msgid "Last Modified on" -msgstr "Última modificación el" +msgstr "Última modificación en" #. module: account_chart_update #: model:ir.model.fields,field_description:account_chart_update.field_wizard_update_charts_accounts_account_write_uid @@ -266,110 +253,125 @@ msgstr "Última actualización por" #: model:ir.model.fields,field_description:account_chart_update.field_wizard_update_charts_accounts_tax_write_date #: model:ir.model.fields,field_description:account_chart_update.field_wizard_update_charts_accounts_write_date msgid "Last Updated on" -msgstr "Última actualización en" +msgstr "Última actualización el" #. module: account_chart_update #: model:ir.ui.view,arch_db:account_chart_update.view_update_multi_chart msgid "Log" -msgstr "" +msgstr "Registro (Log)" #. module: account_chart_update #: model:ir.model.fields,field_description:account_chart_update.field_wizard_update_charts_accounts_log msgid "Messages and Errors" -msgstr "" +msgstr "Mensajes y errores" #. module: account_chart_update -#: code:addons/account_chart_update/wizard/wizard_chart_update.py:478 +#: code:addons/account_chart_update/wizard/wizard_chart_update.py:474 #, python-format msgid "Name or description not found." -msgstr "" +msgstr "Nombre o descripción no encontrada." #. module: account_chart_update #: model:ir.model.fields,field_description:account_chart_update.field_wizard_update_charts_accounts_new_accounts msgid "New accounts" -msgstr "" +msgstr "Nuevas cuentas" #. module: account_chart_update #: model:ir.model.fields,field_description:account_chart_update.field_wizard_update_charts_accounts_new_fps msgid "New fiscal positions" -msgstr "" +msgstr "Nuevas posiciones fiscales" #. module: account_chart_update #: model:ir.model.fields,field_description:account_chart_update.field_wizard_update_charts_accounts_new_taxes msgid "New taxes" -msgstr "" +msgstr "Nuevos impuestos" #. module: account_chart_update #: selection:wizard.update.charts.accounts.account,type:0 #: selection:wizard.update.charts.accounts.fiscal.position,type:0 #: selection:wizard.update.charts.accounts.tax,type:0 msgid "New template" -msgstr "" +msgstr "Nueva plantilla" #. module: account_chart_update #: model:ir.ui.view,arch_db:account_chart_update.view_update_multi_chart msgid "Next" -msgstr "" +msgstr "Siguiente" #. module: account_chart_update -#: code:addons/account_chart_update/wizard/wizard_chart_update.py:524 +#: code:addons/account_chart_update/wizard/wizard_chart_update.py:517 #, python-format msgid "No account found with this code." -msgstr "" +msgstr "No se ha encontrado cuenta con este código." #. module: account_chart_update -#: code:addons/account_chart_update/wizard/wizard_chart_update.py:557 +#: code:addons/account_chart_update/wizard/wizard_chart_update.py:551 #, python-format msgid "No fiscal position found with this name." -msgstr "" +msgstr "No se ha encontrado posición fiscal con este nombre." #. module: account_chart_update #: model:ir.model.fields,help:account_chart_update.field_wizard_update_charts_accounts_code_digits -msgid "" -"No. of digits to use for account code. Make sure it is the same number as " -"existing accounts." -msgstr "" +msgid "No. of digits to use for account code. Make sure it is the same number as existing accounts." +msgstr "Nº de dígitos a usar para el código de cuenta. Asegúrese que es el mismo número que las cuentas existentes." #. module: account_chart_update #: model:ir.ui.view,arch_db:account_chart_update.view_update_multi_chart msgid "Note: Only the changed fields are updated." -msgstr "" +msgstr "Nota: Solo los campos cambiados se actualizan." #. module: account_chart_update #: model:ir.model.fields,field_description:account_chart_update.field_wizard_update_charts_accounts_account_notes #: model:ir.model.fields,field_description:account_chart_update.field_wizard_update_charts_accounts_fiscal_position_notes #: model:ir.model.fields,field_description:account_chart_update.field_wizard_update_charts_accounts_tax_notes msgid "Notes" -msgstr "" +msgstr "Notas" #. module: account_chart_update -#: code:addons/account_chart_update/wizard/wizard_chart_update.py:233 +#: code:addons/account_chart_update/wizard/wizard_chart_update.py:246 #, python-format -msgid "" -"One or more errors detected!\n" +msgid "One or more errors detected!\n" +"\n" +"%s" +msgstr "Se ha detectado uno o más errores\n" "\n" "%s" -msgstr "" #. module: account_chart_update #: model:ir.ui.view,arch_db:account_chart_update.view_update_multi_chart msgid "Other options" -msgstr "" +msgstr "Otras opciones" + +#. module: account_chart_update +#: code:addons/account_chart_update/wizard/wizard_chart_update.py:667 +#, python-format +msgid "Post-updated tax %s." +msgstr "Impuesto %s post-actualizado." #. module: account_chart_update #: model:ir.ui.view,arch_db:account_chart_update.view_update_multi_chart msgid "Previous" -msgstr "" +msgstr "Anterior" #. module: account_chart_update #: model:ir.ui.view,arch_db:account_chart_update.view_update_multi_chart msgid "Records to create/update" -msgstr "" +msgstr "Registros a crear/actualizar" + +#. module: account_chart_update +#: model:ir.model.fields,field_description:account_chart_update.field_wizard_update_charts_accounts_rejected_new_account_number +msgid "Rejected new account number" +msgstr "Número de nuevas cuentas rechazadas" + +#. module: account_chart_update +#: model:ir.model.fields,field_description:account_chart_update.field_wizard_update_charts_accounts_rejected_updated_account_number +msgid "Rejected updated account number" +msgstr "Número de cuentas a actualizar rechazadas" #. module: account_chart_update #: selection:wizard.update.charts.accounts,state:0 msgid "Select records to update" -msgstr "" +msgstr "Seleccione registros a actualizar" #. module: account_chart_update #: model:ir.model.fields,field_description:account_chart_update.field_wizard_update_charts_accounts_state @@ -379,62 +381,60 @@ msgstr "Estado" #. module: account_chart_update #: model:ir.ui.view,arch_db:account_chart_update.view_update_multi_chart msgid "Summary of created objects" -msgstr "" +msgstr "Resumen de objetos creados" #. module: account_chart_update #: model:ir.ui.view,arch_db:account_chart_update.view_update_multi_chart msgid "Summary of updated objects" -msgstr "" +msgstr "Resumen de objetos actualizados" #. module: account_chart_update #: model:ir.model.fields,field_description:account_chart_update.field_wizard_update_charts_accounts_tax_type_tax_use msgid "Tax Scope" -msgstr "" +msgstr "Ámbito del impuesto" #. module: account_chart_update -#: code:addons/account_chart_update/wizard/wizard_chart_update.py:458 +#: code:addons/account_chart_update/wizard/wizard_chart_update.py:456 #, python-format msgid "Tax is disabled." -msgstr "" +msgstr "Impuesto desactivado." #. module: account_chart_update #: model:ir.model.fields,field_description:account_chart_update.field_wizard_update_charts_accounts_tax_tax_id msgid "Tax template" -msgstr "" +msgstr "Plantilla de impuesto" #. module: account_chart_update #: model:ir.model,name:account_chart_update.model_wizard_update_charts_accounts_tax msgid "Tax that needs to be updated (new or updated in the template)." -msgstr "" +msgstr "Impuesto que necesita ser actualizado (nuevo o modificado en la plantilla)." #. module: account_chart_update #: selection:wizard.update.charts.accounts.tax,type:0 msgid "Tax to deactivate" -msgstr "" +msgstr "Impuesto a deactivar" #. module: account_chart_update #: model:ir.model.fields,field_description:account_chart_update.field_wizard_update_charts_accounts_tax_update_tax_id msgid "Tax to update" -msgstr "" +msgstr "Impuesto a actualizar" #. module: account_chart_update #: model:ir.model.fields,field_description:account_chart_update.field_wizard_update_charts_accounts_tax_ids #: model:ir.ui.view,arch_db:account_chart_update.view_update_multi_chart msgid "Taxes" -msgstr "" +msgstr "Impuestos" #. module: account_chart_update #: model:ir.ui.view,arch_db:account_chart_update.view_update_multi_chart -msgid "" -"This wizard will update your accounts, taxes and fiscal positions according " -"to the selected chart template" -msgstr "" +msgid "This wizard will update your accounts, taxes and fiscal positions according to the selected chart template" +msgstr "Este asistente actualizará sus cuentas, impuestos y posiciones fiscales de acuerdo con el plan contable seleccionado" #. module: account_chart_update -#: code:addons/account_chart_update/wizard/wizard_chart_update.py:506 +#: code:addons/account_chart_update/wizard/wizard_chart_update.py:501 #, python-format msgid "To deactivate: not in the template" -msgstr "" +msgstr "A deactivar: no está en la plantilla" #. module: account_chart_update #: model:ir.model.fields,field_description:account_chart_update.field_wizard_update_charts_accounts_account_type @@ -446,80 +446,80 @@ msgstr "Tipo" #. module: account_chart_update #: model:ir.model.fields,field_description:account_chart_update.field_wizard_update_charts_accounts_update_account msgid "Update accounts" -msgstr "" +msgstr "Actualizar cuentas" #. module: account_chart_update #: model:ir.actions.act_window,name:account_chart_update.action_wizard_update_chart msgid "Update chart of accounts" -msgstr "" +msgstr "Actualizar plan contable" #. module: account_chart_update #: model:ir.ui.view,arch_db:account_chart_update.view_account_config_settings msgid "Update chart template" -msgstr "" +msgstr "Actualizar plan contable" #. module: account_chart_update #: model:ir.model.fields,field_description:account_chart_update.field_wizard_update_charts_accounts_account_update_chart_wizard_id #: model:ir.model.fields,field_description:account_chart_update.field_wizard_update_charts_accounts_fiscal_position_update_chart_wizard_id #: model:ir.model.fields,field_description:account_chart_update.field_wizard_update_charts_accounts_tax_update_chart_wizard_id msgid "Update chart wizard" -msgstr "" +msgstr "Asistente de actualización de plan contable" #. module: account_chart_update #: model:ir.model.fields,field_description:account_chart_update.field_wizard_update_charts_accounts_update_fiscal_position msgid "Update fiscal positions" -msgstr "" +msgstr "Actualizar posiciones fiscales" #. module: account_chart_update #: model:ir.ui.view,arch_db:account_chart_update.view_update_multi_chart msgid "Update records?" -msgstr "" +msgstr "¿Actualizar registros?" #. module: account_chart_update #: model:ir.model.fields,field_description:account_chart_update.field_wizard_update_charts_accounts_update_tax msgid "Update taxes" -msgstr "" +msgstr "Actualizar impuestos" #. module: account_chart_update -#: code:addons/account_chart_update/wizard/wizard_chart_update.py:638 +#: code:addons/account_chart_update/wizard/wizard_chart_update.py:635 #, python-format msgid "Updated account %s." -msgstr "" +msgstr "Cuenta %s actualizada." #. module: account_chart_update #: model:ir.model.fields,field_description:account_chart_update.field_wizard_update_charts_accounts_updated_accounts msgid "Updated accounts" -msgstr "" +msgstr "Cuentas actualizadas" #. module: account_chart_update #: model:ir.model.fields,field_description:account_chart_update.field_wizard_update_charts_accounts_updated_fps msgid "Updated fiscal positions" -msgstr "" +msgstr "Posiciones fiscales actualizadas" #. module: account_chart_update -#: code:addons/account_chart_update/wizard/wizard_chart_update.py:593 +#: code:addons/account_chart_update/wizard/wizard_chart_update.py:588 #, python-format msgid "Updated tax %s." -msgstr "" +msgstr "Impuesto %s actualizado." #. module: account_chart_update #: model:ir.model.fields,field_description:account_chart_update.field_wizard_update_charts_accounts_updated_taxes msgid "Updated taxes" -msgstr "" +msgstr "Impuestos actualizados" #. module: account_chart_update #: selection:wizard.update.charts.accounts.account,type:0 #: selection:wizard.update.charts.accounts.fiscal.position,type:0 #: selection:wizard.update.charts.accounts.tax,type:0 msgid "Updated template" -msgstr "" +msgstr "Plantilla actualizada" #. module: account_chart_update #: selection:wizard.update.charts.accounts,state:0 msgid "Wizard completed" -msgstr "" +msgstr "Asistente completado" #. module: account_chart_update #: model:ir.model,name:account_chart_update.model_wizard_update_charts_accounts msgid "wizard.update.charts.accounts" -msgstr "" +msgstr "wizard.update.charts.accounts" diff --git a/account_chart_update/readme/CONTRIBUTORS.rst b/account_chart_update/readme/CONTRIBUTORS.rst new file mode 100644 index 000000000..f247d64d2 --- /dev/null +++ b/account_chart_update/readme/CONTRIBUTORS.rst @@ -0,0 +1,6 @@ +* Pedro M. Baeza +* Jairo Llopis +* Jacques-Etienne Baudoux +* Sylvain Van Hoof +* Nacho Muñoz + diff --git a/account_chart_update/readme/DESCRIPTION.rst b/account_chart_update/readme/DESCRIPTION.rst new file mode 100644 index 000000000..16bcfce38 --- /dev/null +++ b/account_chart_update/readme/DESCRIPTION.rst @@ -0,0 +1,12 @@ +This is a pretty useful tool to update Odoo installations after tax reforms +on the official 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. diff --git a/account_chart_update/readme/ROADMAP.rst b/account_chart_update/readme/ROADMAP.rst new file mode 100644 index 000000000..40b5a6a8a --- /dev/null +++ b/account_chart_update/readme/ROADMAP.rst @@ -0,0 +1,5 @@ +* Generate and update account reconcile models. +* Generate XML-ID for fiscal position tax and account mapping lines. +* Allow to select independently operations to perform (create, update, + deactivate). +* Detect fiscal positions to deactivate? diff --git a/account_chart_update/readme/USAGE.rst b/account_chart_update/readme/USAGE.rst new file mode 100644 index 000000000..011aafa72 --- /dev/null +++ b/account_chart_update/readme/USAGE.rst @@ -0,0 +1,7 @@ +The wizard, accesible from *Accounting > Settings > Update chart of accounts*, +lets the user select what kind of objects must be checked/updated, and whether +old records must be checked for changes and updates. + +It will display all the objects to be created / updated / deactivated with some +information about the detected differences, and allow the user to exclude +records individually. diff --git a/account_chart_update/static/description/index.html b/account_chart_update/static/description/index.html new file mode 100644 index 000000000..c0d18befa --- /dev/null +++ b/account_chart_update/static/description/index.html @@ -0,0 +1,457 @@ + + + + + + +Detect changes and update the Account Chart from a template + + + +
+

Detect changes and update the Account Chart from a template

+ + +

Mature License: AGPL-3 OCA/account-financial-tools Translate me on Weblate Try me on Runbot

+

This is a pretty useful tool to update Odoo installations after tax reforms +on the official 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.
  • +
+

Table of contents

+ +
+

Usage

+

The wizard, accesible from Accounting > Settings > Update chart of accounts, +lets the user select what kind of objects must be checked/updated, and whether +old records must be checked for changes and updates.

+

It will display all the objects to be created / updated / deactivated with some +information about the detected differences, and allow the user to exclude +records individually.

+
+
+

Known issues / Roadmap

+
    +
  • Generate and update account reconcile models.
  • +
  • Generate XML-ID for fiscal position tax and account mapping lines.
  • +
  • Allow to select independently operations to perform (create, update, +deactivate).
  • +
  • Detect fiscal positions to deactivate?
  • +
+
+
+

Bug Tracker

+

Bugs are tracked on GitHub Issues. +In case of trouble, please check there if your issue has already been reported. +If you spotted it first, help us smashing it by providing a detailed and welcomed +feedback.

+

Do not contact contributors directly about support or help with technical issues.

+
+
+

Credits

+
+

Authors

+
    +
  • Tecnativa
  • +
  • BCIM
  • +
  • Okia
  • +
+
+
+

Contributors

+ +
+
+

Maintainers

+

This module is maintained by the OCA.

+Odoo Community Association +

OCA, or the Odoo Community Association, is a nonprofit organization whose +mission is to support the collaborative development of Odoo features and +promote its widespread use.

+

This module is part of the OCA/account-financial-tools project on GitHub.

+

You are welcome to contribute. To learn how please visit https://odoo-community.org/page/Contribute.

+
+
+
+ + diff --git a/account_chart_update/tests/__init__.py b/account_chart_update/tests/__init__.py new file mode 100644 index 000000000..243c1b0ff --- /dev/null +++ b/account_chart_update/tests/__init__.py @@ -0,0 +1,4 @@ +# -*- coding: utf-8 -*- +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). + +from . import test_account_chart_update diff --git a/account_chart_update/tests/test_account_chart_update.py b/account_chart_update/tests/test_account_chart_update.py new file mode 100644 index 000000000..e3b2d0268 --- /dev/null +++ b/account_chart_update/tests/test_account_chart_update.py @@ -0,0 +1,306 @@ +# -*- coding: utf-8 -*- +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). + +from odoo import fields +from odoo.tests import common + + +class TestAccountChartUpdate(common.HttpCase): + at_install = False + post_install = True + + def _create_xml_id(self, record): + return self.env['ir.model.data'].create({ + 'module': 'account_chart_update', + 'name': "%s-%s" % (record._table, record.id), + 'model': record._name, + 'res_id': record.id, + }) + + def _create_account_tmpl(self, name, code, user_type, chart_template): + record = self.env['account.account.template'].create({ + 'name': name, + 'code': code, + 'user_type_id': user_type.id, + 'chart_template_id': chart_template and chart_template.id, + }) + self._create_xml_id(record) + return record + + def _create_tax_tmpl(self, name, chart_template): + record = self.env['account.tax.template'].create({ + 'name': name, + 'amount': 0, + 'chart_template_id': chart_template.id, + 'tax_group_id': self.env.ref('account.tax_group_taxes').id, + }) + self._create_xml_id(record) + return record + + def _create_fp_tmpl(self, name, chart_template): + record = self.env['account.fiscal.position.template'].create({ + 'name': name, + 'chart_template_id': chart_template.id, + }) + self._create_xml_id(record) + return record + + def setUp(self): + super(TestAccountChartUpdate, self).setUp() + # Make sure user is in English + self.env.user.lang = 'en_US' + self.account_type = self.env['account.account.type'].create({ + 'name': 'Test account_chart_update account type', + }) + self.account_template = self._create_account_tmpl( + 'Test', '100000', self.account_type, False, + ) + self.chart_template = self.env['account.chart.template'].create({ + 'name': 'Test account_chart_update chart', + 'currency_id': self.env.ref('base.EUR').id, + 'code_digits': 6, + 'transfer_account_id': self.account_template.id, + }) + self.account_template.chart_template_id = self.chart_template.id + self.account_template_pl = self._create_account_tmpl( + 'Undistributed Profits/Losses', '999999', + self.env.ref("account.data_unaffected_earnings"), + self.chart_template, + ) + self.tax_template = self._create_tax_tmpl( + 'Test tax', self.chart_template, + ) + self.fp_template = self._create_fp_tmpl('Test fp', self.chart_template) + self.fp_template_tax = self.env[ + 'account.fiscal.position.tax.template' + ].create({ + 'tax_src_id': self.tax_template.id, + 'position_id': self.fp_template.id, + }) + self._create_xml_id(self.fp_template_tax) + self.fp_template_account = self.env[ + 'account.fiscal.position.account.template' + ].create({ + 'account_src_id': self.account_template.id, + 'account_dest_id': self.account_template.id, + 'position_id': self.fp_template.id, + }) + self._create_xml_id(self.fp_template_account) + self.tax_group = self.env['account.tax.group'].create({ + 'name': 'Test tax group', + }) + self.company = self.env['res.company'].create({ + 'name': 'Test account_chart_update company', + 'currency_id': self.chart_template.currency_id.id, + }) + # Load chart of template into company + wizard = self.env['wizard.multi.charts.accounts'].create({ + 'company_id': self.company.id, + 'chart_template_id': self.chart_template.id, + 'code_digits': self.chart_template.code_digits, + 'transfer_account_id': self.account_template.id, + 'currency_id': self.chart_template.currency_id.id, + 'bank_account_code_prefix': '572', + 'cash_account_code_prefix': '570', + }) + wizard.onchange_chart_template_id() + wizard.execute() + self.tax = self.env['account.tax'].search([ + ('name', '=', self.tax_template.name), + ('company_id', '=', self.company.id), + ]) + self.account = self.env['account.account'].search([ + ('code', '=', self.account_template.code), + ('company_id', '=', self.company.id), + ]) + self.fp = self.env['account.fiscal.position'].search([ + ('name', '=', self.fp_template.name), + ('company_id', '=', self.company.id), + ]) + # Prepare wizard values + self.wizard_obj = self.env['wizard.update.charts.accounts'] + self.wizard_vals = { + 'company_id': self.company.id, + 'chart_template_id': self.chart_template.id, + 'code_digits': 6, + 'lang': 'en_US' + } + + def test_chart_update(self): + # Test no changes + wizard = self.wizard_obj.create(self.wizard_vals) + wizard.action_find_records() + self.assertEqual(wizard.state, 'ready') + self.assertFalse(wizard.tax_ids) + self.assertFalse(wizard.account_ids) + self.assertFalse(wizard.fiscal_position_ids) + wizard.unlink() + # Add templates + new_tax_tmpl = self._create_tax_tmpl( + 'Test tax 2', self.chart_template, + ) + new_account_tmpl = self._create_account_tmpl( + 'Test account 2', '333333', self.account_type, self.chart_template, + ) + new_fp = self._create_fp_tmpl('Test fp 2', self.chart_template) + fp_template_tax = self.env[ + 'account.fiscal.position.tax.template' + ].create({ + 'tax_src_id': self.tax_template.id, + 'position_id': new_fp.id, + }) + self._create_xml_id(fp_template_tax) + fp_template_account = self.env[ + 'account.fiscal.position.account.template' + ].create({ + 'account_src_id': self.account_template.id, + 'account_dest_id': self.account_template.id, + 'position_id': new_fp.id, + }) + self._create_xml_id(fp_template_account) + # Check that no action is performed if the option is not selected + wizard_vals = self.wizard_vals.copy() + wizard_vals.update({ + 'update_tax': False, + 'update_account': False, + 'update_fiscal_position': False, + }) + wizard = self.wizard_obj.create(wizard_vals) + wizard.action_find_records() + self.assertFalse(wizard.tax_ids) + self.assertFalse(wizard.account_ids) + self.assertFalse(wizard.fiscal_position_ids) + wizard.unlink() + # Now do the real one for detecting additions + wizard = self.wizard_obj.create(self.wizard_vals) + wizard.action_find_records() + self.assertTrue(wizard.tax_ids) + self.assertEqual(wizard.tax_ids.tax_id, new_tax_tmpl) + self.assertEqual(wizard.tax_ids.type, 'new') + self.assertTrue(wizard.account_ids) + self.assertEqual(wizard.account_ids.account_id, new_account_tmpl) + self.assertEqual(wizard.tax_ids.type, 'new') + self.assertTrue(wizard.fiscal_position_ids) + self.assertEqual(wizard.fiscal_position_ids.fiscal_position_id, new_fp) + self.assertEqual(wizard.fiscal_position_ids.type, 'new') + wizard.action_update_records() + self.assertEqual(wizard.state, 'done') + self.assertEqual(wizard.new_taxes, 1) + self.assertEqual(wizard.new_accounts, 1) + self.assertEqual(wizard.new_fps, 1) + self.assertTrue(wizard.log) + new_tax = self.env['account.tax'].search([ + ('name', '=', new_tax_tmpl.name), + ('company_id', '=', self.company.id), + ]) + self.assertTrue(new_tax) + new_account = self.env['account.account'].search([ + ('code', '=', new_account_tmpl.code), + ('company_id', '=', self.company.id), + ]) + self.assertTrue(new_account) + fp = self.env['account.fiscal.position'].search([ + ('name', '=', new_fp.name), + ('company_id', '=', self.company.id), + ]) + self.assertTrue(fp) + self.assertTrue(fp.tax_ids) + self.assertTrue(fp.account_ids) + wizard.unlink() + # Update objects + self.tax_template.description = "Test description" + self.tax_template.tax_group_id = self.tax_group.id + self.tax_template.refund_account_id = new_account_tmpl.id + self.account_template.name = "Other name" + self.fp_template.note = "Test note" + self.fp_template.account_ids.account_dest_id = new_account_tmpl.id + self.fp_template.tax_ids.tax_dest_id = self.tax_template.id + wizard = self.wizard_obj.create(self.wizard_vals) + wizard.action_find_records() + self.assertTrue(wizard.tax_ids) + self.assertEqual(wizard.tax_ids.tax_id, self.tax_template) + self.assertEqual(wizard.tax_ids.type, 'updated') + self.assertTrue(wizard.account_ids) + self.assertEqual(wizard.account_ids.account_id, self.account_template) + self.assertEqual(wizard.account_ids.type, 'updated') + self.assertTrue(wizard.fiscal_position_ids) + self.assertTrue(wizard.fiscal_position_ids.type, 'updated') + self.assertEqual( + wizard.fiscal_position_ids.fiscal_position_id, self.fp_template, + ) + self.assertEqual(wizard.fiscal_position_ids.type, 'updated') + wizard.action_update_records() + self.assertEqual(wizard.updated_taxes, 1) + self.assertEqual(wizard.updated_accounts, 1) + self.assertEqual(wizard.updated_fps, 1) + self.assertEqual(self.tax.description, self.tax_template.description) + self.assertEqual(self.tax.tax_group_id, self.tax_group) + self.assertEqual(self.tax.refund_account_id, new_account) + self.assertEqual(self.account.name, self.account_template.name) + self.assertEqual(self.fp.note, self.fp_template.note) + self.assertEqual(self.fp.account_ids.account_dest_id, new_account) + self.assertEqual(self.fp.tax_ids.tax_dest_id, self.tax) + wizard.unlink() + # Remove objects + new_tax_tmpl.unlink() + wizard = self.wizard_obj.create(self.wizard_vals) + wizard.action_find_records() + self.assertTrue(wizard.tax_ids) + self.assertEqual(wizard.tax_ids.update_tax_id, new_tax) + self.assertEqual(wizard.tax_ids.type, 'deleted') + wizard.action_update_records() + self.assertEqual(wizard.deleted_taxes, 1) + self.assertFalse(new_tax.active) + wizard.unlink() + # Errors on account update + self.account_template.reconcile = True + self.env['account.move'].create({ + 'name': 'Test move', + 'journal_id': self.env['account.journal'].search([ + ('company_id', '=', self.company.id), + ], limit=1).id, + 'date': fields.Date.today(), + 'line_ids': [ + (0, 0, { + 'account_id': self.account.id, + 'name': 'Test move line', + 'debit': 10, + 'credit': 0, + }), + (0, 0, { + 'account_id': self.account.id, + 'name': 'Test move line2', + 'debit': 0, + 'credit': 10, + }), + ] + }) + self.tax_template.description = "Other description" + wizard = self.wizard_obj.create(self.wizard_vals) + wizard.action_find_records() + with self.assertRaises(Exception): + wizard.action_update_records() + # Errors on account update - continuing after that + wizard.continue_on_errors = True + wizard.action_update_records() + self.assertFalse(self.account.reconcile) + self.assertEqual(self.tax.description, self.tax_template.description) + self.assertEqual(wizard.rejected_updated_account_number, 1) + self.assertEqual(wizard.updated_accounts, 0) + wizard.unlink() + # Errors on account_creation + self.account_template.reconcile = False + new_account_tmpl_2 = self._create_account_tmpl( + 'Test account 3', '444444', self.account_type, self.chart_template, + ) + wizard = self.wizard_obj.create(self.wizard_vals) + wizard.action_find_records() + self.assertEqual(wizard.account_ids.type, 'new') + new_account_tmpl_2.code = '333333' # Trick the code for forcing error + with self.assertRaises(Exception): + wizard.action_update_records() + wizard.continue_on_errors = True + wizard.action_update_records() + self.assertEqual(wizard.rejected_new_account_number, 1) + self.assertEqual(wizard.new_accounts, 0) + wizard.unlink() diff --git a/account_chart_update/wizard/__init__.py b/account_chart_update/wizard/__init__.py index 3c52c6a91..3c2acb990 100644 --- a/account_chart_update/wizard/__init__.py +++ b/account_chart_update/wizard/__init__.py @@ -1,6 +1,4 @@ # -*- coding: utf-8 -*- -# © 2010 Zikzakmedia S.L. (http://www.zikzakmedia.com) -# © 2010 Pexego Sistemas Informáticos S.L.(http://www.pexego.es) # License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). from . import wizard_chart_update diff --git a/account_chart_update/wizard/wizard_chart_update.py b/account_chart_update/wizard/wizard_chart_update.py index 492452488..bf16b05ee 100644 --- a/account_chart_update/wizard/wizard_chart_update.py +++ b/account_chart_update/wizard/wizard_chart_update.py @@ -6,14 +6,17 @@ # © 2015 Antonio Espinosa # © 2016 Jairo Llopis # © 2016 Jacques-Etienne Baudoux +# Copyright 2018 Tecnativa - Pedro M. Baeza # License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). -from openerp import models, fields, api, exceptions, _, tools +from odoo import _, api, exceptions, fields, models, tools +from odoo.tools import config from contextlib import closing from cStringIO import StringIO import logging _logger = logging.getLogger(__name__) +EXCEPTION_TEXT = u"Traceback (most recent call last)" class WizardUpdateChartsAccounts(models.TransientModel): @@ -76,12 +79,14 @@ class WizardUpdateChartsAccounts(models.TransientModel): new_accounts = fields.Integer( string='New accounts', compute="_compute_new_accounts_count") + rejected_new_account_number = fields.Integer() new_fps = fields.Integer( string='New fiscal positions', compute="_compute_new_fps_count") updated_taxes = fields.Integer( string='Updated taxes', compute="_compute_updated_taxes_count") + rejected_updated_account_number = fields.Integer() updated_accounts = fields.Integer( string='Updated accounts', compute="_compute_updated_accounts_count") @@ -115,7 +120,8 @@ class WizardUpdateChartsAccounts(models.TransientModel): @api.depends('account_ids') def _compute_new_accounts_count(self): self.new_accounts = len( - self.account_ids.filtered(lambda x: x.type == 'new')) + self.account_ids.filtered(lambda x: x.type == 'new') + ) - self.rejected_new_account_number @api.multi @api.depends('fiscal_position_ids') @@ -133,7 +139,8 @@ class WizardUpdateChartsAccounts(models.TransientModel): @api.depends('account_ids') def _compute_updated_accounts_count(self): self.updated_accounts = len( - self.account_ids.filtered(lambda x: x.type == 'updated')) + self.account_ids.filtered(lambda x: x.type == 'updated') + ) - self.rejected_updated_account_number @api.multi @api.depends('fiscal_position_ids') @@ -208,30 +215,35 @@ class WizardUpdateChartsAccounts(models.TransientModel): def action_update_records(self): """Action that creates/updates/deletes the selected elements.""" self = self.with_context(lang=self.lang) - + self.rejected_new_account_number = 0 + self.rejected_updated_account_number = 0 with closing(StringIO()) as log_output: handler = logging.StreamHandler(log_output) _logger.addHandler(handler) - # Create or update the records. if self.update_tax: self._update_taxes() + perform_rest = True if self.update_account: self._update_accounts() - if self.update_fiscal_position: + if (EXCEPTION_TEXT in log_output.getvalue() and + not self.continue_on_errors): # Abort early + perform_rest = False + # Clear this cache for avoiding incorrect account hits (as it was + # queried before account creation) + self.find_account_by_templates.clear_cache(self) + if self.update_tax and perform_rest: + self._update_taxes_pending_for_accounts() + if self.update_fiscal_position and perform_rest: self._update_fiscal_positions() - # Store new chart in the company self.company_id.chart_template_id = self.chart_template_id - _logger.removeHandler(handler) self.log = log_output.getvalue() - # Check if errors where detected and wether we should stop. - if self.log and not self.continue_on_errors: + if EXCEPTION_TEXT in self.log and not self.continue_on_errors: raise exceptions.Warning( _("One or more errors detected!\n\n%s") % self.log) - # Store the data and go to the next step. self.state = 'done' return self._reopen() @@ -295,11 +307,11 @@ class WizardUpdateChartsAccounts(models.TransientModel): pos_id = self.find_fp_by_templates(tpl.position_id) src_id = self.find_account_by_templates(tpl.account_src_id) dest_id = self.find_account_by_templates(tpl.account_dest_id) - mappings = self.env["account.fiscal.position.account"].search([ + existing = self.env["account.fiscal.position.account"].search([ ("position_id", "=", pos_id), ("account_src_id", "=", src_id), + ("account_dest_id", "=", dest_id), ]) - existing = mappings.filtered(lambda x: x.account_dest_id == dest) if not existing: # create a new mapping result.append((0, 0, { @@ -322,11 +334,11 @@ class WizardUpdateChartsAccounts(models.TransientModel): pos_id = self.find_fp_by_templates(tpl.position_id) src_id = self.find_tax_by_templates(tpl.tax_src_id) dest_id = self.find_tax_by_templates(tpl.tax_dest_id) - mappings = self.env["account.fiscal.position.tax"].search([ + existing = self.env["account.fiscal.position.tax"].search([ ("position_id", "=", pos_id), ("tax_src_id", "=", src_id), + ("tax_dest_id", "=", dest_id), ]) - existing = mappings.filtered(lambda x: x.tax_dest_id.id == dest_id) if not existing: # create a new mapping result.append((0, 0, { @@ -342,40 +354,24 @@ class WizardUpdateChartsAccounts(models.TransientModel): return result @api.model - @tools.ormcache("template") - def fields_to_ignore(self, template): + @tools.ormcache("name") + def fields_to_ignore(self, template, name): """Get fields that will not be used when checking differences. - :param str template: - The template record. - - :return set: - Fields to ignore in diff. + :param str template: A template record. + :param str name: The name of the template model. + :return set: Fields to ignore in diff. """ - specials = { + specials_mapping = { + "account.tax.template": { + "children_tax_ids", + }, "account.account.template": { "code", }, - "account.tax.template": { - "account_id", - "refund_account_id", - } } - to_include = { - "account.fiscal.position.template": [ - 'tax_ids', - 'account_ids', - ], - } - specials = ({"display_name", "__last_update"} | - specials.get(template._name, set())) - for key, field in template._fields.iteritems(): - if (template._name in to_include and - key in to_include[template._name]): - continue - if ".template" in field.get_description(self.env).get( - "relation", ""): - specials.add(key) + specials = ({"display_name", "__last_update", "company_id"} | + specials_mapping.get(name, set())) return set(models.MAGIC_COLUMNS) | specials @api.model @@ -391,43 +387,44 @@ class WizardUpdateChartsAccounts(models.TransientModel): Fields that are different in both records, and the expected value. """ result = dict() - ignore = self.fields_to_ignore(template) + ignore = self.fields_to_ignore(template, template._name) for key, field in template._fields.iteritems(): if key in ignore: continue - relation = expected = t = None - # Code must be padded to check equality - if key == "code": - expected = self.padded_code(template.code) + expected = t = None # Translate template records to reals for comparison - else: - relation = field.get_description(self.env).get("relation", "") - if relation: - if ".tax.template" in relation: - t = "tax" - elif ".account.template" in relation: - t = "account" - if t: - find = getattr( - self, - "find_%s%s_by_templates" % ( - "fp_" if ".fiscal.position" in relation - else "", - t)) - if ".fiscal.position" in relation: - # Special case - expected = find(template[key], real[key]) - else: - expected = find(template[key]) + relation = field.get_description(self.env).get("relation", "") + if relation: + if ".tax.template" in relation: + t = "tax" + elif ".account.template" in relation: + t = "account" + if t: + find = getattr( + self, + "find_%s%s_by_templates" % ( + "fp_" if ".fiscal.position" in relation + else "", + t)) + if ".fiscal.position" in relation: + # Special case: if something is returned, then + # there's any difference, so it will get non equal + # when comparing, although we get the warning + # "Comparing apples with oranges" + expected = find(template[key], real[key]) + else: + exp_id = find(template[key]) + expected = self.env[relation[:-9]].browse(exp_id) # Register detected differences try: - if not relation: - if expected is not None and expected != real[key]: + if expected is not None: + if expected != [] and expected != real[key]: result[key] = expected - elif template[key] != real[key]: - result[key] = template[key] - elif expected: - result[key] = expected + elif template[key] != real[key]: + result[key] = template[key] + if isinstance(result.get(key, False), models.Model): + # Avoid to cache recordset references + result[key] = result[key].id except KeyError: pass return result @@ -464,12 +461,10 @@ class WizardUpdateChartsAccounts(models.TransientModel): """Search for, and load, tax templates to create/update/delete.""" found_taxes_ids = [] self.tax_ids.unlink() - # Search for changes between template and real tax for template in self.chart_template_ids.mapped("tax_template_ids"): # Check if the template matches a real tax tax_id = self.find_tax_by_templates(template) - if not tax_id: # Tax to be created self.tax_ids.create({ @@ -480,7 +475,6 @@ class WizardUpdateChartsAccounts(models.TransientModel): }) else: found_taxes_ids.append(tax_id) - # Check the tax for changes tax = self.env['account.tax'].browse(tax_id) notes = self.diff_notes(template, tax) @@ -493,14 +487,13 @@ class WizardUpdateChartsAccounts(models.TransientModel): 'update_tax_id': tax_id, 'notes': notes, }) - # search for taxes not in the template and propose them for # deactivation - taxes_to_delete = self.env['account.tax'].search( + taxes_to_deactivate = self.env['account.tax'].search( [('company_id', '=', self.company_id.id), ("id", "not in", found_taxes_ids), ("active", "=", True)]) - for tax in taxes_to_delete: + for tax in taxes_to_deactivate: self.tax_ids.create({ 'update_chart_wizard_id': self.id, 'type': 'deleted', @@ -512,11 +505,9 @@ class WizardUpdateChartsAccounts(models.TransientModel): def _find_accounts(self): """Load account templates to create/update.""" self.account_ids.unlink() - for template in self.chart_template_ids.mapped("account_ids"): # Search for a real account that matches the template account_id = self.find_account_by_templates(template) - if not account_id: # Account to be created self.account_ids.create({ @@ -581,35 +572,20 @@ class WizardUpdateChartsAccounts(models.TransientModel): # Deactivate tax if wiz_tax.type == 'deleted': tax.active = False - _logger.debug(_("Deactivated tax %s."), tax) + _logger.info(_("Deactivated tax %s."), "'%s'" % tax.name) continue - # Create tax if wiz_tax.type == 'new': - tax = template._generate_tax(self.company_id) - tax = tax['tax_template_to_tax'][template.id] - _logger.debug(_("Created tax %s."), template.name) - + template._generate_tax(self.company_id) + _logger.info(_("Created tax %s."), "'%s'" % template.name) # Update tax else: for key, value in self.diff_fields(template, tax).iteritems(): + # We defer update because account might not be created yet + if key in {'account_id', 'refund_account_id'}: + continue tax[key] = value - _logger.debug(_("Updated tax %s."), template.name) - wiz_tax.update_tax_id = tax - - def _create_account_from_template(self, account_template): - return self.env["account.account"].create({ - 'name': account_template.name, - 'currency_id': account_template.currency_id.id, - 'code': self.padded_code(account_template.code), - 'user_type_id': account_template.user_type_id.id, - 'reconcile': account_template.reconcile, - 'note': account_template.note, - 'tax_ids': [ - (6, 0, [self.find_tax_by_templates(account_template.tax_ids)]), - ], - 'company_id': self.company_id.id, - }) + _logger.info(_("Updated tax %s."), "'%s'" % template.name) @api.multi def _update_accounts(self): @@ -619,18 +595,35 @@ class WizardUpdateChartsAccounts(models.TransientModel): wiz_account.account_id) if wiz_account.type == 'new': # Create the account + tax_template_ref = { + tax.id: self.find_tax_by_templates(tax) for tax in + template.tax_ids + } + vals = self.chart_template_id._get_account_vals( + self.company_id, template, + self.padded_code(template.code), + tax_template_ref, + ) try: with self.env.cr.savepoint(): - account = ( - self._create_account_from_template( - template)) - _logger.debug( + self.chart_template_id.create_record_with_xmlid( + self.company_id, template, 'account.account', vals, + ) + _logger.info( _("Created account %s."), - account.code) - except exceptions.except_orm: - _logger.exception( - _("Exception creating account %s."), - template.code) + "'%s - %s'" % (vals['code'], vals['name']), + ) + except Exception: + self.rejected_new_account_number += 1 + if config['test_enable']: + _logger.info(EXCEPTION_TEXT) + else: # pragma: no cover + _logger.exception( + "ERROR: " + _("Exception creating account %s."), + "'%s - %s'" % (template.code, template.name), + ) + if not self.continue_on_errors: + break else: # Update the account try: @@ -638,15 +631,21 @@ class WizardUpdateChartsAccounts(models.TransientModel): for key, value in (self.diff_fields(template, account) .iteritems()): account[key] = value - _logger.debug(_("Updated account %s."), account) - except exceptions.except_orm: - _logger.exception( - _("Exception writing account %s."), - account) - wiz_account.update_account_id = account - - if self.update_tax: - self._update_taxes_pending_for_accounts() + _logger.info( + _("Updated account %s."), + "'%s - %s'" % (account.code, account.name), + ) + except Exception: + self.rejected_updated_account_number += 1 + if config['test_enable']: + _logger.info(EXCEPTION_TEXT) + else: # pragma: no cover + _logger.exception( + "ERROR: " + _("Exception writing account %s."), + "'%s - %s'" % (account.code, account.name), + ) + if not self.continue_on_errors: + break @api.multi def _update_taxes_pending_for_accounts(self): @@ -657,10 +656,15 @@ class WizardUpdateChartsAccounts(models.TransientModel): for wiz_tax in self.tax_ids: if wiz_tax.type == "deleted" or not wiz_tax.update_tax_id: continue - - for field in ("account_id", "refund_account_id"): - wiz_tax.update_tax_id[field] = ( - self.find_account_by_templates(wiz_tax.tax_id[field])) + template = wiz_tax.tax_id + tax = wiz_tax.update_tax_id + done = False + for key, value in self.diff_fields(template, tax).iteritems(): + if key in {'account_id', 'refund_account_id'}: + tax[key] = value + done = True + if done: + _logger.info(_("Post-updated tax %s."), "'%s'" % tax.name) def _prepare_fp_vals(self, fp_template): # Tax mappings @@ -698,16 +702,17 @@ class WizardUpdateChartsAccounts(models.TransientModel): wiz_fp.fiscal_position_id) if wiz_fp.type == 'new': # Create a new fiscal position - fp = self.env['account.fiscal.position'].create( - self._prepare_fp_vals(template)) + self.chart_template_id.create_record_with_xmlid( + self.company_id, template, 'account.fiscal.position', + self._prepare_fp_vals(template), + ) else: # Update the given fiscal position for key, value in self.diff_fields(template, fp).iteritems(): fp[key] = value - wiz_fp.update_fiscal_position_id = fp - _logger.debug( + _logger.info( _("Created or updated fiscal position %s."), - template.name) + "'%s'" % template.name) class WizardUpdateChartsAccountsTax(models.TransientModel):