Browse Source

[REF] contract: make recurring_invoicing_offset a computed field

In preparation to making it user modifiable.
pull/434/head
Stéphane Bidoul (ACSONE) 5 years ago
parent
commit
8b30ed0a3e
No known key found for this signature in database GPG Key ID: BCAB2555446B5B92
  1. 2
      contract/__manifest__.py
  2. 34
      contract/models/abstract_contract_line.py
  3. 48
      contract/models/contract_line.py
  4. 72
      contract/tests/test_contract.py

2
contract/__manifest__.py

@ -4,7 +4,7 @@
# Copyright 2016-2018 Tecnativa - Carlos Dauden # Copyright 2016-2018 Tecnativa - Carlos Dauden
# Copyright 2017 Tecnativa - Vicent Cubells # Copyright 2017 Tecnativa - Vicent Cubells
# Copyright 2016-2017 LasLabs Inc. # 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). # License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).
{ {

34
contract/models/abstract_contract_line.py

@ -70,9 +70,20 @@ class ContractAbstractContractLine(models.AbstractModel):
[('pre-paid', 'Pre-paid'), ('post-paid', 'Post-paid')], [('pre-paid', 'Pre-paid'), ('post-paid', 'Post-paid')],
default='pre-paid', default='pre-paid',
string='Invoicing type', 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, 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( recurring_interval = fields.Integer(
default=1, default=1,
string='Invoice Every', string='Invoice Every',
@ -115,6 +126,27 @@ class ContractAbstractContractLine(models.AbstractModel):
ondelete='cascade', 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( @api.depends(
'automatic_price', 'automatic_price',
'specific_price', 'specific_price',

48
contract/models/contract_line.py

@ -375,32 +375,20 @@ class ContractLine(models.Model):
return self._get_recurring_next_date( return self._get_recurring_next_date(
date_start, date_start,
recurring_invoicing_type, recurring_invoicing_type,
self._get_default_recurring_invoicing_offset(
recurring_invoicing_type, recurring_rule_type
),
recurring_rule_type, recurring_rule_type,
recurring_interval, recurring_interval,
max_date_end=False, 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 @api.model
def _get_recurring_next_date( def _get_recurring_next_date(
self, self,
next_period_date_start, next_period_date_start,
recurring_invoicing_type, recurring_invoicing_type,
recurring_invoicing_offset,
recurring_rule_type, recurring_rule_type,
recurring_interval, recurring_interval,
max_date_end, max_date_end,
@ -408,17 +396,23 @@ class ContractLine(models.Model):
next_period_date_end = self._get_next_period_date_end( next_period_date_end = self._get_next_period_date_end(
next_period_date_start, next_period_date_start,
recurring_invoicing_type, recurring_invoicing_type,
recurring_invoicing_offset,
recurring_rule_type, recurring_rule_type,
recurring_interval, recurring_interval,
max_date_end=max_date_end, max_date_end=max_date_end,
) )
if not next_period_date_end: if not next_period_date_end:
return False return False
offset = self._get_offset(recurring_invoicing_type, recurring_rule_type)
if recurring_invoicing_type == 'pre-paid': 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 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 return recurring_next_date
@api.model @api.model
@ -426,6 +420,7 @@ class ContractLine(models.Model):
self, self,
next_period_date_start, next_period_date_start,
recurring_invoicing_type, recurring_invoicing_type,
recurring_invoicing_offset,
recurring_rule_type, recurring_rule_type,
recurring_interval, recurring_interval,
max_date_end, max_date_end,
@ -448,18 +443,20 @@ class ContractLine(models.Model):
) )
else: else:
# special algorithm when the next invoice date is forced # 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': if recurring_invoicing_type == 'pre-paid':
next_period_date_end = ( next_period_date_end = (
next_invoice_date next_invoice_date
- offset
- relativedelta(days=recurring_invoicing_offset)
+ self.get_relative_delta( + self.get_relative_delta(
recurring_rule_type, recurring_interval recurring_rule_type, recurring_interval
) )
- relativedelta(days=1) - relativedelta(days=1)
) )
else: # post-paid 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: if max_date_end and next_period_date_end > max_date_end:
# end date is past max_date_end: trim it # end date is past max_date_end: trim it
next_period_date_end = max_date_end next_period_date_end = max_date_end
@ -481,6 +478,7 @@ class ContractLine(models.Model):
@api.depends( @api.depends(
'next_period_date_start', 'next_period_date_start',
'recurring_invoicing_type', 'recurring_invoicing_type',
'recurring_invoicing_offset',
'recurring_rule_type', 'recurring_rule_type',
'recurring_interval', 'recurring_interval',
'date_end', '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_end = self._get_next_period_date_end(
rec.next_period_date_start, rec.next_period_date_start,
rec.recurring_invoicing_type, rec.recurring_invoicing_type,
rec.recurring_invoicing_offset,
rec.recurring_rule_type, rec.recurring_rule_type,
rec.recurring_interval, rec.recurring_interval,
max_date_end=rec.date_end, max_date_end=rec.date_end,
@ -538,6 +537,7 @@ class ContractLine(models.Model):
rec.recurring_next_date = self._get_recurring_next_date( rec.recurring_next_date = self._get_recurring_next_date(
rec.date_start, rec.date_start,
rec.recurring_invoicing_type, rec.recurring_invoicing_type,
rec.recurring_invoicing_offset,
rec.recurring_rule_type, rec.recurring_rule_type,
rec.recurring_interval, rec.recurring_interval,
max_date_end=rec.date_end, max_date_end=rec.date_end,
@ -673,6 +673,7 @@ class ContractLine(models.Model):
last_date_invoiced = self._get_next_period_date_end( last_date_invoiced = self._get_next_period_date_end(
first_date_invoiced, first_date_invoiced,
self.recurring_invoicing_type, self.recurring_invoicing_type,
self.recurring_invoicing_offset,
self.recurring_rule_type, self.recurring_rule_type,
self.recurring_interval, self.recurring_interval,
max_date_end=(self.date_end if stop_at_date_end else False), 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( recurring_next_date = rec._get_recurring_next_date(
last_date_invoiced + relativedelta(days=1), last_date_invoiced + relativedelta(days=1),
rec.recurring_invoicing_type, rec.recurring_invoicing_type,
rec.recurring_invoicing_offset,
rec.recurring_rule_type, rec.recurring_rule_type,
rec.recurring_interval, rec.recurring_interval,
max_date_end=rec.date_end, max_date_end=rec.date_end,
@ -778,6 +780,7 @@ class ContractLine(models.Model):
new_recurring_next_date = self._get_recurring_next_date( new_recurring_next_date = self._get_recurring_next_date(
new_date_start, new_date_start,
rec.recurring_invoicing_type, rec.recurring_invoicing_type,
rec.recurring_invoicing_offset,
rec.recurring_rule_type, rec.recurring_rule_type,
rec.recurring_interval, rec.recurring_interval,
max_date_end=new_date_end max_date_end=new_date_end
@ -842,6 +845,7 @@ class ContractLine(models.Model):
recurring_next_date = self._get_recurring_next_date( recurring_next_date = self._get_recurring_next_date(
date_start, date_start,
self.recurring_invoicing_type, self.recurring_invoicing_type,
self.recurring_invoicing_offset,
self.recurring_rule_type, self.recurring_rule_type,
self.recurring_interval, self.recurring_interval,
max_date_end=date_end, max_date_end=date_end,

72
contract/tests/test_contract.py

@ -537,6 +537,33 @@ class TestContract(TestContractBase):
'There was an error and the view couldn\'t be opened.', '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): def test_get_recurring_next_date(self):
"""Test different combination to compute recurring_next_date """Test different combination to compute recurring_next_date
Combination format Combination format
@ -555,87 +582,92 @@ class TestContract(TestContractBase):
def error_message( def error_message(
date_start, date_start,
recurring_invoicing_type, recurring_invoicing_type,
recurring_invoicing_offset,
recurring_rule_type, recurring_rule_type,
recurring_interval, recurring_interval,
max_date_end, 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 = [ combinations = [
( (
to_date('2018-01-01'), 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), False),
), ),
( (
to_date('2018-01-01'), 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')), to_date('2018-01-15')),
), ),
( (
False, 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-15')),
), ),
( (
to_date('2018-01-01'), 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), False),
), ),
( (
to_date('2018-02-01'), 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), False),
), ),
( (
to_date('2018-01-16'), 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')), to_date('2018-01-15')),
), ),
( (
False, 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-01-15')),
), ),
( (
to_date('2018-03-01'), 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), False),
), ),
( (
to_date('2018-01-31'), 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), False),
), ),
( (
to_date('2018-01-06'), 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), False),
), ),
( (
to_date('2018-02-28'), 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), False),
), ),
( (
to_date('2018-01-05'), 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), False),
), ),
( (
to_date('2018-01-05'), 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), False),
), ),
( (
to_date('2019-01-05'), 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), False),
), ),
] ]

Loading…
Cancel
Save