From 090aeca070c00fdf78d20fe1fcfb9aff2905a433 Mon Sep 17 00:00:00 2001 From: Benoit Aimont Date: Wed, 13 May 2026 10:45:21 +0200 Subject: [PATCH] [IMP] contract - add method _get_contract_line_total_value --- contract/models/contract_line.py | 26 ++++++++++++-- contract/tests/test_contract.py | 60 +++++++++++++++++++++++++++++++- 2 files changed, 83 insertions(+), 3 deletions(-) diff --git a/contract/models/contract_line.py b/contract/models/contract_line.py index dea62497a3..ee68fef2fb 100644 --- a/contract/models/contract_line.py +++ b/contract/models/contract_line.py @@ -8,8 +8,8 @@ from dateutil.relativedelta import relativedelta -from odoo import api, fields, models -from odoo.exceptions import ValidationError +from odoo import _, api, fields, models +from odoo.exceptions import UserError, ValidationError class ContractLine(models.Model): @@ -312,3 +312,25 @@ def _get_quantity_to_invoice( ): self.ensure_one() return self.quantity if not self.display_type else 0.0 + + def _get_contract_line_total_value(self): + """Return the total value of this contract line over its full period. + + Raises NotImplementedError if the line has no date_end, since a + total cannot be computed without a known end date. + """ + self.ensure_one() + if not self.date_end: + raise UserError( + _( + "Cannot compute total value for contract line '%s': " + "no end date is set.", + self.display_name, + ) + ) + quantity = self._get_quantity_to_invoice( + self.date_start, + self.date_end, + self.date_start, + ) + return quantity * self.price_unit diff --git a/contract/tests/test_contract.py b/contract/tests/test_contract.py index 7e7029d0b8..96073b686f 100644 --- a/contract/tests/test_contract.py +++ b/contract/tests/test_contract.py @@ -10,7 +10,7 @@ from freezegun import freeze_time from odoo import Command, fields -from odoo.exceptions import ValidationError +from odoo.exceptions import UserError, ValidationError from odoo.tests import Form, common @@ -1610,3 +1610,61 @@ def test_analytic_distribution(self): self.assertEqual( new_contract_line.analytic_distribution, {str(analytic_account.id): 100} ) + + def test_get_contract_line_total_value_basic(self): + """Total value = quantity × price_unit over the full period.""" + self.acct_line.write( + { + "quantity": 3, + "price_unit": 400.0, + "date_start": "2026-01-01", + "date_end": "2026-12-31", + "recurring_next_date": "2026-01-01", + } + ) + self.assertEqual(self.acct_line._get_contract_line_total_value(), 3 * 400.0) + + def test_get_contract_line_total_value_single_unit(self): + self.acct_line.write( + { + "quantity": 1, + "price_unit": 1200.0, + "date_start": "2026-01-01", + "date_end": "2026-12-31", + "recurring_next_date": "2026-01-01", + } + ) + self.assertEqual(self.acct_line._get_contract_line_total_value(), 1200.0) + + def test_get_contract_line_total_value_zero_price(self): + self.acct_line.write( + { + "quantity": 5, + "price_unit": 0.0, + "date_start": "2026-01-01", + "date_end": "2026-06-30", + "recurring_next_date": "2026-01-01", + } + ) + self.assertEqual(self.acct_line._get_contract_line_total_value(), 0.0) + + def test_get_contract_line_total_value_no_date_end_raises(self): + """A line without date_end cannot compute a total: UserError expected.""" + line = self.env["contract.line"].new( + { + "contract_id": self.contract.id, + "product_id": self.product_1.id, + "name": "Test", + "quantity": 1, + "price_unit": 1000.0, + "date_start": to_date("2026-01-01"), + "recurring_next_date": to_date("2026-01-01"), + "recurring_interval": 1, + "recurring_rule_type": "monthly", + "recurring_invoicing_type": "pre-paid", + "uom_id": self.product_1.uom_id.id, + } + ) + line.date_end = False + with self.assertRaises(UserError): + line._get_contract_line_total_value()