diff --git a/fiscal_company_account/__manifest__.py b/fiscal_company_account/__manifest__.py index c66c1fa..6321fb8 100644 --- a/fiscal_company_account/__manifest__.py +++ b/fiscal_company_account/__manifest__.py @@ -5,7 +5,7 @@ { "name": "CAE - Account", - "version": "12.0.1.1.0", + "version": "16.0.1.0.0", "category": "CAE", "summary": "Glue Module between CAE and Account modules", "author": "GRAP", @@ -14,18 +14,15 @@ "depends": ["fiscal_company_base", "account"], "data": [ "security/ir_rule.xml", - "views/menu.xml", - "views/view_account_move.xml", - "views/view_res_partner.xml", + # "views/menu.xml", + # "views/view_account_move.xml", + # "views/view_res_partner.xml", ], "demo": [ "demo/res_groups.xml", "demo/account_account.xml", "demo/account_journal.xml", "demo/account_tax.xml", - "demo/product_category.xml", - "demo/product_product.xml", - "demo/product_template.xml", "demo/res_partner.xml", "demo/ir_property.xml", ], diff --git a/fiscal_company_account/demo/account_account.xml b/fiscal_company_account/demo/account_account.xml index 6d15a6f..44961e3 100644 --- a/fiscal_company_account/demo/account_account.xml +++ b/fiscal_company_account/demo/account_account.xml @@ -9,65 +9,96 @@ Copyright (C) 2013-Today GRAP (http://www.grap.coop) - 401_CAE + 401CAE CAE - Payable - (test) - + liability_payable - 411_CAE + 411CAE CAE - Receivable - (test) - + asset_receivable - 401_CAE_CUS + 401CAECUSTOM CAE - Custom Payable - (test) - + liability_payable - 411_CAE_CUS + 411CAECUSTOM CAE - Custom Receivable - (test) - + asset_receivable + + + + + + 4456CAE + CAE - VAT Received - (test) + liability_current + + + + + + 4457CAE + CAE - VAT Deductible - (test) + liability_current - + + + 512CAEBANKDEFAULT + CAE - Bank (Default Account) - (test) + asset_cash + + + + + 512CAEBANKSUSPENSE + CAE - Bank (Suspense Account) - (test) + asset_current + + + - CAE_512 - CAE - Bank - (test) - + 512CAEBANKPAYMENT + CAE - Bank (Payment Account) - (test) + asset_current + - CAE_581 + 581CAE CAE - Cash - (test) - + asset_cash - CAE_601 + 607CAE CAE - Expenses - (test) - + expense - CAE_702 + 707CAE CAE - Product Sales - (test) - + income diff --git a/fiscal_company_account/demo/account_journal.xml b/fiscal_company_account/demo/account_journal.xml index e7d7def..4c21787 100644 --- a/fiscal_company_account/demo/account_journal.xml +++ b/fiscal_company_account/demo/account_journal.xml @@ -26,6 +26,18 @@ Copyright (C) 2013-Today GRAP (http://www.grap.coop) CAE_BANK CAE - Bank Journal - (test) bank + + + + + + + + + + + + diff --git a/fiscal_company_account/demo/account_tax.xml b/fiscal_company_account/demo/account_tax.xml index 255e27a..beedd50 100644 --- a/fiscal_company_account/demo/account_tax.xml +++ b/fiscal_company_account/demo/account_tax.xml @@ -1,4 +1,5 @@ + - + + + 100 + base + + + + 100 + tax + + + + 100 + base + + + + 100 + tax + + + + Sale Tax 20% (Price Excl.) + sale + percent + 20.0 + + + + + + + + + + + + + + + + + + + + 100 + base + + + + 100 + tax + + + + 100 + base + + + + 100 + tax + + + + Purchase Tax 20% (Price Excl.) + purchase + percent + 20.0 + + + + + + + + + + + + + + + diff --git a/fiscal_company_account/demo/ir_property.xml b/fiscal_company_account/demo/ir_property.xml index 54fde0f..6d21653 100644 --- a/fiscal_company_account/demo/ir_property.xml +++ b/fiscal_company_account/demo/ir_property.xml @@ -7,32 +7,76 @@ Copyright (C) 2013-Today GRAP (http://www.grap.coop) --> + - CAE - property_account_receivable CH1 + property_account_receivable_id - CAE - property_account_receivable CH2 + property_account_receivable_id - CAE - property_account_payable CH1 + property_account_payable_id - CAE - property_account_payable CH2 + property_account_payable_id + + + property_account_income_categ_id + + + + + + + property_account_income_categ_id + + + + + + + property_account_income_categ_id + + + + + + + property_account_expense_categ_id + + + + + + + CAE - property_account_expense_categ_id CH1 + + + + + + + property_account_expense_categ_id + + + + + diff --git a/fiscal_company_account/demo/product_category.xml b/fiscal_company_account/demo/product_category.xml deleted file mode 100644 index b426583..0000000 --- a/fiscal_company_account/demo/product_category.xml +++ /dev/null @@ -1,16 +0,0 @@ - - - - - - - diff --git a/fiscal_company_account/demo/product_product.xml b/fiscal_company_account/demo/product_product.xml deleted file mode 100644 index 8cfbfd9..0000000 --- a/fiscal_company_account/demo/product_product.xml +++ /dev/null @@ -1,22 +0,0 @@ - - - - - - CAE - Mother Product Variant With Property - - - - - - - diff --git a/fiscal_company_account/demo/product_template.xml b/fiscal_company_account/demo/product_template.xml deleted file mode 100644 index 20cf77d..0000000 --- a/fiscal_company_account/demo/product_template.xml +++ /dev/null @@ -1,22 +0,0 @@ - - - - - - CAE - Mother Product Template With Property - - - - - - - diff --git a/fiscal_company_account/demo/res_partner.xml b/fiscal_company_account/demo/res_partner.xml index d29051f..1425b6b 100644 --- a/fiscal_company_account/demo/res_partner.xml +++ b/fiscal_company_account/demo/res_partner.xml @@ -7,24 +7,18 @@ License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html). - CAE - Mother Partner With Property + CAE - Mother Partner - - - - CAE - Fiscal 1 Partner With Property + + CAE - Fiscal 1 Partner - - - + + CAE - Fiscal 2 Partner + + diff --git a/fiscal_company_account/models/__init__.py b/fiscal_company_account/models/__init__.py index 93d9db9..e6d329a 100644 --- a/fiscal_company_account/models/__init__.py +++ b/fiscal_company_account/models/__init__.py @@ -1,16 +1,11 @@ # Accounting Part from . import account_account from . import account_bank_statement -from . import account_invoice from . import account_journal from . import account_move from . import account_move_line from . import account_payment -from . import account_reconciliation_widget from . import account_tax -# Propagation properties Part -from . import fiscal_property_propagate_mixin -from . import product_template from . import res_company from . import res_partner diff --git a/fiscal_company_account/models/account_account.py b/fiscal_company_account/models/account_account.py index 80284f6..75c0535 100644 --- a/fiscal_company_account/models/account_account.py +++ b/fiscal_company_account/models/account_account.py @@ -3,13 +3,20 @@ # @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 +from odoo import fields, models class AccountAccount(models.Model): _name = "account.account" _inherit = [ "account.account", - "include.fiscal.company.search.mixin", - "fiscal.child.check.mixin", + "fiscal.company.change.search.domain.mixin", + "fiscal.company.check.company.mixin", ] + + _fiscal_company_forbid_fiscal_type = ["fiscal_child"] + + company_id = fields.Many2one(default=lambda self: self._default_company_id()) + + def _default_company_id(self): + return self.env.company.fiscal_company_id diff --git a/fiscal_company_account/models/account_bank_statement.py b/fiscal_company_account/models/account_bank_statement.py index 905a261..b6f8fda 100644 --- a/fiscal_company_account/models/account_bank_statement.py +++ b/fiscal_company_account/models/account_bank_statement.py @@ -7,6 +7,8 @@ class AccountBankStatement(models.Model): _name = "account.bank.statement" - _inherit = ["account.bank.statement", "fiscal.mother.check.mixin"] + _inherit = ["account.bank.statement", "fiscal.company.check.company.mixin"] + + _fiscal_company_forbid_fiscal_type = ["fiscal_mother"] company_id = fields.Many2one(related=False) diff --git a/fiscal_company_account/models/account_invoice.py b/fiscal_company_account/models/account_invoice.py deleted file mode 100644 index 5d16739..0000000 --- a/fiscal_company_account/models/account_invoice.py +++ /dev/null @@ -1,10 +0,0 @@ -# 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 AccountInvoice(models.Model): - _name = "account.invoice" - _inherit = ["account.invoice", "fiscal.mother.check.mixin"] diff --git a/fiscal_company_account/models/account_journal.py b/fiscal_company_account/models/account_journal.py index 065233d..9dd4e82 100644 --- a/fiscal_company_account/models/account_journal.py +++ b/fiscal_company_account/models/account_journal.py @@ -8,4 +8,48 @@ class AccountJournal(models.Model): _name = "account.journal" - _inherit = ["account.journal", "include.fiscal.company.search.mixin"] + _inherit = [ + "account.journal", + "fiscal.company.change.search.domain.mixin", + ] + + def _get_journal_dashboard_data_batched(self): + # Modify Context to add domain based on allowed companies + # when making request on account.move + return super( + AccountJournal, self.with_context(add_company_domain=True) + )._get_journal_dashboard_data_batched() + + def _get_journal_dashboard_outstanding_payments(self): + """Oustanding payments are computed in an non overloable way. + so in a integrated company context, it compute the values + for the all CAE. + Feature is so disabled in that context + """ + if self.env.company.fiscal_type == "fiscal_child": + return {x.id: (0, 0) for x in self} + return super()._get_journal_dashboard_outstanding_payments() + + def _get_sale_purchase_graph_data(self): + """Invoice amount repartition over the time is computed + in a non overloable way, so in a integrated company context, + it compute the values for the all CAE. + Feature is so disabled in that context. + """ + res = super()._get_sale_purchase_graph_data() + if self.env.company.fiscal_type == "fiscal_child": + for k, _v in res.items(): + res[k][0]["values"] = [] + return res + + def _get_bank_cash_graph_data(self): + """Statement amount repartition over the time is computed + in a non overloable way, so in a integrated company context, + it compute the values for the all CAE. + Feature is so disabled in that context. + """ + res = super()._get_bank_cash_graph_data() + if self.env.company.fiscal_type == "fiscal_child": + for k, _v in res.items(): + res[k][0]["values"] = [] + return res diff --git a/fiscal_company_account/models/account_move.py b/fiscal_company_account/models/account_move.py index 99eb228..d2e552b 100644 --- a/fiscal_company_account/models/account_move.py +++ b/fiscal_company_account/models/account_move.py @@ -3,20 +3,55 @@ # @author: Sylvain LE GAL (https://twitter.com/legalsylvain) # License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html). -from odoo import fields, models +from odoo import api, fields, models class AccountMove(models.Model): _name = "account.move" - _inherit = ["account.move", "fiscal.mother.check.mixin"] - - company_id = fields.Many2one( - related=False, default=lambda x: x._default_company_id() - ) - - def _default_company_id(self): - if self.env.context.get("force_company", False): - return self.env.context["force_company"] - if self.env.context.get("company_id", False): - return self.env.context["company_id"] - return self.env.user.company_id.id + _inherit = [ + "account.move", + "fiscal.company.check.company.mixin", + ] + + _fiscal_company_forbid_fiscal_type = ["fiscal_mother"] + + journal_id = fields.Many2one(check_company=False) + + @api.onchange("partner_id") + def _onchange_partner_id(self): + return super( + AccountMove, self.with_context(fiscal_company_disable_switch_company=True) + )._onchange_partner_id() + + @api.depends("journal_id") + def _compute_company_id(self): + """In odoo Core, the company of an account move is the company of the journal, + that allows to write account moves in group level. + However, this behaviour is not correct in a CAE context. + So we disable the call of super in this case, and set the current company. + """ + for move in self.filtered( + lambda x: x.journal_id.company_id.fiscal_type == "fiscal_mother" + ): + move.company_id = self.env.company + return super( + AccountMove, + self.filtered( + lambda x: x.journal_id.company_id.fiscal_type != "fiscal_mother" + ), + )._compute_company_id() + + @api.model + def _where_calc(self, domain, active_test=True): + """Used only in a dashboard context (see account_journal.py) + to add extra filters, based on current allowed companies + to filter in an integrated company context, data of other integrated + companies of the same CAE.""" + if self.env.context.get("add_company_domain", False): + if self.env.context.get("allowed_company_ids"): + domain += [ + ("company_id", "in", self.env.context.get("allowed_company_ids")) + ] + else: + domain += [("company_id", "=", self.env.company.id)] + return super()._where_calc(domain, active_test=active_test) diff --git a/fiscal_company_account/models/account_move_line.py b/fiscal_company_account/models/account_move_line.py index 0d7c42e..ff3cfe5 100644 --- a/fiscal_company_account/models/account_move_line.py +++ b/fiscal_company_account/models/account_move_line.py @@ -8,6 +8,13 @@ class AccountMoveLine(models.Model): _name = "account.move.line" - _inherit = ["account.move.line", "fiscal.mother.check.mixin"] + _inherit = [ + "account.move.line", + "fiscal.company.check.company.mixin", + ] - company_id = fields.Many2one(related="move_id.company_id") + _fiscal_company_forbid_fiscal_type = ["fiscal_mother"] + + account_id = fields.Many2one(check_company=False) + + tax_ids = fields.Many2many(check_company=False) diff --git a/fiscal_company_account/models/account_payment.py b/fiscal_company_account/models/account_payment.py index f8c8f27..67636e4 100644 --- a/fiscal_company_account/models/account_payment.py +++ b/fiscal_company_account/models/account_payment.py @@ -1,4 +1,5 @@ -# Copyright (C) 2018-Today: GRAP (http://www.grap.coop) +# Copyright (C) 2013-Today: GRAP (http://www.grap.coop) +# @author: Julien WESTE # @author: Sylvain LE GAL (https://twitter.com/legalsylvain) # License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html). @@ -6,9 +7,8 @@ class AccountPayment(models.Model): - _name = "account.payment" - _inherit = ["account.payment", "fiscal.mother.check.mixin"] + _inherit = "account.payment" - company_id = fields.Many2one( - related=False, default=lambda self: self.env.user.company_id - ) + destination_account_id = fields.Many2one(check_company=False) + + outstanding_account_id = fields.Many2one(check_company=False) diff --git a/fiscal_company_account/models/account_reconciliation_widget.py b/fiscal_company_account/models/account_reconciliation_widget.py deleted file mode 100644 index 77176bb..0000000 --- a/fiscal_company_account/models/account_reconciliation_widget.py +++ /dev/null @@ -1,41 +0,0 @@ -# Copyright (C) 2020-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 AccountReconciliationWidget(models.AbstractModel): - _inherit = "account.reconciliation.widget" - - @api.model - def _set_domain_to_current_company(self, domain): - result = [] - for item in domain: - if "company_id" in str(item): - result.append(("company_id", "=", self.env.user.company_id.id)) - else: - result.append(item) - return result - - @api.model - def _domain_move_lines_for_reconciliation( - self, st_line, aml_accounts, partner_id, excluded_ids=None, search_str=False - ): - domain = super()._domain_move_lines_for_reconciliation( - st_line, - aml_accounts, - partner_id, - excluded_ids=excluded_ids, - search_str=search_str, - ) - return self._set_domain_to_current_company(domain) - - @api.model - def _domain_move_lines_for_manual_reconciliation( - self, account_id, partner_id=False, excluded_ids=None, search_str=False - ): - domain = super()._domain_move_lines_for_manual_reconciliation( - account_id, partner_id=False, excluded_ids=None, search_str=search_str - ) - return self._set_domain_to_current_company(domain) diff --git a/fiscal_company_account/models/account_tax.py b/fiscal_company_account/models/account_tax.py index e86a211..1885f17 100644 --- a/fiscal_company_account/models/account_tax.py +++ b/fiscal_company_account/models/account_tax.py @@ -3,29 +3,16 @@ # @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 +from odoo import models class AccountTax(models.Model): _name = "account.tax" _inherit = [ "account.tax", - "include.fiscal.company.search.mixin", - "fiscal.child.check.mixin", + "fiscal.company.change.search.domain.mixin", + "fiscal.company.change.filtered.mixin", + "fiscal.company.check.company.mixin", ] - @api.multi - def filtered(self, func): - # a lot of function in Odoo are filtering taxes by the current company - # for exemple in 'addons/account/models/account_invoice.py#L1809' - # In our CAE case, we replace the current company by the fiscal one. - - # TODO, improve that ugly code. - if ( - not self.env.context.get("dont_change_filter", False) - and callable(func) - and "company_id" in func.__code__.co_names - ): - company = self.env.user.company_id.fiscal_company_id - return super().filtered(lambda x: x.company_id == company) - return super().filtered(func) + _fiscal_company_forbid_fiscal_type = ["fiscal_child"] diff --git a/fiscal_company_account/models/fiscal_property_propagate_mixin.py b/fiscal_company_account/models/fiscal_property_propagate_mixin.py deleted file mode 100644 index 6c5f2b3..0000000 --- a/fiscal_company_account/models/fiscal_property_propagate_mixin.py +++ /dev/null @@ -1,98 +0,0 @@ -# Copyright (C) 2013 - Today: GRAP (http://www.grap.coop) -# @author: Julien WESTE -# @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 FiscalPropertyPropagateMixin(models.AbstractModel): - _name = "fiscal.property.propagate.mixin" - _description = "Fiscal Property Propagation Features Mixin" - - @api.model - def _fiscal_property_creation_list(self): - """Overload me to define property fields to create to a new - fiscal company - """ - return [] - - @api.multi - def _fiscal_property_propagation_list(self): - """Overload me to define property fields to propagate to all - fiscal company. - """ - self.ensure_one() - return [] - - # Overload Section - @api.model - def create(self, vals): - res = super().create(vals) - res._propagate_fiscal_property_to_all_companies(vals) - return res - - @api.multi - def write(self, vals): - res = super().write(vals) - self._propagate_fiscal_property_to_all_companies(vals) - return res - - # Custom Function - @api.multi - def _propagate_fiscal_property_to_all_companies(self, vals): - """ - Propagate a property of objects of for all fiscal - childs of a mother company - """ - IrModelFields = self.env["ir.model.fields"] - IrProperty = self.env["ir.property"] - company_id = self.env.context.get("force_company", False) - if company_id: - current_company = self.env["res.company"].browse(company_id) - else: - current_company = self.env.user.company_id - - if current_company.fiscal_type not in ("fiscal_child", "fiscal_mother"): - return True - - for obj in self: - property_name_list = [ - x for x in obj._fiscal_property_propagation_list() if x in vals - ] - for property_name in property_name_list: - property_value = vals[property_name] - - # Get fields information - field = IrModelFields.search( - [("model", "=", self._name), ("name", "=", property_name)] - )[0] - - # Get all companies and remove the current company which - # property has been just written - company_ids = current_company.fiscal_company_id.fiscal_child_ids.ids - company_ids.remove(current_company.id) - - # Delete all property - domain = [ - ("res_id", "=", f"{self._name},{obj.id}"), - ("fields_id", "=", field.id), - ("company_id", "in", company_ids), - ] - properties = IrProperty.search(domain) - properties.unlink() - - # Create property for all fiscal childs - if property_value: - for company_id in company_ids: - IrProperty.create( - { - "name": property_name, - "res_id": f"{self._name},{obj.id}", - "value": property_value, - "fields_id": field.id, - "type": field.ttype, - "company_id": company_id, - } - ) diff --git a/fiscal_company_account/models/product_template.py b/fiscal_company_account/models/product_template.py deleted file mode 100644 index 1cff61b..0000000 --- a/fiscal_company_account/models/product_template.py +++ /dev/null @@ -1,32 +0,0 @@ -# Copyright (C) 2013 - Today: GRAP (http://www.grap.coop) -# @author: Julien WESTE -# @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 ProductTemplate(models.Model): - _name = "product.template" - _inherit = ["product.template", "fiscal.property.propagate.mixin"] - - _FISCAL_PROPERTY_LIST = [ - "property_account_expense_id", - "property_account_income_id", - ] - - @api.model - def _fiscal_property_creation_list(self): - res = super()._fiscal_property_creation_list() - return res + self._FISCAL_PROPERTY_LIST - - @api.multi - def _fiscal_property_propagation_list(self): - self.ensure_one() - res = super()._fiscal_property_propagation_list() - # Propagation only for object that belong to the fiscal_mother - # company - if self.company_id.fiscal_type == "fiscal_mother": - res = res + self._FISCAL_PROPERTY_LIST - return res diff --git a/fiscal_company_account/models/res_company.py b/fiscal_company_account/models/res_company.py index 2a39606..52e368b 100644 --- a/fiscal_company_account/models/res_company.py +++ b/fiscal_company_account/models/res_company.py @@ -8,45 +8,11 @@ class ResCompany(models.Model): _inherit = "res.company" - _PROPERTY_MODEL_LIST = ["res.partner", "product.template"] + @api.depends("country_id", "parent_id.country_id", "fiscal_type") + def compute_account_tax_fiscal_country(self): + for company in self.filtered(lambda x: x.fiscal_type == "fiscal_child"): + company.account_fiscal_country_id = company.parent_id.country_id - @api.model - def create(self, vals): - res = super().create(vals) - if vals.get("fiscal_type") == "fiscal_child": - res._propagate_properties_to_new_fiscal_child() - return res - - @api.multi - def write(self, vals): - res = super().write(vals) - if vals.get("fiscal_type") == "fiscal_child": - self._propagate_properties_to_new_fiscal_child() - return res - - @api.multi - def _propagate_properties_to_new_fiscal_child(self): - """ - Propagate all properties of objects (product, category) for a new - fiscal company - """ - IrModelFields = self.env["ir.model.fields"] - IrProperty = self.env["ir.property"] - for company in self: - for model_name in self._PROPERTY_MODEL_LIST: - 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.search( - [ - ("fields_id", "=", field.id), - ("company_id", "=", company.fiscal_company_id.id), - ] - ) - # Duplicate properties for the new fiscal childc company - for existing_property in existing_properties: - existing_property.copy(default={"company_id": company.id}) + return super( + ResCompany, self.filtered(lambda x: x.fiscal_type != "fiscal_child") + ).compute_account_tax_fiscal_country() diff --git a/fiscal_company_account/models/res_partner.py b/fiscal_company_account/models/res_partner.py index 7065718..6cff5f6 100644 --- a/fiscal_company_account/models/res_partner.py +++ b/fiscal_company_account/models/res_partner.py @@ -3,90 +3,11 @@ # License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html). -from odoo import api, fields, models +from odoo import fields, models class ResPartner(models.Model): - _name = "res.partner" - _inherit = ["res.partner", "fiscal.property.propagate.mixin"] + _inherit = "res.partner" - _FISCAL_PROPERTY_LIST = [ - "property_account_position_id", - "property_account_payable_id", - "property_account_receivable_id", - "property_payment_term_id", - "property_supplier_payment_term_id", - ] - - @api.model - def _fiscal_property_creation_list(self): - res = super()._fiscal_property_creation_list() - return res + self._FISCAL_PROPERTY_LIST - - @api.multi - def _fiscal_property_propagation_list(self): - self.ensure_one() - res = super()._fiscal_property_propagation_list() - # Propagation only for object that belong to the fiscal_mother - # company - if self.company_id.fiscal_type == "fiscal_mother": - res = res + self._FISCAL_PROPERTY_LIST - return res - - # - # the reason of the following lines are : - # property_account_position_id is a property - # 1) using this field, a domain is automatically added by the orm / web - # in the name_get for exemple: - # ('company_id', 'in', (current_company, False)) - # this domain is not fixed for the time being, by any decorator - # So the selection can not be done in a fiscal_company context - # 2) also, we have to rewrite after a create, because the _inverse - # function doesn't seems to work for properties, (only for create, - # it works for regular write) - @api.model - def create(self, vals): - res = super().create(vals) - if "no_property_account_position_id" in vals.keys(): - res.write( - { - "property_account_position_id": vals[ - "no_property_account_position_id" - ] - } - ) - return res - - no_property_account_position_id = fields.Many2one( - string="Fiscal Position (*)", - comodel_name="account.fiscal.position", - domain=lambda x: x._domain_no_property_account_position_id(), - compute="_compute_no_property_account_position_id", - inverse="_inverse_no_property_account_position_id", - ) - - def _domain_no_property_account_position_id(self): - return [ - ( - "company_id", - "in", - [ - self.env.user.company_id.id, - self.env.user.company_id.fiscal_company_id.id, - ], - ) - ] - - def _compute_no_property_account_position_id(self): - for partner in self: - partner.no_property_account_position_id = ( - partner.property_account_position_id - ) - - def _inverse_no_property_account_position_id(self): - for partner in self: - partner.property_account_position_id = ( - partner.no_property_account_position_id - ) - - # + # Display all accessible fiscal position + property_account_position_id = fields.Many2one(domain=[]) diff --git a/fiscal_company_account/readme/CREDITS.rst b/fiscal_company_account/readme/CREDITS.rst deleted file mode 100644 index 26111c6..0000000 --- a/fiscal_company_account/readme/CREDITS.rst +++ /dev/null @@ -1,6 +0,0 @@ -The development of this module has been financially supported by: - -* GRAP, Groupement Régional Alimentaire de Proximité (http://www.grap.coop) - -Porting from odoo V8 to odoo V10 has been funded by : - * BABEL.COOP, leverage cooperation through the digital age () diff --git a/fiscal_company_account/readme/DEVELOP.rst b/fiscal_company_account/readme/DEVELOP.rst index a7069ce..28e886a 100644 --- a/fiscal_company_account/readme/DEVELOP.rst +++ b/fiscal_company_account/readme/DEVELOP.rst @@ -1,9 +1,9 @@ -* For the migration to V>12, take care of the tax.filtered occurences in - Odoo and OCA modules. - There are a lot of ``filtered(lambda x: x.company_id == current_company)`` - in Odoo. The module ``fiscal_company_account`` alter the behaviour of the function - ``filtered`` of the ``account.tax`` module, to filter on the mother fiscal company. - However, the changes is imperfect, and multiple filters (company and not company filters) - will fail. - During migration, please run: - ``rgrep "tax.*filtered.*company_id"`` +For the migration, take care of the tax.filtered occurences in Odoo and OCA modules. +There are a lot of ``filtered(lambda x: x.company_id == current_company)`` +in Odoo. The module ``fiscal_company_account`` alter the behaviour of the function +``filtered`` of the ``account.tax`` module, to filter on the mother fiscal company. +However, the changes is imperfect, and multiple filters (company and not company filters) +will fail. + +During migration, please run: +``rgrep "tax.*filtered.*company_id"`` diff --git a/fiscal_company_account/security/ir_rule.xml b/fiscal_company_account/security/ir_rule.xml index d9ce260..84e5d07 100644 --- a/fiscal_company_account/security/ir_rule.xml +++ b/fiscal_company_account/security/ir_rule.xml @@ -8,90 +8,40 @@ Copyright (C) 2013-Today GRAP (http://www.grap.coop) - - - - - - ['|',('company_id','=',False),('company_id','child_of',[user.company_id.id])] - - - - - - - - ['|',('company_id','=',False),('company_id','child_of',[user.company_id.id])] - - - - + + - ['|', '|', - ('company_id','=',False), - ('company_id','child_of',[user.company_id.id]), - ('company_id','=',user.company_id.fiscal_company_id.id), - ] + + [('company_id', 'in', user._include_fiscal_company_ids(company_ids))] + - - + + - ['|', '|', - ('company_id','=',False), - ('company_id','child_of',[user.company_id.id]), - ('company_id','=',user.company_id.fiscal_company_id.id), - ] + + [('company_id', 'in', user._include_fiscal_company_ids(company_ids))] + - - + + - ['|', '|', - ('company_id','=',False), - ('company_id','child_of',[user.company_id.id]), - ('company_id','=',user.company_id.fiscal_company_id.id), - ] + + [('company_id', 'in', user._include_fiscal_company_ids(company_ids))] + - - + + - ['|', '|', - ('company_id','=',False), - ('company_id','child_of',[user.company_id.id]), - ('company_id','=',user.company_id.fiscal_company_id.id), - ] - - - - - - - Account Fiscal Year - CAE Rule - - ['|', '|', - ('company_id','=',False), - ('company_id','child_of',[user.company_id.id]), - ('company_id','=',user.company_id.fiscal_company_id.id), - ] + + [('company_id', 'in', user._include_fiscal_company_ids(company_ids))] + diff --git a/fiscal_company_account/tests/__init__.py b/fiscal_company_account/tests/__init__.py index db8382f..2e37774 100644 --- a/fiscal_company_account/tests/__init__.py +++ b/fiscal_company_account/tests/__init__.py @@ -1,3 +1,3 @@ -from . import test_tax_filtered -from . import test_decorator -from . import test_propagate_properties +from . import test_account_journal_dashboard +from . import test_mixin_change_filtered +from . import test_mixin_change_search_domain diff --git a/fiscal_company_account/tests/test_abstract.py b/fiscal_company_account/tests/test_abstract.py new file mode 100644 index 0000000..0cecc87 --- /dev/null +++ b/fiscal_company_account/tests/test_abstract.py @@ -0,0 +1,15 @@ +# 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.addons.fiscal_company_base.tests import test_abstract + + +class TestAbstract(test_abstract.TestAbstract): + @classmethod + def setUpClass(cls): + super().setUpClass() + cls.AccountAccount = cls.env["account.account"] + cls.ProductProduct = cls.env["product.product"] + cls.AccountMove = cls.env["account.move"] + cls.AccountJournal = cls.env["account.journal"] diff --git a/fiscal_company_account/tests/test_account_journal_dashboard.py b/fiscal_company_account/tests/test_account_journal_dashboard.py new file mode 100644 index 0000000..0dc3734 --- /dev/null +++ b/fiscal_company_account/tests/test_account_journal_dashboard.py @@ -0,0 +1,62 @@ +# 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). + +import json + +from odoo import Command + +from .test_abstract import TestAbstract + + +class TestAccountJournalDashboard(TestAbstract): + @classmethod + def setUpClass(cls): + super().setUpClass() + cls.user_accountant.company_id = cls.child_company + + move_vals = { + "partner_id": cls.ResPartner.with_user(cls.user_accountant) + .search([])[0] + .id, + "move_type": "out_invoice", + "line_ids": [ + Command.create( + { + "product_id": cls.ProductProduct.with_user(cls.user_accountant) + .search([])[0] + .id + } + ) + ], + } + cls.move1 = cls.AccountMove.with_user(cls.user_accountant).create(move_vals) + cls.move2 = cls.AccountMove.with_user(cls.user_accountant).create(move_vals) + cls.move3 = cls.AccountMove.with_user(cls.user_accountant).create(move_vals) + (cls.move1 | cls.move2).action_post() + + def test_01_dashboard_in_fiscal_child(self): + self.user_accountant.company_id = self.child_company + sale_journal = self.AccountJournal.with_user(self.user_accountant).search( + [("type", "=", "sale")] + ) + res = json.loads(sale_journal.kanban_dashboard) + self.assertEqual(res.get("number_draft"), 1) + self.assertEqual(res.get("number_waiting"), 2) + self.assertEqual(res.get("entries_count"), 3) + + res = json.loads(sale_journal.kanban_dashboard_graph) + self.assertEqual(len(res[0].get("values")), 0) + + def test_02_dashboard_in_fiscal_mother(self): + self.user_accountant.company_id = self.mother_company + sale_journal = self.AccountJournal.with_user(self.user_accountant).search( + [("type", "=", "sale")] + ) + res = json.loads(sale_journal.kanban_dashboard) + self.assertEqual(res.get("number_draft"), 0) + self.assertEqual(res.get("number_waiting"), 0) + self.assertEqual(res.get("entries_count"), 0) + + res = json.loads(sale_journal.kanban_dashboard_graph) + self.assertEqual(len(res[0].get("values")), 6) diff --git a/fiscal_company_account/tests/test_decorator.py b/fiscal_company_account/tests/test_decorator.py deleted file mode 100644 index bc8cd36..0000000 --- a/fiscal_company_account/tests/test_decorator.py +++ /dev/null @@ -1,56 +0,0 @@ -# Copyright (C) 2013 - Today: GRAP (http://www.grap.coop) -# @author Julien WESTE -# @author Sylvain LE GAL (https://twitter.com/legalsylvain) -# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html). - -from odoo.tests.common import TransactionCase - -# from odoo.addons.fiscal_company_base.fix_test import fix_required_field - - -class TestDecorator(TransactionCase): - """Tests for Account Fiscal Company Module (Decorator)""" - - def setUp(self): - super().setUp() - self.AccountAccount = self.env["account.account"] - self.AccountJournal = self.env["account.journal"] - self.user_accountant = self.env.ref("fiscal_company_base.user_accountant") - self.child_company = self.env.ref("fiscal_company_base.company_fiscal_child_1") - self.mother_company = self.env.ref("fiscal_company_base.company_fiscal_mother") - # fix_required_field(self, 'DROP') - - # def tearDown(self): - # self.cr.rollback() - # fix_required_field(self, 'SET') - # super().tearDown() - - # Test Section - def test_01_decorator_account_account(self): - """Searching an account in a child company should return - accounts of the mother company""" - self.user_accountant.company_id = self.child_company.id - res = self.AccountAccount.sudo(self.user_accountant).search( - [("company_id", "=", self.mother_company.id)] - ) - self.assertNotEqual( - len(res), - 0, - "Searching accounts in a fiscal child company should return" - " accounts of the mother company", - ) - - # Test Section - def test_02_decorator_account_journal(self): - """Searching a journal in a child company should return - journals of the mother company""" - self.user_accountant.company_id = self.child_company.id - res = self.AccountJournal.sudo(self.user_accountant).search( - [("company_id", "=", self.mother_company.id)] - ) - self.assertNotEqual( - len(res), - 0, - "Searching accounts in a fiscal child company should return" - " accounts of the mother company", - ) diff --git a/fiscal_company_account/tests/test_tax_filtered.py b/fiscal_company_account/tests/test_mixin_change_filtered.py similarity index 84% rename from fiscal_company_account/tests/test_tax_filtered.py rename to fiscal_company_account/tests/test_mixin_change_filtered.py index 22ba049..822f9e1 100644 --- a/fiscal_company_account/tests/test_tax_filtered.py +++ b/fiscal_company_account/tests/test_mixin_change_filtered.py @@ -2,19 +2,16 @@ # @author Sylvain LE GAL (https://twitter.com/legalsylvain) # License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html). -from odoo.tests.common import TransactionCase +from .test_abstract import TestAbstract -class TestTaxFiltered(TransactionCase): - """Tests for Account Fiscal Company Module (Tax filter)""" - - def setUp(self): - super().setUp() - self.taxes = self.env.ref("fiscal_company_account.sale_tax_20") | self.env.ref( +class TestMixinChangeFiltered(TestAbstract): + @classmethod + def setUpClass(cls): + super().setUpClass() + cls.taxes = cls.env.ref("fiscal_company_account.sale_tax_20") | cls.env.ref( "fiscal_company_account.purchase_tax_20" ) - self.child_company = self.env.ref("fiscal_company_base.company_fiscal_child_1") - self.mother_company = self.env.ref("fiscal_company_base.company_fiscal_mother") # Test Section def test_01_filter_company(self): @@ -71,7 +68,7 @@ def test_01_filter_company(self): len(filtered_taxes), 0, "Filtering taxes by name should worker (3/3" ) - def test_that_should_fail(self): + def test_02_filter_company_that_should_fail(self): # we test multiple filter in the same time. # The non company condition will not be applyed (and should be). # It is a limitation of the current implementation. diff --git a/fiscal_company_account/tests/test_mixin_change_search_domain.py b/fiscal_company_account/tests/test_mixin_change_search_domain.py new file mode 100644 index 0000000..bb016a6 --- /dev/null +++ b/fiscal_company_account/tests/test_mixin_change_search_domain.py @@ -0,0 +1,42 @@ +# Copyright (C) 2013 - Today: GRAP (http://www.grap.coop) +# @author Julien WESTE +# @author Sylvain LE GAL (https://twitter.com/legalsylvain) +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html). + + +from .test_abstract import TestAbstract + + +class TestMixinChangeSearchDomain(TestAbstract): + @classmethod + def setUpClass(cls): + super().setUpClass() + cls.user_accountant.company_id = cls.child_company.id + + # Test Section + def test_01_mixin_change_search_domain_account_account(self): + """Searching an account in a child company should return + accounts of the mother company""" + res = self.AccountAccount.with_user(self.user_accountant).search( + [("company_id", "=", self.mother_company.id)] + ) + self.assertNotEqual( + len(res), + 0, + "Searching accounts in a fiscal child company should return" + " accounts of the mother company", + ) + + def test_02_mixin_change_search_domain_account_journal(self): + """Searching a journal in a child company should return + journals of the mother company""" + self.user_accountant.company_id = self.child_company.id + res = self.AccountJournal.with_user(self.user_accountant).search( + [("company_id", "=", self.mother_company.id)] + ) + self.assertNotEqual( + len(res), + 0, + "Searching accounts in a fiscal child company should return" + " accounts of the mother company", + ) diff --git a/fiscal_company_account/tests/test_propagate_properties.py b/fiscal_company_account/tests/test_propagate_properties.py deleted file mode 100644 index aca7dd8..0000000 --- a/fiscal_company_account/tests/test_propagate_properties.py +++ /dev/null @@ -1,295 +0,0 @@ -# Copyright (C) 2013 - Today: GRAP (http://www.grap.coop) -# @author Julien WESTE -# @author Sylvain LE GAL (https://twitter.com/legalsylvain) -# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html). - -from odoo.tests.common import TransactionCase - -# from odoo.addons.fiscal_company_base.fix_test import fix_required_field - - -class TestPropagateProperties(TransactionCase): - """Tests for Account Fiscal Company Module (Propagate Properties)""" - - def setUp(self): - super().setUp() - self.ResCompany = self.env["res.company"] - self.ResPartner = self.env["res.partner"] - self.ProductTemplate = self.env["product.template"] - self.ProductProduct = self.env["product.product"] - - self.mother_company = self.env.ref("fiscal_company_base.company_fiscal_mother") - self.child_company = self.env.ref("fiscal_company_base.company_fiscal_child_1") - self.account_expense_cae = self.env.ref( - "fiscal_company_account.account_expense_cae" - ) - self.account_income_cae = self.env.ref( - "fiscal_company_account.account_income_cae" - ) - self.account_payable_cae = self.env.ref( - "fiscal_company_account.account_payable_cae" - ) - self.account_receivable_cae = self.env.ref( - "fiscal_company_account.account_receivable_cae" - ) - self.account_custom_payable_cae = self.env.ref( - "fiscal_company_account.account_custom_payable_cae" - ) - self.account_custom_receivable_cae = self.env.ref( - "fiscal_company_account.account_custom_receivable_cae" - ) - - self.accountant = self.env.ref("fiscal_company_base.user_accountant") - - # Object with demo accounting properties - self.product_template_mother_property = self.env.ref( - "fiscal_company_account.product_template_mother_property" - ) - self.product_product_mother_property = self.env.ref( - "fiscal_company_account.product_product_mother_property" - ) - self.partner_mother_property = self.env.ref( - "fiscal_company_account.partner_mother_property" - ) - - # Object without demo accounting properties - self.product_template_mother = self.env.ref( - "fiscal_company_product.product_template_mother" - ) - self.product_template_child = self.env.ref( - "fiscal_company_product.product_template_child" - ) - self.product_product_mother = self.env.ref( - "fiscal_company_product.product_product_mother" - ) - self.product_product_child = self.env.ref( - "fiscal_company_product.product_product_child" - ) - # fix_required_field(self, 'DROP') - - # def tearDown(self): - # self.cr.rollback() - # fix_required_field(self, 'SET') - # super().tearDown() - - # Test Section - def test_01_account_property_propagation_new_company(self): - """Create a new child company must propagate properties""" - - # Create a new Child company - child_company = self.ResCompany.create( - { - "name": "Your Test Child Company", - "fiscal_type": "fiscal_child", - "fiscal_company_id": self.mother_company.id, - "parent_id": self.mother_company.id, - } - ) - - # Change current company and load objects with the new context - self.env.user.company_id = child_company.id - - # Check if product properties has been propagated for the new company - product = self.ProductProduct.browse([self.product_product_mother_property.id]) - self.assertEqual( - product.property_account_expense_id.id, - self.account_expense_cae.id, - "Create a new child company must set expense account property" - " of the mother company to the new child company for product.", - ) - - self.assertEqual( - product.property_account_income_id.id, - self.account_income_cae.id, - "Create a new child company must set income account property" - " of the mother company to the new child company for product.", - ) - - # Check if template properties has been propagated for the new company - template = self.ProductTemplate.browse( - [self.product_template_mother_property.id] - ) - self.assertEqual( - template.property_account_expense_id.id, - self.account_expense_cae.id, - "Create a new child company must set expense account property" - " of the mother company to the new child company for template.", - ) - - self.assertEqual( - template.property_account_income_id.id, - self.account_income_cae.id, - "Create a new child company must set income account property" - " of the mother company to the new child company for template.", - ) - - # Check if custom partner properties has been propagated for the new - # company - partner = self.ResPartner.browse([self.partner_mother_property.id]) - self.assertEqual( - partner.property_account_payable_id.id, - self.account_custom_payable_cae.id, - "Create a new child company must set custom payable account" - " property of the mother company to the new child company for" - " partner.", - ) - - self.assertEqual( - partner.property_account_receivable_id.id, - self.account_custom_receivable_cae.id, - "Create a new child company must set custom receivable account" - " property of the mother company to the new child company for" - " partner.", - ) - - def test_03_template_mother_propagate_fiscal_property_to_all(self): - """Change a template property of a fiscal company must change the value - for all other companies if the template belong to the - fiscal mother company""" - - template_id = self.env.ref("fiscal_company_product.product_template_mother").id - - ProductTemplateAccountant = self.env["product.template"].sudo(self.accountant) - self.accountant.company_id = self.mother_company.id - - templateInMotherCompany = ProductTemplateAccountant.browse(template_id) - - templateInMotherCompany.write( - { - "property_account_expense_id": self.account_expense_cae.id, - "property_account_income_id": self.account_income_cae.id, - } - ) - - # Change current company and load template with the new context - self.accountant.company_id = self.child_company.id - - # self.env.user.company_id = self.child_company.id - templateInChildCompany = ProductTemplateAccountant.browse(template_id) - - # Check if properties has been propagated to the other company - self.assertEqual( - templateInChildCompany.property_account_expense_id.id, - self.account_expense_cae.id, - "Change an expense property for a template in a mother company" - " must change the value for all the other fiscal company.", - ) - - self.assertEqual( - templateInChildCompany.property_account_income_id.id, - self.account_income_cae.id, - "Change an income property for a template in a mother company" - " must change the value for all the other fiscal company.", - ) - - def test_04_template_child_propagate_fiscal_property_to_all(self): - """Change a template property of a fiscal company must not change the - value for all other companies if the template belong to a - fiscal child company""" - - # Change current company - self.env.user.company_id = self.child_company.id - - self.product_template_child.write( - { - "property_account_expense_id": self.account_expense_cae.id, - "property_account_income_id": self.account_income_cae.id, - } - ) - - # Change current company and load template with the new context - self.env.user.company_id = self.mother_company.id - template = self.ProductTemplate.browse([self.product_template_child.id]) - - # Check if properties has not been propagated to the other company - self.assertNotEqual( - template.property_account_expense_id.id, - self.account_expense_cae.id, - "Change an expense property for a template in a child company" - " must change the value for all the other fiscal company.", - ) - - self.assertNotEqual( - template.property_account_income_id.id, - self.account_income_cae.id, - "Change an income property for a template in a child company" - " must not change the value for all the other fiscal company.", - ) - - def test_05_product_mother_propagate_fiscal_property_to_all(self): - """Change a product property of a fiscal company must change the value - for all other companies if the product belong to the - fiscal mother company""" - - product_id = self.env.ref( - "fiscal_company_account.product_product_mother_property" - ).id - - ProductProductAccountant = self.env["product.product"].sudo(self.accountant) - - # Change current company - self.accountant.company_id = self.mother_company.id - - productInMotherCompany = ProductProductAccountant.browse(product_id) - - productInMotherCompany.write( - { - "property_account_expense_id": self.account_expense_cae.id, - "property_account_income_id": self.account_income_cae.id, - } - ) - - # Change current company and load template with the new context - self.accountant.company_id = self.child_company.id - - # self.env.user.company_id = self.child_company.id - productInChildCompany = ProductProductAccountant.browse(product_id) - - # Check if properties has been propagated to the other company - self.assertEqual( - productInChildCompany.property_account_expense_id.id, - self.account_expense_cae.id, - "Change an expense property for a product in a mother company" - " must change the value for all the other fiscal company.", - ) - - self.assertEqual( - productInChildCompany.property_account_income_id.id, - self.account_income_cae.id, - "Change an income property for a product in a mother company" - " must change the value for all the other fiscal company.", - ) - - def test_06_product_child_propagate_fiscal_property_to_all(self): - """Change a product property of a fiscal company must not change the - value for all other companies if the product belong to a - fiscal child company""" - - # Change current company - self.env.user.company_id = self.child_company.id - - self.product_product_child.write( - { - "property_account_expense_id": self.account_expense_cae.id, - "property_account_income_id": self.account_income_cae.id, - } - ) - - # Change current company and load product with the new context - self.env.user.company_id = self.mother_company.id - product = self.ProductProduct.browse([self.product_product_child.id]) - - # Check if properties has not been propagated to the other company - self.assertNotEqual( - product.property_account_expense_id.id, - self.account_expense_cae.id, - "Change an expense property for a product in a child company" - " must change the value for all the other fiscal company.", - ) - - self.assertNotEqual( - product.property_account_income_id.id, - self.account_income_cae.id, - "Change an income property for a product in a child company" - " must not change the value for all the other fiscal company.", - ) diff --git a/fiscal_company_base/demo/res_partner_company.xml b/fiscal_company_base/demo/res_partner_company.xml index 0e1dd0c..bc0c777 100644 --- a/fiscal_company_base/demo/res_partner_company.xml +++ b/fiscal_company_base/demo/res_partner_company.xml @@ -29,6 +29,7 @@ Copyright (C) 2015-Today GRAP (http://www.grap.coop) CAE Company + @@ -43,6 +44,7 @@ Copyright (C) 2015-Today GRAP (http://www.grap.coop) Integrated Company 1 (Service) + @@ -56,6 +58,7 @@ Copyright (C) 2015-Today GRAP (http://www.grap.coop) + Integrated Company 2 (Production) diff --git a/fiscal_company_base/models/__init__.py b/fiscal_company_base/models/__init__.py index b455d87..854c25b 100644 --- a/fiscal_company_base/models/__init__.py +++ b/fiscal_company_base/models/__init__.py @@ -1,3 +1,6 @@ +from . import models from . import res_company +from . import res_users from . import fiscal_company_change_search_domain_mixin +from . import fiscal_company_change_filtered_mixin from . import fiscal_company_check_company_mixin diff --git a/fiscal_company_base/models/fiscal_company_change_filtered_mixin.py b/fiscal_company_base/models/fiscal_company_change_filtered_mixin.py new file mode 100644 index 0000000..b09122f --- /dev/null +++ b/fiscal_company_base/models/fiscal_company_change_filtered_mixin.py @@ -0,0 +1,32 @@ +# 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 import models + + +class FiscalCompanyChangeFilteredMixin(models.AbstractModel): + """This abstract change the filtered features for models. + + if a filtered is call with "lambda x: x.company_id = company_id" + it will be replace by ('company_id', '=', fiscal_company_id) + """ + + _name = "fiscal.company.change.filtered.mixin" + _description = "Fiscal Company : Change Filtered Mixin" + + def filtered(self, func): + # a lot of function in Odoo are filtering taxes by the current company + # for exemple in 'addons/account/models/account_invoice.py#L1809' + # In our CAE case, we replace the current company by the fiscal one. + + # TODO, improve that ugly code. + if ( + not self.env.context.get("dont_change_filter", False) + and callable(func) + and "company_id" in func.__code__.co_names + ): + company = self.env.company.fiscal_company_id + return super().filtered(lambda x: x.company_id == company) + return super().filtered(func) diff --git a/fiscal_company_base/models/fiscal_company_change_search_domain_mixin.py b/fiscal_company_base/models/fiscal_company_change_search_domain_mixin.py index 2e80da4..ed5d81b 100644 --- a/fiscal_company_base/models/fiscal_company_change_search_domain_mixin.py +++ b/fiscal_company_base/models/fiscal_company_change_search_domain_mixin.py @@ -49,8 +49,15 @@ def _fiscal_company_change_domain(self, domain): new_operator = "in" elif item[1] in ["!=", "not in"]: new_operator = "not in" + elif item[1] in ["child_of"]: + new_operator = "child_of" else: - raise NotImplementedError("Not implemented operator") + raise NotImplementedError( + "fiscal.company.change.search.domain.mixin:" + f" Not implemented operator {item[1]}" + f" ({self._name}).\n" + f" Full Domain: {domain}" + ) if has_false: new_company_ids.append(False) diff --git a/fiscal_company_base/models/models.py b/fiscal_company_base/models/models.py new file mode 100644 index 0000000..926f4f5 --- /dev/null +++ b/fiscal_company_base/models/models.py @@ -0,0 +1,16 @@ +# 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 import models + +with_company_original = models.BaseModel.with_company + + +def with_company(self, company): + if self.env.context.get("fiscal_company_disable_switch_company", False): + return self + return with_company_original(self, company) + + +models.BaseModel.with_company = with_company diff --git a/fiscal_company_base/models/res_users.py b/fiscal_company_base/models/res_users.py new file mode 100644 index 0000000..3ea2b61 --- /dev/null +++ b/fiscal_company_base/models/res_users.py @@ -0,0 +1,18 @@ +# 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 import models + + +class ResUsers(models.Model): + _inherit = "res.users" + + def _include_fiscal_company_ids(self, company_ids): + companies = self.env["res.company"].browse(company_ids) + return ( + company_ids + + companies.filtered(lambda x: x.fiscal_type == "fiscal_child") + .mapped("parent_id") + .ids + ) diff --git a/fiscal_company_base/readme/DEVELOP.rst b/fiscal_company_base/readme/DEVELOP.rst index b130f06..776ec76 100644 --- a/fiscal_company_base/readme/DEVELOP.rst +++ b/fiscal_company_base/readme/DEVELOP.rst @@ -1,4 +1,32 @@ -This module introduces 2 mixin: +This module introduce a contextual key to change the behaviour of with_company. + +For exemple, in odoo/addons/account/models/account_move.py file, the following +code is present + +.. code-block:: python + + @api.onchange('partner_id') + def _onchange_partner_id(self): + self = self.with_company(self.journal_id.company_id) + ... + +That's annoying, because the company of the journal is not the same as +the company of the account move. So, in a CAE context, the company of the move +will be the integrated company, and the company of the journal will be the CAE. + +So, it's possible to write the following code, to disable locally the with_company call +with the following syntax. + + +.. code-block:: python + + @api.onchange("partner_id") + def _onchange_partner_id(self): + return super( + AccountMove, self.with_context(fiscal_company_disable_switch_company=True) + )._onchange_partner_id() + +This module also introduces 2 mixin: ``fiscal.company.change.search.domain.mixin`` ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ diff --git a/fiscal_company_base/tests/__init__.py b/fiscal_company_base/tests/__init__.py index f434a12..637a31b 100644 --- a/fiscal_company_base/tests/__init__.py +++ b/fiscal_company_base/tests/__init__.py @@ -1,3 +1,5 @@ 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_with_company diff --git a/fiscal_company_base/tests/models.py b/fiscal_company_base/tests/models.py index b69d827..34a22f4 100644 --- a/fiscal_company_base/tests/models.py +++ b/fiscal_company_base/tests/models.py @@ -1,21 +1,42 @@ from odoo import fields, models +class ModelFiscalCompanyChangeFilteredMixin(models.Model): + _name = "model.fiscal.company.change.filtered.mixin" + _description = "model.fiscal.company.change.filtered.mixin" + _inherit = ["fiscal.company.change.filtered.mixin"] + + company_id = fields.Many2one(comodel_name="res.company") + + class ModelFiscalCompanyChangeSearchDomainMixin(models.Model): _name = "model.fiscal.company.change.search.domain.mixin" - _inherit = ["fiscal.company.change.search.domain.mixin"] - _description = "model.fiscal.company.change.search.domain.mixin" + _inherit = ["fiscal.company.change.search.domain.mixin"] company_id = fields.Many2one(comodel_name="res.company") -class FiscalCompanyCheckCompanyMixinFiscalMother(models.Model): +class ModelFiscalCompanyCheckCompanyMixinFiscalMother(models.Model): _name = "model.fiscal.company.check.company.mixin.fiscal.mother" - _inherit = ["fiscal.company.check.company.mixin"] - _description = "model.fiscal.company.change.search.domain.mixin" + _inherit = ["fiscal.company.check.company.mixin"] _fiscal_company_forbid_fiscal_type = ["fiscal_mother"] company_id = fields.Many2one(comodel_name="res.company") + + +class ModelWithCompany(models.Model): + _name = "model.with.company" + _description = "model.with.company" + + def with_company_disabled(self, company): + return self.with_context( + fiscal_company_disable_switch_company=True + ).with_company(company) + + def with_company_enabled(self, company): + return self.with_context( + fiscal_company_disable_switch_company=False + ).with_company(company) diff --git a/fiscal_company_base/tests/test_abstract.py b/fiscal_company_base/tests/test_abstract.py index 48e4d9d..ad35873 100644 --- a/fiscal_company_base/tests/test_abstract.py +++ b/fiscal_company_base/tests/test_abstract.py @@ -9,9 +9,10 @@ class TestAbstract(TransactionCase): @classmethod def setUpClass(cls): super().setUpClass() - cls.ResCompany = cls.env["res.company"] + cls.ResPartner = cls.env["res.partner"] cls.group_company = cls.env.ref("fiscal_company_base.company_group") cls.mother_company = cls.env.ref("fiscal_company_base.company_fiscal_mother") cls.child_company = cls.env.ref("fiscal_company_base.company_fiscal_child_1") cls.normal_company = cls.env.ref("base.main_company") + cls.user_accountant = cls.env.ref("fiscal_company_base.user_accountant") diff --git a/fiscal_company_base/tests/test_fiscal_company_change_filtered_mixin.py b/fiscal_company_base/tests/test_fiscal_company_change_filtered_mixin.py new file mode 100644 index 0000000..5f9774e --- /dev/null +++ b/fiscal_company_base/tests/test_fiscal_company_change_filtered_mixin.py @@ -0,0 +1,44 @@ +# 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 TestFiscalCompanyChangeFilteredMixin(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 ModelFiscalCompanyChangeFilteredMixin + + cls.loader.update_registry((ModelFiscalCompanyChangeFilteredMixin,)) + + cls.model = cls.env["model.fiscal.company.change.filtered.mixin"] + cls.items = cls.model.create( + [ + {"company_id": False}, + {"company_id": cls.group_company.id}, + {"company_id": cls.normal_company.id}, + {"company_id": cls.mother_company.id}, + {"company_id": cls.child_company.id}, + ] + ) + + @classmethod + def tearDownClass(cls): + cls.loader.restore_registry() + return super().tearDownClass() + + def test_filtered(self): + result = self.items.filtered(lambda x: True) + self.assertEqual(len(self.items), 5) + + result = self.items.with_company(self.child_company).filtered( + lambda x: x.company_id == self.child_company + ) + self.assertEqual(len(result), 1) + self.assertEqual(result.company_id, self.mother_company) diff --git a/fiscal_company_base/tests/test_fiscal_company_check_company_mixin.py b/fiscal_company_base/tests/test_fiscal_company_check_company_mixin.py index dcf957d..4064114 100644 --- a/fiscal_company_base/tests/test_fiscal_company_check_company_mixin.py +++ b/fiscal_company_base/tests/test_fiscal_company_check_company_mixin.py @@ -15,9 +15,9 @@ def setUpClass(cls): # Load a test model using odoo_test_helper cls.loader = FakeModelLoader(cls.env, cls.__module__) cls.loader.backup_registry() - from .models import FiscalCompanyCheckCompanyMixinFiscalMother + from .models import ModelFiscalCompanyCheckCompanyMixinFiscalMother - cls.loader.update_registry((FiscalCompanyCheckCompanyMixinFiscalMother,)) + cls.loader.update_registry((ModelFiscalCompanyCheckCompanyMixinFiscalMother,)) cls.model_fiscal_mother = cls.env[ "model.fiscal.company.check.company.mixin.fiscal.mother" diff --git a/fiscal_company_base/tests/test_with_company.py b/fiscal_company_base/tests/test_with_company.py new file mode 100644 index 0000000..2f870e1 --- /dev/null +++ b/fiscal_company_base/tests/test_with_company.py @@ -0,0 +1,33 @@ +# 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 TestWithCompany(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 ModelWithCompany + + cls.loader.update_registry((ModelWithCompany,)) + + cls.model_with_company = cls.env["model.with.company"] + + @classmethod + def tearDownClass(cls): + cls.loader.restore_registry() + return super().tearDownClass() + + def test_with_company_change_disabled(self): + res = self.model_with_company.with_company_disabled(self.mother_company) + self.assertEqual(res.env.company, self.normal_company) + + def test_with_company_change_enabled(self): + res = self.model_with_company.with_company_enabled(self.mother_company) + self.assertEqual(res.env.company, self.mother_company)