From 7feba6312bd5c4c1561c32b7e7bb13ae226fc946 Mon Sep 17 00:00:00 2001 From: Enric Tobella Date: Fri, 17 Mar 2023 10:43:45 +0100 Subject: [PATCH] [IMP] account_chart_update: Update account groups too --- .../security/ir.model.access.csv | 2 + .../tests/test_account_chart_update.py | 60 +++++ .../wizard/wizard_chart_update.py | 244 +++++++++++++++++- .../wizard/wizard_chart_update_view.xml | 68 +++++ 4 files changed, 372 insertions(+), 2 deletions(-) diff --git a/account_chart_update/security/ir.model.access.csv b/account_chart_update/security/ir.model.access.csv index 38346742..6ce9de54 100644 --- a/account_chart_update/security/ir.model.access.csv +++ b/account_chart_update/security/ir.model.access.csv @@ -7,3 +7,5 @@ access_wizard_account_matching,wizard.account.matching,model_wizard_account_matc access_wizard_update_charts_accounts_tax,wizard.update.charts.accounts.tax,model_wizard_update_charts_accounts_tax,base.group_erp_manager,1,1,1,1 access_wizard_matching,wizard.matching,model_wizard_matching,base.group_erp_manager,1,1,1,1 access_wizard_update_charts_accounts_fiscal_position,wizard.update.charts.accounts.fiscal.position,model_wizard_update_charts_accounts_fiscal_position,base.group_erp_manager,1,1,1,1 +access_wizard_account_groupo_matching,wizard.account.group.matching,model_wizard_account_group_matching,base.group_erp_manager,1,1,1,1 +access_wizard_update_charts_accounts_account_group,wizard.update.charts.accounts.account.group,model_wizard_update_charts_accounts_account_group,base.group_erp_manager,1,1,1,1 diff --git a/account_chart_update/tests/test_account_chart_update.py b/account_chart_update/tests/test_account_chart_update.py index 567761d0..f08826d9 100644 --- a/account_chart_update/tests/test_account_chart_update.py +++ b/account_chart_update/tests/test_account_chart_update.py @@ -457,6 +457,66 @@ class TestAccountChartUpdate(common.HttpCase): self.assertEqual(wizard.new_accounts, 0) wizard.unlink() + def test_chart_update_groups(self): + self.wizard_vals.update( + { + "update_account": False, + "update_tax": False, + "update_fiscal_position": False, + "recreate_xml_ids": True, + } + ) + template_1 = self.env["account.group.template"].create( + { + "name": "TEST", + "code_prefix_start": "TESTZ", + "chart_template_id": self.chart_template.id, + } + ) + self._create_xml_id(template_1) + template_2 = self.env["account.group.template"].create( + { + "name": "TEST", + "code_prefix_start": "TESTY", + "chart_template_id": self.chart_template.id, + } + ) + self._create_xml_id(template_2) + group_1 = self.env["account.group"].create( + { + "name": "TEST", + "code_prefix_start": "TESTZ", + "code_prefix_end": "TESZZ", + "company_id": self.company.id, + } + ) + wizard = self.wizard_obj.create(self.wizard_vals) + wizard.action_find_records() + self.assertEqual(2, len(wizard.account_group_ids)) + self.assertEqual( + template_1, + wizard.account_group_ids.filtered( + lambda r: r.type == "updated" + ).account_group_id, + ) + self.assertEqual( + group_1, + wizard.account_group_ids.filtered( + lambda r: r.type == "updated" + ).update_account_group_id, + ) + self.assertEqual( + template_2, + wizard.account_group_ids.filtered( + lambda r: r.type == "new" + ).account_group_id, + ) + wizard.action_update_records() + self.assertEqual(1, wizard.updated_account_groups) + self.assertEqual(1, wizard.new_account_groups) + self.assertEqual("TESTZ", group_1.code_prefix_end) + self.assertTrue(list(group_1.get_xml_id().values())[0]) + def test_matching(self): # Test XML-ID matching self.tax_template.name = "Test 1 tax name changed" diff --git a/account_chart_update/wizard/wizard_chart_update.py b/account_chart_update/wizard/wizard_chart_update.py index 0e937f35..7db646df 100644 --- a/account_chart_update/wizard/wizard_chart_update.py +++ b/account_chart_update/wizard/wizard_chart_update.py @@ -84,6 +84,12 @@ class WizardUpdateChartsAccounts(models.TransientModel): default=True, help="Existing accounts are updated. Accounts are searched by code.", ) + update_account_group = fields.Boolean( + string="Update account groups", + default=True, + help="Existing account groups are updated. " + "Account groups are searched by prefix_code_start.", + ) update_fiscal_position = fields.Boolean( string="Update fiscal positions", default=True, @@ -106,6 +112,11 @@ class WizardUpdateChartsAccounts(models.TransientModel): inverse_name="update_chart_wizard_id", string="Accounts", ) + account_group_ids = fields.One2many( + comodel_name="wizard.update.charts.accounts.account.group", + inverse_name="update_chart_wizard_id", + string="Account Groups", + ) fiscal_position_ids = fields.One2many( comodel_name="wizard.update.charts.accounts.fiscal.position", inverse_name="update_chart_wizard_id", @@ -113,6 +124,7 @@ class WizardUpdateChartsAccounts(models.TransientModel): ) new_taxes = fields.Integer(compute="_compute_new_taxes_count") new_accounts = fields.Integer(compute="_compute_new_accounts_count") + new_account_groups = fields.Integer(compute="_compute_new_account_groups_count") rejected_new_account_number = fields.Integer() new_fps = fields.Integer( string="New fiscal positions", compute="_compute_new_fps_count" @@ -120,6 +132,9 @@ class WizardUpdateChartsAccounts(models.TransientModel): updated_taxes = fields.Integer(compute="_compute_updated_taxes_count") rejected_updated_account_number = fields.Integer() updated_accounts = fields.Integer(compute="_compute_updated_accounts_count") + updated_account_groups = fields.Integer( + compute="_compute_updated_account_groups_count" + ) updated_fps = fields.Integer( string="Updated fiscal positions", compute="_compute_updated_fps_count" ) @@ -141,6 +156,13 @@ class WizardUpdateChartsAccounts(models.TransientModel): domain=lambda self: self._domain_account_field_ids(), default=lambda self: self._default_account_field_ids(), ) + account_group_field_ids = fields.Many2many( + comodel_name="ir.model.fields", + relation="wizard_update_charts_account_group_fields_rel", + string="Account fields", + domain=lambda self: self._domain_account_group_field_ids(), + default=lambda self: self._default_account_group_field_ids(), + ) fp_field_ids = fields.Many2many( comodel_name="ir.model.fields", relation="wizard_update_charts_fp_fields_rel", @@ -160,6 +182,12 @@ class WizardUpdateChartsAccounts(models.TransientModel): string="Accounts matching", default=lambda self: self._default_account_matching_ids(), ) + account_group_matching_ids = fields.One2many( + comodel_name="wizard.account.group.matching", + inverse_name="update_chart_wizard_id", + string="Account groups matching", + default=lambda self: self._default_account_group_matching_ids(), + ) fp_matching_ids = fields.One2many( comodel_name="wizard.fp.matching", inverse_name="update_chart_wizard_id", @@ -179,6 +207,9 @@ class WizardUpdateChartsAccounts(models.TransientModel): def _domain_account_field_ids(self): return self._domain_per_name("account.account.template") + def _domain_account_group_field_ids(self): + return self._domain_per_name("account.group.template") + def _domain_fp_field_ids(self): return self._domain_per_name("account.fiscal.position.template") @@ -196,6 +227,14 @@ class WizardUpdateChartsAccounts(models.TransientModel): ) ] + def _default_account_group_field_ids(self): + return [ + (4, x.id) + for x in self.env["ir.model.fields"].search( + self._domain_account_group_field_ids() + ) + ] + def _default_fp_field_ids(self): return [ (4, x.id) @@ -206,14 +245,12 @@ class WizardUpdateChartsAccounts(models.TransientModel): vals = [] for seq, opt in enumerate(ordered_opts, 1): vals.append((0, False, {"sequence": seq, "matching_value": opt})) - all_options = self.env[model_name]._get_matching_selection() all_options = map(lambda x: x[0], all_options) all_options = list(set(all_options) - set(ordered_opts)) for seq, opt in enumerate(all_options, len(ordered_opts) + 1): vals.append((0, False, {"sequence": seq, "matching_value": opt})) - return vals def _default_fp_matching_ids(self): @@ -228,6 +265,10 @@ class WizardUpdateChartsAccounts(models.TransientModel): ordered_opts = ["xml_id", "code", "name"] return self._get_matching_ids("wizard.account.matching", ordered_opts) + def _default_account_group_matching_ids(self): + ordered_opts = ["xml_id", "code_prefix_start"] + return self._get_matching_ids("wizard.account.group.matching", ordered_opts) + @api.model def _get_lang_selection_options(self): """Gets the available languages for the selection.""" @@ -250,6 +291,12 @@ class WizardUpdateChartsAccounts(models.TransientModel): - self.rejected_new_account_number ) + @api.depends("account_group_ids") + def _compute_new_account_groups_count(self): + self.new_account_groups = len( + self.account_group_ids.filtered(lambda x: x.type == "new") + ) + @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")) @@ -265,6 +312,12 @@ class WizardUpdateChartsAccounts(models.TransientModel): - self.rejected_updated_account_number ) + @api.depends("account_group_ids") + def _compute_updated_account_groups_count(self): + self.updated_account_groups = len( + self.account_group_ids.filtered(lambda x: x.type == "updated") + ) + @api.depends("fiscal_position_ids") def _compute_updated_fps_count(self): self.updated_fps = len( @@ -315,6 +368,8 @@ class WizardUpdateChartsAccounts(models.TransientModel): self._find_taxes() if self.update_account: self._find_accounts() + if self.update_account_group: + self._find_account_groups() if self.update_fiscal_position: self._find_fiscal_positions() # Write the results, and go to the next step. @@ -372,6 +427,8 @@ class WizardUpdateChartsAccounts(models.TransientModel): # 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_account_group and perform_rest: + self._update_account_groups() if self.update_tax and perform_rest: self._update_taxes_pending_for_accounts(todo_dict) if self.update_fiscal_position and perform_rest: @@ -538,6 +595,39 @@ class WizardUpdateChartsAccounts(models.TransientModel): for matching in self.account_matching_ids.sorted("sequence"): if matching.matching_value == "xml_id": real = self.env["account.account"] + for template in templates: + try: + real |= self.env.ref(self._get_real_xml_name(template)) + except BaseException: + _logger.info("Is not real xml Name") + if not real: + continue + criteria = ("id", "in", real.ids) + elif matching.matching_value == "code": + codes = templates.mapped("code") + if not codes: + continue + criteria = ("code", "in", list(map(self.padded_code, codes))) + else: + field_name = matching.matching_value + field_values = templates.mapped(field_name) + if not field_values: + continue + criteria = (field_name, "in", field_values) + result = account_model.search( + [criteria, ("company_id", "=", self.company_id.id)] + ) + if result: + return result.id + return False + + @tools.ormcache("templates") + def find_account_group_by_templates(self, templates): + """Find an account that matches the template.""" + account_model = self.env["account.group"] + for matching in self.account_group_matching_ids.sorted("sequence"): + if matching.matching_value == "xml_id": + real = self.env["account.group"] for template in templates: try: real |= self.env.ref(self._get_real_xml_name(template)) @@ -680,6 +770,11 @@ class WizardUpdateChartsAccounts(models.TransientModel): "account.account.template": set(self.env["mail.thread"]._fields) | {"chart_template_id", "root_id", "nocreate"}, "account.fiscal.position.template": {"chart_template_id"}, + "account.group.template": { + "chart_template_id", + "parent_id", + "code_prefix_end", + }, } specials = { "display_name", @@ -706,6 +801,7 @@ class WizardUpdateChartsAccounts(models.TransientModel): "account.tax.template": self.tax_field_ids, "account.account.template": self.account_field_ids, "account.fiscal.position.template": self.fp_field_ids, + "account.group.template": self.account_group_field_ids, } to_include = template_field_mapping[template._name].mapped("name") for key, field in template._fields.items(): @@ -902,6 +998,57 @@ class WizardUpdateChartsAccounts(models.TransientModel): } ) + def _find_account_groups(self): + """Load account templates to create/update.""" + self.account_group_ids.unlink() + for template in self.env["account.group.template"].search( + [("chart_template_id", "in", self.chart_template_ids.ids)] + ): + # Search for a real account that matches the template + account_group_id = self.find_account_group_by_templates(template) + if not account_group_id: + # Account to be created + self.account_group_ids.create( + { + "account_group_id": template.id, + "update_chart_wizard_id": self.id, + "type": "new", + "notes": _("No account found with this code."), + } + ) + else: + # Check the account for changes + account_group = self.env["account.group"].browse(account_group_id) + notes = self.diff_notes(template, account_group) + code_prefix_end = ( + template.code_prefix_end + if template.code_prefix_end + and template.code_prefix_end < template.code_prefix_start + else template.code_prefix_start + ) + if code_prefix_end != account_group.code_prefix_end: + notes += (notes and "\n" or "") + _( + "Differences in these fields: %s." + ) % template._fields["code_prefix_end"].get_description(self.env)[ + "string" + ] + if self.recreate_xml_ids and self.missing_xml_id( + template, account_group + ): + notes += (notes and "\n" or "") + _("Missing XML-ID.") + + if notes: + # Account to be updated + self.account_group_ids.create( + { + "account_group_id": template.id, + "update_chart_wizard_id": self.id, + "type": "updated", + "update_account_group_id": account_group_id, + "notes": notes, + } + ) + def _find_fiscal_positions(self): """Load fiscal position templates to create/update.""" wiz_fp = self.env["wizard.update.charts.accounts.fiscal.position"] @@ -1168,6 +1315,52 @@ class WizardUpdateChartsAccounts(models.TransientModel): "account_ids": [(0, 0, x) for x in account_mapping], } + def _prepare_account_group_vals(self, template): + return { + "name": template.name, + "code_prefix_start": template.code_prefix_start, + "code_prefix_end": template.code_prefix_end, + "company_id": self.company_id.id, + } + + def _update_account_groups(self): + """Process fiscal position templates to create/update.""" + for wiz_account_group in self.account_group_ids: + account_group, template = ( + wiz_account_group.update_account_group_id, + wiz_account_group.account_group_id, + ) + if wiz_account_group.type == "new": + # Create a new fiscal position + self.chart_template_id.create_record_with_xmlid( + self.company_id, + template, + "account.group", + self._prepare_account_group_vals(template), + ) + _logger.info(_("Created account group %s."), "'%s'" % template.name) + else: + for key, value in self.diff_fields(template, account_group).items(): + account_group[key] = value + _logger.info(_("Updated account group %s."), "'%s'" % template.name) + code_prefix_end = ( + template.code_prefix_end + if template.code_prefix_end + and template.code_prefix_end < template.code_prefix_start + else template.code_prefix_start + ) + if code_prefix_end != account_group.code_prefix_end: + account_group.code_prefix_end = code_prefix_end + _logger.info(_("Updated account group %s."), "'%s'" % template.name) + if self.recreate_xml_ids and self.missing_xml_id( + template, account_group + ): + self.recreate_xml_id(template, account_group) + _logger.info( + _("Updated account group %s. (Recreated XML-ID)"), + "'%s'" % template.name, + ) + def _update_fiscal_positions(self): """Process fiscal position templates to create/update.""" for wiz_fp in self.fiscal_position_ids: @@ -1352,3 +1545,50 @@ class WizardFpMatching(models.TransientModel): vals = super(WizardFpMatching, self)._get_matching_selection() vals += self._selection_from_files("account.fiscal.position.template", ["name"]) return vals + + +class WizardAccountGroupMatching(models.TransientModel): + _name = "wizard.account.group.matching" + _description = "Wizard Account Group Matching" + _inherit = "wizard.matching" + + def _get_matching_selection(self): + vals = super()._get_matching_selection() + vals += self._selection_from_files( + "account.group.template", ["code_prefix_start"] + ) + return vals + + +class WizardUpdateChartsAccountsAccountGroup(models.TransientModel): + _name = "wizard.update.charts.accounts.account.group" + _description = ( + "Account group that needs to be updated (new or updated in the template)." + ) + + account_group_id = fields.Many2one( + comodel_name="account.group.template", + string="Account group template", + required=True, + ) + update_chart_wizard_id = fields.Many2one( + comodel_name="wizard.update.charts.accounts", + string="Update chart wizard", + required=True, + ondelete="cascade", + ) + type = fields.Selection( + selection=[("new", "New template"), ("updated", "Updated template")], + readonly=False, + ) + update_account_group_id = fields.Many2one( + comodel_name="account.group", + string="Account group to update", + required=False, + ondelete="set null", + ) + notes = fields.Text(readonly=True) + recreate_xml_ids = fields.Boolean( + string="Recreate missing XML-IDs", + related="update_chart_wizard_id.recreate_xml_ids", + ) diff --git a/account_chart_update/wizard/wizard_chart_update_view.xml b/account_chart_update/wizard/wizard_chart_update_view.xml index 7640fb61..087df635 100644 --- a/account_chart_update/wizard/wizard_chart_update_view.xml +++ b/account_chart_update/wizard/wizard_chart_update_view.xml @@ -49,6 +49,7 @@ + + + + + + + + + +
+ + + + + + + + + + + + +
+
+
+
@@ -331,6 +398,7 @@ +