Skip to content

Commit

Permalink
[IMP] product_contract: Add posibility to compute date_start of line …
Browse files Browse the repository at this point in the history
…using confirmation date_start

With these changes, we allow the contract line start date to be computed
using the order confirmation date. When the product is configured with
any of the options set in contract_start_date_method other than manual,
the start date will be calculated based on the established date and the
selected period.

Additionally, we can force the month in which we will work in case the
frequency is yearly, quarterly, or semesterly.

Is not added support for daily, weekly or monthlylastday in this commit.
  • Loading branch information
CarlosRoca13 committed Aug 29, 2024
1 parent 88d2cb9 commit f96a46e
Show file tree
Hide file tree
Showing 10 changed files with 200 additions and 27 deletions.
7 changes: 7 additions & 0 deletions product_contract/README.rst
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,12 @@ To use this module, you need to:
product
3. Define default recurrence rules

Known issues / Roadmap
======================

Is not added support for dates computed from confirmation date of sale
order for recurrent types daily, weekly or monthlylastday

Bug Tracker
===========

Expand Down Expand Up @@ -80,6 +86,7 @@ Contributors

- Ernesto Tejeda
- Pedro M. Baeza
- Carlos Roca

- David Jaen <[email protected]>

Expand Down
65 changes: 65 additions & 0 deletions product_contract/models/product_template.py
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,71 @@ class ProductTemplate(models.Model):
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.
""",
)
force_month_yearly = fields.Selection(
[
("1", "January"),
("2", "February"),
("3", "March"),
("4", "April"),
("5", "May"),
("6", "June"),
("7", "July"),
("8", "August"),
("9", "September"),
("10", "October"),
("11", "November"),
("12", "December"),
],
"Force Month",
)
force_month_quarterly = fields.Selection(
[
("1", "1"),
("2", "2"),
("3", "3"),
],
"Force Month",
)
force_month_semesterly = fields.Selection(
[
("1", "1"),
("2", "2"),
("3", "3"),
("4", "4"),
("5", "5"),
("6", "6"),
],
"Force Month",
)

def write(self, vals):
if "is_contract" in vals and vals["is_contract"] is False:
Expand Down
1 change: 1 addition & 0 deletions product_contract/models/sale_order.py
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,7 @@ def action_create_contract(self):
line_to_create_contract = rec.order_line.filtered(
lambda r: not r.contract_id and r.product_id.is_contract
)
line_to_create_contract._set_contract_line_start_date()
line_to_update_contract = rec.order_line.filtered(
lambda r: r.contract_id
and r.product_id.is_contract
Expand Down
84 changes: 74 additions & 10 deletions product_contract/models/sale_order_line.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,13 @@
from odoo import _, api, fields, models
from odoo.exceptions import ValidationError

MONTH_NB_MAPPING = {
"monthly": 1,
"quarterly": 3,
"semesterly": 6,
"yearly": 12,
}


class SaleOrderLine(models.Model):
_inherit = "sale.order.line"
Expand Down Expand Up @@ -81,6 +88,9 @@ class SaleOrderLine(models.Model):
string="Renewal type",
help="Specify Interval for automatic renewal.",
)
contract_start_date_method = fields.Selection(
related="product_id.contract_start_date_method"
)

@api.constrains("contract_id")
def _check_contact_is_not_terminated(self):
Expand Down Expand Up @@ -109,15 +119,18 @@ def _get_auto_renew_rule_type(self):

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(),
int(self.product_uom_qty),
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)
)
- relativedelta(days=1)
)
return date_end

@api.depends("product_id")
Expand All @@ -127,8 +140,9 @@ def _compute_auto_renew(self):
rec.product_uom_qty = rec.product_id.default_qty
rec.recurring_rule_type = rec.product_id.recurring_rule_type
rec.recurring_invoicing_type = rec.product_id.recurring_invoicing_type
rec.date_start = rec.date_start or fields.Date.today()

contract_start_date_method = self.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:
Expand Down Expand Up @@ -269,3 +283,53 @@ def _compute_qty_to_invoice(self):
res = super()._compute_qty_to_invoice()
self.filtered("product_id.is_contract").update({"qty_to_invoice": 0.0})
return res

def _set_contract_line_start_date(self):
"""Set date start of lines using it's method and the confirmation date."""
for line in self:
if (
line.contract_start_date_method == "manual"
or line.recurring_rule_type in ["daily", "weekly", "monthlylastday"]
):
continue
is_end = "end_" in line.contract_start_date_method
today = fields.Date.today()
month_period = month = today.month
month_nb = MONTH_NB_MAPPING[line.recurring_rule_type]
# The period number is started by 0 to be able calculate the month
period_number = (month - 1) // month_nb
if line.recurring_rule_type == "yearly":
month_period = 1
elif line.recurring_rule_type != "monthly":
# Checking quarterly and semesterly
month_period = period_number * month_nb + 1
forced_month = 0
if line.recurring_rule_type != "monthly":
forced_value = int(
line.product_id["force_month_%s" % line.recurring_rule_type]
)
if forced_value:
# When the selected period is yearly, the period_number field is
# 0, so forced_month will take the value of the forced month set
# on product.
forced_month = month_nb * period_number + forced_value
# If forced_month is set, use it, but if it isn't use the month_period
start_date = today + relativedelta(
day=1, month=forced_month or month_period
)
if is_end:
increment = month_nb if not forced_month else 0
start_date = (
start_date
+ relativedelta(months=1 + increment)
- relativedelta(days=1)
)
if "_next" in line.contract_start_date_method and start_date <= today:
start_date = start_date + relativedelta(months=month_nb)
if is_end:
start_date = (
start_date
+ relativedelta(day=1, months=1)
- relativedelta(days=1)
)
line.date_start = start_date
1 change: 1 addition & 0 deletions product_contract/readme/CONTRIBUTORS.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,4 +3,5 @@
- [Tecnativa](https://www.tecnativa.com):
- Ernesto Tejeda
- Pedro M. Baeza
- Carlos Roca
- David Jaen \<<[email protected]>\>
27 changes: 17 additions & 10 deletions product_contract/static/description/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -380,11 +380,12 @@ <h1 class="title">Recurring - Product Contract</h1>
<div class="contents local topic" id="contents">
<ul class="simple">
<li><a class="reference internal" href="#usage" id="toc-entry-1">Usage</a></li>
<li><a class="reference internal" href="#bug-tracker" id="toc-entry-2">Bug Tracker</a></li>
<li><a class="reference internal" href="#credits" id="toc-entry-3">Credits</a><ul>
<li><a class="reference internal" href="#authors" id="toc-entry-4">Authors</a></li>
<li><a class="reference internal" href="#contributors" id="toc-entry-5">Contributors</a></li>
<li><a class="reference internal" href="#maintainers" id="toc-entry-6">Maintainers</a></li>
<li><a class="reference internal" href="#known-issues-roadmap" id="toc-entry-2">Known issues / Roadmap</a></li>
<li><a class="reference internal" href="#bug-tracker" id="toc-entry-3">Bug Tracker</a></li>
<li><a class="reference internal" href="#credits" id="toc-entry-4">Credits</a><ul>
<li><a class="reference internal" href="#authors" id="toc-entry-5">Authors</a></li>
<li><a class="reference internal" href="#contributors" id="toc-entry-6">Contributors</a></li>
<li><a class="reference internal" href="#maintainers" id="toc-entry-7">Maintainers</a></li>
</ul>
</li>
</ul>
Expand All @@ -399,38 +400,44 @@ <h1><a class="toc-backref" href="#toc-entry-1">Usage</a></h1>
<li>Define default recurrence rules</li>
</ol>
</div>
<div class="section" id="known-issues-roadmap">
<h1><a class="toc-backref" href="#toc-entry-2">Known issues / Roadmap</a></h1>
<p>Is not added support for dates computed from confirmation date of sale
order for recurrent types daily, weekly or monthlylastday</p>
</div>
<div class="section" id="bug-tracker">
<h1><a class="toc-backref" href="#toc-entry-2">Bug Tracker</a></h1>
<h1><a class="toc-backref" href="#toc-entry-3">Bug Tracker</a></h1>
<p>Bugs are tracked on <a class="reference external" href="https://github.com/OCA/contract/issues">GitHub Issues</a>.
In case of trouble, please check there if your issue has already been reported.
If you spotted it first, help us to smash it by providing a detailed and welcomed
<a class="reference external" href="https://github.com/OCA/contract/issues/new?body=module:%20product_contract%0Aversion:%2017.0%0A%0A**Steps%20to%20reproduce**%0A-%20...%0A%0A**Current%20behavior**%0A%0A**Expected%20behavior**">feedback</a>.</p>
<p>Do not contact contributors directly about support or help with technical issues.</p>
</div>
<div class="section" id="credits">
<h1><a class="toc-backref" href="#toc-entry-3">Credits</a></h1>
<h1><a class="toc-backref" href="#toc-entry-4">Credits</a></h1>
<div class="section" id="authors">
<h2><a class="toc-backref" href="#toc-entry-4">Authors</a></h2>
<h2><a class="toc-backref" href="#toc-entry-5">Authors</a></h2>
<ul class="simple">
<li>LasLabs</li>
<li>ACSONE SA/NV</li>
</ul>
</div>
<div class="section" id="contributors">
<h2><a class="toc-backref" href="#toc-entry-5">Contributors</a></h2>
<h2><a class="toc-backref" href="#toc-entry-6">Contributors</a></h2>
<ul class="simple">
<li>Ted Salmon &lt;<a class="reference external" href="mailto:tsalmon&#64;laslabs.com">tsalmon&#64;laslabs.com</a>&gt;</li>
<li>Souheil Bejaoui &lt;<a class="reference external" href="mailto:souheil.bejaoui&#64;acsone.eu">souheil.bejaoui&#64;acsone.eu</a>&gt;</li>
<li><a class="reference external" href="https://www.tecnativa.com">Tecnativa</a>:<ul>
<li>Ernesto Tejeda</li>
<li>Pedro M. Baeza</li>
<li>Carlos Roca</li>
</ul>
</li>
<li>David Jaen &lt;<a class="reference external" href="mailto:david.jaen.revert&#64;gmail.com">david.jaen.revert&#64;gmail.com</a>&gt;</li>
</ul>
</div>
<div class="section" id="maintainers">
<h2><a class="toc-backref" href="#toc-entry-6">Maintainers</a></h2>
<h2><a class="toc-backref" href="#toc-entry-7">Maintainers</a></h2>
<p>This module is maintained by the OCA.</p>
<a class="reference external image-reference" href="https://odoo-community.org">
<img alt="Odoo Community Association" src="https://odoo-community.org/logo.png" />
Expand Down
13 changes: 13 additions & 0 deletions product_contract/views/product_template.xml
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,19 @@
</group>
<group>
<field name="is_auto_renew" />
<field name="contract_start_date_method" required="True" />
<field
name="force_month_yearly"
invisible="contract_start_date_method == 'manual' or recurring_rule_type != 'yearly'"
/>
<field
name="force_month_quarterly"
invisible="contract_start_date_method == 'manual' or recurring_rule_type != 'quarterly'"
/>
<field
name="force_month_semesterly"
invisible="contract_start_date_method == 'manual' or recurring_rule_type != 'semesterly'"
/>
</group>
<group>
<group invisible="is_auto_renew == False">
Expand Down
12 changes: 8 additions & 4 deletions product_contract/views/sale_order.xml
Original file line number Diff line number Diff line change
Expand Up @@ -71,7 +71,7 @@
<field name="date_start" required="is_contract" />
</group>
<group invisible="not is_contract">
<field name="date_end" required="is_contract" />
<field name="date_end" />
</group>
<group invisible="not is_contract">
<field name="is_auto_renew" />
Expand Down Expand Up @@ -114,8 +114,13 @@
/>
<field name="recurring_rule_type" optional="hide" />
<field name="recurring_invoicing_type" optional="hide" />
<field name="date_start" optional="hide" required="is_contract" />
<field name="date_end" required="is_contract" optional="hide" />
<field name="contract_start_date_method" column_invisible="1" />
<field
name="date_start"
optional="hide"
required="is_contract and contract_start_date_method == 'manual'"
/>
<field name="date_end" optional="hide" />
<field name="is_auto_renew" optional="hide" />
<field
name="auto_renew_interval"
Expand All @@ -131,7 +136,6 @@
required="is_auto_renew"
optional="hide"
/>
<field name="date_end" column_invisible="parent.is_contract == False" />
</xpath>
</field>
</record>
Expand Down
3 changes: 3 additions & 0 deletions product_contract/wizards/product_contract_configurator.py
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,9 @@ class ProductContractConfigurator(models.TransientModel):
string="Renewal type",
help="Specify Interval for automatic renewal.",
)
contract_start_date_method = fields.Selection(
related="product_id.contract_start_date_method"
)

@api.depends("product_id", "company_id")
def _compute_contract_template_id(self):
Expand Down
14 changes: 11 additions & 3 deletions product_contract/wizards/product_contract_configurator_views.xml
Original file line number Diff line number Diff line change
Expand Up @@ -14,12 +14,20 @@
<separator colspan="4" string="Recurrence Invoicing" />
<group>
<field name="recurring_rule_type" />
<field name="date_start" required="1" />
<field name="is_auto_renew" />
<field name="contract_start_date_method" invisible="1" />
<field
name="date_start"
required="contract_start_date_method == 'manual'"
invisible="contract_start_date_method != 'manual'"
/>
<field name="is_auto_renew" />
</group>
<group>
<field name="recurring_invoicing_type" />
<field name="date_end" required="1" />
<field
name="date_end"
invisible="contract_start_date_method != 'manual'"
/>
<label
for="auto_renew_interval"
invisible="not is_auto_renew"
Expand Down

0 comments on commit f96a46e

Please sign in to comment.