[IMP] contract: Make recurrence mechanism on contract line

Make recurrence mechanism on contract line and some other refactoring

[FIX] - Keep contract_cron on account_analytic_account model

contract_cron defined with no_update option.
Changing it, will cause issue to past version installation.

[IMP] - Fix recurring_next_date default value

recurring_next_date should have start_date as default value in prepaid policy
and start_date + invoicing_interval if postpaid

[FIX] - Fix test check no journal

[IMP] - Return created invoices on recurring_create_invoice

[IMP] - Specific process to compute recurring_next_date for  monthly-last-day

fixes: #198

[ADD] - Add Post-migration script to bring recurrence info from contract to contract lines

[ADD] - Add search filter based on date_end and recurring_next_date

 - not_finished filter in contract search view
 - finished filter in contract search view
 - Next Invoice group by in contract search view

[ADD] - Add unit tests

- cases to compute first recurring next date
- contract recurring_next_date
- contract date_end

[IMP] - Improve Unit tests

[12.0][IMP] - Add strat/stop wizard to contract line

[12.0][IMP] - Add pause button to contract line

[IMP] - Add state filed in contract line form

[FIX] - stop don't change date_end for finished contract line

[IMP] - Change contract line buttons visibility

Add renewal process with termination notice

[FIX] - don't consider stop_date If it is after the contract line end_date

[IMP] - consider more cases in stop_plan_successor

[IMP] - cancel upcoming line on stop

[IMP] - Chnage next invoice date on un-cancel

[IMP] - Post message in contract on contract line actions

[IMP] - check contract line overlap

[FIX] - invoice last period for post-paid case

[IMP] - Add primary views for contract

[IMP] - don't use related filed for partner_id and pricelist_id

[FIX] - fix stop_plan_successor case 5

contract line start in the suspension period and end after it

[IMP] - improve cancel/uncancel process

[FIX] - Test if start_date is set before compute

[FIX] - date_end include in the period in auto_renew case

[FIX] - in suspension case, contract line should start a day after the end

[IMP] - confirm message on contract line cancel

[IMP] - hide recurring_invoicing_type if recurring_rule_type is monthlylastday

for the monthlylastday case, pre-paid is logicly impossible,
if monthlylastday is set, we consider only post-paid case

[IMP] - Improve unit tests

[IMP] - store last_date_invoiced on contract_line

Improve CRITERIA_ALLOWED_DICT

[IMP] - code improvement

[IMP] - Use last_date_invoiced to set marker in invoice description

[IMP] - add migration script to init last_day_invoiced and some other improvement

[FIX] - a contract line suspended should start a day after the suspension end

[IMP] - don't allow to unlink uncnaceled contrac line

[FIX] - check date_start before onchange

[FIX] - compute recurring_next_date for contract

[IMP] - get contract line default data onchange product_id

[IMP] - Add responsible to contract form view

[FIX] - contract recurring_next_date ignore canceled lines

[FIX] - fix _get_invoiced_period if recurring_next_date manually updated

[IMP] - archive contract_line on contract archive
This commit is contained in:
sbejaoui
2018-10-30 18:50:41 +01:00
committed by Pedro M. Baeza
parent 336fb4502e
commit 2e701748e3
30 changed files with 4072 additions and 1213 deletions

View File

@@ -0,0 +1,428 @@
# Copyright 2018 ACSONE SA/NV.
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).
import itertools
from collections import namedtuple
from odoo.fields import Date
Criteria = namedtuple(
'Criteria',
[
'when', # Contract line relatively to today (BEFORE, IN, AFTER)
'has_date_end', # Is date_end set on contract line (bool)
'has_last_date_invoiced', # Is last_date_invoiced set on contract line
'is_auto_renew', # Is is_auto_renew set on contract line (bool)
'has_successor', # Is contract line has_successor (bool)
'predecessor_has_successor',
# Is contract line predecessor has successor (bool)
# In almost of the cases
# contract_line.predecessor.successor == contract_line
# But at cancel action,
# contract_line.predecessor.successor == False
# This is to permit plan_successor on predecessor
# If contract_line.predecessor.successor != False
# and contract_line is canceled, we don't allow uncancel
# else we re-link contract_line and its predecessor
'canceled', # Is contract line canceled (bool)
],
)
Allowed = namedtuple(
'Allowed',
['plan_successor', 'stop_plan_successor', 'stop', 'cancel', 'uncancel'],
)
def _expand_none(criteria):
variations = []
for attribute, value in criteria._asdict().items():
if value is None:
if attribute == 'when':
variations.append(['BEFORE', 'IN', 'AFTER'])
else:
variations.append([True, False])
else:
variations.append([value])
return itertools.product(*variations)
def _add(matrix, criteria, allowed):
""" Expand None values to True/False combination """
for c in _expand_none(criteria):
matrix[c] = allowed
CRITERIA_ALLOWED_DICT = {
Criteria(
when='BEFORE',
has_date_end=True,
has_last_date_invoiced=False,
is_auto_renew=True,
has_successor=False,
predecessor_has_successor=None,
canceled=False,
): Allowed(
plan_successor=False,
stop_plan_successor=True,
stop=True,
cancel=True,
uncancel=False,
),
Criteria(
when='BEFORE',
has_date_end=True,
has_last_date_invoiced=False,
is_auto_renew=False,
has_successor=True,
predecessor_has_successor=None,
canceled=False,
): Allowed(
plan_successor=False,
stop_plan_successor=False,
stop=True,
cancel=True,
uncancel=False,
),
Criteria(
when='BEFORE',
has_date_end=True,
has_last_date_invoiced=False,
is_auto_renew=False,
has_successor=False,
predecessor_has_successor=None,
canceled=False,
): Allowed(
plan_successor=True,
stop_plan_successor=True,
stop=True,
cancel=True,
uncancel=False,
),
Criteria(
when='BEFORE',
has_date_end=False,
has_last_date_invoiced=False,
is_auto_renew=False,
has_successor=False,
predecessor_has_successor=None,
canceled=False,
): Allowed(
plan_successor=False,
stop_plan_successor=True,
stop=True,
cancel=True,
uncancel=False,
),
Criteria(
when='IN',
has_date_end=True,
has_last_date_invoiced=False,
is_auto_renew=True,
has_successor=False,
predecessor_has_successor=None,
canceled=False,
): Allowed(
plan_successor=False,
stop_plan_successor=True,
stop=True,
cancel=True,
uncancel=False,
),
Criteria(
when='IN',
has_date_end=True,
has_last_date_invoiced=False,
is_auto_renew=False,
has_successor=True,
predecessor_has_successor=None,
canceled=False,
): Allowed(
plan_successor=False,
stop_plan_successor=False,
stop=True,
cancel=True,
uncancel=False,
),
Criteria(
when='IN',
has_date_end=True,
has_last_date_invoiced=False,
is_auto_renew=False,
has_successor=False,
predecessor_has_successor=None,
canceled=False,
): Allowed(
plan_successor=True,
stop_plan_successor=True,
stop=True,
cancel=True,
uncancel=False,
),
Criteria(
when='IN',
has_date_end=False,
has_last_date_invoiced=False,
is_auto_renew=False,
has_successor=False,
predecessor_has_successor=None,
canceled=False,
): Allowed(
plan_successor=False,
stop_plan_successor=True,
stop=True,
cancel=True,
uncancel=False,
),
Criteria(
when='BEFORE',
has_date_end=True,
has_last_date_invoiced=True,
is_auto_renew=True,
has_successor=False,
predecessor_has_successor=None,
canceled=False,
): Allowed(
plan_successor=False,
stop_plan_successor=True,
stop=True,
cancel=False,
uncancel=False,
),
Criteria(
when='BEFORE',
has_date_end=True,
has_last_date_invoiced=True,
is_auto_renew=False,
has_successor=True,
predecessor_has_successor=None,
canceled=False,
): Allowed(
plan_successor=False,
stop_plan_successor=False,
stop=True,
cancel=False,
uncancel=False,
),
Criteria(
when='BEFORE',
has_date_end=True,
has_last_date_invoiced=True,
is_auto_renew=False,
has_successor=False,
predecessor_has_successor=None,
canceled=False,
): Allowed(
plan_successor=True,
stop_plan_successor=True,
stop=True,
cancel=False,
uncancel=False,
),
Criteria(
when='BEFORE',
has_date_end=False,
has_last_date_invoiced=True,
is_auto_renew=False,
has_successor=False,
predecessor_has_successor=None,
canceled=False,
): Allowed(
plan_successor=False,
stop_plan_successor=True,
stop=True,
cancel=False,
uncancel=False,
),
Criteria(
when='IN',
has_date_end=True,
has_last_date_invoiced=True,
is_auto_renew=True,
has_successor=False,
predecessor_has_successor=None,
canceled=False,
): Allowed(
plan_successor=False,
stop_plan_successor=True,
stop=True,
cancel=False,
uncancel=False,
),
Criteria(
when='IN',
has_date_end=True,
has_last_date_invoiced=True,
is_auto_renew=False,
has_successor=True,
predecessor_has_successor=None,
canceled=False,
): Allowed(
plan_successor=False,
stop_plan_successor=False,
stop=True,
cancel=False,
uncancel=False,
),
Criteria(
when='IN',
has_date_end=True,
has_last_date_invoiced=True,
is_auto_renew=False,
has_successor=False,
predecessor_has_successor=None,
canceled=False,
): Allowed(
plan_successor=True,
stop_plan_successor=True,
stop=True,
cancel=False,
uncancel=False,
),
Criteria(
when='IN',
has_date_end=False,
has_last_date_invoiced=True,
is_auto_renew=False,
has_successor=False,
predecessor_has_successor=None,
canceled=False,
): Allowed(
plan_successor=False,
stop_plan_successor=True,
stop=True,
cancel=False,
uncancel=False,
),
Criteria(
when='AFTER',
has_date_end=True,
has_last_date_invoiced=None,
is_auto_renew=True,
has_successor=False,
predecessor_has_successor=None,
canceled=False,
): Allowed(
plan_successor=False,
stop_plan_successor=False,
stop=False,
cancel=False,
uncancel=False,
),
Criteria(
when='AFTER',
has_date_end=True,
has_last_date_invoiced=None,
is_auto_renew=False,
has_successor=True,
predecessor_has_successor=None,
canceled=False,
): Allowed(
plan_successor=False,
stop_plan_successor=False,
stop=False,
cancel=False,
uncancel=False,
),
Criteria(
when='AFTER',
has_date_end=True,
has_last_date_invoiced=None,
is_auto_renew=False,
has_successor=False,
predecessor_has_successor=None,
canceled=False,
): Allowed(
plan_successor=True,
stop_plan_successor=False,
stop=False,
cancel=False,
uncancel=False,
),
Criteria(
when=None,
has_date_end=None,
has_last_date_invoiced=None,
is_auto_renew=None,
has_successor=None,
predecessor_has_successor=False,
canceled=True,
): Allowed(
plan_successor=False,
stop_plan_successor=False,
stop=False,
cancel=False,
uncancel=True,
),
Criteria(
when=None,
has_date_end=None,
has_last_date_invoiced=None,
is_auto_renew=None,
has_successor=None,
predecessor_has_successor=True,
canceled=True,
): Allowed(
plan_successor=False,
stop_plan_successor=False,
stop=False,
cancel=False,
uncancel=False,
),
}
criteria_allowed_dict = {}
for c in CRITERIA_ALLOWED_DICT:
_add(criteria_allowed_dict, c, CRITERIA_ALLOWED_DICT[c])
def compute_when(date_start, date_end):
today = Date.today()
if today < date_start:
return 'BEFORE'
if date_end and today > date_end:
return 'AFTER'
return 'IN'
def compute_criteria(
date_start,
date_end,
has_last_date_invoiced,
is_auto_renew,
successor_contract_line_id,
predecessor_contract_line_id,
is_canceled,
):
return Criteria(
when=compute_when(date_start, date_end),
has_date_end=bool(date_end),
has_last_date_invoiced=bool(has_last_date_invoiced),
is_auto_renew=is_auto_renew,
has_successor=bool(successor_contract_line_id),
predecessor_has_successor=bool(
predecessor_contract_line_id.successor_contract_line_id
),
canceled=is_canceled,
)
def get_allowed(
date_start,
date_end,
has_last_date_invoiced,
is_auto_renew,
successor_contract_line_id,
predecessor_contract_line_id,
is_canceled,
):
criteria = compute_criteria(
date_start,
date_end,
has_last_date_invoiced,
is_auto_renew,
successor_contract_line_id,
predecessor_contract_line_id,
is_canceled,
)
if criteria in criteria_allowed_dict:
return criteria_allowed_dict[criteria]
return False