diff --git a/contract/__manifest__.py b/contract/__manifest__.py index 274ab732..f698f180 100644 --- a/contract/__manifest__.py +++ b/contract/__manifest__.py @@ -4,7 +4,7 @@ # Copyright 2016-2018 Tecnativa - Carlos Dauden # Copyright 2017 Tecnativa - Vicent Cubells # Copyright 2016-2017 LasLabs Inc. -# Copyright 2018 ACSONE SA/NV +# Copyright 2018-2019 ACSONE SA/NV # License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). { diff --git a/contract/models/abstract_contract_line.py b/contract/models/abstract_contract_line.py index c1320208..d4d94691 100644 --- a/contract/models/abstract_contract_line.py +++ b/contract/models/abstract_contract_line.py @@ -70,9 +70,20 @@ class ContractAbstractContractLine(models.AbstractModel): [('pre-paid', 'Pre-paid'), ('post-paid', 'Post-paid')], default='pre-paid', string='Invoicing type', - help="Specify if process date is 'from' or 'to' invoicing date", + help=( + "Specify if the invoice must be generated at the beginning " + "(pre-paid) or end (post-paid) of the period." + ), required=True, ) + recurring_invoicing_offset = fields.Integer( + compute="_compute_recurring_invoicing_offset", + string="Invoicing offset", + help=( + "Number of days to offset the invoice from the period end " + "date (in post-paid mode) or beginning date (in pre-paid mode)." + ) + ) recurring_interval = fields.Integer( default=1, string='Invoice Every', @@ -115,6 +126,27 @@ class ContractAbstractContractLine(models.AbstractModel): ondelete='cascade', ) + @api.model + def _get_default_recurring_invoicing_offset( + self, recurring_invoicing_type, recurring_rule_type + ): + if ( + recurring_invoicing_type == 'pre-paid' + or recurring_rule_type == 'monthlylastday' + ): + return 0 + else: + return 1 + + @api.depends('recurring_invoicing_type', 'recurring_rule_type') + def _compute_recurring_invoicing_offset(self): + for rec in self: + rec.recurring_invoicing_offset = ( + self._get_default_recurring_invoicing_offset( + rec.recurring_invoicing_type, rec.recurring_rule_type + ) + ) + @api.depends( 'automatic_price', 'specific_price', diff --git a/contract/models/contract_line.py b/contract/models/contract_line.py index aacb2c1d..6054b303 100644 --- a/contract/models/contract_line.py +++ b/contract/models/contract_line.py @@ -375,32 +375,20 @@ class ContractLine(models.Model): return self._get_recurring_next_date( date_start, recurring_invoicing_type, + self._get_default_recurring_invoicing_offset( + recurring_invoicing_type, recurring_rule_type + ), recurring_rule_type, recurring_interval, max_date_end=False, ) - @api.model - def _get_offset(self, recurring_invoicing_type, recurring_rule_type): - """Return a relativedelta to offset the invoice date compared - to the period start or end date. - - This method will disappear when the offset becomes user controlled. - """ - if ( - recurring_invoicing_type == 'pre-paid' - or recurring_rule_type == 'monthlylastday' - ): - offset = 0 - else: - offset = 1 - return relativedelta(days=offset) - @api.model def _get_recurring_next_date( self, next_period_date_start, recurring_invoicing_type, + recurring_invoicing_offset, recurring_rule_type, recurring_interval, max_date_end, @@ -408,17 +396,23 @@ class ContractLine(models.Model): next_period_date_end = self._get_next_period_date_end( next_period_date_start, recurring_invoicing_type, + recurring_invoicing_offset, recurring_rule_type, recurring_interval, max_date_end=max_date_end, ) if not next_period_date_end: return False - offset = self._get_offset(recurring_invoicing_type, recurring_rule_type) if recurring_invoicing_type == 'pre-paid': - recurring_next_date = next_period_date_start + offset + recurring_next_date = ( + next_period_date_start + + relativedelta(days=recurring_invoicing_offset) + ) else: # post-paid - recurring_next_date = next_period_date_end + offset + recurring_next_date = ( + next_period_date_end + + relativedelta(days=recurring_invoicing_offset) + ) return recurring_next_date @api.model @@ -426,6 +420,7 @@ class ContractLine(models.Model): self, next_period_date_start, recurring_invoicing_type, + recurring_invoicing_offset, recurring_rule_type, recurring_interval, max_date_end, @@ -448,18 +443,20 @@ class ContractLine(models.Model): ) else: # special algorithm when the next invoice date is forced - offset = self._get_offset(recurring_invoicing_type, recurring_rule_type) if recurring_invoicing_type == 'pre-paid': next_period_date_end = ( next_invoice_date - - offset + - relativedelta(days=recurring_invoicing_offset) + self.get_relative_delta( recurring_rule_type, recurring_interval ) - relativedelta(days=1) ) else: # post-paid - next_period_date_end = next_invoice_date - offset + next_period_date_end = ( + next_invoice_date + - relativedelta(days=recurring_invoicing_offset) + ) if max_date_end and next_period_date_end > max_date_end: # end date is past max_date_end: trim it next_period_date_end = max_date_end @@ -481,6 +478,7 @@ class ContractLine(models.Model): @api.depends( 'next_period_date_start', 'recurring_invoicing_type', + 'recurring_invoicing_offset', 'recurring_rule_type', 'recurring_interval', 'date_end', @@ -491,6 +489,7 @@ class ContractLine(models.Model): rec.next_period_date_end = self._get_next_period_date_end( rec.next_period_date_start, rec.recurring_invoicing_type, + rec.recurring_invoicing_offset, rec.recurring_rule_type, rec.recurring_interval, max_date_end=rec.date_end, @@ -538,6 +537,7 @@ class ContractLine(models.Model): rec.recurring_next_date = self._get_recurring_next_date( rec.date_start, rec.recurring_invoicing_type, + rec.recurring_invoicing_offset, rec.recurring_rule_type, rec.recurring_interval, max_date_end=rec.date_end, @@ -673,6 +673,7 @@ class ContractLine(models.Model): last_date_invoiced = self._get_next_period_date_end( first_date_invoiced, self.recurring_invoicing_type, + self.recurring_invoicing_offset, self.recurring_rule_type, self.recurring_interval, max_date_end=(self.date_end if stop_at_date_end else False), @@ -702,6 +703,7 @@ class ContractLine(models.Model): recurring_next_date = rec._get_recurring_next_date( last_date_invoiced + relativedelta(days=1), rec.recurring_invoicing_type, + rec.recurring_invoicing_offset, rec.recurring_rule_type, rec.recurring_interval, max_date_end=rec.date_end, @@ -778,6 +780,7 @@ class ContractLine(models.Model): new_recurring_next_date = self._get_recurring_next_date( new_date_start, rec.recurring_invoicing_type, + rec.recurring_invoicing_offset, rec.recurring_rule_type, rec.recurring_interval, max_date_end=new_date_end @@ -842,6 +845,7 @@ class ContractLine(models.Model): recurring_next_date = self._get_recurring_next_date( date_start, self.recurring_invoicing_type, + self.recurring_invoicing_offset, self.recurring_rule_type, self.recurring_interval, max_date_end=date_end, diff --git a/contract/tests/test_contract.py b/contract/tests/test_contract.py index b9ed4edd..e8c49b6f 100644 --- a/contract/tests/test_contract.py +++ b/contract/tests/test_contract.py @@ -537,6 +537,33 @@ class TestContract(TestContractBase): 'There was an error and the view couldn\'t be opened.', ) + def test_get_default_recurring_invoicing_offset(self): + clm = self.env['contract.line'] + self.assertEqual( + clm._get_default_recurring_invoicing_offset( + "pre-paid", "monthly" + ), + 0 + ) + self.assertEqual( + clm._get_default_recurring_invoicing_offset( + "post-paid", "monthly" + ), + 1 + ) + self.assertEqual( + clm._get_default_recurring_invoicing_offset( + "pre-paid", "monthlylastday" + ), + 0 + ) + self.assertEqual( + clm._get_default_recurring_invoicing_offset( + "post-paid", "monthlylastday" + ), + 0 + ) + def test_get_recurring_next_date(self): """Test different combination to compute recurring_next_date Combination format @@ -555,87 +582,92 @@ class TestContract(TestContractBase): def error_message( date_start, recurring_invoicing_type, + recurring_invoicing_offset, recurring_rule_type, recurring_interval, max_date_end, ): - return "Error in %s every %d %s case, start with %s (max_date_end=%s)" % ( - recurring_invoicing_type, - recurring_interval, - recurring_rule_type, - date_start, - max_date_end, + return ( + "Error in %s-%d every %d %s case, " + "start with %s (max_date_end=%s)" % ( + recurring_invoicing_type, + recurring_invoicing_offset, + recurring_interval, + recurring_rule_type, + date_start, + max_date_end, + ) ) combinations = [ ( to_date('2018-01-01'), - (to_date('2018-01-01'), 'pre-paid', 'monthly', 1, + (to_date('2018-01-01'), 'pre-paid', 0, 'monthly', 1, False), ), ( to_date('2018-01-01'), - (to_date('2018-01-01'), 'pre-paid', 'monthly', 1, + (to_date('2018-01-01'), 'pre-paid', 0, 'monthly', 1, to_date('2018-01-15')), ), ( False, - (to_date('2018-01-16'), 'pre-paid', 'monthly', 1, + (to_date('2018-01-16'), 'pre-paid', 0, 'monthly', 1, to_date('2018-01-15')), ), ( to_date('2018-01-01'), - (to_date('2018-01-01'), 'pre-paid', 'monthly', 2, + (to_date('2018-01-01'), 'pre-paid', 0, 'monthly', 2, False), ), ( to_date('2018-02-01'), - (to_date('2018-01-01'), 'post-paid', 'monthly', 1, + (to_date('2018-01-01'), 'post-paid', 1, 'monthly', 1, False), ), ( to_date('2018-01-16'), - (to_date('2018-01-01'), 'post-paid', 'monthly', 1, + (to_date('2018-01-01'), 'post-paid', 1, 'monthly', 1, to_date('2018-01-15')), ), ( False, - (to_date('2018-01-16'), 'post-paid', 'monthly', 1, + (to_date('2018-01-16'), 'post-paid', 1, 'monthly', 1, to_date('2018-01-15')), ), ( to_date('2018-03-01'), - (to_date('2018-01-01'), 'post-paid', 'monthly', 2, + (to_date('2018-01-01'), 'post-paid', 1, 'monthly', 2, False), ), ( to_date('2018-01-31'), - (to_date('2018-01-05'), 'post-paid', 'monthlylastday', 1, + (to_date('2018-01-05'), 'post-paid', 0, 'monthlylastday', 1, False), ), ( to_date('2018-01-06'), - (to_date('2018-01-06'), 'pre-paid', 'monthlylastday', 1, + (to_date('2018-01-06'), 'pre-paid', 0, 'monthlylastday', 1, False), ), ( to_date('2018-02-28'), - (to_date('2018-01-05'), 'post-paid', 'monthlylastday', 2, + (to_date('2018-01-05'), 'post-paid', 0, 'monthlylastday', 2, False), ), ( to_date('2018-01-05'), - (to_date('2018-01-05'), 'pre-paid', 'monthlylastday', 2, + (to_date('2018-01-05'), 'pre-paid', 0, 'monthlylastday', 2, False), ), ( to_date('2018-01-05'), - (to_date('2018-01-05'), 'pre-paid', 'yearly', 1, + (to_date('2018-01-05'), 'pre-paid', 0, 'yearly', 1, False), ), ( to_date('2019-01-05'), - (to_date('2018-01-05'), 'post-paid', 'yearly', 1, + (to_date('2018-01-05'), 'post-paid', 1, 'yearly', 1, False), ), ]