Skip to content

Commit

Permalink
[IMP] product_contract: add Recurrence Number to compute start and en…
Browse files Browse the repository at this point in the history
…d dates
  • Loading branch information
sbejaoui committed Nov 21, 2024
1 parent 17c7d97 commit 0bcce04
Show file tree
Hide file tree
Showing 10 changed files with 225 additions and 218 deletions.
5 changes: 1 addition & 4 deletions product_contract/models/__init__.py
Original file line number Diff line number Diff line change
@@ -1,11 +1,8 @@
# Copyright 2017 LasLabs Inc.
# Copyright 2018 ACSONE SA/NV
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html).

from . import contract
from . import contract_line
from . import product_template
from . import sale_order
from . import sale_order_line_contract_mixin
from . import sale_order_line
from . import res_company
from . import res_config_settings
2 changes: 1 addition & 1 deletion product_contract/models/product_template.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ class ProductTemplate(models.Model):
string="Contract Template",
company_dependent=True,
)
default_qty = fields.Integer(string="Default Quantity", default=1)
default_qty = fields.Integer(string="Recurrence Number", default=1)
recurring_rule_type = fields.Selection(
[
("daily", "Day(s)"),
Expand Down
109 changes: 2 additions & 107 deletions product_contract/models/sale_order_line.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,64 +16,8 @@


class SaleOrderLine(models.Model):
_inherit = "sale.order.line"

is_contract = fields.Boolean(
string="Is a contract", related="product_id.is_contract"
)
contract_id = fields.Many2one(
comodel_name="contract.contract", string="Contract", copy=False
)
contract_template_id = fields.Many2one(
comodel_name="contract.template",
string="Contract Template",
compute="_compute_contract_template_id",
)
recurring_rule_type = fields.Selection(related="product_id.recurring_rule_type")
recurring_invoicing_type = fields.Selection(
related="product_id.recurring_invoicing_type"
)
date_start = fields.Date()
date_end = fields.Date()

contract_line_id = fields.Many2one(
comodel_name="contract.line",
string="Contract Line to replace",
required=False,
copy=False,
)
is_auto_renew = fields.Boolean(
string="Auto Renew",
compute="_compute_auto_renew",
default=False,
store=True,
readonly=False,
)
auto_renew_interval = fields.Integer(
default=1,
string="Renew Every",
compute="_compute_auto_renew",
store=True,
readonly=False,
help="Renew every (Days/Week/Month/Year)",
)
auto_renew_rule_type = fields.Selection(
[
("daily", "Day(s)"),
("weekly", "Week(s)"),
("monthly", "Month(s)"),
("yearly", "Year(s)"),
],
default="yearly",
compute="_compute_auto_renew",
store=True,
readonly=False,
string="Renewal type",
help="Specify Interval for automatic renewal.",
)
contract_start_date_method = fields.Selection(
related="product_id.contract_start_date_method"
)
_name = "sale.order.line"
_inherit = ["sale.order.line", "sale.order.line.contract.mixin"]

@api.constrains("contract_id")
def _check_contact_is_not_terminated(self):
Expand All @@ -86,55 +30,6 @@ def _check_contact_is_not_terminated(self):
_("You can't upsell or downsell a terminated contract")
)

@api.depends("product_id", "order_id.company_id")
def _compute_contract_template_id(self):
for rec in self:
rec.contract_template_id = rec.product_id.with_company(
rec.order_id.company_id
).property_contract_template_id

def _get_auto_renew_rule_type(self):
"""monthly last day don't make sense for auto_renew_rule_type"""
self.ensure_one()
if self.recurring_rule_type == "monthlylastday":
return "monthly"
return self.recurring_rule_type

def _get_date_end(self):
self.ensure_one()
contract_start_date_method = self.product_id.contract_start_date_method
date_end = False
if contract_start_date_method == "manual":
contract_line_model = self.env["contract.line"]
date_end = (
self.date_start
+ contract_line_model.get_relative_delta(
self._get_auto_renew_rule_type(),
int(self.product_uom_qty),
)
- relativedelta(days=1)
)
return date_end

@api.depends("product_id")
def _compute_auto_renew(self):
for rec in self:
if rec.product_id.is_contract:
rec.product_uom_qty = rec.product_id.default_qty
contract_start_date_method = rec.product_id.contract_start_date_method
if contract_start_date_method == "manual":
rec.date_start = rec.date_start or fields.Date.today()
rec.date_end = rec._get_date_end()
rec.is_auto_renew = rec.product_id.is_auto_renew
if rec.is_auto_renew:
rec.auto_renew_interval = rec.product_id.auto_renew_interval
rec.auto_renew_rule_type = rec.product_id.auto_renew_rule_type

@api.onchange("date_start", "product_uom_qty")
def onchange_date_start(self):
for rec in self.filtered("product_id.is_contract"):
rec.date_end = rec._get_date_end() if rec.date_start else False

def _get_contract_line_qty(self):
"""Returns the amount that will be placed in new contract lines."""
self.ensure_one()
Expand Down
204 changes: 204 additions & 0 deletions product_contract/models/sale_order_line_contract_mixin.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,204 @@
# Copyright 2024 ACSONE SA/NV
# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl).

from dateutil.relativedelta import relativedelta

from odoo import api, fields, models


class SaleOrderLineContractMixin(models.AbstractModel):
_name = "sale.order.line.contract.mixin"
_description = "Sale Order Line Contract Mixin"

is_contract = fields.Boolean(
string="Is a contract", related="product_id.is_contract"
)
product_id = fields.Many2one("product.product")
partner_id = fields.Many2one("res.partner")
company_id = fields.Many2one("res.company")
product_uom_qty = fields.Float("Quantity")
contract_id = fields.Many2one(comodel_name="contract.contract", string="Contract")
contract_template_id = fields.Many2one(
comodel_name="contract.template",
string="Contract Template",
compute="_compute_contract_template_id",
)
recurrence_number = fields.Integer(
compute="_compute_product_contract_data",
precompute=True,
store=True,
readonly=False,
)
recurring_rule_type = fields.Selection(
[
("daily", "Day(s)"),
("weekly", "Week(s)"),
("monthly", "Month(s)"),
("monthlylastday", "Month(s) last day"),
("quarterly", "Quarter(s)"),
("semesterly", "Semester(s)"),
("yearly", "Year(s)"),
],
default="monthly",
string="Recurrence",
help="Specify Interval for automatic invoice generation.",
compute="_compute_product_contract_data",
precompute=True,
store=True,
readonly=False,
)
recurring_invoicing_type = fields.Selection(
[("pre-paid", "Pre-paid"), ("post-paid", "Post-paid")],
default="pre-paid",
string="Invoicing type",
help=(
"Specify if the invoice must be generated at the beginning "
"(pre-paid) or end (post-paid) of the period."
),
compute="_compute_product_contract_data",
precompute=True,
store=True,
readonly=False,
)
date_start = fields.Date(
compute="_compute_contract_line_date_start",
store=True,
readonly=False,
)
date_end = fields.Date(
compute="_compute_contract_line_date_end",
store=True,
readonly=False,
)
contract_line_id = fields.Many2one(
comodel_name="contract.line",
string="Contract Line to replace",
required=False,
)
is_auto_renew = fields.Boolean(
string="Auto Renew",
compute="_compute_product_contract_data",
precompute=True,
default=False,
store=True,
readonly=False,
)
auto_renew_interval = fields.Integer(
default=1,
string="Renew Every",
compute="_compute_product_contract_data",
precompute=True,
store=True,
readonly=False,
help="Renew every (Days/Week/Month/Year)",
)
auto_renew_rule_type = fields.Selection(
[
("daily", "Day(s)"),
("weekly", "Week(s)"),
("monthly", "Month(s)"),
("yearly", "Year(s)"),
],
default="yearly",
compute="_compute_product_contract_data",
precompute=True,
store=True,
readonly=False,
string="Renewal type",
help="Specify Interval for automatic renewal.",
)
contract_start_date_method = fields.Selection(
[
("manual", "Manual"),
("start_this", "Start of current period"),
("end_this", "End of current period"),
("start_next", "Start of next period"),
("end_next", "End of next period"),
],
"Start Date Method",
default="manual",
help="""This field allows to define how the start date of the contract will
be calculated:
- Manual: The start date will be selected by the user, by default will be the
date of sale confirmation.
- Start of current period: The start date will be the first day of the actual
period selected on 'Invoicing Every' field. Example: If we are on 2024/08/27
and the period selected is 'Year(s)' the start date will be 2024/01/01.
- End of current period: The start date will be the last day of the actual
period selected on 'Invoicing Every' field. Example: If we are on 2024/08/27
and the period selected is 'Year(s)' the start date will be 2024/12/31.
- Start of next period: The start date will be the first day of the next
period selected on 'Invoicing Every' field. Example: If we are on 2024/08/27
and the period selected is 'Year(s)' the start date will be 2025/01/01.
- End of next period: The start date will be the last day of the actual
period selected on 'Invoicing Every' field. Example: If we are on 2024/08/27
and the period selected is 'Year(s)' the start date will be 2025/12/31.
""",
compute="_compute_product_contract_data",
precompute=True,
store=True,
readonly=False,
)

@api.depends("product_id", "company_id")
def _compute_contract_template_id(self):
for rec in self:
rec.contract_template_id = rec.product_id.with_company(
rec.company_id
).property_contract_template_id

@api.depends("product_id")
def _compute_product_contract_data(self):
for rec in self:
vals = {
"recurrence_number": 0,
"recurring_rule_type": False,
"recurring_invoicing_type": False,
"is_auto_renew": False,
"auto_renew_interval": False,
"auto_renew_rule_type": False,
"contract_start_date_method": False,
}
if rec.product_id.is_contract:
p = rec.product_id
vals = {
"recurrence_number": p.default_qty,
"recurring_rule_type": p.recurring_rule_type,
"recurring_invoicing_type": p.recurring_invoicing_type,
"is_auto_renew": p.is_auto_renew,
"auto_renew_interval": p.auto_renew_interval,
"auto_renew_rule_type": p.auto_renew_rule_type,
"contract_start_date_method": p.contract_start_date_method,
}
rec.update(vals)

@api.depends("contract_start_date_method")
def _compute_contract_line_date_start(self):
for rec in self:
if rec.contract_start_date_method == "manual":
rec.date_start = rec.date_start or fields.Date.today()

@api.depends("date_start", "recurring_rule_type", "recurrence_number")
def _compute_contract_line_date_end(self):
for rec in self:
rec.date_end = rec._get_date_end() if rec.date_start else False

def _get_auto_renew_rule_type(self):
"""monthly last day don't make sense for auto_renew_rule_type"""
self.ensure_one()
if self.recurring_rule_type == "monthlylastday":
return "monthly"
return self.recurring_rule_type

def _get_date_end(self):
self.ensure_one()
contract_line_model = self.env["contract.line"]
date_end = (
self.date_start
+ contract_line_model.get_relative_delta(
self._get_auto_renew_rule_type(), self.recurrence_number
)
- relativedelta(days=1)
)
return date_end
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,8 @@ export class ProductContractConfiguratorController extends formView.Controller {
await super.onRecordSaved(...arguments);
const {
product_uom_qty,
recurrence_number,
recurring_rule_type,
contract_id,
date_start,
date_end,
Expand All @@ -27,6 +29,8 @@ export class ProductContractConfiguratorController extends formView.Controller {
infos: {
productContractConfiguration: {
product_uom_qty,
recurrence_number,
recurring_rule_type,
contract_id,
date_start,
date_end,
Expand Down
4 changes: 4 additions & 0 deletions product_contract/static/src/js/sale_product_field.esm.js
Original file line number Diff line number Diff line change
Expand Up @@ -24,9 +24,13 @@ patch(SaleOrderLineProductField.prototype, {

async _openContractConfigurator(isNew = false) {
const actionContext = {
active_model: this.props.record.resModel,
active_id: this.props.record.resId,
default_product_id: this.props.record.data.product_id[0],
default_partner_id: this.props.record.model.root.data.partner_id[0],
default_company_id: this.props.record.model.root.data.company_id[0],
default_recurrence_number: this.props.record.data.recurrence_number,
default_recurring_rule_type: this.props.record.data.recurring_rule_type,
default_product_uom_qty: this.props.record.data.product_uom_qty,
default_contract_id: this.props.record.data.contract_id[0],
default_date_start: this.props.record.data.date_start,
Expand Down
Loading

0 comments on commit 0bcce04

Please sign in to comment.