diff --git a/contract_sale_generation/models/contract_line.py b/contract_sale_generation/models/contract_line.py index a3005b8b8b..45d8bd847e 100644 --- a/contract_sale_generation/models/contract_line.py +++ b/contract_sale_generation/models/contract_line.py @@ -1,12 +1,36 @@ # Copyright (C) 2020 Open Source Integrators # License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). -from odoo import models +from odoo import _, api, exceptions, models class ContractLine(models.Model): _inherit = "contract.line" + @api.constrains( + "display_type", + "product_id", + ) + def _constrain_sale_product_display(self): + """If the contract generates a sale, its lines must comply with sale order lines.""" + sale_contract_lines = self.filtered( + lambda line: line.contract_id.generation_type == "sale" + ) + for line in sale_contract_lines: + # Stay aligned with accountable_required_fields SQL constraint: + # CHECK( + # display_type IS NOT NULL + # OR (product_id IS NOT NULL AND product_uom IS NOT NULL) + # ) + if not (line.display_type or (line.product_id and line.uom_id)): + raise exceptions.ValidationError( + _( + "The line %(name)s must set a Product/UoM " + "or have a different display type", + name=line.name, + ) + ) + def _prepare_sale_line_vals(self, dates, order_id=False): sale_line_vals = { "product_id": self.product_id.id, diff --git a/contract_sale_generation/tests/test_contract_sale.py b/contract_sale_generation/tests/test_contract_sale.py index 51ae1ee90f..e1aceadd51 100644 --- a/contract_sale_generation/tests/test_contract_sale.py +++ b/contract_sale_generation/tests/test_contract_sale.py @@ -3,7 +3,7 @@ # Copyright 2017 Angel Moya # License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). -from odoo import fields +from odoo import exceptions, fields from odoo.exceptions import ValidationError from odoo.tests.common import SavepointCase @@ -112,3 +112,22 @@ def test_contract_sale_analytic(self): orders = self.env["sale.order"].browse() orders |= self.contract.recurring_create_sale() self.assertEqual(self.analytic_account, orders.mapped("analytic_account_id")) + + def test_constrain_sale_contract_line(self): + """If the contract generates a sale, its lines must have a product.""" + # Arrange + contract = self.contract.copy( + default={ + "generation_type": "sale", + } + ) + # pre-condition + self.assertEqual(contract.generation_type, "sale") + + # Act + with self.assertRaises(exceptions.ValidationError) as ve: + contract.contract_line_ids.product_id = False + + # Assert + exc_message = ve.exception.args[0] + self.assertIn("must set a Product", exc_message)