[IMP] contract: support pre-paid for monthlylastday

monthlylastday is (almost) not a special case anymore \o/.
montlylastday is simply a montly period where the
periods are aligned on month boundaries.
The last bit of special casing is that postpaid generates
invoice the day after the last dasy of the period, except
for monthlylastday where the invoice is generated on the
last day of the period. This last exception will disappear
when we put the offset under user control.

This is a breaking change because the post-paid/pre-paid
mode becomes relevant for monthlylastday invoicing.
The field becomes visible in the UI. Code that generate
monthlylastday contract lines must now correctly set
the pre-paid/post-paid mode too. Some tests have had
to be adapted to reflect that.
This commit is contained in:
Stéphane Bidoul (ACSONE)
2019-12-08 12:31:04 +01:00
committed by Francisco Ivan Anton Prieto
parent 4b2f0d2e25
commit 7649f5b385
4 changed files with 73 additions and 15 deletions

View File

@@ -0,0 +1,10 @@
def migrate(cr, version):
# pre-paid/post-paid becomes significant for monthlylastday too,
# make sure it has the value that was implied for previous versions.
cr.execute(
"""\
UPDATE contract_line
SET recurring_invoicing_type = 'post-paid'
WHERE recurring_rule_type = 'monthlylastday'
"""
)

View File

@@ -380,6 +380,22 @@ class ContractLine(models.Model):
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,
@@ -398,12 +414,11 @@ class ContractLine(models.Model):
) )
if not next_period_date_end: if not next_period_date_end:
return False return False
if recurring_rule_type == 'monthlylastday': offset = self._get_offset(recurring_invoicing_type, recurring_rule_type)
recurring_next_date = next_period_date_end if recurring_invoicing_type == 'pre-paid':
elif recurring_invoicing_type == 'pre-paid': recurring_next_date = next_period_date_start + offset
recurring_next_date = next_period_date_start
else: # post-paid else: # post-paid
recurring_next_date = next_period_date_end + relativedelta(days=1) recurring_next_date = next_period_date_end + offset
return recurring_next_date return recurring_next_date
@api.model @api.model
@@ -433,20 +448,18 @@ class ContractLine(models.Model):
) )
else: else:
# special algorithm when the next invoice date is forced # special algorithm when the next invoice date is forced
if recurring_rule_type == 'monthlylastday': offset = self._get_offset(recurring_invoicing_type, recurring_rule_type)
next_period_date_end = next_invoice_date if recurring_invoicing_type == 'pre-paid':
elif recurring_invoicing_type == 'pre-paid':
next_period_date_end = ( next_period_date_end = (
next_invoice_date next_invoice_date
- 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 - relativedelta( next_period_date_end = next_invoice_date - offset
days=1
)
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

View File

@@ -614,12 +614,17 @@ class TestContract(TestContractBase):
False), False),
), ),
( (
to_date('2018-01-31'), to_date('2018-01-06'),
(to_date('2018-01-06'), 'pre-paid', 'monthlylastday', 1, (to_date('2018-01-06'), 'pre-paid', 'monthlylastday', 1,
False), False),
), ),
( (
to_date('2018-02-28'), to_date('2018-02-28'),
(to_date('2018-01-05'), 'post-paid', 'monthlylastday', 2,
False),
),
(
to_date('2018-01-05'),
(to_date('2018-01-05'), 'pre-paid', 'monthlylastday', 2, (to_date('2018-01-05'), 'pre-paid', 'monthlylastday', 2,
False), False),
), ),
@@ -1363,7 +1368,7 @@ class TestContract(TestContractBase):
len(invoice_lines), len(invoice_lines),
) )
def test_get_period_to_invoice_monthlylastday(self): def test_get_period_to_invoice_monthlylastday_postpaid(self):
self.acct_line.date_start = '2018-01-05' self.acct_line.date_start = '2018-01-05'
self.acct_line.recurring_invoicing_type = 'post-paid' self.acct_line.recurring_invoicing_type = 'post-paid'
self.acct_line.recurring_rule_type = 'monthlylastday' self.acct_line.recurring_rule_type = 'monthlylastday'
@@ -1394,6 +1399,37 @@ class TestContract(TestContractBase):
self.assertEqual(last, to_date('2018-03-15')) self.assertEqual(last, to_date('2018-03-15'))
self.acct_line.manual_renew_needed = True self.acct_line.manual_renew_needed = True
def test_get_period_to_invoice_monthlylastday_prepaid(self):
self.acct_line.date_start = '2018-01-05'
self.acct_line.recurring_invoicing_type = 'pre-paid'
self.acct_line.recurring_rule_type = 'monthlylastday'
self.acct_line.date_end = '2018-03-15'
self.acct_line._onchange_date_start()
first, last, recurring_next_date = \
self.acct_line._get_period_to_invoice(
self.acct_line.last_date_invoiced,
self.acct_line.recurring_next_date,
)
self.assertEqual(first, to_date('2018-01-05'))
self.assertEqual(last, to_date('2018-01-31'))
self.contract.recurring_create_invoice()
first, last, recurring_next_date = \
self.acct_line._get_period_to_invoice(
self.acct_line.last_date_invoiced,
self.acct_line.recurring_next_date,
)
self.assertEqual(first, to_date('2018-02-01'))
self.assertEqual(last, to_date('2018-02-28'))
self.contract.recurring_create_invoice()
first, last, recurring_next_date = \
self.acct_line._get_period_to_invoice(
self.acct_line.last_date_invoiced,
self.acct_line.recurring_next_date,
)
self.assertEqual(first, to_date('2018-03-01'))
self.assertEqual(last, to_date('2018-03-15'))
self.acct_line.manual_renew_needed = True
def test_get_period_to_invoice_monthly_pre_paid_2(self): def test_get_period_to_invoice_monthly_pre_paid_2(self):
self.acct_line.date_start = '2018-01-05' self.acct_line.date_start = '2018-01-05'
self.acct_line.recurring_invoicing_type = 'pre-paid' self.acct_line.recurring_invoicing_type = 'pre-paid'

View File

@@ -59,8 +59,7 @@
</div> </div>
</group> </group>
<group> <group>
<field name="recurring_invoicing_type" <field name="recurring_invoicing_type"/>
attrs="{'invisible': [('recurring_rule_type', '=', 'monthlylastday')]}"/>
</group> </group>
</group> </group>
</sheet> </sheet>