mirror of
https://github.com/OCA/contract.git
synced 2025-02-13 17:57:24 +02:00
[IMP+REF] contract: Allow to set recurrency at header level
Big refactoring for allowing to define recurrency at header level for simplifying the use of the module for most of the cases where you don't need different recurrency at line level.
This commit is contained in:
committed by
Francisco Ivan Anton Prieto
parent
fc4eb98c74
commit
3374384101
@@ -16,7 +16,10 @@ from .contract_line_constraints import get_allowed
|
||||
class ContractLine(models.Model):
|
||||
_name = "contract.line"
|
||||
_description = "Contract Line"
|
||||
_inherit = "contract.abstract.contract.line"
|
||||
_inherit = [
|
||||
"contract.abstract.contract.line",
|
||||
"contract.recurrency.mixin",
|
||||
]
|
||||
_order = "sequence,id"
|
||||
|
||||
sequence = fields.Integer(string="Sequence",)
|
||||
@@ -34,22 +37,8 @@ class ContractLine(models.Model):
|
||||
analytic_tag_ids = fields.Many2many(
|
||||
comodel_name="account.analytic.tag", string="Analytic Tags",
|
||||
)
|
||||
date_start = fields.Date(
|
||||
string="Date Start",
|
||||
required=True,
|
||||
default=lambda self: fields.Date.context_today(self),
|
||||
)
|
||||
date_end = fields.Date(string="Date End", index=True)
|
||||
recurring_next_date = fields.Date(string="Date of Next Invoice")
|
||||
last_date_invoiced = fields.Date(
|
||||
string="Last Date Invoiced", readonly=True, copy=False
|
||||
)
|
||||
next_period_date_start = fields.Date(
|
||||
string="Next Period Start", compute="_compute_next_period_date_start",
|
||||
)
|
||||
next_period_date_end = fields.Date(
|
||||
string="Next Period End", compute="_compute_next_period_date_end",
|
||||
)
|
||||
date_start = fields.Date(required=True)
|
||||
date_end = fields.Date(compute="_compute_date_end", store=True, readonly=False)
|
||||
termination_notice_date = fields.Date(
|
||||
string="Termination notice date",
|
||||
compute="_compute_termination_notice_date",
|
||||
@@ -119,6 +108,29 @@ class ContractLine(models.Model):
|
||||
default=True,
|
||||
)
|
||||
|
||||
@api.depends(
|
||||
"last_date_invoiced", "date_start", "date_end", "contract_id.last_date_invoiced"
|
||||
)
|
||||
def _compute_next_period_date_start(self):
|
||||
"""Rectify next period date start if another line in the contract has been
|
||||
already invoiced previously.
|
||||
"""
|
||||
for rec in self:
|
||||
lines = rec.contract_id.contract_line_ids
|
||||
if not rec.last_date_invoiced and any(lines.mapped("last_date_invoiced")):
|
||||
next_period_date_start = max(
|
||||
lines.filtered("last_date_invoiced").mapped("last_date_invoiced")
|
||||
) + relativedelta(days=1)
|
||||
if rec.date_end and next_period_date_start > rec.date_end:
|
||||
next_period_date_start = False
|
||||
rec.next_period_date_start = next_period_date_start
|
||||
else:
|
||||
super(ContractLine, rec)._compute_next_period_date_start()
|
||||
|
||||
@api.depends("contract_id.date_end", "contract_id.line_recurrence")
|
||||
def _compute_date_end(self):
|
||||
self._set_recurrence_field("date_end")
|
||||
|
||||
@api.depends(
|
||||
"date_end", "termination_notice_rule_type", "termination_notice_interval",
|
||||
)
|
||||
@@ -389,115 +401,6 @@ class ContractLine(models.Model):
|
||||
max_date_end=False,
|
||||
)
|
||||
|
||||
@api.model
|
||||
def get_next_invoice_date(
|
||||
self,
|
||||
next_period_date_start,
|
||||
recurring_invoicing_type,
|
||||
recurring_invoicing_offset,
|
||||
recurring_rule_type,
|
||||
recurring_interval,
|
||||
max_date_end,
|
||||
):
|
||||
next_period_date_end = self.get_next_period_date_end(
|
||||
next_period_date_start,
|
||||
recurring_rule_type,
|
||||
recurring_interval,
|
||||
max_date_end=max_date_end,
|
||||
)
|
||||
if not next_period_date_end:
|
||||
return False
|
||||
if recurring_invoicing_type == "pre-paid":
|
||||
recurring_next_date = next_period_date_start + relativedelta(
|
||||
days=recurring_invoicing_offset
|
||||
)
|
||||
else: # post-paid
|
||||
recurring_next_date = next_period_date_end + relativedelta(
|
||||
days=recurring_invoicing_offset
|
||||
)
|
||||
return recurring_next_date
|
||||
|
||||
@api.model
|
||||
def get_next_period_date_end(
|
||||
self,
|
||||
next_period_date_start,
|
||||
recurring_rule_type,
|
||||
recurring_interval,
|
||||
max_date_end,
|
||||
next_invoice_date=False,
|
||||
recurring_invoicing_type=False,
|
||||
recurring_invoicing_offset=False,
|
||||
):
|
||||
"""Compute the end date for the next period.
|
||||
|
||||
The next period normally depends on recurrence options only.
|
||||
It is however possible to provide it a next invoice date, in
|
||||
which case this method can adjust the next period based on that
|
||||
too. In that scenario it required the invoicing type and offset
|
||||
arguments.
|
||||
"""
|
||||
if not next_period_date_start:
|
||||
return False
|
||||
if max_date_end and next_period_date_start > max_date_end:
|
||||
# start is past max date end: there is no next period
|
||||
return False
|
||||
if not next_invoice_date:
|
||||
# regular algorithm
|
||||
next_period_date_end = (
|
||||
next_period_date_start
|
||||
+ self.get_relative_delta(recurring_rule_type, recurring_interval)
|
||||
- relativedelta(days=1)
|
||||
)
|
||||
else:
|
||||
# special algorithm when the next invoice date is forced
|
||||
if recurring_invoicing_type == "pre-paid":
|
||||
next_period_date_end = (
|
||||
next_invoice_date
|
||||
- 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 - 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
|
||||
return next_period_date_end
|
||||
|
||||
@api.depends("last_date_invoiced", "date_start", "date_end")
|
||||
def _compute_next_period_date_start(self):
|
||||
for rec in self:
|
||||
if rec.last_date_invoiced:
|
||||
next_period_date_start = rec.last_date_invoiced + relativedelta(days=1)
|
||||
else:
|
||||
next_period_date_start = rec.date_start
|
||||
if rec.date_end and next_period_date_start > rec.date_end:
|
||||
next_period_date_start = False
|
||||
rec.next_period_date_start = next_period_date_start
|
||||
|
||||
@api.depends(
|
||||
"next_period_date_start",
|
||||
"recurring_invoicing_type",
|
||||
"recurring_invoicing_offset",
|
||||
"recurring_rule_type",
|
||||
"recurring_interval",
|
||||
"date_end",
|
||||
"recurring_next_date",
|
||||
)
|
||||
def _compute_next_period_date_end(self):
|
||||
for rec in self:
|
||||
rec.next_period_date_end = self.get_next_period_date_end(
|
||||
rec.next_period_date_start,
|
||||
rec.recurring_rule_type,
|
||||
rec.recurring_interval,
|
||||
max_date_end=rec.date_end,
|
||||
next_invoice_date=rec.recurring_next_date,
|
||||
recurring_invoicing_type=rec.recurring_invoicing_type,
|
||||
recurring_invoicing_offset=rec.recurring_invoicing_offset,
|
||||
)
|
||||
|
||||
@api.model
|
||||
def _get_first_date_end(
|
||||
self, date_start, auto_renew_rule_type, auto_renew_interval
|
||||
@@ -520,24 +423,6 @@ class ContractLine(models.Model):
|
||||
rec.date_start, rec.auto_renew_rule_type, rec.auto_renew_interval,
|
||||
)
|
||||
|
||||
@api.onchange(
|
||||
"date_start",
|
||||
"date_end",
|
||||
"recurring_invoicing_type",
|
||||
"recurring_rule_type",
|
||||
"recurring_interval",
|
||||
)
|
||||
def _onchange_date_start(self):
|
||||
for rec in self.filtered("date_start"):
|
||||
rec.recurring_next_date = self.get_next_invoice_date(
|
||||
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,
|
||||
)
|
||||
|
||||
@api.constrains("is_canceled", "is_auto_renew")
|
||||
def _check_auto_renew_canceled_lines(self):
|
||||
for rec in self:
|
||||
@@ -548,7 +433,9 @@ class ContractLine(models.Model):
|
||||
|
||||
@api.constrains("recurring_next_date", "date_start")
|
||||
def _check_recurring_next_date_start_date(self):
|
||||
for line in self.filtered("recurring_next_date"):
|
||||
for line in self:
|
||||
if line.display_type == "line_section" or not line.recurring_next_date:
|
||||
continue
|
||||
if line.date_start and line.recurring_next_date:
|
||||
if line.date_start > line.recurring_next_date:
|
||||
raise ValidationError(
|
||||
@@ -564,14 +451,6 @@ class ContractLine(models.Model):
|
||||
)
|
||||
def _check_last_date_invoiced(self):
|
||||
for rec in self.filtered("last_date_invoiced"):
|
||||
if rec.date_start and rec.date_start > rec.last_date_invoiced:
|
||||
raise ValidationError(
|
||||
_(
|
||||
"You can't have the start date after the date of last "
|
||||
"invoice for the contract line '%s'"
|
||||
)
|
||||
% rec.name
|
||||
)
|
||||
if rec.date_end and rec.date_end < rec.last_date_invoiced:
|
||||
raise ValidationError(
|
||||
_(
|
||||
@@ -580,6 +459,16 @@ class ContractLine(models.Model):
|
||||
)
|
||||
% rec.name
|
||||
)
|
||||
if not rec.contract_id.line_recurrence:
|
||||
continue
|
||||
if rec.date_start and rec.date_start > rec.last_date_invoiced:
|
||||
raise ValidationError(
|
||||
_(
|
||||
"You can't have the start date after the date of last "
|
||||
"invoice for the contract line '%s'"
|
||||
)
|
||||
% rec.name
|
||||
)
|
||||
if (
|
||||
rec.recurring_next_date
|
||||
and rec.recurring_next_date <= rec.last_date_invoiced
|
||||
@@ -719,51 +608,6 @@ class ContractLine(models.Model):
|
||||
}
|
||||
)
|
||||
|
||||
def _init_last_date_invoiced(self):
|
||||
"""Used to init last_date_invoiced for migration purpose"""
|
||||
for rec in self:
|
||||
last_date_invoiced = rec.recurring_next_date - relativedelta(days=1)
|
||||
if rec.recurring_rule_type == "monthlylastday":
|
||||
last_date_invoiced = (
|
||||
rec.recurring_next_date
|
||||
- self.get_relative_delta(
|
||||
rec.recurring_rule_type, rec.recurring_interval - 1
|
||||
)
|
||||
- relativedelta(days=1)
|
||||
)
|
||||
elif rec.recurring_invoicing_type == "post-paid":
|
||||
last_date_invoiced = (
|
||||
rec.recurring_next_date
|
||||
- self.get_relative_delta(
|
||||
rec.recurring_rule_type, rec.recurring_interval
|
||||
)
|
||||
- relativedelta(days=1)
|
||||
)
|
||||
if last_date_invoiced > rec.date_start:
|
||||
rec.last_date_invoiced = last_date_invoiced
|
||||
|
||||
@api.model
|
||||
def get_relative_delta(self, recurring_rule_type, interval):
|
||||
"""Return a relativedelta for one period.
|
||||
|
||||
When added to the first day of the period,
|
||||
it gives the first day of the next period.
|
||||
"""
|
||||
if recurring_rule_type == "daily":
|
||||
return relativedelta(days=interval)
|
||||
elif recurring_rule_type == "weekly":
|
||||
return relativedelta(weeks=interval)
|
||||
elif recurring_rule_type == "monthly":
|
||||
return relativedelta(months=interval)
|
||||
elif recurring_rule_type == "monthlylastday":
|
||||
return relativedelta(months=interval, day=1)
|
||||
elif recurring_rule_type == "quarterly":
|
||||
return relativedelta(months=3 * interval)
|
||||
elif recurring_rule_type == "semesterly":
|
||||
return relativedelta(months=6 * interval)
|
||||
else:
|
||||
return relativedelta(years=interval)
|
||||
|
||||
def _delay(self, delay_delta):
|
||||
"""
|
||||
Delay a contract line
|
||||
@@ -1145,7 +989,6 @@ class ContractLine(models.Model):
|
||||
new_line = self.plan_successor(
|
||||
date_start, date_end, is_auto_renew, post_message=False
|
||||
)
|
||||
new_line._onchange_date_start()
|
||||
return new_line
|
||||
|
||||
def _renew_extend_line(self, date_end):
|
||||
|
||||
Reference in New Issue
Block a user