mirror of
https://github.com/OCA/contract.git
synced 2025-02-13 17:57:24 +02:00
[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
This commit is contained in:
@@ -9,7 +9,7 @@
|
|||||||
|
|
||||||
{
|
{
|
||||||
'name': 'Recurring - Contracts Management',
|
'name': 'Recurring - Contracts Management',
|
||||||
'version': '12.0.2.0.0',
|
'version': '12.0.2.0.1',
|
||||||
'category': 'Contract Management',
|
'category': 'Contract Management',
|
||||||
'license': 'AGPL-3',
|
'license': 'AGPL-3',
|
||||||
'author': "OpenERP SA, "
|
'author': "OpenERP SA, "
|
||||||
|
|||||||
@@ -1 +0,0 @@
|
|||||||
from . import contract_line_constraints
|
|
||||||
@@ -1,281 +0,0 @@
|
|||||||
# Copyright 2018 ACSONE SA/NV.
|
|
||||||
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).
|
|
||||||
|
|
||||||
from collections import namedtuple
|
|
||||||
from odoo.fields import Date
|
|
||||||
|
|
||||||
CRITERIA = namedtuple(
|
|
||||||
'CRITERIA',
|
|
||||||
[
|
|
||||||
'WHEN',
|
|
||||||
'HAS_DATE_END',
|
|
||||||
'IS_AUTO_RENEW',
|
|
||||||
'HAS_SUCCESSOR',
|
|
||||||
'PREDECESSOR_HAS_SUCCESSOR',
|
|
||||||
'CANCELED',
|
|
||||||
],
|
|
||||||
)
|
|
||||||
ALLOWED = namedtuple(
|
|
||||||
'ALLOWED',
|
|
||||||
['PLAN_SUCCESSOR', 'STOP_PLAN_SUCCESSOR', 'STOP', 'CANCEL', 'UN_CANCEL'],
|
|
||||||
)
|
|
||||||
|
|
||||||
CRITERIA_ALLOWED_DICT = {
|
|
||||||
CRITERIA(
|
|
||||||
WHEN='BEFORE',
|
|
||||||
HAS_DATE_END=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=True,
|
|
||||||
UN_CANCEL=False,
|
|
||||||
),
|
|
||||||
CRITERIA(
|
|
||||||
WHEN='BEFORE',
|
|
||||||
HAS_DATE_END=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=True,
|
|
||||||
UN_CANCEL=False,
|
|
||||||
),
|
|
||||||
CRITERIA(
|
|
||||||
WHEN='BEFORE',
|
|
||||||
HAS_DATE_END=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=True,
|
|
||||||
UN_CANCEL=False,
|
|
||||||
),
|
|
||||||
CRITERIA(
|
|
||||||
WHEN='BEFORE',
|
|
||||||
HAS_DATE_END=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,
|
|
||||||
UN_CANCEL=False,
|
|
||||||
),
|
|
||||||
CRITERIA(
|
|
||||||
WHEN='IN',
|
|
||||||
HAS_DATE_END=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=True,
|
|
||||||
UN_CANCEL=False,
|
|
||||||
),
|
|
||||||
CRITERIA(
|
|
||||||
WHEN='IN',
|
|
||||||
HAS_DATE_END=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=True,
|
|
||||||
UN_CANCEL=False,
|
|
||||||
),
|
|
||||||
CRITERIA(
|
|
||||||
WHEN='IN',
|
|
||||||
HAS_DATE_END=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=True,
|
|
||||||
UN_CANCEL=False,
|
|
||||||
),
|
|
||||||
CRITERIA(
|
|
||||||
WHEN='IN',
|
|
||||||
HAS_DATE_END=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,
|
|
||||||
UN_CANCEL=False,
|
|
||||||
),
|
|
||||||
CRITERIA(
|
|
||||||
WHEN='AFTER',
|
|
||||||
HAS_DATE_END=True,
|
|
||||||
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,
|
|
||||||
UN_CANCEL=False,
|
|
||||||
),
|
|
||||||
CRITERIA(
|
|
||||||
WHEN='AFTER',
|
|
||||||
HAS_DATE_END=True,
|
|
||||||
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,
|
|
||||||
UN_CANCEL=False,
|
|
||||||
),
|
|
||||||
CRITERIA(
|
|
||||||
WHEN='AFTER',
|
|
||||||
HAS_DATE_END=True,
|
|
||||||
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,
|
|
||||||
UN_CANCEL=False,
|
|
||||||
),
|
|
||||||
CRITERIA(
|
|
||||||
WHEN=None,
|
|
||||||
HAS_DATE_END=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,
|
|
||||||
UN_CANCEL=True,
|
|
||||||
),
|
|
||||||
CRITERIA(
|
|
||||||
WHEN=None,
|
|
||||||
HAS_DATE_END=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,
|
|
||||||
UN_CANCEL=False,
|
|
||||||
),
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
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,
|
|
||||||
is_auto_renew,
|
|
||||||
successor_contract_line_id,
|
|
||||||
predecessor_contract_line_id,
|
|
||||||
is_canceled,
|
|
||||||
):
|
|
||||||
if is_canceled:
|
|
||||||
if (
|
|
||||||
not predecessor_contract_line_id
|
|
||||||
or not predecessor_contract_line_id.successor_contract_line_id
|
|
||||||
):
|
|
||||||
return CRITERIA(
|
|
||||||
WHEN=None,
|
|
||||||
HAS_DATE_END=None,
|
|
||||||
IS_AUTO_RENEW=None,
|
|
||||||
HAS_SUCCESSOR=None,
|
|
||||||
PREDECESSOR_HAS_SUCCESSOR=False,
|
|
||||||
CANCELED=True,
|
|
||||||
)
|
|
||||||
else:
|
|
||||||
return CRITERIA(
|
|
||||||
WHEN=None,
|
|
||||||
HAS_DATE_END=None,
|
|
||||||
IS_AUTO_RENEW=None,
|
|
||||||
HAS_SUCCESSOR=None,
|
|
||||||
PREDECESSOR_HAS_SUCCESSOR=True,
|
|
||||||
CANCELED=True,
|
|
||||||
)
|
|
||||||
when = compute_when(date_start, date_end)
|
|
||||||
has_date_end = date_end if not date_end else True
|
|
||||||
is_auto_renew = is_auto_renew
|
|
||||||
has_successor = True if successor_contract_line_id else False
|
|
||||||
canceled = is_canceled
|
|
||||||
return CRITERIA(
|
|
||||||
WHEN=when,
|
|
||||||
HAS_DATE_END=has_date_end,
|
|
||||||
IS_AUTO_RENEW=is_auto_renew,
|
|
||||||
HAS_SUCCESSOR=has_successor,
|
|
||||||
PREDECESSOR_HAS_SUCCESSOR=None,
|
|
||||||
CANCELED=canceled,
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
def get_allowed(
|
|
||||||
date_start,
|
|
||||||
date_end,
|
|
||||||
is_auto_renew,
|
|
||||||
successor_contract_line_id,
|
|
||||||
predecessor_contract_line_id,
|
|
||||||
is_canceled,
|
|
||||||
):
|
|
||||||
criteria = compute_criteria(
|
|
||||||
date_start,
|
|
||||||
date_end,
|
|
||||||
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
|
|
||||||
@@ -1,10 +1,16 @@
|
|||||||
# -*- coding: utf-8 -*-
|
# -*- coding: utf-8 -*-
|
||||||
# Copyright 2018 ACSONE SA/NV
|
# Copyright 2018 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).
|
||||||
|
import logging
|
||||||
|
from odoo import SUPERUSER_ID, api
|
||||||
|
|
||||||
|
|
||||||
|
_logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
def migrate(cr, version):
|
def migrate(cr, version):
|
||||||
"""Copy recurrence info from contract to contract lines."""
|
"""Copy recurrence info from contract to contract lines and compute
|
||||||
|
last_date_invoiced"""
|
||||||
|
|
||||||
cr.execute(
|
cr.execute(
|
||||||
"""UPDATE account_analytic_invoice_line AS contract_line
|
"""UPDATE account_analytic_invoice_line AS contract_line
|
||||||
@@ -17,3 +23,10 @@ def migrate(cr, version):
|
|||||||
FROM account_analytic_account AS contract
|
FROM account_analytic_account AS contract
|
||||||
WHERE contract.id=contract_line.contract_id"""
|
WHERE contract.id=contract_line.contract_id"""
|
||||||
)
|
)
|
||||||
|
|
||||||
|
_logger.info("order all contract line")
|
||||||
|
env = api.Environment(cr, SUPERUSER_ID, {})
|
||||||
|
contract_lines = env["account.analytic.invoice.line"].search(
|
||||||
|
[("recurring_next_date", "!=", False)]
|
||||||
|
)
|
||||||
|
contract_lines._init_last_date_invoiced()
|
||||||
|
|||||||
24
contract/migrations/12.0.2.0.0/pre-migration.py
Normal file
24
contract/migrations/12.0.2.0.0/pre-migration.py
Normal file
@@ -0,0 +1,24 @@
|
|||||||
|
# Copyright 2018 ACSONE SA/NV
|
||||||
|
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).
|
||||||
|
|
||||||
|
import logging
|
||||||
|
|
||||||
|
from odoo import SUPERUSER_ID, api
|
||||||
|
|
||||||
|
_logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
|
def migrate(cr, version):
|
||||||
|
"""
|
||||||
|
set recurring_next_date to false for finished contract
|
||||||
|
"""
|
||||||
|
_logger.info("order all contract line")
|
||||||
|
with api.Environment(cr, SUPERUSER_ID, {}) as env:
|
||||||
|
contracts = env["account.analytic.account"].search([])
|
||||||
|
finished_contract = contracts.filtered(
|
||||||
|
lambda c: not c.create_invoice_visibility
|
||||||
|
)
|
||||||
|
cr.execute(
|
||||||
|
"UPDATE account_analytic_account set recurring_next_date=null where id in (%)"
|
||||||
|
% ','.join(finished_contract.ids)
|
||||||
|
)
|
||||||
@@ -11,7 +11,7 @@ from odoo import api, models, fields
|
|||||||
|
|
||||||
class AbstractAccountAnalyticContract(models.AbstractModel):
|
class AbstractAccountAnalyticContract(models.AbstractModel):
|
||||||
_name = 'account.abstract.analytic.contract'
|
_name = 'account.abstract.analytic.contract'
|
||||||
_description = 'Abstract Account Analytic Contract'
|
_description = 'Abstract Recurring Contract'
|
||||||
|
|
||||||
# These fields will not be synced to the contract
|
# These fields will not be synced to the contract
|
||||||
NO_SYNC = ['name', 'partner_id']
|
NO_SYNC = ['name', 'partner_id']
|
||||||
@@ -40,7 +40,9 @@ class AbstractAccountAnalyticContract(models.AbstractModel):
|
|||||||
'res.company',
|
'res.company',
|
||||||
string='Company',
|
string='Company',
|
||||||
required=True,
|
required=True,
|
||||||
default=lambda self: self.env.user.company_id,
|
default=lambda self: self.env['res.company']._company_default_get(
|
||||||
|
self._name
|
||||||
|
),
|
||||||
)
|
)
|
||||||
|
|
||||||
@api.onchange('contract_type')
|
@api.onchange('contract_type')
|
||||||
|
|||||||
@@ -14,7 +14,7 @@ from odoo.tools.translate import _
|
|||||||
|
|
||||||
class AccountAbstractAnalyticContractLine(models.AbstractModel):
|
class AccountAbstractAnalyticContractLine(models.AbstractModel):
|
||||||
_name = 'account.abstract.analytic.contract.line'
|
_name = 'account.abstract.analytic.contract.line'
|
||||||
_description = 'Account Abstract Analytic Contract Line'
|
_description = 'Abstract Recurring Contract Line'
|
||||||
|
|
||||||
product_id = fields.Many2one(
|
product_id = fields.Many2one(
|
||||||
'product.product', string='Product', required=True
|
'product.product', string='Product', required=True
|
||||||
@@ -130,7 +130,9 @@ class AccountAbstractAnalyticContractLine(models.AbstractModel):
|
|||||||
),
|
),
|
||||||
pricelist=line.contract_id.pricelist_id.id,
|
pricelist=line.contract_id.pricelist_id.id,
|
||||||
partner=line.contract_id.partner_id.id,
|
partner=line.contract_id.partner_id.id,
|
||||||
date=line.env.context.get('old_date', fields.Date.today()),
|
date=line.env.context.get(
|
||||||
|
'old_date', fields.Date.context_today(line)
|
||||||
|
),
|
||||||
)
|
)
|
||||||
line.price_unit = product.price
|
line.price_unit = product.price
|
||||||
else:
|
else:
|
||||||
@@ -182,7 +184,7 @@ class AccountAbstractAnalyticContractLine(models.AbstractModel):
|
|||||||
):
|
):
|
||||||
vals['uom_id'] = self.product_id.uom_id
|
vals['uom_id'] = self.product_id.uom_id
|
||||||
|
|
||||||
date = self.recurring_next_date or fields.Date.today()
|
date = self.recurring_next_date or fields.Date.context_today(self)
|
||||||
partner = self.contract_id.partner_id or self.env.user.partner_id
|
partner = self.contract_id.partner_id or self.env.user.partner_id
|
||||||
|
|
||||||
product = self.product_id.with_context(
|
product = self.product_id.with_context(
|
||||||
|
|||||||
@@ -62,7 +62,7 @@ class AccountAnalyticAccount(models.Model):
|
|||||||
recurring_next_date = contract.recurring_invoice_line_ids.filtered(
|
recurring_next_date = contract.recurring_invoice_line_ids.filtered(
|
||||||
'create_invoice_visibility'
|
'create_invoice_visibility'
|
||||||
).mapped('recurring_next_date')
|
).mapped('recurring_next_date')
|
||||||
if recurring_next_date:
|
if recurring_next_date and all(recurring_next_date):
|
||||||
contract.recurring_next_date = min(recurring_next_date)
|
contract.recurring_next_date = min(recurring_next_date)
|
||||||
|
|
||||||
@api.depends('recurring_invoice_line_ids.create_invoice_visibility')
|
@api.depends('recurring_invoice_line_ids.create_invoice_visibility')
|
||||||
@@ -123,8 +123,10 @@ class AccountAnalyticAccount(models.Model):
|
|||||||
vals = contract_line._convert_to_write(contract_line.read()[0])
|
vals = contract_line._convert_to_write(contract_line.read()[0])
|
||||||
# Remove template link field
|
# Remove template link field
|
||||||
vals.pop('contract_template_id', False)
|
vals.pop('contract_template_id', False)
|
||||||
vals['date_start'] = fields.Date.today()
|
vals['date_start'] = fields.Date.context_today(contract_line)
|
||||||
vals['recurring_next_date'] = fields.Date.today()
|
vals['recurring_next_date'] = fields.Date.context_today(
|
||||||
|
contract_line
|
||||||
|
)
|
||||||
self.recurring_invoice_line_ids._onchange_date_start()
|
self.recurring_invoice_line_ids._onchange_date_start()
|
||||||
new_lines.append((0, 0, vals))
|
new_lines.append((0, 0, vals))
|
||||||
return new_lines
|
return new_lines
|
||||||
|
|||||||
@@ -7,7 +7,7 @@ from dateutil.relativedelta import relativedelta
|
|||||||
from odoo import api, fields, models, _
|
from odoo import api, fields, models, _
|
||||||
from odoo.exceptions import ValidationError
|
from odoo.exceptions import ValidationError
|
||||||
|
|
||||||
from ..data.contract_line_constraints import get_allowed
|
from .contract_line_constraints import get_allowed
|
||||||
|
|
||||||
|
|
||||||
class AccountAnalyticInvoiceLine(models.Model):
|
class AccountAnalyticInvoiceLine(models.Model):
|
||||||
@@ -16,14 +16,21 @@ class AccountAnalyticInvoiceLine(models.Model):
|
|||||||
|
|
||||||
contract_id = fields.Many2one(
|
contract_id = fields.Many2one(
|
||||||
comodel_name='account.analytic.account',
|
comodel_name='account.analytic.account',
|
||||||
string='Analytic Account',
|
string='Contract',
|
||||||
required=True,
|
required=True,
|
||||||
|
index=True,
|
||||||
ondelete='cascade',
|
ondelete='cascade',
|
||||||
oldname='analytic_account_id',
|
oldname='analytic_account_id',
|
||||||
)
|
)
|
||||||
date_start = fields.Date(string='Date Start', default=fields.Date.today())
|
date_start = fields.Date(
|
||||||
|
string='Date Start',
|
||||||
|
default=lambda self: fields.Date.context_today(self),
|
||||||
|
)
|
||||||
date_end = fields.Date(string='Date End', index=True)
|
date_end = fields.Date(string='Date End', index=True)
|
||||||
recurring_next_date = fields.Date(string='Date of Next Invoice')
|
recurring_next_date = fields.Date(string='Date of Next Invoice')
|
||||||
|
last_date_invoiced = fields.Date(
|
||||||
|
string='Last Date Invoiced', readonly=True
|
||||||
|
)
|
||||||
create_invoice_visibility = fields.Boolean(
|
create_invoice_visibility = fields.Boolean(
|
||||||
compute='_compute_create_invoice_visibility'
|
compute='_compute_create_invoice_visibility'
|
||||||
)
|
)
|
||||||
@@ -33,7 +40,8 @@ class AccountAnalyticInvoiceLine(models.Model):
|
|||||||
required=False,
|
required=False,
|
||||||
readonly=True,
|
readonly=True,
|
||||||
copy=False,
|
copy=False,
|
||||||
help="Contract Line created by this one.",
|
help="In case of restart after suspension, this field contain the new "
|
||||||
|
"contract line created.",
|
||||||
)
|
)
|
||||||
predecessor_contract_line_id = fields.Many2one(
|
predecessor_contract_line_id = fields.Many2one(
|
||||||
comodel_name='account.analytic.invoice.line',
|
comodel_name='account.analytic.invoice.line',
|
||||||
@@ -72,7 +80,7 @@ class AccountAnalyticInvoiceLine(models.Model):
|
|||||||
|
|
||||||
@api.multi
|
@api.multi
|
||||||
def _compute_state(self):
|
def _compute_state(self):
|
||||||
today = fields.Date.today()
|
today = fields.Date.context_today(self)
|
||||||
for rec in self:
|
for rec in self:
|
||||||
if rec.is_canceled:
|
if rec.is_canceled:
|
||||||
rec.state = 'canceled'
|
rec.state = 'canceled'
|
||||||
@@ -90,6 +98,7 @@ class AccountAnalyticInvoiceLine(models.Model):
|
|||||||
@api.depends(
|
@api.depends(
|
||||||
'date_start',
|
'date_start',
|
||||||
'date_end',
|
'date_end',
|
||||||
|
'last_date_invoiced',
|
||||||
'is_auto_renew',
|
'is_auto_renew',
|
||||||
'successor_contract_line_id',
|
'successor_contract_line_id',
|
||||||
'predecessor_contract_line_id',
|
'predecessor_contract_line_id',
|
||||||
@@ -101,19 +110,20 @@ class AccountAnalyticInvoiceLine(models.Model):
|
|||||||
allowed = get_allowed(
|
allowed = get_allowed(
|
||||||
rec.date_start,
|
rec.date_start,
|
||||||
rec.date_end,
|
rec.date_end,
|
||||||
|
rec.last_date_invoiced,
|
||||||
rec.is_auto_renew,
|
rec.is_auto_renew,
|
||||||
rec.successor_contract_line_id,
|
rec.successor_contract_line_id,
|
||||||
rec.predecessor_contract_line_id,
|
rec.predecessor_contract_line_id,
|
||||||
rec.is_canceled,
|
rec.is_canceled,
|
||||||
)
|
)
|
||||||
if allowed:
|
if allowed:
|
||||||
rec.is_plan_successor_allowed = allowed.PLAN_SUCCESSOR
|
rec.is_plan_successor_allowed = allowed.plan_successor
|
||||||
rec.is_stop_plan_successor_allowed = (
|
rec.is_stop_plan_successor_allowed = (
|
||||||
allowed.STOP_PLAN_SUCCESSOR
|
allowed.stop_plan_successor
|
||||||
)
|
)
|
||||||
rec.is_stop_allowed = allowed.STOP
|
rec.is_stop_allowed = allowed.stop
|
||||||
rec.is_cancel_allowed = allowed.CANCEL
|
rec.is_cancel_allowed = allowed.cancel
|
||||||
rec.is_un_cancel_allowed = allowed.UN_CANCEL
|
rec.is_un_cancel_allowed = allowed.uncancel
|
||||||
|
|
||||||
@api.constrains('is_auto_renew', 'successor_contract_line_id', 'date_end')
|
@api.constrains('is_auto_renew', 'successor_contract_line_id', 'date_end')
|
||||||
def _check_allowed(self):
|
def _check_allowed(self):
|
||||||
@@ -135,14 +145,14 @@ class AccountAnalyticInvoiceLine(models.Model):
|
|||||||
)
|
)
|
||||||
if not rec.date_end:
|
if not rec.date_end:
|
||||||
raise ValidationError(
|
raise ValidationError(
|
||||||
_("An auto-renew line should have a " "date end ")
|
_("An auto-renew line must have a end date")
|
||||||
)
|
)
|
||||||
else:
|
else:
|
||||||
if not rec.date_end and rec.successor_contract_line_id:
|
if not rec.date_end and rec.successor_contract_line_id:
|
||||||
raise ValidationError(
|
raise ValidationError(
|
||||||
_(
|
_(
|
||||||
"A contract line with a successor "
|
"A contract line with a successor "
|
||||||
"should have date end"
|
"must have a end date"
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -150,7 +160,7 @@ class AccountAnalyticInvoiceLine(models.Model):
|
|||||||
def _check_overlap_successor(self):
|
def _check_overlap_successor(self):
|
||||||
for rec in self:
|
for rec in self:
|
||||||
if rec.date_end and rec.successor_contract_line_id:
|
if rec.date_end and rec.successor_contract_line_id:
|
||||||
if rec.date_end > rec.successor_contract_line_id.date_start:
|
if rec.date_end >= rec.successor_contract_line_id.date_start:
|
||||||
raise ValidationError(
|
raise ValidationError(
|
||||||
_("Contract line and its successor overlapped")
|
_("Contract line and its successor overlapped")
|
||||||
)
|
)
|
||||||
@@ -159,7 +169,7 @@ class AccountAnalyticInvoiceLine(models.Model):
|
|||||||
def _check_overlap_predecessor(self):
|
def _check_overlap_predecessor(self):
|
||||||
for rec in self:
|
for rec in self:
|
||||||
if rec.predecessor_contract_line_id:
|
if rec.predecessor_contract_line_id:
|
||||||
if rec.date_start < rec.predecessor_contract_line_id.date_end:
|
if rec.date_start <= rec.predecessor_contract_line_id.date_end:
|
||||||
raise ValidationError(
|
raise ValidationError(
|
||||||
_("Contract line and its predecessor overlapped")
|
_("Contract line and its predecessor overlapped")
|
||||||
)
|
)
|
||||||
@@ -183,7 +193,10 @@ class AccountAnalyticInvoiceLine(models.Model):
|
|||||||
)
|
)
|
||||||
|
|
||||||
@api.onchange(
|
@api.onchange(
|
||||||
'is_auto_renew', 'auto_renew_rule_type', 'auto_renew_interval'
|
'date_start',
|
||||||
|
'is_auto_renew',
|
||||||
|
'auto_renew_rule_type',
|
||||||
|
'auto_renew_interval',
|
||||||
)
|
)
|
||||||
def _onchange_is_auto_renew(self):
|
def _onchange_is_auto_renew(self):
|
||||||
"""Date end should be auto-computed if a contract line is set to
|
"""Date end should be auto-computed if a contract line is set to
|
||||||
@@ -219,22 +232,45 @@ class AccountAnalyticInvoiceLine(models.Model):
|
|||||||
if line.date_start > line.recurring_next_date:
|
if line.date_start > line.recurring_next_date:
|
||||||
raise ValidationError(
|
raise ValidationError(
|
||||||
_(
|
_(
|
||||||
"You can't have a next invoicing date before the "
|
"You can't have a date of next invoice anterior "
|
||||||
"start of the contract '%s'"
|
"to the start of the contract line '%s'"
|
||||||
)
|
)
|
||||||
% line.contract_id.name
|
% line.name
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@api.constrains('date_start', 'date_end', 'last_date_invoiced')
|
||||||
|
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(
|
||||||
|
_(
|
||||||
|
"You can't have the end date before the date of last "
|
||||||
|
"invoice for the contract line '%s'"
|
||||||
|
)
|
||||||
|
% rec.name
|
||||||
|
)
|
||||||
|
|
||||||
@api.constrains('recurring_next_date')
|
@api.constrains('recurring_next_date')
|
||||||
def _check_recurring_next_date_recurring_invoices(self):
|
def _check_recurring_next_date_recurring_invoices(self):
|
||||||
for line in self.filtered('contract_id.recurring_invoices'):
|
for rec in self.filtered('contract_id.recurring_invoices'):
|
||||||
if not line.recurring_next_date:
|
if not rec.recurring_next_date and (
|
||||||
|
not rec.last_date_invoiced
|
||||||
|
or rec.last_date_invoiced < rec.date_end
|
||||||
|
):
|
||||||
raise ValidationError(
|
raise ValidationError(
|
||||||
_(
|
_(
|
||||||
"You must supply a next invoicing date for contract "
|
"You must supply a date of next invoice for contract "
|
||||||
"'%s'"
|
"line '%s'"
|
||||||
)
|
)
|
||||||
% line.contract_id.name
|
% rec.name
|
||||||
)
|
)
|
||||||
|
|
||||||
@api.constrains('date_start')
|
@api.constrains('date_start')
|
||||||
@@ -242,8 +278,8 @@ class AccountAnalyticInvoiceLine(models.Model):
|
|||||||
for line in self.filtered('contract_id.recurring_invoices'):
|
for line in self.filtered('contract_id.recurring_invoices'):
|
||||||
if not line.date_start:
|
if not line.date_start:
|
||||||
raise ValidationError(
|
raise ValidationError(
|
||||||
_("You must supply a start date for contract '%s'")
|
_("You must supply a start date for contract line '%s'")
|
||||||
% line.contract_id.name
|
% line.name
|
||||||
)
|
)
|
||||||
|
|
||||||
@api.constrains('date_start', 'date_end')
|
@api.constrains('date_start', 'date_end')
|
||||||
@@ -253,39 +289,28 @@ class AccountAnalyticInvoiceLine(models.Model):
|
|||||||
if line.date_start > line.date_end:
|
if line.date_start > line.date_end:
|
||||||
raise ValidationError(
|
raise ValidationError(
|
||||||
_(
|
_(
|
||||||
"Contract '%s' start date can't be later than "
|
"Contract line '%s' start date can't be later than"
|
||||||
"end date"
|
" end date"
|
||||||
)
|
)
|
||||||
% line.contract_id.name
|
% line.name
|
||||||
)
|
)
|
||||||
|
|
||||||
@api.depends('recurring_next_date', 'date_start', 'date_end')
|
@api.depends('recurring_next_date', 'date_start', 'date_end')
|
||||||
def _compute_create_invoice_visibility(self):
|
def _compute_create_invoice_visibility(self):
|
||||||
today = fields.Date.today()
|
today = fields.Date.context_today(self)
|
||||||
for line in self:
|
for rec in self:
|
||||||
if line.date_start:
|
if rec.date_start:
|
||||||
if today < line.date_start:
|
if today < rec.date_start:
|
||||||
line.create_invoice_visibility = False
|
rec.create_invoice_visibility = False
|
||||||
elif not line.date_end:
|
else:
|
||||||
line.create_invoice_visibility = True
|
rec.create_invoice_visibility = bool(
|
||||||
elif line.recurring_next_date:
|
rec.recurring_next_date
|
||||||
if line.recurring_invoicing_type == 'pre-paid':
|
)
|
||||||
line.create_invoice_visibility = (
|
|
||||||
line.recurring_next_date <= line.date_end
|
|
||||||
)
|
|
||||||
else:
|
|
||||||
line.create_invoice_visibility = (
|
|
||||||
line.recurring_next_date
|
|
||||||
- line.get_relative_delta(
|
|
||||||
line.recurring_rule_type,
|
|
||||||
line.recurring_interval,
|
|
||||||
)
|
|
||||||
) <= line.date_end
|
|
||||||
|
|
||||||
@api.model
|
@api.model
|
||||||
def recurring_create_invoice(self, contract=False):
|
def _get_recurring_create_invoice_domain(self, contract=False):
|
||||||
domain = []
|
domain = []
|
||||||
date_ref = fields.Date.today()
|
date_ref = fields.Date.context_today(self)
|
||||||
if contract:
|
if contract:
|
||||||
contract.ensure_one()
|
contract.ensure_one()
|
||||||
date_ref = contract.recurring_next_date
|
date_ref = contract.recurring_next_date
|
||||||
@@ -296,39 +321,36 @@ class AccountAnalyticInvoiceLine(models.Model):
|
|||||||
('contract_id.recurring_invoices', '=', True),
|
('contract_id.recurring_invoices', '=', True),
|
||||||
('recurring_next_date', '<=', date_ref),
|
('recurring_next_date', '<=', date_ref),
|
||||||
('is_canceled', '=', False),
|
('is_canceled', '=', False),
|
||||||
# '|',
|
|
||||||
# ('date_end', '=', False),
|
|
||||||
# ('date_end', '>=', date_ref),
|
|
||||||
# with this leaf, it's impossible to invoice the last period
|
|
||||||
# in post-paid case.
|
|
||||||
# i.e: date_end = 15/03 recurring_next_date = 31/03
|
|
||||||
# A solution for this, is to only check recurring_next_date
|
|
||||||
# and filter with create_invoice_visibility
|
|
||||||
]
|
]
|
||||||
)
|
)
|
||||||
lines = self.search(domain).filtered('create_invoice_visibility')
|
return domain
|
||||||
if lines:
|
|
||||||
return lines._recurring_create_invoice()
|
|
||||||
return False
|
|
||||||
|
|
||||||
@api.multi
|
@api.model
|
||||||
def _recurring_create_invoice(self):
|
def recurring_create_invoice(self, contract=False):
|
||||||
|
domain = self._get_recurring_create_invoice_domain(contract)
|
||||||
|
contract_to_invoice = self.read_group(
|
||||||
|
domain, ['id', 'contract_id'], ['contract_id']
|
||||||
|
)
|
||||||
|
return self._recurring_create_invoice(contract_to_invoice)
|
||||||
|
|
||||||
|
@api.model
|
||||||
|
def _recurring_create_invoice(self, contract_to_invoice):
|
||||||
"""Create invoices from contracts
|
"""Create invoices from contracts
|
||||||
|
|
||||||
:return: invoices created
|
:return: invoices created
|
||||||
"""
|
"""
|
||||||
invoices = self.env['account.invoice']
|
invoices = self.env['account.invoice']
|
||||||
for contract in self.mapped('contract_id'):
|
for contract in contract_to_invoice:
|
||||||
lines = self.filtered(lambda l: l.contract_id == contract)
|
lines = self.search(contract['__domain'])
|
||||||
invoices |= lines._create_invoice()
|
if lines:
|
||||||
lines._update_recurring_next_date()
|
invoices |= lines._create_invoice()
|
||||||
|
lines._update_recurring_next_date()
|
||||||
return invoices
|
return invoices
|
||||||
|
|
||||||
@api.multi
|
@api.multi
|
||||||
def _create_invoice(self):
|
def _create_invoice(self):
|
||||||
"""
|
"""
|
||||||
:param invoice: If not False add lines to this invoice
|
:return: invoice created
|
||||||
:return: invoice created or updated
|
|
||||||
"""
|
"""
|
||||||
contract = self.mapped('contract_id')
|
contract = self.mapped('contract_id')
|
||||||
date_invoice = min(self.mapped('recurring_next_date'))
|
date_invoice = min(self.mapped('recurring_next_date'))
|
||||||
@@ -359,10 +381,8 @@ class AccountAnalyticInvoiceLine(models.Model):
|
|||||||
invoice_line_vals = invoice_line._convert_to_write(invoice_line._cache)
|
invoice_line_vals = invoice_line._convert_to_write(invoice_line._cache)
|
||||||
# Insert markers
|
# Insert markers
|
||||||
contract = self.contract_id
|
contract = self.contract_id
|
||||||
lang_obj = self.env['res.lang']
|
first_date_invoiced, last_date_invoiced = self._get_invoiced_period()
|
||||||
lang = lang_obj.search([('code', '=', contract.partner_id.lang)])
|
name = self._insert_markers(first_date_invoiced, last_date_invoiced)
|
||||||
date_format = lang.date_format or '%m/%d/%Y'
|
|
||||||
name = self._insert_markers(date_format)
|
|
||||||
invoice_line_vals.update(
|
invoice_line_vals.update(
|
||||||
{
|
{
|
||||||
'name': name,
|
'name': name,
|
||||||
@@ -373,26 +393,98 @@ class AccountAnalyticInvoiceLine(models.Model):
|
|||||||
return invoice_line_vals
|
return invoice_line_vals
|
||||||
|
|
||||||
@api.multi
|
@api.multi
|
||||||
def _insert_markers(self, date_format):
|
def _get_invoiced_period(self):
|
||||||
self.ensure_one()
|
self.ensure_one()
|
||||||
date_from = fields.Date.from_string(self.recurring_next_date)
|
first_date_invoiced = (
|
||||||
date_to = date_from + self.get_relative_delta(
|
self.last_date_invoiced + relativedelta(days=1)
|
||||||
self.recurring_rule_type, self.recurring_interval
|
if self.last_date_invoiced
|
||||||
|
else self.date_start
|
||||||
)
|
)
|
||||||
|
if self.recurring_rule_type == 'monthlylastday':
|
||||||
|
last_date_invoiced = first_date_invoiced + self.get_relative_delta(
|
||||||
|
self.recurring_rule_type, self.recurring_interval - 1
|
||||||
|
)
|
||||||
|
else:
|
||||||
|
last_date_invoiced = (
|
||||||
|
first_date_invoiced
|
||||||
|
+ self.get_relative_delta(
|
||||||
|
self.recurring_rule_type, self.recurring_interval
|
||||||
|
)
|
||||||
|
- relativedelta(days=1)
|
||||||
|
)
|
||||||
|
if self.date_end and self.date_end < last_date_invoiced:
|
||||||
|
last_date_invoiced = self.date_end
|
||||||
|
return first_date_invoiced, last_date_invoiced
|
||||||
|
|
||||||
|
@api.multi
|
||||||
|
def _insert_markers(self, first_date_invoiced, last_date_invoiced):
|
||||||
|
self.ensure_one()
|
||||||
|
lang_obj = self.env['res.lang']
|
||||||
|
lang = lang_obj.search(
|
||||||
|
[('code', '=', self.contract_id.partner_id.lang)]
|
||||||
|
)
|
||||||
|
date_format = lang.date_format or '%m/%d/%Y'
|
||||||
name = self.name
|
name = self.name
|
||||||
name = name.replace('#START#', date_from.strftime(date_format))
|
name = name.replace(
|
||||||
name = name.replace('#END#', date_to.strftime(date_format))
|
'#START#', first_date_invoiced.strftime(date_format)
|
||||||
|
)
|
||||||
|
name = name.replace('#END#', last_date_invoiced.strftime(date_format))
|
||||||
return name
|
return name
|
||||||
|
|
||||||
@api.multi
|
@api.multi
|
||||||
def _update_recurring_next_date(self):
|
def _update_recurring_next_date(self):
|
||||||
for line in self:
|
for rec in self:
|
||||||
ref_date = line.recurring_next_date or fields.Date.today()
|
old_date = rec.recurring_next_date
|
||||||
old_date = fields.Date.from_string(ref_date)
|
|
||||||
new_date = old_date + self.get_relative_delta(
|
new_date = old_date + self.get_relative_delta(
|
||||||
line.recurring_rule_type, line.recurring_interval
|
rec.recurring_rule_type, rec.recurring_interval
|
||||||
)
|
)
|
||||||
line.recurring_next_date = new_date
|
|
||||||
|
if rec.recurring_rule_type == 'monthlylastday':
|
||||||
|
rec.last_date_invoiced = (
|
||||||
|
old_date
|
||||||
|
if rec.date_end and old_date < rec.date_end
|
||||||
|
else rec.date_end
|
||||||
|
)
|
||||||
|
elif rec.recurring_invoicing_type == 'post-paid':
|
||||||
|
rec.last_date_invoiced = (
|
||||||
|
old_date - relativedelta(days=1)
|
||||||
|
if rec.date_end and old_date < rec.date_end
|
||||||
|
else rec.date_end
|
||||||
|
)
|
||||||
|
elif rec.recurring_invoicing_type == 'pre-paid':
|
||||||
|
rec.last_date_invoiced = (
|
||||||
|
new_date - relativedelta(days=1)
|
||||||
|
if rec.date_end and new_date < rec.date_end
|
||||||
|
else rec.date_end
|
||||||
|
)
|
||||||
|
if (
|
||||||
|
rec.last_date_invoiced
|
||||||
|
and rec.last_date_invoiced == rec.date_end
|
||||||
|
):
|
||||||
|
rec.recurring_next_date = False
|
||||||
|
else:
|
||||||
|
rec.recurring_next_date = new_date
|
||||||
|
|
||||||
|
@api.multi
|
||||||
|
def _init_last_date_invoiced(self):
|
||||||
|
"""Used to init last_date_invoiced for migration purpose"""
|
||||||
|
for rec in self:
|
||||||
|
if rec.recurring_rule_type == 'monthlylastday':
|
||||||
|
last_date_invoiced = (
|
||||||
|
rec.recurring_next_date
|
||||||
|
- self.get_relative_delta(
|
||||||
|
rec.recurring_rule_type, rec.recurring_interval
|
||||||
|
)
|
||||||
|
)
|
||||||
|
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
|
@api.model
|
||||||
def get_relative_delta(self, recurring_rule_type, interval):
|
def get_relative_delta(self, recurring_rule_type, interval):
|
||||||
@@ -408,13 +500,20 @@ class AccountAnalyticInvoiceLine(models.Model):
|
|||||||
return relativedelta(years=interval)
|
return relativedelta(years=interval)
|
||||||
|
|
||||||
@api.multi
|
@api.multi
|
||||||
def delay(self, delay_delta):
|
def _delay(self, delay_delta):
|
||||||
"""
|
"""
|
||||||
Delay a contract line
|
Delay a contract line
|
||||||
:param delay_delta: delay relative delta
|
:param delay_delta: delay relative delta
|
||||||
:return: delayed contract line
|
:return: delayed contract line
|
||||||
"""
|
"""
|
||||||
for rec in self:
|
for rec in self:
|
||||||
|
if rec.last_date_invoiced:
|
||||||
|
raise ValidationError(
|
||||||
|
_(
|
||||||
|
"You can't delay a contract line "
|
||||||
|
"invoiced at least one time."
|
||||||
|
)
|
||||||
|
)
|
||||||
old_date_start = rec.date_start
|
old_date_start = rec.date_start
|
||||||
old_date_end = rec.date_end
|
old_date_end = rec.date_end
|
||||||
new_date_start = rec.date_start + delay_delta
|
new_date_start = rec.date_start + delay_delta
|
||||||
@@ -424,30 +523,12 @@ class AccountAnalyticInvoiceLine(models.Model):
|
|||||||
rec.recurring_rule_type,
|
rec.recurring_rule_type,
|
||||||
rec.recurring_interval,
|
rec.recurring_interval,
|
||||||
)
|
)
|
||||||
rec.date_end = (
|
if rec.date_end:
|
||||||
rec.date_end
|
rec.date_end += delay_delta
|
||||||
if not rec.date_end
|
|
||||||
else rec.date_end + delay_delta
|
|
||||||
)
|
|
||||||
rec.date_start = new_date_start
|
rec.date_start = new_date_start
|
||||||
msg = _(
|
|
||||||
"""Contract line for <strong>{product}</strong>
|
|
||||||
delayed: <br/>
|
|
||||||
- <strong>Start</strong>: {old_date_start} -- {new_date_start}
|
|
||||||
<br/>
|
|
||||||
- <strong>End</strong>: {old_date_end} -- {new_date_end}
|
|
||||||
""".format(
|
|
||||||
product=rec.name,
|
|
||||||
old_date_start=old_date_start,
|
|
||||||
new_date_start=rec.date_start,
|
|
||||||
old_date_end=old_date_end,
|
|
||||||
new_date_end=rec.date_end,
|
|
||||||
)
|
|
||||||
)
|
|
||||||
rec.contract_id.message_post(body=msg)
|
|
||||||
|
|
||||||
@api.multi
|
@api.multi
|
||||||
def stop(self, date_end):
|
def stop(self, date_end, post_message=True):
|
||||||
"""
|
"""
|
||||||
Put date_end on contract line
|
Put date_end on contract line
|
||||||
We don't consider contract lines that end's before the new end date
|
We don't consider contract lines that end's before the new end date
|
||||||
@@ -460,25 +541,23 @@ class AccountAnalyticInvoiceLine(models.Model):
|
|||||||
if date_end < rec.date_start:
|
if date_end < rec.date_start:
|
||||||
rec.cancel()
|
rec.cancel()
|
||||||
else:
|
else:
|
||||||
old_date_end = rec.date_end
|
if not rec.date_end or rec.date_end > date_end:
|
||||||
date_end = (
|
if post_message:
|
||||||
rec.date_end
|
old_date_end = rec.date_end
|
||||||
if rec.date_end and rec.date_end < date_end
|
msg = _(
|
||||||
else date_end
|
"""Contract line for <strong>{product}</strong>
|
||||||
)
|
stopped: <br/>
|
||||||
rec.write({'date_end': date_end, 'is_auto_renew': False})
|
- <strong>End</strong>: {old_end} -- {new_end}
|
||||||
|
""".format(
|
||||||
msg = _(
|
product=rec.name,
|
||||||
"""Contract line for <strong>{product}</strong>
|
old_end=old_date_end,
|
||||||
stopped: <br/>
|
new_end=rec.date_end,
|
||||||
- <strong>End</strong>: {old_date_end} -- {new_date_end}
|
)
|
||||||
""".format(
|
)
|
||||||
product=rec.name,
|
rec.contract_id.message_post(body=msg)
|
||||||
old_date_end=old_date_end,
|
rec.write({'date_end': date_end, 'is_auto_renew': False})
|
||||||
new_date_end=rec.date_end,
|
else:
|
||||||
)
|
rec.write({'is_auto_renew': False})
|
||||||
)
|
|
||||||
rec.contract_id.message_post(body=msg)
|
|
||||||
return True
|
return True
|
||||||
|
|
||||||
@api.multi
|
@api.multi
|
||||||
@@ -505,7 +584,12 @@ class AccountAnalyticInvoiceLine(models.Model):
|
|||||||
|
|
||||||
@api.multi
|
@api.multi
|
||||||
def plan_successor(
|
def plan_successor(
|
||||||
self, date_start, date_end, is_auto_renew, recurring_next_date=False
|
self,
|
||||||
|
date_start,
|
||||||
|
date_end,
|
||||||
|
is_auto_renew,
|
||||||
|
recurring_next_date=False,
|
||||||
|
post_message=True,
|
||||||
):
|
):
|
||||||
"""
|
"""
|
||||||
Create a copy of a contract line in a new interval
|
Create a copy of a contract line in a new interval
|
||||||
@@ -530,20 +614,20 @@ class AccountAnalyticInvoiceLine(models.Model):
|
|||||||
)
|
)
|
||||||
rec.successor_contract_line_id = new_line
|
rec.successor_contract_line_id = new_line
|
||||||
contract_line |= new_line
|
contract_line |= new_line
|
||||||
|
if post_message:
|
||||||
msg = _(
|
msg = _(
|
||||||
"""Contract line for <strong>{product}</strong>
|
"""Contract line for <strong>{product}</strong>
|
||||||
planned a successor: <br/>
|
planned a successor: <br/>
|
||||||
- <strong>Start</strong>: {new_date_start}
|
- <strong>Start</strong>: {new_date_start}
|
||||||
<br/>
|
<br/>
|
||||||
- <strong>End</strong>: {new_date_end}
|
- <strong>End</strong>: {new_date_end}
|
||||||
""".format(
|
""".format(
|
||||||
product=rec.name,
|
product=rec.name,
|
||||||
new_date_start=new_line.date_start,
|
new_date_start=new_line.date_start,
|
||||||
new_date_end=new_line.date_end,
|
new_date_end=new_line.date_end,
|
||||||
|
)
|
||||||
)
|
)
|
||||||
)
|
rec.contract_id.message_post(body=msg)
|
||||||
rec.contract_id.message_post(body=msg)
|
|
||||||
return contract_line
|
return contract_line
|
||||||
|
|
||||||
@api.multi
|
@api.multi
|
||||||
@@ -590,34 +674,64 @@ class AccountAnalyticInvoiceLine(models.Model):
|
|||||||
delay = (date_end - rec.date_start) + timedelta(days=1)
|
delay = (date_end - rec.date_start) + timedelta(days=1)
|
||||||
else:
|
else:
|
||||||
delay = (date_end - date_start) + timedelta(days=1)
|
delay = (date_end - date_start) + timedelta(days=1)
|
||||||
rec.delay(delay)
|
rec._delay(delay)
|
||||||
contract_line |= rec
|
contract_line |= rec
|
||||||
else:
|
else:
|
||||||
if rec.date_end and rec.date_end < date_start:
|
if rec.date_end and rec.date_end < date_start:
|
||||||
rec.stop(date_start)
|
rec.stop(date_start, post_message=False)
|
||||||
elif (
|
elif (
|
||||||
rec.date_end
|
rec.date_end
|
||||||
and rec.date_end > date_start
|
and rec.date_end > date_start
|
||||||
and rec.date_end < date_end
|
and rec.date_end < date_end
|
||||||
):
|
):
|
||||||
new_date_start = date_end
|
new_date_start = date_end + relativedelta(days=1)
|
||||||
new_date_end = date_end + (rec.date_end - date_start)
|
new_date_end = (
|
||||||
rec.stop(date_start)
|
date_end
|
||||||
|
+ (rec.date_end - date_start)
|
||||||
|
+ relativedelta(days=1)
|
||||||
|
)
|
||||||
|
rec.stop(
|
||||||
|
date_start - relativedelta(days=1), post_message=False
|
||||||
|
)
|
||||||
contract_line |= rec.plan_successor(
|
contract_line |= rec.plan_successor(
|
||||||
new_date_start, new_date_end, is_auto_renew
|
new_date_start,
|
||||||
|
new_date_end,
|
||||||
|
is_auto_renew,
|
||||||
|
post_message=False,
|
||||||
)
|
)
|
||||||
else:
|
else:
|
||||||
new_date_start = date_end
|
new_date_start = date_end + relativedelta(days=1)
|
||||||
new_date_end = (
|
if rec.date_end:
|
||||||
rec.date_end
|
new_date_end = (
|
||||||
if not rec.date_end
|
rec.date_end
|
||||||
else rec.date_end + (date_end - date_start)
|
+ (date_end - date_start)
|
||||||
)
|
+ relativedelta(days=1)
|
||||||
rec.stop(date_start)
|
)
|
||||||
contract_line |= rec.plan_successor(
|
else:
|
||||||
new_date_start, new_date_end, is_auto_renew
|
new_date_end = rec.date_end
|
||||||
)
|
|
||||||
|
|
||||||
|
rec.stop(
|
||||||
|
date_start - relativedelta(days=1), post_message=False
|
||||||
|
)
|
||||||
|
contract_line |= rec.plan_successor(
|
||||||
|
new_date_start,
|
||||||
|
new_date_end,
|
||||||
|
is_auto_renew,
|
||||||
|
post_message=False,
|
||||||
|
)
|
||||||
|
msg = _(
|
||||||
|
"""Contract line for <strong>{product}</strong>
|
||||||
|
suspended: <br/>
|
||||||
|
- <strong>Suspension Start</strong>: {new_date_start}
|
||||||
|
<br/>
|
||||||
|
- <strong>Suspension End</strong>: {new_date_end}
|
||||||
|
""".format(
|
||||||
|
product=rec.name,
|
||||||
|
new_date_start=date_start,
|
||||||
|
new_date_end=date_end,
|
||||||
|
)
|
||||||
|
)
|
||||||
|
rec.contract_id.message_post(body=msg)
|
||||||
return contract_line
|
return contract_line
|
||||||
|
|
||||||
@api.multi
|
@api.multi
|
||||||
@@ -671,7 +785,7 @@ class AccountAnalyticInvoiceLine(models.Model):
|
|||||||
self.ensure_one()
|
self.ensure_one()
|
||||||
context = {
|
context = {
|
||||||
'default_contract_line_id': self.id,
|
'default_contract_line_id': self.id,
|
||||||
'default_recurring_next_date': fields.Date.today(),
|
'default_recurring_next_date': fields.Date.context_today(self),
|
||||||
}
|
}
|
||||||
context.update(self.env.context)
|
context.update(self.env.context)
|
||||||
view_id = self.env.ref(
|
view_id = self.env.ref(
|
||||||
@@ -772,16 +886,31 @@ class AccountAnalyticInvoiceLine(models.Model):
|
|||||||
res = self.env['account.analytic.invoice.line']
|
res = self.env['account.analytic.invoice.line']
|
||||||
for rec in self:
|
for rec in self:
|
||||||
is_auto_renew = rec.is_auto_renew
|
is_auto_renew = rec.is_auto_renew
|
||||||
rec.stop(rec.date_end)
|
rec.stop(rec.date_end, post_message=False)
|
||||||
date_start, date_end = rec._get_renewal_dates()
|
date_start, date_end = rec._get_renewal_dates()
|
||||||
new_line = rec.plan_successor(date_start, date_end, is_auto_renew)
|
new_line = rec.plan_successor(
|
||||||
|
date_start, date_end, is_auto_renew, post_message=False
|
||||||
|
)
|
||||||
new_line._onchange_date_start()
|
new_line._onchange_date_start()
|
||||||
res |= new_line
|
res |= new_line
|
||||||
|
msg = _(
|
||||||
|
"""Contract line for <strong>{product}</strong>
|
||||||
|
renewed: <br/>
|
||||||
|
- <strong>Start</strong>: {new_date_start}
|
||||||
|
<br/>
|
||||||
|
- <strong>End</strong>: {new_date_end}
|
||||||
|
""".format(
|
||||||
|
product=rec.name,
|
||||||
|
new_date_start=date_start,
|
||||||
|
new_date_end=date_end,
|
||||||
|
)
|
||||||
|
)
|
||||||
|
rec.contract_id.message_post(body=msg)
|
||||||
return res
|
return res
|
||||||
|
|
||||||
@api.model
|
@api.model
|
||||||
def _contract_line_to_renew_domain(self):
|
def _contract_line_to_renew_domain(self):
|
||||||
date_ref = fields.datetime.today() + self.get_relative_delta(
|
date_ref = fields.Date.context_today(self) + self.get_relative_delta(
|
||||||
self.termination_notice_rule_type, self.termination_notice_interval
|
self.termination_notice_rule_type, self.termination_notice_interval
|
||||||
)
|
)
|
||||||
return [
|
return [
|
||||||
|
|||||||
428
contract/models/contract_line_constraints.py
Normal file
428
contract/models/contract_line_constraints.py
Normal 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
|
||||||
@@ -161,6 +161,7 @@ class TestContract(TestContractBase):
|
|||||||
|
|
||||||
def test_contract_daily(self):
|
def test_contract_daily(self):
|
||||||
recurring_next_date = to_date('2018-02-23')
|
recurring_next_date = to_date('2018-02-23')
|
||||||
|
last_date_invoiced = to_date('2018-02-22')
|
||||||
self.acct_line.recurring_next_date = '2018-02-22'
|
self.acct_line.recurring_next_date = '2018-02-22'
|
||||||
self.acct_line.recurring_rule_type = 'daily'
|
self.acct_line.recurring_rule_type = 'daily'
|
||||||
self.contract.pricelist_id = False
|
self.contract.pricelist_id = False
|
||||||
@@ -172,9 +173,11 @@ class TestContract(TestContractBase):
|
|||||||
self.assertEqual(
|
self.assertEqual(
|
||||||
self.acct_line.recurring_next_date, recurring_next_date
|
self.acct_line.recurring_next_date, recurring_next_date
|
||||||
)
|
)
|
||||||
|
self.assertEqual(self.acct_line.last_date_invoiced, last_date_invoiced)
|
||||||
|
|
||||||
def test_contract_weekly(self):
|
def test_contract_weekly_post_paid(self):
|
||||||
recurring_next_date = to_date('2018-03-01')
|
recurring_next_date = to_date('2018-03-01')
|
||||||
|
last_date_invoiced = to_date('2018-02-21')
|
||||||
self.acct_line.recurring_next_date = '2018-02-22'
|
self.acct_line.recurring_next_date = '2018-02-22'
|
||||||
self.acct_line.recurring_rule_type = 'weekly'
|
self.acct_line.recurring_rule_type = 'weekly'
|
||||||
self.acct_line.recurring_invoicing_type = 'post-paid'
|
self.acct_line.recurring_invoicing_type = 'post-paid'
|
||||||
@@ -186,11 +189,14 @@ class TestContract(TestContractBase):
|
|||||||
self.assertEqual(
|
self.assertEqual(
|
||||||
self.acct_line.recurring_next_date, recurring_next_date
|
self.acct_line.recurring_next_date, recurring_next_date
|
||||||
)
|
)
|
||||||
|
self.assertEqual(self.acct_line.last_date_invoiced, last_date_invoiced)
|
||||||
|
|
||||||
def test_contract_yearly(self):
|
def test_contract_weekly_pre_paid(self):
|
||||||
recurring_next_date = to_date('2019-02-22')
|
recurring_next_date = to_date('2018-03-01')
|
||||||
|
last_date_invoiced = to_date('2018-02-28')
|
||||||
self.acct_line.recurring_next_date = '2018-02-22'
|
self.acct_line.recurring_next_date = '2018-02-22'
|
||||||
self.acct_line.recurring_rule_type = 'yearly'
|
self.acct_line.recurring_rule_type = 'weekly'
|
||||||
|
self.acct_line.recurring_invoicing_type = 'pre-paid'
|
||||||
self.contract.recurring_create_invoice()
|
self.contract.recurring_create_invoice()
|
||||||
invoices_weekly = self.env['account.invoice'].search(
|
invoices_weekly = self.env['account.invoice'].search(
|
||||||
[('contract_id', '=', self.contract.id)]
|
[('contract_id', '=', self.contract.id)]
|
||||||
@@ -199,9 +205,44 @@ class TestContract(TestContractBase):
|
|||||||
self.assertEqual(
|
self.assertEqual(
|
||||||
self.acct_line.recurring_next_date, recurring_next_date
|
self.acct_line.recurring_next_date, recurring_next_date
|
||||||
)
|
)
|
||||||
|
self.assertEqual(self.acct_line.last_date_invoiced, last_date_invoiced)
|
||||||
|
|
||||||
|
def test_contract_yearly_post_paid(self):
|
||||||
|
recurring_next_date = to_date('2019-02-22')
|
||||||
|
last_date_invoiced = to_date('2018-02-21')
|
||||||
|
self.acct_line.recurring_next_date = '2018-02-22'
|
||||||
|
self.acct_line.recurring_rule_type = 'yearly'
|
||||||
|
self.acct_line.recurring_invoicing_type = 'post-paid'
|
||||||
|
self.contract.recurring_create_invoice()
|
||||||
|
invoices_weekly = self.env['account.invoice'].search(
|
||||||
|
[('contract_id', '=', self.contract.id)]
|
||||||
|
)
|
||||||
|
self.assertTrue(invoices_weekly)
|
||||||
|
self.assertEqual(
|
||||||
|
self.acct_line.recurring_next_date, recurring_next_date
|
||||||
|
)
|
||||||
|
self.assertEqual(self.acct_line.last_date_invoiced, last_date_invoiced)
|
||||||
|
|
||||||
|
def test_contract_yearly_pre_paid(self):
|
||||||
|
recurring_next_date = to_date('2019-02-22')
|
||||||
|
last_date_invoiced = to_date('2019-02-21')
|
||||||
|
self.acct_line.date_end = '2020-02-22'
|
||||||
|
self.acct_line.recurring_next_date = '2018-02-22'
|
||||||
|
self.acct_line.recurring_rule_type = 'yearly'
|
||||||
|
self.acct_line.recurring_invoicing_type = 'pre-paid'
|
||||||
|
self.contract.recurring_create_invoice()
|
||||||
|
invoices_weekly = self.env['account.invoice'].search(
|
||||||
|
[('contract_id', '=', self.contract.id)]
|
||||||
|
)
|
||||||
|
self.assertTrue(invoices_weekly)
|
||||||
|
self.assertEqual(
|
||||||
|
self.acct_line.recurring_next_date, recurring_next_date
|
||||||
|
)
|
||||||
|
self.assertEqual(self.acct_line.last_date_invoiced, last_date_invoiced)
|
||||||
|
|
||||||
def test_contract_monthly_lastday(self):
|
def test_contract_monthly_lastday(self):
|
||||||
recurring_next_date = to_date('2018-03-31')
|
recurring_next_date = to_date('2018-03-31')
|
||||||
|
last_date_invoiced = to_date('2018-02-22')
|
||||||
self.acct_line.recurring_next_date = '2018-02-22'
|
self.acct_line.recurring_next_date = '2018-02-22'
|
||||||
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'
|
||||||
@@ -213,21 +254,41 @@ class TestContract(TestContractBase):
|
|||||||
self.assertEqual(
|
self.assertEqual(
|
||||||
self.acct_line.recurring_next_date, recurring_next_date
|
self.acct_line.recurring_next_date, recurring_next_date
|
||||||
)
|
)
|
||||||
|
self.assertEqual(self.acct_line.last_date_invoiced, last_date_invoiced)
|
||||||
|
|
||||||
def test_last_invoice_post_paid(self):
|
def test_last_invoice_post_paid(self):
|
||||||
recurring_next_date = to_date('2018-04-30')
|
self.acct_line.date_start = '2018-01-01'
|
||||||
self.acct_line.recurring_next_date = '2018-03-31'
|
|
||||||
self.acct_line.date_end = '2018-03-15'
|
|
||||||
self.acct_line.recurring_invoicing_type = 'post-paid'
|
self.acct_line.recurring_invoicing_type = 'post-paid'
|
||||||
|
self.acct_line.date_end = '2018-03-15'
|
||||||
|
self.acct_line._onchange_date_start()
|
||||||
|
self.assertTrue(self.acct_line.create_invoice_visibility)
|
||||||
|
self.assertEqual(
|
||||||
|
self.acct_line.recurring_next_date, to_date('2018-02-01')
|
||||||
|
)
|
||||||
|
self.assertFalse(self.acct_line.last_date_invoiced)
|
||||||
self.contract.recurring_create_invoice()
|
self.contract.recurring_create_invoice()
|
||||||
|
self.assertEqual(
|
||||||
|
self.acct_line.recurring_next_date, to_date('2018-03-01')
|
||||||
|
)
|
||||||
|
self.assertEqual(
|
||||||
|
self.acct_line.last_date_invoiced, to_date('2018-01-31')
|
||||||
|
)
|
||||||
|
self.contract.recurring_create_invoice()
|
||||||
|
self.assertEqual(
|
||||||
|
self.acct_line.recurring_next_date, to_date('2018-04-01')
|
||||||
|
)
|
||||||
|
self.assertEqual(
|
||||||
|
self.acct_line.last_date_invoiced, to_date('2018-02-28')
|
||||||
|
)
|
||||||
|
self.contract.recurring_create_invoice()
|
||||||
|
self.assertEqual(
|
||||||
|
self.acct_line.last_date_invoiced, to_date('2018-03-15')
|
||||||
|
)
|
||||||
|
self.assertFalse(self.acct_line.recurring_next_date)
|
||||||
|
self.assertFalse(self.acct_line.create_invoice_visibility)
|
||||||
invoices = self.env['account.invoice'].search(
|
invoices = self.env['account.invoice'].search(
|
||||||
[('contract_id', '=', self.contract.id)]
|
[('contract_id', '=', self.contract.id)]
|
||||||
)
|
)
|
||||||
self.assertTrue(invoices)
|
|
||||||
self.assertEqual(
|
|
||||||
self.acct_line.recurring_next_date, recurring_next_date
|
|
||||||
)
|
|
||||||
self.assertFalse(self.acct_line.create_invoice_visibility)
|
|
||||||
self.contract.recurring_create_invoice()
|
self.contract.recurring_create_invoice()
|
||||||
new_invoices = self.env['account.invoice'].search(
|
new_invoices = self.env['account.invoice'].search(
|
||||||
[('contract_id', '=', self.contract.id)]
|
[('contract_id', '=', self.contract.id)]
|
||||||
@@ -239,19 +300,38 @@ class TestContract(TestContractBase):
|
|||||||
)
|
)
|
||||||
|
|
||||||
def test_last_invoice_pre_paid(self):
|
def test_last_invoice_pre_paid(self):
|
||||||
recurring_next_date = to_date('2018-04-01')
|
self.acct_line.date_start = '2018-01-01'
|
||||||
self.acct_line.recurring_next_date = '2018-03-01'
|
|
||||||
self.acct_line.date_end = '2018-03-15'
|
|
||||||
self.acct_line.recurring_invoicing_type = 'pre-paid'
|
self.acct_line.recurring_invoicing_type = 'pre-paid'
|
||||||
|
self.acct_line.date_end = '2018-03-15'
|
||||||
|
self.acct_line._onchange_date_start()
|
||||||
|
self.assertTrue(self.acct_line.create_invoice_visibility)
|
||||||
|
self.assertEqual(
|
||||||
|
self.acct_line.recurring_next_date, to_date('2018-01-01')
|
||||||
|
)
|
||||||
|
self.assertFalse(self.acct_line.last_date_invoiced)
|
||||||
self.contract.recurring_create_invoice()
|
self.contract.recurring_create_invoice()
|
||||||
|
self.assertEqual(
|
||||||
|
self.acct_line.recurring_next_date, to_date('2018-02-01')
|
||||||
|
)
|
||||||
|
self.assertEqual(
|
||||||
|
self.acct_line.last_date_invoiced, to_date('2018-01-31')
|
||||||
|
)
|
||||||
|
self.contract.recurring_create_invoice()
|
||||||
|
self.assertEqual(
|
||||||
|
self.acct_line.last_date_invoiced, to_date('2018-02-28')
|
||||||
|
)
|
||||||
|
self.assertEqual(
|
||||||
|
self.acct_line.last_date_invoiced, to_date('2018-02-28')
|
||||||
|
)
|
||||||
|
self.contract.recurring_create_invoice()
|
||||||
|
self.assertEqual(
|
||||||
|
self.acct_line.last_date_invoiced, to_date('2018-03-15')
|
||||||
|
)
|
||||||
|
self.assertFalse(self.acct_line.recurring_next_date)
|
||||||
|
self.assertFalse(self.acct_line.create_invoice_visibility)
|
||||||
invoices = self.env['account.invoice'].search(
|
invoices = self.env['account.invoice'].search(
|
||||||
[('contract_id', '=', self.contract.id)]
|
[('contract_id', '=', self.contract.id)]
|
||||||
)
|
)
|
||||||
self.assertTrue(invoices)
|
|
||||||
self.assertEqual(
|
|
||||||
self.acct_line.recurring_next_date, recurring_next_date
|
|
||||||
)
|
|
||||||
self.assertFalse(self.acct_line.create_invoice_visibility)
|
|
||||||
self.contract.recurring_create_invoice()
|
self.contract.recurring_create_invoice()
|
||||||
new_invoices = self.env['account.invoice'].search(
|
new_invoices = self.env['account.invoice'].search(
|
||||||
[('contract_id', '=', self.contract.id)]
|
[('contract_id', '=', self.contract.id)]
|
||||||
@@ -454,48 +534,6 @@ class TestContract(TestContractBase):
|
|||||||
)
|
)
|
||||||
self.assertEqual(last_count, init_count + 1)
|
self.assertEqual(last_count, init_count + 1)
|
||||||
|
|
||||||
def test_compute_create_invoice_visibility(self):
|
|
||||||
self.acct_line.write(
|
|
||||||
{
|
|
||||||
'recurring_next_date': '2018-01-15',
|
|
||||||
'date_start': '2018-01-01',
|
|
||||||
'is_auto_renew': False,
|
|
||||||
'date_end': False,
|
|
||||||
}
|
|
||||||
)
|
|
||||||
self.assertTrue(self.contract.create_invoice_visibility)
|
|
||||||
self.acct_line.date_end = '2018-02-01'
|
|
||||||
self.contract.refresh()
|
|
||||||
self.assertTrue(self.contract.create_invoice_visibility)
|
|
||||||
self.acct_line.date_end = '2018-01-01'
|
|
||||||
self.contract.refresh()
|
|
||||||
self.assertFalse(self.contract.create_invoice_visibility)
|
|
||||||
|
|
||||||
def test_compute_create_invoice_visibility_for_contract_line(self):
|
|
||||||
self.acct_line.write(
|
|
||||||
{
|
|
||||||
'recurring_next_date': '2018-01-15',
|
|
||||||
'date_start': '2018-01-01',
|
|
||||||
'is_auto_renew': False,
|
|
||||||
'date_end': False,
|
|
||||||
}
|
|
||||||
)
|
|
||||||
self.assertTrue(self.acct_line.create_invoice_visibility)
|
|
||||||
self.acct_line.date_end = '2018-02-01'
|
|
||||||
self.assertTrue(self.acct_line.create_invoice_visibility)
|
|
||||||
self.acct_line.date_end = '2018-01-01'
|
|
||||||
self.assertFalse(self.acct_line.create_invoice_visibility)
|
|
||||||
self.acct_line.write(
|
|
||||||
{
|
|
||||||
'date_start': fields.Date.today() + relativedelta(months=2),
|
|
||||||
'recurring_next_date': fields.Date.today()
|
|
||||||
+ relativedelta(months=2),
|
|
||||||
'is_auto_renew': False,
|
|
||||||
'date_end': False,
|
|
||||||
}
|
|
||||||
)
|
|
||||||
self.assertFalse(self.acct_line.create_invoice_visibility)
|
|
||||||
|
|
||||||
def test_act_show_contract(self):
|
def test_act_show_contract(self):
|
||||||
show_contract = self.partner.with_context(
|
show_contract = self.partner.with_context(
|
||||||
contract_type='sale'
|
contract_type='sale'
|
||||||
@@ -608,6 +646,10 @@ class TestContract(TestContractBase):
|
|||||||
self.acct_line.write({'date_end': False, 'is_auto_renew': False})
|
self.acct_line.write({'date_end': False, 'is_auto_renew': False})
|
||||||
self.assertFalse(self.contract.date_end)
|
self.assertFalse(self.contract.date_end)
|
||||||
|
|
||||||
|
def test_last_date_invoiced_prepaid(self):
|
||||||
|
self.contract.recurring_create_invoice()
|
||||||
|
self
|
||||||
|
|
||||||
def test_stop_contract_line(self):
|
def test_stop_contract_line(self):
|
||||||
"""It should put end to the contract line"""
|
"""It should put end to the contract line"""
|
||||||
self.acct_line.write(
|
self.acct_line.write(
|
||||||
@@ -735,13 +777,21 @@ class TestContract(TestContractBase):
|
|||||||
self.acct_line.stop_plan_successor(
|
self.acct_line.stop_plan_successor(
|
||||||
suspension_start, suspension_end, True
|
suspension_start, suspension_end, True
|
||||||
)
|
)
|
||||||
self.assertEqual(self.acct_line.date_end, suspension_start)
|
self.assertEqual(
|
||||||
|
self.acct_line.date_end, suspension_start - relativedelta(days=1)
|
||||||
|
)
|
||||||
new_line = self.env['account.analytic.invoice.line'].search(
|
new_line = self.env['account.analytic.invoice.line'].search(
|
||||||
[('predecessor_contract_line_id', '=', self.acct_line.id)]
|
[('predecessor_contract_line_id', '=', self.acct_line.id)]
|
||||||
)
|
)
|
||||||
self.assertTrue(new_line)
|
self.assertTrue(new_line)
|
||||||
new_date_end = suspension_end + (end_date - suspension_start)
|
new_date_end = (
|
||||||
self.assertEqual(new_line.date_start, suspension_end)
|
suspension_end
|
||||||
|
+ (end_date - suspension_start)
|
||||||
|
+ relativedelta(days=1)
|
||||||
|
)
|
||||||
|
self.assertEqual(
|
||||||
|
new_line.date_start, suspension_end + relativedelta(days=1)
|
||||||
|
)
|
||||||
self.assertEqual(new_line.date_end, new_date_end)
|
self.assertEqual(new_line.date_end, new_date_end)
|
||||||
|
|
||||||
def test_stop_plan_successor_contract_line_3(self):
|
def test_stop_plan_successor_contract_line_3(self):
|
||||||
@@ -767,13 +817,21 @@ class TestContract(TestContractBase):
|
|||||||
self.acct_line.stop_plan_successor(
|
self.acct_line.stop_plan_successor(
|
||||||
suspension_start, suspension_end, True
|
suspension_start, suspension_end, True
|
||||||
)
|
)
|
||||||
self.assertEqual(self.acct_line.date_end, suspension_start)
|
self.assertEqual(
|
||||||
|
self.acct_line.date_end, suspension_start - relativedelta(days=1)
|
||||||
|
)
|
||||||
new_line = self.env['account.analytic.invoice.line'].search(
|
new_line = self.env['account.analytic.invoice.line'].search(
|
||||||
[('predecessor_contract_line_id', '=', self.acct_line.id)]
|
[('predecessor_contract_line_id', '=', self.acct_line.id)]
|
||||||
)
|
)
|
||||||
self.assertTrue(new_line)
|
self.assertTrue(new_line)
|
||||||
new_date_end = end_date + (suspension_end - suspension_start)
|
new_date_end = (
|
||||||
self.assertEqual(new_line.date_start, suspension_end)
|
end_date
|
||||||
|
+ (suspension_end - suspension_start)
|
||||||
|
+ relativedelta(days=1)
|
||||||
|
)
|
||||||
|
self.assertEqual(
|
||||||
|
new_line.date_start, suspension_end + relativedelta(days=1)
|
||||||
|
)
|
||||||
self.assertEqual(new_line.date_end, new_date_end)
|
self.assertEqual(new_line.date_end, new_date_end)
|
||||||
|
|
||||||
def test_stop_plan_successor_contract_line_3_without_end_date(self):
|
def test_stop_plan_successor_contract_line_3_without_end_date(self):
|
||||||
@@ -800,12 +858,16 @@ class TestContract(TestContractBase):
|
|||||||
self.acct_line.stop_plan_successor(
|
self.acct_line.stop_plan_successor(
|
||||||
suspension_start, suspension_end, False
|
suspension_start, suspension_end, False
|
||||||
)
|
)
|
||||||
self.assertEqual(self.acct_line.date_end, suspension_start)
|
self.assertEqual(
|
||||||
|
self.acct_line.date_end, suspension_start - relativedelta(days=1)
|
||||||
|
)
|
||||||
new_line = self.env['account.analytic.invoice.line'].search(
|
new_line = self.env['account.analytic.invoice.line'].search(
|
||||||
[('predecessor_contract_line_id', '=', self.acct_line.id)]
|
[('predecessor_contract_line_id', '=', self.acct_line.id)]
|
||||||
)
|
)
|
||||||
self.assertTrue(new_line)
|
self.assertTrue(new_line)
|
||||||
self.assertEqual(new_line.date_start, suspension_end)
|
self.assertEqual(
|
||||||
|
new_line.date_start, suspension_end + relativedelta(days=1)
|
||||||
|
)
|
||||||
self.assertFalse(new_line.date_end)
|
self.assertFalse(new_line.date_end)
|
||||||
|
|
||||||
def test_stop_plan_successor_contract_line_4(self):
|
def test_stop_plan_successor_contract_line_4(self):
|
||||||
@@ -1112,7 +1174,9 @@ class TestContract(TestContractBase):
|
|||||||
self.acct_line.stop_plan_successor(
|
self.acct_line.stop_plan_successor(
|
||||||
suspension_start, suspension_end, True
|
suspension_start, suspension_end, True
|
||||||
)
|
)
|
||||||
self.assertEqual(self.acct_line.date_end, suspension_start)
|
self.assertEqual(
|
||||||
|
self.acct_line.date_end, suspension_start - relativedelta(days=1)
|
||||||
|
)
|
||||||
new_line = self.env['account.analytic.invoice.line'].search(
|
new_line = self.env['account.analytic.invoice.line'].search(
|
||||||
[('predecessor_contract_line_id', '=', self.acct_line.id)]
|
[('predecessor_contract_line_id', '=', self.acct_line.id)]
|
||||||
)
|
)
|
||||||
@@ -1121,10 +1185,13 @@ class TestContract(TestContractBase):
|
|||||||
self.assertTrue(new_line.is_canceled)
|
self.assertTrue(new_line.is_canceled)
|
||||||
self.assertFalse(self.acct_line.successor_contract_line_id)
|
self.assertFalse(self.acct_line.successor_contract_line_id)
|
||||||
self.assertEqual(new_line.predecessor_contract_line_id, self.acct_line)
|
self.assertEqual(new_line.predecessor_contract_line_id, self.acct_line)
|
||||||
new_line.uncancel(suspension_end)
|
new_line.uncancel(suspension_end + relativedelta(days=1))
|
||||||
self.assertFalse(new_line.is_canceled)
|
self.assertFalse(new_line.is_canceled)
|
||||||
self.assertEqual(self.acct_line.successor_contract_line_id, new_line)
|
self.assertEqual(self.acct_line.successor_contract_line_id, new_line)
|
||||||
self.assertEqual(new_line.recurring_next_date, suspension_end)
|
self.assertEqual(
|
||||||
|
new_line.recurring_next_date,
|
||||||
|
suspension_end + relativedelta(days=1),
|
||||||
|
)
|
||||||
|
|
||||||
def test_cancel_uncancel_with_predecessor_has_successor(self):
|
def test_cancel_uncancel_with_predecessor_has_successor(self):
|
||||||
suspension_start = fields.Date.today() + relativedelta(months=6)
|
suspension_start = fields.Date.today() + relativedelta(months=6)
|
||||||
@@ -1199,3 +1266,107 @@ class TestContract(TestContractBase):
|
|||||||
self.assertTrue(new_line.is_auto_renew)
|
self.assertTrue(new_line.is_auto_renew)
|
||||||
self.assertEqual(new_line.date_start, to_date('2019-01-01'))
|
self.assertEqual(new_line.date_start, to_date('2019-01-01'))
|
||||||
self.assertEqual(new_line.date_end, to_date('2019-12-31'))
|
self.assertEqual(new_line.date_end, to_date('2019-12-31'))
|
||||||
|
|
||||||
|
def test_cron_recurring_create_invoice(self):
|
||||||
|
self.acct_line.date_start = '2018-01-01'
|
||||||
|
self.acct_line.recurring_invoicing_type = 'post-paid'
|
||||||
|
self.acct_line.date_end = '2018-03-15'
|
||||||
|
self.acct_line._onchange_date_start()
|
||||||
|
contracts = self.contract
|
||||||
|
for i in range(10):
|
||||||
|
contracts |= self.contract.copy()
|
||||||
|
self.env['account.analytic.account'].cron_recurring_create_invoice()
|
||||||
|
invoices = self.env['account.invoice'].search(
|
||||||
|
[('contract_id', 'in', contracts.ids)]
|
||||||
|
)
|
||||||
|
self.assertEqual(len(contracts), len(invoices))
|
||||||
|
|
||||||
|
def test_get_invoiced_period_monthlylastday(self):
|
||||||
|
self.acct_line.date_start = '2018-01-05'
|
||||||
|
self.acct_line.recurring_invoicing_type = 'post-paid'
|
||||||
|
self.acct_line.recurring_rule_type = 'monthlylastday'
|
||||||
|
self.acct_line.date_end = '2018-03-15'
|
||||||
|
self.acct_line._onchange_date_start()
|
||||||
|
first, last = self.acct_line._get_invoiced_period()
|
||||||
|
self.assertEqual(first, to_date('2018-01-05'))
|
||||||
|
self.assertEqual(last, to_date('2018-01-31'))
|
||||||
|
self.contract.recurring_create_invoice()
|
||||||
|
first, last = self.acct_line._get_invoiced_period()
|
||||||
|
self.assertEqual(first, to_date('2018-02-01'))
|
||||||
|
self.assertEqual(last, to_date('2018-02-28'))
|
||||||
|
self.contract.recurring_create_invoice()
|
||||||
|
first, last = self.acct_line._get_invoiced_period()
|
||||||
|
self.assertEqual(first, to_date('2018-03-01'))
|
||||||
|
self.assertEqual(last, to_date('2018-03-15'))
|
||||||
|
|
||||||
|
def test_get_invoiced_period_monthly_post_paid(self):
|
||||||
|
self.acct_line.date_start = '2018-01-05'
|
||||||
|
self.acct_line.recurring_invoicing_type = 'post-paid'
|
||||||
|
self.acct_line.recurring_rule_type = 'monthly'
|
||||||
|
self.acct_line.date_end = '2018-03-15'
|
||||||
|
self.acct_line._onchange_date_start()
|
||||||
|
first, last = self.acct_line._get_invoiced_period()
|
||||||
|
self.assertEqual(first, to_date('2018-01-05'))
|
||||||
|
self.assertEqual(last, to_date('2018-02-04'))
|
||||||
|
self.contract.recurring_create_invoice()
|
||||||
|
first, last = self.acct_line._get_invoiced_period()
|
||||||
|
self.assertEqual(first, to_date('2018-02-05'))
|
||||||
|
self.assertEqual(last, to_date('2018-03-04'))
|
||||||
|
self.contract.recurring_create_invoice()
|
||||||
|
first, last = self.acct_line._get_invoiced_period()
|
||||||
|
self.assertEqual(first, to_date('2018-03-05'))
|
||||||
|
self.assertEqual(last, to_date('2018-03-15'))
|
||||||
|
|
||||||
|
def test_get_invoiced_period_monthly_pre_paid(self):
|
||||||
|
self.acct_line.date_start = '2018-01-05'
|
||||||
|
self.acct_line.recurring_invoicing_type = 'pre-paid'
|
||||||
|
self.acct_line.recurring_rule_type = 'monthly'
|
||||||
|
self.acct_line.date_end = '2018-03-15'
|
||||||
|
self.acct_line._onchange_date_start()
|
||||||
|
first, last = self.acct_line._get_invoiced_period()
|
||||||
|
self.assertEqual(first, to_date('2018-01-05'))
|
||||||
|
self.assertEqual(last, to_date('2018-02-04'))
|
||||||
|
self.contract.recurring_create_invoice()
|
||||||
|
first, last = self.acct_line._get_invoiced_period()
|
||||||
|
self.assertEqual(first, to_date('2018-02-05'))
|
||||||
|
self.assertEqual(last, to_date('2018-03-04'))
|
||||||
|
self.contract.recurring_create_invoice()
|
||||||
|
first, last = self.acct_line._get_invoiced_period()
|
||||||
|
self.assertEqual(first, to_date('2018-03-05'))
|
||||||
|
self.assertEqual(last, to_date('2018-03-15'))
|
||||||
|
|
||||||
|
def test_get_invoiced_period_yearly_post_paid(self):
|
||||||
|
self.acct_line.date_start = '2018-01-05'
|
||||||
|
self.acct_line.recurring_invoicing_type = 'post-paid'
|
||||||
|
self.acct_line.recurring_rule_type = 'yearly'
|
||||||
|
self.acct_line.date_end = '2020-03-15'
|
||||||
|
self.acct_line._onchange_date_start()
|
||||||
|
first, last = self.acct_line._get_invoiced_period()
|
||||||
|
self.assertEqual(first, to_date('2018-01-05'))
|
||||||
|
self.assertEqual(last, to_date('2019-01-04'))
|
||||||
|
self.contract.recurring_create_invoice()
|
||||||
|
first, last = self.acct_line._get_invoiced_period()
|
||||||
|
self.assertEqual(first, to_date('2019-01-05'))
|
||||||
|
self.assertEqual(last, to_date('2020-01-04'))
|
||||||
|
self.contract.recurring_create_invoice()
|
||||||
|
first, last = self.acct_line._get_invoiced_period()
|
||||||
|
self.assertEqual(first, to_date('2020-01-05'))
|
||||||
|
self.assertEqual(last, to_date('2020-03-15'))
|
||||||
|
|
||||||
|
def test_get_invoiced_period_yearly_pre_paid(self):
|
||||||
|
self.acct_line.date_start = '2018-01-05'
|
||||||
|
self.acct_line.recurring_invoicing_type = 'pre-paid'
|
||||||
|
self.acct_line.recurring_rule_type = 'yearly'
|
||||||
|
self.acct_line.date_end = '2020-03-15'
|
||||||
|
self.acct_line._onchange_date_start()
|
||||||
|
first, last = self.acct_line._get_invoiced_period()
|
||||||
|
self.assertEqual(first, to_date('2018-01-05'))
|
||||||
|
self.assertEqual(last, to_date('2019-01-04'))
|
||||||
|
self.contract.recurring_create_invoice()
|
||||||
|
first, last = self.acct_line._get_invoiced_period()
|
||||||
|
self.assertEqual(first, to_date('2019-01-05'))
|
||||||
|
self.assertEqual(last, to_date('2020-01-04'))
|
||||||
|
self.contract.recurring_create_invoice()
|
||||||
|
first, last = self.acct_line._get_invoiced_period()
|
||||||
|
self.assertEqual(first, to_date('2020-01-05'))
|
||||||
|
self.assertEqual(last, to_date('2020-03-15'))
|
||||||
|
|||||||
@@ -101,6 +101,8 @@
|
|||||||
<field name="recurring_next_date"
|
<field name="recurring_next_date"
|
||||||
required="1"
|
required="1"
|
||||||
attrs="{'invisible': [('create_invoice_visibility', '=', False)]}"/>
|
attrs="{'invisible': [('create_invoice_visibility', '=', False)]}"/>
|
||||||
|
<field name="last_date_invoiced"
|
||||||
|
groups="base.group_no_one"/>
|
||||||
<field name="create_invoice_visibility"
|
<field name="create_invoice_visibility"
|
||||||
invisible="1"/>
|
invisible="1"/>
|
||||||
<field name="is_plan_successor_allowed"
|
<field name="is_plan_successor_allowed"
|
||||||
|
|||||||
Reference in New Issue
Block a user