From b80ffea9f6cea4b0e7b87cbfd67e66f27f404df9 Mon Sep 17 00:00:00 2001 From: Sylvain LE GAL Date: Tue, 21 Jan 2025 02:19:50 +0100 Subject: [PATCH 1/4] [FIX] fiscal_company_account: Add migration script to set account on partner properties in child companies --- fiscal_company_account/__manifest__.py | 2 +- .../migrations/16.0.3.0.0/post-migration.py | 30 +++++++++++++++++++ 2 files changed, 31 insertions(+), 1 deletion(-) create mode 100644 fiscal_company_account/migrations/16.0.3.0.0/post-migration.py diff --git a/fiscal_company_account/__manifest__.py b/fiscal_company_account/__manifest__.py index de17ce7..bc77bc1 100644 --- a/fiscal_company_account/__manifest__.py +++ b/fiscal_company_account/__manifest__.py @@ -5,7 +5,7 @@ { "name": "CAE - Account", - "version": "16.0.2.1.2", + "version": "16.0.3.0.0", "category": "CAE", "summary": "Glue Module between CAE and Account modules", "author": "GRAP", diff --git a/fiscal_company_account/migrations/16.0.3.0.0/post-migration.py b/fiscal_company_account/migrations/16.0.3.0.0/post-migration.py new file mode 100644 index 0000000..0c3c677 --- /dev/null +++ b/fiscal_company_account/migrations/16.0.3.0.0/post-migration.py @@ -0,0 +1,30 @@ +# License AGPL-3 - See http://www.gnu.org/licenses/agpl-3.0.html +import logging + +from openupgradelib import openupgrade + +_logger = logging.getLogger(__name__) + + +@openupgrade.migrate() +def migrate(env, version): + IrProperty = env["ir.property"] + mother_companies = env["res.company"].search( + [("fiscal_type", "=", "fiscal_mother")] + ) + + todo_list = [ + ("property_account_receivable_id", "res.partner"), + ("property_account_payable_id", "res.partner"), + ] + + for field, model in todo_list: + for mother_company in mother_companies: + value = IrProperty.with_company(mother_company)._get(field, model) + if value: + for company in mother_company.child_ids: + _logger.info( + f"Company {company.code}-{company.name}. Model {model}." + " Field: {field}. New default value: {value}" + ) + IrProperty._set_default(field, model, value, company=company) From f44def6e62c5d763fd63a2b039acdc7b017b2b41 Mon Sep 17 00:00:00 2001 From: Sylvain LE GAL Date: Tue, 21 Jan 2025 22:20:05 +0100 Subject: [PATCH 2/4] [IMP] When creating a new child company, items will have properties in child context that comes from setting in the mother company --- fiscal_company_account/models/res_partner.py | 11 ++-- fiscal_company_base/models/__init__.py | 2 + ...l_company_propagate_child_company_mixin.py | 24 +++++++++ fiscal_company_base/models/res_company.py | 46 ++++++++++++++++ fiscal_company_base/models/res_partner.py | 11 ++++ fiscal_company_base/readme/DEVELOP.rst | 32 ++++++++++- fiscal_company_base/tests/__init__.py | 13 ++--- fiscal_company_base/tests/models.py | 21 ++++++++ ...l_company_propagate_child_company_mixin.py | 53 +++++++++++++++++++ 9 files changed, 203 insertions(+), 10 deletions(-) create mode 100644 fiscal_company_base/models/fiscal_company_propagate_child_company_mixin.py create mode 100644 fiscal_company_base/models/res_partner.py create mode 100644 fiscal_company_base/tests/test_fiscal_company_propagate_child_company_mixin.py diff --git a/fiscal_company_account/models/res_partner.py b/fiscal_company_account/models/res_partner.py index 6cff5f6..8f0bf5f 100644 --- a/fiscal_company_account/models/res_partner.py +++ b/fiscal_company_account/models/res_partner.py @@ -3,11 +3,16 @@ # License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html). -from odoo import fields, models +from odoo import models class ResPartner(models.Model): _inherit = "res.partner" - # Display all accessible fiscal position - property_account_position_id = fields.Many2one(domain=[]) + def _fiscal_property_creation_list(self): + res = super()._fiscal_property_creation_list() + res += [ + "property_account_payable_id", + "property_account_receivable_id", + ] + return res diff --git a/fiscal_company_base/models/__init__.py b/fiscal_company_base/models/__init__.py index 854c25b..8202174 100644 --- a/fiscal_company_base/models/__init__.py +++ b/fiscal_company_base/models/__init__.py @@ -4,3 +4,5 @@ from . import fiscal_company_change_search_domain_mixin from . import fiscal_company_change_filtered_mixin from . import fiscal_company_check_company_mixin +from . import fiscal_company_propagate_child_company_mixin +from . import res_partner diff --git a/fiscal_company_base/models/fiscal_company_propagate_child_company_mixin.py b/fiscal_company_base/models/fiscal_company_propagate_child_company_mixin.py new file mode 100644 index 0000000..12cc254 --- /dev/null +++ b/fiscal_company_base/models/fiscal_company_propagate_child_company_mixin.py @@ -0,0 +1,24 @@ +# Copyright (C) 2025 - Today: GRAP (http://www.grap.coop) +# @author: Sylvain LE GAL (https://twitter.com/legalsylvain) +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html). + + +from odoo import api, models + + +class FiscalCompanyPropagateChildCompanyMixin(models.AbstractModel): + """This abstract allow to propagate properties of mother companies + in child companies context. + - When creating a new child company, all the properties defined + at mother company will be set in the new child company. + """ + + _name = "fiscal.company.propagate.child.company.mixin" + _description = "Fiscal Company : Propagate in Child Company Mixin" + + @api.model + def _fiscal_property_creation_list(self): + """Overload me to define property fields to create to a new + fiscal company + """ + return [] diff --git a/fiscal_company_base/models/res_company.py b/fiscal_company_base/models/res_company.py index c040666..e883143 100644 --- a/fiscal_company_base/models/res_company.py +++ b/fiscal_company_base/models/res_company.py @@ -38,11 +38,32 @@ class ResCompany(models.Model): ) def _get_fiscal_propagated_fields(self): + """Return list of company fields that should be propagated to the + child companies""" return ["vat"] + def _get_model_from_properties_propagation(self): + """Return list of models that inherit from + fiscal.company.propagate.child.company.mixin + and that should propagate properties to child companies. + """ + return ["res.partner"] + + @api.model_create_multi + def create(self, vals_list): + companies = super().create(vals_list) + for company, vals in zip(companies, vals_list, strict=True): + if vals.get("fiscal_type") == "fiscal_child": + company._propagate_properties_to_new_fiscal_child() + return companies + def write(self, vals): res = super().write(vals) cae_companies = self.filtered(lambda x: x.fiscal_type == "fiscal_mother") + + if vals.get("fiscal_type") == "fiscal_child": + self._propagate_properties_to_new_fiscal_child() + new_vals = {} for field in self._get_fiscal_propagated_fields(): @@ -141,3 +162,28 @@ def _check_fiscal_type_with_child_type(self): error_types=error_types, ) ) + + def _propagate_properties_to_new_fiscal_child(self): + """ + Propagate all properties of some models for a new child company + """ + IrModelFields = self.env["ir.model.fields"] + IrProperty = self.env["ir.property"] + for company in self: + for model_name in self._get_model_from_properties_propagation(): + CurrentModel = self.env[model_name] + property_name_list = CurrentModel._fiscal_property_creation_list() + for property_name in property_name_list: + field = IrModelFields.search( + [("model", "=", model_name), ("name", "=", property_name)] + )[0] + # Get existing properties + existing_properties = IrProperty.sudo().search( + [ + ("fields_id", "=", field.id), + ("company_id", "=", company.fiscal_company_id.id), + ] + ) + # Duplicate properties for the new fiscal child company + for existing_property in existing_properties: + existing_property.copy(default={"company_id": company.id}) diff --git a/fiscal_company_base/models/res_partner.py b/fiscal_company_base/models/res_partner.py new file mode 100644 index 0000000..ff2a29e --- /dev/null +++ b/fiscal_company_base/models/res_partner.py @@ -0,0 +1,11 @@ +# Copyright (C) 2018 - Today: GRAP (http://www.grap.coop) +# @author: Sylvain LE GAL (https://twitter.com/legalsylvain) +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html). + + +from odoo import models + + +class ResPartner(models.Model): + _name = "res.partner" + _inherit = ["res.partner", "fiscal.company.propagate.child.company.mixin"] diff --git a/fiscal_company_base/readme/DEVELOP.rst b/fiscal_company_base/readme/DEVELOP.rst index 776ec76..4820eaf 100644 --- a/fiscal_company_base/readme/DEVELOP.rst +++ b/fiscal_company_base/readme/DEVELOP.rst @@ -26,7 +26,7 @@ with the following syntax. AccountMove, self.with_context(fiscal_company_disable_switch_company=True) )._onchange_partner_id() -This module also introduces 2 mixin: +This module also introduces many mixin: ``fiscal.company.change.search.domain.mixin`` ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ @@ -60,3 +60,33 @@ fiscal_type of the company. _inherit = ["fiscal.company.check.company.mixin"] _fiscal_company_forbid_fiscal_type = ["fiscal_mother"] + +``fiscal.company.propagate.child.company.mixin`` +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +The model that inherits this abstract will see some properties +set at mother level propagated at child level, when creating +a new child company. + + +**Usage** + +.. code-block:: python + + class MyModel(models.Model): + _name = "my.model" + _inherit = ["fiscal.company.propagate.child.company.mixin"] + + def _fiscal_property_creation_list(self): + res = super()._fiscal_property_creation_list() + res += ["property_field_1", "property_field_2"] + return res + + class ResCompany(models.Model): + _inherit = "res.company" + + def _get_model_from_properties_propagation(self): + res = super()._get_model_from_properties_propagation() + res += ["my.model"] + return res + diff --git a/fiscal_company_base/tests/__init__.py b/fiscal_company_base/tests/__init__.py index 2b37abb..7a33762 100644 --- a/fiscal_company_base/tests/__init__.py +++ b/fiscal_company_base/tests/__init__.py @@ -1,6 +1,7 @@ -from . import test_fiscal_type_constrains -from . import test_fiscal_company_change_filtered_mixin -from . import test_fiscal_company_change_search_domain_mixin -from . import test_fiscal_company_check_company_mixin -from . import test_fiscal_company_propagated_fields -from . import test_with_company +# from . import test_fiscal_type_constrains +# from . import test_fiscal_company_change_filtered_mixin +# from . import test_fiscal_company_change_search_domain_mixin +# from . import test_fiscal_company_check_company_mixin +# from . import test_fiscal_company_propagated_fields +from . import test_fiscal_company_propagate_child_company_mixin +# from . import test_with_company diff --git a/fiscal_company_base/tests/models.py b/fiscal_company_base/tests/models.py index 34a22f4..c16ed11 100644 --- a/fiscal_company_base/tests/models.py +++ b/fiscal_company_base/tests/models.py @@ -27,6 +27,27 @@ class ModelFiscalCompanyCheckCompanyMixinFiscalMother(models.Model): company_id = fields.Many2one(comodel_name="res.company") +class ModelFiscalCompanyPropagateChildCompanyMixin(models.Model): + _name = "model.fiscal.company.propagate.child.company.mixin" + _description = "fiscal.company.propagate.child.company.mixin" + _inherit = ["fiscal.company.propagate.child.company.mixin"] + + company_dependent_field = fields.Char(company_dependent=True) + + def _fiscal_property_creation_list(self): + return ["company_dependent_field"] + + +# pylint: disable=R8180 +class ModelFiscalCompanyPropagateChildCompanyMixinResCompany(models.Model): + _inherit = "res.company" + + def _get_model_from_properties_propagation(self): + res = super()._get_model_from_properties_propagation() + res += ["model.fiscal.company.propagate.child.company.mixin"] + return res + + class ModelWithCompany(models.Model): _name = "model.with.company" _description = "model.with.company" diff --git a/fiscal_company_base/tests/test_fiscal_company_propagate_child_company_mixin.py b/fiscal_company_base/tests/test_fiscal_company_propagate_child_company_mixin.py new file mode 100644 index 0000000..fb9c12a --- /dev/null +++ b/fiscal_company_base/tests/test_fiscal_company_propagate_child_company_mixin.py @@ -0,0 +1,53 @@ +# Copyright (C) 2024 - Today: GRAP (http://www.grap.coop) +# @author Sylvain LE GAL (https://twitter.com/legalsylvain) +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html). +from odoo_test_helper import FakeModelLoader + +from .test_abstract import TestAbstract + + +class TestFiscalCompanyPropagateChildCompanyMixin(TestAbstract): + @classmethod + def setUpClass(cls): + super().setUpClass() + # Load a test model using odoo_test_helper + cls.loader = FakeModelLoader(cls.env, cls.__module__) + cls.loader.backup_registry() + from .models import ( + ModelFiscalCompanyPropagateChildCompanyMixin, + ModelFiscalCompanyPropagateChildCompanyMixinResCompany, + ) + + cls.loader.update_registry( + ( + ModelFiscalCompanyPropagateChildCompanyMixin, + ModelFiscalCompanyPropagateChildCompanyMixinResCompany, + ) + ) + + cls.model_propagate_child_company = cls.env[ + "model.fiscal.company.propagate.child.company.mixin" + ] + + @classmethod + def tearDownClass(cls): + cls.loader.restore_registry() + return super().tearDownClass() + + def test_check_propagation_ok(self): + item = self.model_propagate_child_company.with_company( + self.mother_company + ).create({"company_dependent_field": "BOB"}) + new_child_company = self.env["res.company"].create( + { + "name": "NEW FISCAL CHILD", + "parent_id": self.mother_company.id, + "fiscal_type": "fiscal_child", + } + ) + self.assertEqual( + item.with_company(new_child_company).company_dependent_field, "BOB" + ) + self.assertEqual( + item.with_company(self.normal_company).company_dependent_field, False + ) From 45261da1765ff45e47815c134aa6531f3bab5c01 Mon Sep 17 00:00:00 2001 From: Sylvain LE GAL Date: Tue, 21 Jan 2025 22:24:48 +0100 Subject: [PATCH 3/4] [IMP] make required field for partner account properties --- fiscal_company_account/models/res_partner.py | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/fiscal_company_account/models/res_partner.py b/fiscal_company_account/models/res_partner.py index 8f0bf5f..9422f69 100644 --- a/fiscal_company_account/models/res_partner.py +++ b/fiscal_company_account/models/res_partner.py @@ -3,12 +3,14 @@ # License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html). -from odoo import models +from odoo import fields, models class ResPartner(models.Model): _inherit = "res.partner" + is_coa_installed = fields.Boolean(compute="_compute_is_coa_installed") + def _fiscal_property_creation_list(self): res = super()._fiscal_property_creation_list() res += [ @@ -16,3 +18,8 @@ def _fiscal_property_creation_list(self): "property_account_receivable_id", ] return res + + def _compute_is_coa_installed(self): + result = bool(self.env.company.fiscal_company_id.chart_template_id) + for partner in self: + partner.is_coa_installed = result From 85599ed670e9b4f4951e5746d66f21133b6794a0 Mon Sep 17 00:00:00 2001 From: Sylvain LE GAL Date: Thu, 23 Jan 2025 15:36:33 +0100 Subject: [PATCH 4/4] fixup! [IMP] make required field for partner account properties --- fiscal_company_base/tests/__init__.py | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/fiscal_company_base/tests/__init__.py b/fiscal_company_base/tests/__init__.py index 7a33762..fb8f675 100644 --- a/fiscal_company_base/tests/__init__.py +++ b/fiscal_company_base/tests/__init__.py @@ -1,7 +1,7 @@ -# from . import test_fiscal_type_constrains -# from . import test_fiscal_company_change_filtered_mixin -# from . import test_fiscal_company_change_search_domain_mixin -# from . import test_fiscal_company_check_company_mixin -# from . import test_fiscal_company_propagated_fields +from . import test_fiscal_type_constrains +from . import test_fiscal_company_change_filtered_mixin +from . import test_fiscal_company_change_search_domain_mixin +from . import test_fiscal_company_check_company_mixin +from . import test_fiscal_company_propagated_fields from . import test_fiscal_company_propagate_child_company_mixin -# from . import test_with_company +from . import test_with_company