From 068f3edff90a5fa79ff194c93bba8eaab99b732e Mon Sep 17 00:00:00 2001 From: "Claire Bretton (clbr)" Date: Thu, 22 Dec 2022 22:37:47 +0000 Subject: [PATCH] [FIX] account,l10n_lu: Adds a migration for the l10n_lu that will get the latest taxes MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit All taxes change in Luxembourg, we added a script that will create or update taxes so the clients don't have to manually update everything or ask the support to do it for them. We don't modify custom taxes. closes odoo/odoo#108699 Task: 3098941 Related: #104833 X-original-commit: c44a33d67065e20c4ded2a229d0663191c91a0a0 Signed-off-by: William André (wan) Signed-off-by: Claire Bretton --- addons/account/i18n/account.pot | 15 +++ addons/account/models/chart_template.py | 122 ++++++++++++++++++ addons/l10n_lu/__manifest__.py | 2 +- .../2.1/post-migrate_update_taxes.py | 6 + 4 files changed, 144 insertions(+), 1 deletion(-) create mode 100644 addons/l10n_lu/migrations/2.1/post-migrate_update_taxes.py diff --git a/addons/account/i18n/account.pot b/addons/account/i18n/account.pot index 295fe8d8fa5..7a0325239c7 100644 --- a/addons/account/i18n/account.pot +++ b/addons/account/i18n/account.pot @@ -10233,6 +10233,15 @@ msgstr "" msgid "Please create new accounts from the Chart of Accounts menu." msgstr "" +#. module: account +#: code:addons/account/models/chart_template.py:0 +#, python-format +msgid "" +"Please check these taxes. They might be outdated. We did not update them. " +"Indeed, they do not exactly match the taxes of the original version of the localization module." +"
You might want to archive or adapt them.
    " +msgstr "" + #. module: account #: code:addons/account/models/account_payment.py:0 #, python-format @@ -15434,6 +15443,12 @@ msgstr "" msgid "You should update this document to reflect your T&C." msgstr "" +#. module: account +#: code:addons/account/models/chart_template.py:0 +#, python-format +msgid "Your taxes have been updated !" +msgstr "" + #. module: account #: model_terms:ir.ui.view,arch_db:account.view_account_position_form msgid "Zip Range" diff --git a/addons/account/models/chart_template.py b/addons/account/models/chart_template.py index feda64cb8f0..ade889af9cb 100644 --- a/addons/account/models/chart_template.py +++ b/addons/account/models/chart_template.py @@ -7,6 +7,7 @@ from odoo.exceptions import UserError, ValidationError from odoo.http import request from odoo.addons.account.models.account_tax import TYPE_TAX_USE from odoo.addons.account.models.account_account import ACCOUNT_CODE_REGEX +from odoo.tools import html_escape import logging import re @@ -32,6 +33,127 @@ def preserve_existing_tags_on_taxes(cr, registry, module): if xml_records: cr.execute("update ir_model_data set noupdate = 't' where id in %s", [tuple(xml_records.ids)]) +def update_taxes_from_templates(cr, chart_template_xmlid): + def _create_tax_from_template(company, template, old_tax=None): + """ + Create a new tax from template with template xmlid, if there was already an old tax with that xmlid we + remove the xmlid from it but don't modify anything else. + """ + def _remove_xml_id(xml_id): + module, name = xml_id.split(".", 1) + env['ir.model.data'].search([('module', '=', module), ('name', '=', name)]).unlink() + + template_vals = template._get_tax_vals_complete(company) + chart_template = env["account.chart.template"].with_context(default_company_id=company.id) + if old_tax: + xml_id = old_tax.get_external_id().get(old_tax.id) + if xml_id: + _remove_xml_id(xml_id) + chart_template.create_record_with_xmlid(company, template, "account.tax", template_vals) + + def _update_tax_from_template(template, tax): + # -> update the tax : we only updates tax tags + tax_rep_lines = tax.invoice_repartition_line_ids + tax.refund_repartition_line_ids + template_rep_lines = template.invoice_repartition_line_ids + template.refund_repartition_line_ids + for tax_line, template_line in zip(tax_rep_lines, template_rep_lines): + tags_to_add = template_line._get_tags_to_add() + tags_to_unlink = tax_line.tag_ids + if tags_to_add != tags_to_unlink: + tax_line.write({"tag_ids": [(6, 0, tags_to_add.ids)]}) + _cleanup_tags(tags_to_unlink) + + def _get_template_to_tax_xmlid_mapping(company): + """ + This function uses ir_model_data to return a mapping between the tax templates and the taxes, using their xmlid + :returns: { + account.tax.template.id: account.tax.id + } + """ + env['ir.model.data'].flush_model() + env.cr.execute( + """ + SELECT template.res_id AS template_res_id, + tax.res_id AS tax_res_id + FROM ir_model_data tax + JOIN ir_model_data template + ON template.name = substr(tax.name, strpos(tax.name, '_') + 1) + WHERE tax.model = 'account.tax' + AND tax.name LIKE %s + -- tax.name is of the form: {company_id}_{account.tax.template.name} + """, + [r"%s\_%%" % company.id], + ) + tuples = env.cr.fetchall() + return dict(tuples) + + def _is_tax_and_template_same(template, tax): + """ + This function compares account.tax and account.tax.template repartition lines. + A tax is considered the same as the template if they have the same: + - amount_type + - amount + - repartition lines percentages in the same order + """ + tax_rep_lines = tax.invoice_repartition_line_ids + tax.refund_repartition_line_ids + template_rep_lines = template.invoice_repartition_line_ids + template.refund_repartition_line_ids + return ( + tax.amount_type == template.amount_type + and tax.amount == template.amount + and len(tax_rep_lines) == len(template_rep_lines) + and all( + rep_line_tax.factor_percent == rep_line_template.factor_percent + for rep_line_tax, rep_line_template in zip(tax_rep_lines, template_rep_lines) + ) + ) + + def _cleanup_tags(tags): + """ + Checks if the tags are still used in taxes or move lines. If not we delete it. + """ + for tag in tags: + tax_using_tag = env['account.tax.repartition.line'].sudo().search([('tag_ids', 'in', tag.id)], limit=1) + aml_using_tag = env['account.move.line'].sudo().search([('tax_tag_ids', 'in', tag.id)], limit=1) + report_expr_using_tag = tag._get_related_tax_report_expressions() + if not (aml_using_tag or tax_using_tag or report_expr_using_tag): + tag.unlink() + + def _notify_accountant_managers(taxes_to_check): + accountant_manager_group = env.ref("account.group_account_manager") + partner_managers_ids = accountant_manager_group.users.mapped('partner_id') + odoobot = env.ref('base.partner_root') + message_body = _( + "Please check these taxes. They might be outdated. We did not update them. " + "Indeed, they do not exactly match the taxes of the original version of the localization module.
    " + "You might want to archive or adapt them.
      " + ) + for account_tax in taxes_to_check: + message_body += f"
    • {html_escape(account_tax.name)}
    • " + message_body += "
    " + env['mail.thread'].message_notify( + subject=_('Your taxes have been updated !'), + author_id=odoobot.id, + body=message_body, + partner_ids=[partner.id for partner in partner_managers_ids], + ) + + env = api.Environment(cr, SUPERUSER_ID, {}) + chart_template_id = env['ir.model.data']._xmlid_to_res_id(chart_template_xmlid) + companies = env['res.company'].search([('chart_template_id', '=', chart_template_id)]) + outdated_taxes = [] + for company in companies: + template_to_tax = _get_template_to_tax_xmlid_mapping(company) + templates = env['account.tax.template'].with_context(active_test=False).search([("chart_template_id", "=", chart_template_id)]) + for template in templates: + tax = env["account.tax"].browse(template_to_tax.get(template.id)) + if not tax or not _is_tax_and_template_same(template, tax): + _create_tax_from_template(company, template, old_tax=tax) + if tax: + outdated_taxes.append(tax) + else: + _update_tax_from_template(template, tax) + if outdated_taxes: + _notify_accountant_managers(outdated_taxes) + # --------------------------------------------------------------- # Account Templates: Account, Tax, Tax Code and chart. + Wizard # --------------------------------------------------------------- diff --git a/addons/l10n_lu/__manifest__.py b/addons/l10n_lu/__manifest__.py index 54178923efd..8c12cf61729 100644 --- a/addons/l10n_lu/__manifest__.py +++ b/addons/l10n_lu/__manifest__.py @@ -7,7 +7,7 @@ { 'name': 'Luxembourg - Accounting', - 'version': '2.0', + 'version': '2.1', 'category': 'Accounting/Localizations/Account Charts', 'description': """ This is the base module to manage the accounting chart for Luxembourg. diff --git a/addons/l10n_lu/migrations/2.1/post-migrate_update_taxes.py b/addons/l10n_lu/migrations/2.1/post-migrate_update_taxes.py new file mode 100644 index 00000000000..a36a459d145 --- /dev/null +++ b/addons/l10n_lu/migrations/2.1/post-migrate_update_taxes.py @@ -0,0 +1,6 @@ +# Part of Odoo. See LICENSE file for full copyright and licensing details. +from odoo.addons.account.models.chart_template import update_taxes_from_templates + + +def migrate(cr, version): + update_taxes_from_templates(cr, 'l10n_lu.lu_2011_chart_1')