diff --git a/contract/models/abstract_contract.py b/contract/models/abstract_contract.py index 377f32f14..f66650b63 100644 --- a/contract/models/abstract_contract.py +++ b/contract/models/abstract_contract.py @@ -48,6 +48,20 @@ class ContractAbstractContract(models.AbstractModel): help="Mark this check if you want to control recurrrence at line level instead" " of all together for the whole contract.", ) + generation_type = fields.Selection( + string="Generation Type", + selection=lambda self: self._selection_generation_type(), + default=lambda self: self._default_generation_type(), + help="Choose the document that will be automatically generated by cron.", + ) + + @api.model + def _selection_generation_type(self): + return [("invoice", "Invoice")] + + @api.model + def _default_generation_type(self): + return "invoice" @api.onchange("contract_type") def _onchange_contract_type(self): diff --git a/contract/models/contract.py b/contract/models/contract.py index 807042cb2..6ddf7182c 100644 --- a/contract/models/contract.py +++ b/contract/models/contract.py @@ -6,12 +6,16 @@ # Copyright 2018 ACSONE SA/NV # Copyright 2021 Tecnativa - Víctor Martínez # License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). +import logging from odoo import api, fields, models from odoo.exceptions import UserError, ValidationError +from odoo.osv import expression from odoo.tests import Form from odoo.tools.translate import _ +_logger = logging.getLogger(__name__) + class ContractContract(models.Model): _name = "contract.contract" @@ -595,23 +599,46 @@ class ContractContract(models.Model): return moves @api.model - def cron_recurring_create_invoice(self, date_ref=None): + def _get_recurring_create_func(self, create_type="invoice"): + """ + Allows to retrieve the recurring create function depending + on generate_type attribute + """ + if create_type == "invoice": + return self.__class__._recurring_create_invoice + + @api.model + def _cron_recurring_create(self, date_ref=False, create_type="invoice"): + """ + The cron function in order to create recurrent documents + from contracts. + """ + _recurring_create_func = self._get_recurring_create_func( + create_type=create_type + ) if not date_ref: date_ref = fields.Date.context_today(self) domain = self._get_contracts_to_invoice_domain(date_ref) - invoices = self.env["account.move"] + domain = expression.AND( + [ + domain, + [("generation_type", "=", create_type)], + ] + ) + contracts = self.search(domain) + companies = set(contracts.mapped("company_id")) # Invoice by companies, so assignation emails get correct context - companies_to_invoice = self.read_group(domain, ["company_id"], ["company_id"]) - for row in companies_to_invoice: - contracts_to_invoice = ( - self.search(row["__domain"]) - .with_context(allowed_company_ids=[row["company_id"][0]]) - .filtered( - lambda a: not a.date_end or a.recurring_next_date <= a.date_end - ) - ) - invoices |= contracts_to_invoice._recurring_create_invoice(date_ref) - return invoices + for company in companies: + contracts_to_invoice = contracts.filtered( + lambda c: c.company_id == company + and (not c.date_end or c.recurring_next_date <= c.date_end) + ).with_company(company) + _recurring_create_func(contracts_to_invoice, date_ref) + return True + + @api.model + def cron_recurring_create_invoice(self, date_ref=None): + return self._cron_recurring_create(date_ref, create_type="invoice") def action_terminate_contract(self): self.ensure_one() diff --git a/contract/tests/__init__.py b/contract/tests/__init__.py index 2dafab3fc..562940edc 100644 --- a/contract/tests/__init__.py +++ b/contract/tests/__init__.py @@ -1,4 +1,3 @@ -# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). - from . import test_contract from . import test_portal +from . import test_multicompany diff --git a/contract/tests/test_multicompany.py b/contract/tests/test_multicompany.py new file mode 100644 index 000000000..395d5d2e5 --- /dev/null +++ b/contract/tests/test_multicompany.py @@ -0,0 +1,110 @@ +# Copyright 2021 ACSONE SA/NV () +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html). + +from .test_contract import TestContractBase + + +class ContractMulticompanyCase(TestContractBase): + @classmethod + def setUpClass(cls): + super().setUpClass() + chart_template = cls.env.ref("l10n_generic_coa.configurable_chart_template") + cls.company_obj = cls.env["res.company"] + cls.company_1 = cls.env.ref("base.main_company") + vals = {"name": "Company 2"} + cls.company_2 = cls.company_obj.create(vals) + chart_template.try_loading(company=cls.company_2) + cls.env.user.company_ids |= cls.company_2 + + cls.contract_mc = ( + cls.env["contract.contract"] + .with_company(cls.company_2) + .create( + { + "name": "Test Contract MC", + "partner_id": cls.partner.id, + "pricelist_id": cls.partner.property_product_pricelist.id, + "line_recurrence": True, + "contract_type": "purchase", + "contract_line_ids": [ + ( + 0, + 0, + { + "product_id": cls.product_1.id, + "name": "Services from #START# to #END#", + "quantity": 1, + "uom_id": cls.product_1.uom_id.id, + "price_unit": 100, + "discount": 50, + "recurring_rule_type": "monthly", + "recurring_interval": 1, + "date_start": "2018-02-15", + "recurring_next_date": "2018-02-22", + }, + ) + ], + } + ) + ) + cls.line_vals = { + "contract_id": cls.contract_mc.id, + "product_id": cls.product_1.id, + "name": "Services from #START# to #END#", + "quantity": 1, + "uom_id": cls.product_1.uom_id.id, + "price_unit": 100, + "discount": 50, + "recurring_rule_type": "monthly", + "recurring_interval": 1, + "date_start": "2018-01-01", + "recurring_next_date": "2018-01-15", + "is_auto_renew": False, + } + cls.acct_line_mc = ( + cls.env["contract.line"].with_company(cls.company_2).create(cls.line_vals) + ) + + def test_cron_recurring_create_invoice_multi_company(self): + self.acct_line.date_start = "2018-01-01" + self.acct_line.recurring_invoicing_type = "post-paid" + self.acct_line.date_end = "2018-03-15" + + self.acct_line_mc.date_start = "2018-01-01" + self.acct_line_mc.recurring_invoicing_type = "post-paid" + self.acct_line_mc.date_end = "2018-03-15" + + contracts = self.contract2 + contracts_company_2 = self.env["contract.contract"].browse() + for _i in range(10): + contracts |= self.contract.copy() + for _i in range(10): + vals = ( + self.contract_mc.with_company(company=self.company_2) + .with_context(active_test=False) + .copy_data({"company_id": self.company_2.id}) + ) + contracts_company_2 |= self.contract_mc.with_company( + company=self.company_2 + ).create(vals) + self.env["contract.contract"].cron_recurring_create_invoice() + # Check company 1 + invoice_lines_company_1 = self.env["account.move.line"].search( + [("contract_line_id", "in", contracts.mapped("contract_line_ids").ids)] + ) + invoice_lines_company_2 = self.env["account.move.line"].search( + [ + ( + "contract_line_id", + "in", + contracts_company_2.mapped("contract_line_ids").ids, + ) + ] + ) + self.assertEqual( + len(contracts.mapped("contract_line_ids")), len(invoice_lines_company_1) + ) + self.assertEqual( + len(contracts_company_2.mapped("contract_line_ids")), + len(invoice_lines_company_2), + ) diff --git a/contract/views/contract.xml b/contract/views/contract.xml index b8bc04c4d..46d33a82b 100644 --- a/contract/views/contract.xml +++ b/contract/views/contract.xml @@ -40,7 +40,7 @@