diff --git a/account_chart_update/README.rst b/account_chart_update/README.rst
index 51763217f..bbf5fe62d 100644
--- a/account_chart_update/README.rst
+++ b/account_chart_update/README.rst
@@ -14,13 +14,13 @@ Detect changes and update the Account Chart from a template
: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/12.0/account_chart_update
+ :target: https://github.com/OCA/account-financial-tools/tree/13.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-12-0/account-financial-tools-12-0-account_chart_update
+ :target: https://translation.odoo-community.org/projects/account-financial-tools-13-0/account-financial-tools-13-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/12.0
+ :target: https://runbot.odoo-community.org/runbot/92/13.0
:alt: Try me on Runbot
|badge1| |badge2| |badge3| |badge4| |badge5|
@@ -69,7 +69,7 @@ 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 `_.
+`feedback `_.
Do not contact contributors directly about support or help with technical issues.
@@ -110,6 +110,6 @@ 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.
+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 8dbc72fff..bed5720fd 100644
--- a/account_chart_update/__manifest__.py
+++ b/account_chart_update/__manifest__.py
@@ -7,8 +7,8 @@
{
"name": "Detect changes and update the Account Chart from a template",
"summary": "Wizard to update a company's account chart from a template",
- "version": "12.0.1.0.0",
- "author": "Tecnativa, " "BCIM, " "Okia, " "Odoo Community Association (OCA)",
+ "version": "13.0.1.0.0",
+ "author": "Tecnativa, BCIM, Okia, Odoo Community Association (OCA)",
"website": "http://github.com/OCA/account-financial-tools",
"depends": ["account"],
"category": "Accounting",
diff --git a/account_chart_update/static/description/index.html b/account_chart_update/static/description/index.html
index 59094d9eb..5ae507886 100644
--- a/account_chart_update/static/description/index.html
+++ b/account_chart_update/static/description/index.html
@@ -367,7 +367,7 @@ ul.auto-toc {
!! This file is generated by oca-gen-addon-readme !!
!! changes will be overwritten. !!
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! -->
-

+

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.
@@ -418,7 +418,7 @@ deactivate).
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.
+feedback.
Do not contact contributors directly about support or help with technical issues.
@@ -453,7 +453,7 @@ If you spotted it first, help us smashing it by providing a detailed and welcome
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.
+
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/test_account_chart_update.py b/account_chart_update/tests/test_account_chart_update.py
index e9625b73d..deaa074fd 100644
--- a/account_chart_update/tests/test_account_chart_update.py
+++ b/account_chart_update/tests/test_account_chart_update.py
@@ -38,6 +38,14 @@ class TestAccountChartUpdate(common.HttpCase):
"amount": 0,
"chart_template_id": chart_template.id,
"tax_group_id": self.env.ref("account.tax_group_taxes").id,
+ "refund_repartition_line_ids": [
+ (0, 0, {"repartition_type": "base", "factor_percent": 100.0}),
+ (0, 0, {"repartition_type": "tax", "factor_percent": 100.0}),
+ ],
+ "invoice_repartition_line_ids": [
+ (0, 0, {"repartition_type": "base", "factor_percent": 100.0}),
+ (0, 0, {"repartition_type": "tax", "factor_percent": 100.0}),
+ ],
}
)
self._create_xml_id(record)
@@ -60,7 +68,10 @@ class TestAccountChartUpdate(common.HttpCase):
# 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"}
+ {
+ "name": "Test account_chart_update account type",
+ "internal_group": "income",
+ }
)
self.account_template = self._create_account_tmpl(
"Test", "100000", self.account_type, False
@@ -70,7 +81,6 @@ class TestAccountChartUpdate(common.HttpCase):
"name": "Test account_chart_update chart",
"currency_id": self.env.ref("base.EUR").id,
"code_digits": 6,
- "transfer_account_id": self.account_template.id,
"cash_account_code_prefix": "570",
"bank_account_code_prefix": "572",
"transfer_account_code_prefix": "100000",
@@ -115,7 +125,7 @@ class TestAccountChartUpdate(common.HttpCase):
}
)
company_user = self.env.user.copy({"company_id": self.company.id})
- chart_by_company_user = self.chart_template.sudo(company_user)
+ chart_by_company_user = self.chart_template.with_user(company_user)
chart_by_company_user.try_loading_for_current_company()
self.tax = self.env["account.tax"].search(
@@ -153,7 +163,9 @@ class TestAccountChartUpdate(common.HttpCase):
self.assertEqual(name[1], "{} ({})".format(field.field_description, field.name))
name = field.name_get()[0]
self.assertEqual(name[0], field.id)
- self.assertEqual(name[1], "{} ({})".format(field.field_description, field.model))
+ self.assertEqual(
+ name[1], "{} ({})".format(field.field_description, field.model)
+ )
# Test no changes
self.assertEqual(wizard.state, "ready")
self.assertFalse(wizard.tax_ids)
@@ -231,7 +243,10 @@ class TestAccountChartUpdate(common.HttpCase):
# 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
+ repartition = self.tax_template.refund_repartition_line_ids.filtered(
+ lambda r: r.repartition_type == "tax"
+ )
+ repartition.account_id = new_account_tmpl.id
self.account_template.name = "Other name"
self.account_template.tag_ids = [
(6, 0, [self.account_tag_1.id, self.account_tag_2.id])
@@ -259,7 +274,10 @@ class TestAccountChartUpdate(common.HttpCase):
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)
+ repartition = self.tax.refund_repartition_line_ids.filtered(
+ lambda r: r.repartition_type == "tax"
+ )
+ self.assertEqual(repartition.account_id, new_account)
self.assertEqual(self.account.name, self.account_template.name)
self.assertIn(self.account_tag_1, self.account.tag_ids)
self.assertIn(self.account_tag_2, self.account.tag_ids)
@@ -306,6 +324,7 @@ class TestAccountChartUpdate(common.HttpCase):
self.env["account.move"].create(
{
"name": "Test move",
+ "type": "entry",
"journal_id": self.env["account.journal"]
.search([("company_id", "=", self.company.id)], limit=1)
.id,
@@ -319,7 +338,8 @@ class TestAccountChartUpdate(common.HttpCase):
"name": "Test move line",
"debit": 10,
"credit": 0,
- "currency_id": self.ref("base.EUR"),
+ "amount_currency": 8,
+ "currency_id": self.ref("base.GBP"),
},
),
(
@@ -330,6 +350,8 @@ class TestAccountChartUpdate(common.HttpCase):
"name": "Test move line2",
"debit": 0,
"credit": 10,
+ "amount_currency": -8,
+ "currency_id": self.ref("base.GBP"),
},
),
],
diff --git a/account_chart_update/wizard/wizard_chart_update.py b/account_chart_update/wizard/wizard_chart_update.py
index 6823de60f..90d30eda6 100644
--- a/account_chart_update/wizard/wizard_chart_update.py
+++ b/account_chart_update/wizard/wizard_chart_update.py
@@ -37,7 +37,6 @@ class WizardUpdateChartsAccounts(models.TransientModel):
comodel_name="res.company",
string="Company",
required=True,
- ondelete="set null",
default=lambda self: self.env.user.company_id.id,
)
chart_template_id = fields.Many2one(
@@ -233,18 +232,15 @@ class WizardUpdateChartsAccounts(models.TransientModel):
langs = self.env["res.lang"].search([])
return [(lang.code, lang.name) for lang in langs]
- @api.multi
@api.depends("chart_template_id")
def _compute_chart_template_ids(self):
all_parents = self.chart_template_id._get_chart_parent_ids()
self.chart_template_ids = all_parents
- @api.multi
@api.depends("tax_ids")
def _compute_new_taxes_count(self):
self.new_taxes = len(self.tax_ids.filtered(lambda x: x.type == "new"))
- @api.multi
@api.depends("account_ids")
def _compute_new_accounts_count(self):
self.new_accounts = (
@@ -252,17 +248,14 @@ class WizardUpdateChartsAccounts(models.TransientModel):
- self.rejected_new_account_number
)
- @api.multi
@api.depends("fiscal_position_ids")
def _compute_new_fps_count(self):
self.new_fps = len(self.fiscal_position_ids.filtered(lambda x: x.type == "new"))
- @api.multi
@api.depends("tax_ids")
def _compute_updated_taxes_count(self):
self.updated_taxes = len(self.tax_ids.filtered(lambda x: x.type == "updated"))
- @api.multi
@api.depends("account_ids")
def _compute_updated_accounts_count(self):
self.updated_accounts = (
@@ -270,29 +263,24 @@ class WizardUpdateChartsAccounts(models.TransientModel):
- self.rejected_updated_account_number
)
- @api.multi
@api.depends("fiscal_position_ids")
def _compute_updated_fps_count(self):
self.updated_fps = len(
self.fiscal_position_ids.filtered(lambda x: x.type == "updated")
)
- @api.multi
@api.depends("tax_ids")
def _compute_deleted_taxes_count(self):
self.deleted_taxes = len(self.tax_ids.filtered(lambda x: x.type == "deleted"))
- @api.multi
@api.onchange("company_id")
def _onchage_company_update_chart_template(self):
self.chart_template_id = self.company_id.chart_template_id
- @api.multi
def _reopen(self):
return {
"type": "ir.actions.act_window",
"view_mode": "form",
- "view_type": "form",
"res_id": self.id,
"res_model": self._name,
"target": "new",
@@ -302,7 +290,6 @@ class WizardUpdateChartsAccounts(models.TransientModel):
"context": {"default_model": self._name},
}
- @api.multi
def action_init(self):
"""Initial action that sets the initial state."""
self.write(
@@ -317,7 +304,6 @@ class WizardUpdateChartsAccounts(models.TransientModel):
)
return self._reopen()
- @api.multi
def action_find_records(self):
"""Searchs for records to update/create and shows them."""
self = self.with_context(lang=self.lang)
@@ -332,7 +318,6 @@ class WizardUpdateChartsAccounts(models.TransientModel):
self.state = "ready"
return self._reopen()
- @api.multi
def action_update_records(self):
"""Action that creates/updates/deletes the selected elements."""
self = self.with_context(lang=self.lang)
@@ -375,7 +360,13 @@ class WizardUpdateChartsAccounts(models.TransientModel):
(name, module) = external_id.split(".")
return "%s.%d_%s" % (name, self.company_id.id, module)
- @api.multi
+ @tools.ormcache("templates")
+ def find_taxes_by_templates(self, templates):
+ tax_ids = []
+ for tax in templates:
+ tax_ids.append(self.find_tax_by_templates(tax))
+ return self.env["account.tax"].browse(tax_ids)
+
@tools.ormcache("templates")
def find_tax_by_templates(self, templates):
"""Find a tax that matches the template."""
@@ -410,13 +401,60 @@ class WizardUpdateChartsAccounts(models.TransientModel):
return False
+ @tools.ormcache("templates", "current_repartition")
+ def find_repartition_by_templates(
+ self, templates, current_repartition, inverse_name
+ ):
+ result = []
+ for tpl in templates:
+ tax_id = self.find_tax_by_templates(tpl[inverse_name])
+ factor_percent = tpl.factor_percent
+ repartition_type = tpl.repartition_type
+ account_id = self.find_account_by_templates(tpl.account_id)
+ rep_obj = self.env["account.tax.repartition.line"]
+ existing = rep_obj.search(
+ [
+ (inverse_name, "=", tax_id),
+ ("factor_percent", "=", factor_percent),
+ ("repartition_type", "=", repartition_type),
+ ("account_id", "=", account_id),
+ ]
+ )
+ if not existing:
+ # create a new mapping
+ result.append(
+ (
+ 0,
+ 0,
+ {
+ inverse_name: tax_id,
+ "factor_percent": factor_percent,
+ "repartition_type": repartition_type,
+ "account_id": account_id,
+ "tag_ids": [(6, 0, tpl.tag_ids.ids)],
+ },
+ )
+ )
+ else:
+ current_repartition -= existing
+ # Mark to be removed the lines not found
+ if current_repartition:
+ result += [(2, x.id) for x in current_repartition]
+ return result
+
@api.model
@tools.ormcache("code")
def padded_code(self, code):
"""Return a right-zero-padded code with the chosen digits."""
return code.ljust(self.code_digits, "0")
- @api.multi
+ @tools.ormcache("templates")
+ def find_accounts_by_templates(self, templates):
+ account_ids = []
+ for account in templates:
+ account_ids.append(self.find_tax_by_templates(account))
+ return self.env["account.account"].browse(account_ids)
+
@tools.ormcache("templates")
def find_account_by_templates(self, templates):
"""Find an account that matches the template."""
@@ -427,7 +465,7 @@ class WizardUpdateChartsAccounts(models.TransientModel):
for template in templates:
try:
real |= self.env.ref(self._get_real_xml_name(template))
- except:
+ except BaseException:
pass
if not real:
@@ -453,7 +491,6 @@ class WizardUpdateChartsAccounts(models.TransientModel):
return False
- @api.multi
@tools.ormcache("templates")
def find_fp_by_templates(self, templates):
"""Find a real fiscal position from a template."""
@@ -464,7 +501,7 @@ class WizardUpdateChartsAccounts(models.TransientModel):
for template in templates:
try:
real |= self.env.ref(self._get_real_xml_name(template))
- except:
+ except BaseException:
pass
if not real:
@@ -485,7 +522,6 @@ class WizardUpdateChartsAccounts(models.TransientModel):
return False
- @api.multi
@tools.ormcache("templates", "current_fp_accounts")
def find_fp_account_by_templates(self, templates, current_fp_accounts):
result = []
@@ -520,7 +556,6 @@ class WizardUpdateChartsAccounts(models.TransientModel):
result += [(2, x.id) for x in current_fp_accounts]
return result
- @api.multi
@tools.ormcache("templates", "current_fp_taxes")
def find_fp_tax_by_templates(self, templates, current_fp_taxes):
result = []
@@ -566,7 +601,7 @@ class WizardUpdateChartsAccounts(models.TransientModel):
"""
specials_mapping = {
"account.tax.template": {"chart_template_id", "children_tax_ids"},
- "account.account.template": {"chart_template_id"},
+ "account.account.template": {"chart_template_id", "root_id", "nocreate"},
"account.fiscal.position.template": {"chart_template_id"},
}
specials = {
@@ -590,57 +625,49 @@ class WizardUpdateChartsAccounts(models.TransientModel):
"""
result = dict()
ignore = self.fields_to_ignore(template._name)
- to_include = []
- if template._name == "account.tax.template":
- to_include = self.tax_field_ids.mapped("name")
- elif template._name == "account.account.template":
- to_include = self.account_field_ids.mapped("name")
- elif template._name == "account.fiscal.position.template":
- to_include = self.fp_field_ids.mapped("name")
+ template_field_mapping = {
+ "account.tax.template": self.tax_field_ids,
+ "account.account.template": self.account_field_ids,
+ "account.fiscal.position.template": self.fp_field_ids,
+ }
+ to_include = template_field_mapping[template._name].mapped("name")
for key, field in template._fields.items():
if key in ignore or key not in to_include:
continue
- expected = t = None
+ expected = None
# Translate template records to reals for comparison
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 relation == "account.tax.template":
+ expected = self.find_taxes_by_templates(template[key])
+ elif relation == "account.account.template":
+ expected = self.find_accounts_by_templates(template[key])
+ elif relation == "account.fiscal.position.tax.template":
+ expected = self.find_fp_tax_by_templates(template[key], real[key])
+ elif relation == "account.fiscal.position.account.template":
+ expected = self.find_fp_account_by_templates(
+ template[key], real[key]
+ )
+ elif relation == "account.tax.repartition.line.template":
+ expected = self.find_repartition_by_templates(
+ template[key], real[key], field.inverse_name
)
- 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 expected is not None:
- if expected != [] and expected != real[key]:
- result[key] = expected
- else:
- template_value = template[key]
- if template._name == "account.account.template" and key == "code":
- template_value = self.padded_code(template["code"])
- if template_value != real[key]:
- result[key] = template_value
- # Avoid to cache recordset references
+ if expected is not None:
+ if expected != [] and expected != real[key]:
+ result[key] = expected
+ else:
+ template_value = template[key]
+ if template._name == "account.account.template" and key == "code":
+ template_value = self.padded_code(template["code"])
+ if template_value != real[key]:
+ result[key] = template_value
+ # Avoid to cache recordset references
+ if key in result:
if isinstance(real._fields[key], fields.Many2many):
result[key] = [(6, 0, result[key].ids)]
elif isinstance(real._fields[key], fields.Many2one):
result[key] = result[key].id
- except KeyError:
- pass
return result
@api.model
@@ -680,7 +707,6 @@ class WizardUpdateChartsAccounts(models.TransientModel):
]
)
- @api.multi
def _find_taxes(self):
"""Search for, and load, tax templates to create/update/delete."""
found_taxes_ids = []
@@ -740,7 +766,6 @@ class WizardUpdateChartsAccounts(models.TransientModel):
}
)
- @api.multi
def _find_accounts(self):
"""Load account templates to create/update."""
self.account_ids.unlink()
@@ -777,7 +802,6 @@ class WizardUpdateChartsAccounts(models.TransientModel):
}
)
- @api.multi
def _find_fiscal_positions(self):
"""Load fiscal position templates to create/update."""
wiz_fp = self.env["wizard.update.charts.accounts.fiscal.position"]
@@ -841,7 +865,6 @@ class WizardUpdateChartsAccounts(models.TransientModel):
}
)
- @api.multi
def _update_taxes(self):
"""Process taxes to create/update/deactivate."""
for wiz_tax in self.tax_ids:
@@ -859,7 +882,10 @@ class WizardUpdateChartsAccounts(models.TransientModel):
else:
for key, value in self.diff_fields(template, tax).items():
# We defer update because account might not be created yet
- if key in {"account_id", "refund_account_id"}:
+ if key in {
+ "invoice_repartition_line_ids",
+ "refund_repartition_line_ids",
+ }:
continue
tax[key] = value
_logger.info(_("Updated tax %s."), "'%s'" % template.name)
@@ -869,7 +895,6 @@ class WizardUpdateChartsAccounts(models.TransientModel):
_("Updated tax %s. (Recreated XML-IDs)"), "'%s'" % template.name
)
- @api.multi
def _update_accounts(self):
"""Process accounts to create/update."""
for wiz_account in self.account_ids:
@@ -936,7 +961,6 @@ class WizardUpdateChartsAccounts(models.TransientModel):
if not self.continue_on_errors:
break
- @api.multi
def _update_taxes_pending_for_accounts(self):
"""Updates the taxes (created or updated on previous steps) to set
the references to the accounts (the taxes where created/updated first,
@@ -948,10 +972,16 @@ class WizardUpdateChartsAccounts(models.TransientModel):
template = wiz_tax.tax_id
tax = wiz_tax.update_tax_id
done = False
+ vals = {}
for key, value in self.diff_fields(template, tax).items():
- if key in {"account_id", "refund_account_id"}:
- tax[key] = value
+ if key in {
+ "invoice_repartition_line_ids",
+ "refund_repartition_line_ids",
+ }:
+ vals[key] = value
done = True
+ if vals:
+ tax.write(vals)
if done:
_logger.info(_("Post-updated tax %s."), "'%s'" % tax.name)
@@ -987,7 +1017,6 @@ class WizardUpdateChartsAccounts(models.TransientModel):
"account_ids": [(0, 0, x) for x in account_mapping],
}
- @api.multi
def _update_fiscal_positions(self):
"""Process fiscal position templates to create/update."""
for wiz_fp in self.fiscal_position_ids:
@@ -1058,7 +1087,6 @@ class WizardUpdateChartsAccountsAccount(models.TransientModel):
comodel_name="account.account.template",
string="Account template",
required=True,
- ondelete="set null",
)
update_chart_wizard_id = fields.Many2one(
comodel_name="wizard.update.charts.accounts",
@@ -1090,7 +1118,6 @@ class WizardUpdateChartsAccountsFiscalPosition(models.TransientModel):
comodel_name="account.fiscal.position.template",
string="Fiscal position template",
required=True,
- ondelete="set null",
)
update_chart_wizard_id = fields.Many2one(
comodel_name="wizard.update.charts.accounts",
@@ -1141,6 +1168,7 @@ class WizardMatching(models.TransientModel):
class WizardTaxMatching(models.TransientModel):
_name = "wizard.tax.matching"
+ _description = "Wizard Tax Matching"
_inherit = "wizard.matching"
def _get_matching_selection(self):
@@ -1153,6 +1181,7 @@ class WizardTaxMatching(models.TransientModel):
class WizardAccountMatching(models.TransientModel):
_name = "wizard.account.matching"
+ _description = "Wizard Account Matching"
_inherit = "wizard.matching"
def _get_matching_selection(self):
@@ -1163,6 +1192,7 @@ class WizardAccountMatching(models.TransientModel):
class WizardFpMatching(models.TransientModel):
_name = "wizard.fp.matching"
+ _description = "Wizard Fiscal Position Matching"
_inherit = "wizard.matching"
def _get_matching_selection(self):
diff --git a/account_chart_update/wizard/wizard_chart_update_view.xml b/account_chart_update/wizard/wizard_chart_update_view.xml
index 2e67a5413..8cb996a52 100644
--- a/account_chart_update/wizard/wizard_chart_update_view.xml
+++ b/account_chart_update/wizard/wizard_chart_update_view.xml
@@ -239,7 +239,6 @@
Update chart of accounts
ir.actions.act_window
wizard.update.charts.accounts
- form
form
new