Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
64 changes: 32 additions & 32 deletions contract/README.rst
Original file line number Diff line number Diff line change
Expand Up @@ -57,22 +57,22 @@ Usage
2. When creating a contract, fill fields for selecting the invoicing
parameters:

- a journal
- a price list (optional)
- a journal
- a price list (optional)

3. And add the lines to be invoiced with:

- the product with a description, a quantity and a price
- the recurrence parameters: interval (days, weeks, months, months
last day or years), start date, date of next invoice
(automatically computed, can be modified) and end date (optional)
- auto-price, for having a price automatically obtained from the
price list
- #START# - #END# or #INVOICEMONTHNAME# in the description field to
display the start/end date or the start month of the invoiced
period in the invoice line description
- pre-paid (invoice at period start) or post-paid (invoice at start
of next period)
- the product with a description, a quantity and a price
- the recurrence parameters: interval (days, weeks, months, months
last day or years), start date, date of next invoice (automatically
computed, can be modified) and end date (optional)
- auto-price, for having a price automatically obtained from the
price list
- #START# - #END# or #INVOICEMONTHNAME# in the description field to
display the start/end date or the start month of the invoiced
period in the invoice line description
- pre-paid (invoice at period start) or post-paid (invoice at start
of next period)

4. The "Generate Recurring Invoices from Contracts" cron runs daily to
generate the invoices. If you are in debug mode, you can click on the
Expand All @@ -86,7 +86,7 @@ Usage
price list and lines when creating a contract. To use it, just select
the template on the contract and fields will be filled automatically.

- Contracts appear in portal to following users in every contract:
- Contracts appear in portal to following users in every contract:

|image|

Expand All @@ -101,8 +101,8 @@ Usage
Known issues / Roadmap
======================

- Recover states and others functional fields in Contracts.
- Add recurrence flag at template level.
- Recover states and others functional fields in Contracts.
- Add recurrence flag at template level.

Bug Tracker
===========
Expand All @@ -126,33 +126,33 @@ Authors
Contributors
------------

- Angel Moya <angel.moya@domatix.com>
- Angel Moya <angel.moya@domatix.com>

- Dave Lasley <dave@laslabs.com>
- Dave Lasley <dave@laslabs.com>

- Miquel Raïch <miquel.raich@eficent.com>
- Miquel Raïch <miquel.raich@eficent.com>

- Souheil Bejaoui <souheil.bejaoui@acsone.eu>
- Souheil Bejaoui <souheil.bejaoui@acsone.eu>

- Thomas Binsfeld <thomas.binsfeld@acsone.eu>
- Thomas Binsfeld <thomas.binsfeld@acsone.eu>

- Guillaume Vandamme <guillaume.vandamme@acsone.eu>
- Guillaume Vandamme <guillaume.vandamme@acsone.eu>

- Raphaël Reverdy <raphael.reverdy@akretion.com>
- Raphaël Reverdy <raphael.reverdy@akretion.com>

- `Tecnativa <https://www.tecnativa.com>`__:
- `Tecnativa <https://www.tecnativa.com>`__:

- Pedro M. Baeza
- Carlos Dauden
- Vicent Cubells
- Rafael Blasco
- Víctor Martínez
- Pedro M. Baeza
- Carlos Dauden
- Vicent Cubells
- Rafael Blasco
- Víctor Martínez

- Iván Antón <ozono@ozonomultimedia.com>
- Iván Antón <ozono@ozonomultimedia.com>

- `APSL <https://www.apsl.tech>`__:
- `APSL <https://www.apsl.tech>`__:

- Antoni Marroig <amarroig@apsl.net>
- Antoni Marroig <amarroig@apsl.net>

Maintainers
-----------
Expand Down
9 changes: 9 additions & 0 deletions contract/models/contract_line.py
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@ class ContractLine(models.Model):
store=True,
readonly=True,
)
product_id = fields.Many2one(index=True)

@api.model
def _compute_first_recurring_next_date(
Expand Down Expand Up @@ -307,3 +308,11 @@ def _compute_price_unit(self):
line.price_unit = pricelist._get_product_price(product, quantity=1)
else:
line.price_unit = line.specific_price

@api.constrains("is_auto_renew", "auto_renew_interval")
def _check_auto_renew_interval(self):
for rec in self:
if rec.is_auto_renew and not rec.auto_renew_interval:
raise ValidationError(
_("Auto renew interval should be different then 0")
)
4 changes: 2 additions & 2 deletions contract/static/description/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -410,8 +410,8 @@ <h1><a class="toc-backref" href="#toc-entry-2">Usage</a></h1>
<li>And add the lines to be invoiced with:<ul>
<li>the product with a description, a quantity and a price</li>
<li>the recurrence parameters: interval (days, weeks, months, months
last day or years), start date, date of next invoice
(automatically computed, can be modified) and end date (optional)</li>
last day or years), start date, date of next invoice (automatically
computed, can be modified) and end date (optional)</li>
<li>auto-price, for having a price automatically obtained from the
price list</li>
<li>#START# - #END# or #INVOICEMONTHNAME# in the description field to
Expand Down
92 changes: 45 additions & 47 deletions contract_line_successor/README.rst
Original file line number Diff line number Diff line change
Expand Up @@ -37,48 +37,47 @@ Contract Line Successor
Features
--------

- **Successor and Predecessor Management**
- **Successor and Predecessor Management**

- Link contract lines with successor and predecessor lines.
- Plan successors automatically or manually after a stop or
suspension.
- Link contract lines with successor and predecessor lines.
- Plan successors automatically or manually after a stop or
suspension.

- **Contract Line Lifecycle States**
- **Contract Line Lifecycle States**

- Manage contract lines with the following computed states:
- Manage contract lines with the following computed states:

- ``Upcoming``
- ``In-Progress``
- ``To Renew``
- ``Upcoming Close``
- ``Closed``
- ``Canceled``
- ``Upcoming``
- ``In-Progress``
- ``To Renew``
- ``Upcoming Close``
- ``Closed``
- ``Canceled``

- **Lifecycle Operations**
- **Lifecycle Operations**

- Stop a contract line.
- Plan a successor for a contract line.
- Stop and plan a successor in one operation (useful for
suspensions).
- Cancel and un-cancel contract lines.
- Renew contract lines automatically (new line or extension).
- Stop a contract line.
- Plan a successor for a contract line.
- Stop and plan a successor in one operation (useful for suspensions).
- Cancel and un-cancel contract lines.
- Renew contract lines automatically (new line or extension).

- **Auto-Renewal Handling**
- **Auto-Renewal Handling**

- Auto-renewal based on company settings (extend existing line or
create a new one).
- Cron job to automate renewal of eligible contract lines.
- Auto-renewal based on company settings (extend existing line or
create a new one).
- Cron job to automate renewal of eligible contract lines.

- **Data Integrity and Validation**
- **Data Integrity and Validation**

- Prevent invalid successor or predecessor configurations.
- Validate state transitions and date overlaps.
- Ensure clean renewal and cancellation workflows.
- Prevent invalid successor or predecessor configurations.
- Validate state transitions and date overlaps.
- Ensure clean renewal and cancellation workflows.

- **Audit Trail**
- **Audit Trail**

- Automatic posting of chatter messages for lifecycle events like
stops, renewals, suspensions, cancellations, etc.
- Automatic posting of chatter messages for lifecycle events like
stops, renewals, suspensions, cancellations, etc.

**Table of contents**

Expand All @@ -88,19 +87,18 @@ Features
Configuration
=============

- | **Auto-Renewal Strategy**
| In the company settings, define whether renewing a contract line:
- | **Auto-Renewal Strategy**
| In the company settings, define whether renewing a contract line:

- Extends the current line (updates ``date_end``),
- or creates a new successor contract line.
- Extends the current line (updates ``date_end``),
- or creates a new successor contract line.

| Field:
| ``Company > Configuration > Contracts > Create new contract line at renewal``
| Field:
| ``Company > Configuration > Contracts > Create new contract line at renewal``

- | **Scheduled Actions**
| Ensure the scheduled action ``Contract Line: Auto Renew`` is
activated if you want automatic renewal without manual
intervention.
- | **Scheduled Actions**
| Ensure the scheduled action ``Contract Line: Auto Renew`` is
activated if you want automatic renewal without manual intervention.

Usage
=====
Expand All @@ -110,12 +108,12 @@ Usage
2. Once enabled, you will have access to several actions at the contract
line level:

- **Stop** a contract line and optionally **plan a successor**.
- **Handle temporary suspensions** and **resume** the contract line
after the suspension period.
- **Cancel** and **un-cancel** contract lines if necessary.
- **Renew** contract lines either by **extending** the current line
or by **creating a new successor line** automatically.
- **Stop** a contract line and optionally **plan a successor**.
- **Handle temporary suspensions** and **resume** the contract line
after the suspension period.
- **Cancel** and **un-cancel** contract lines if necessary.
- **Renew** contract lines either by **extending** the current line
or by **creating a new successor line** automatically.

Bug Tracker
===========
Expand All @@ -138,7 +136,7 @@ Authors
Contributors
------------

- Souheil Bejaoui souheil.bejaoui@acsone.eu (ACSONE SA/NV)
- Souheil Bejaoui souheil.bejaoui@acsone.eu (ACSONE SA/NV)

Maintainers
-----------
Expand Down
49 changes: 31 additions & 18 deletions contract_line_successor/models/contract_line.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
from datetime import timedelta

from dateutil.relativedelta import relativedelta
from markupsafe import Markup

from odoo import _, api, fields, models
from odoo.exceptions import ValidationError
Expand Down Expand Up @@ -433,11 +434,13 @@ def stop(self, date_end, manual_renew_needed=False, post_message=True):
rec._prepare_value_for_stop(date_end, manual_renew_needed)
)
if post_message:
msg = _(
"""Contract line for <strong>%(product)s</strong>
msg = Markup(
_(
"""Contract line for <strong>%(product)s</strong>
stopped: <br/>
- <strong>End</strong>: %(old_end)s -- %(new_end)s
"""
)
) % {
"product": rec.name,
"old_end": old_date_end,
Expand Down Expand Up @@ -507,13 +510,15 @@ def plan_successor(
rec.successor_contract_line_id = new_line
contract_line |= new_line
if post_message:
msg = _(
"""Contract line for <strong>%(product)s</strong>
msg = Markup(
_(
"""Contract line for <strong>%(product)s</strong>
planned a successor: <br/>
- <strong>Start</strong>: %(new_date_start)s
<br/>
- <strong>End</strong>: %(new_date_end)s
"""
)
) % {
"product": rec.name,
"new_date_start": new_line.date_start,
Expand Down Expand Up @@ -610,13 +615,15 @@ def stop_plan_successor(self, date_start, date_end, is_auto_renew):
is_auto_renew,
post_message=False,
)
msg = _(
"""Contract line for <strong>%(product)s</strong>
msg = Markup(
_(
"""Contract line for <strong>%(product)s</strong>
suspended: <br/>
- <strong>Suspension Start</strong>: %(new_date_start)s
<br/>
- <strong>Suspension End</strong>: %(new_date_end)s
"""
)
) % {
"product": rec.name,
"new_date_start": date_start,
Expand All @@ -630,11 +637,13 @@ def cancel(self):
raise ValidationError(_("Cancel not allowed for this line"))
for contract in self.mapped("contract_id"):
lines = self.filtered(lambda line, c=contract: line.contract_id == c)
msg = _(
"Contract line canceled: %s",
"<br/>- ".join(
[f"<strong>{name}</strong>" for name in lines.mapped("name")]
),
msg = Markup(
_(
"Contract line canceled: %s",
"<br/>- ".join(
[f"<strong>{name}</strong>" for name in lines.mapped("name")]
),
)
)
contract.message_post(body=msg)
self.mapped("predecessor_contract_line_id").write(
Expand All @@ -647,11 +656,13 @@ def uncancel(self, recurring_next_date):
raise ValidationError(_("Un-cancel not allowed for this line"))
for contract in self.mapped("contract_id"):
lines = self.filtered(lambda line, c=contract: line.contract_id == c)
msg = _(
"Contract line Un-canceled: %s",
"<br/>- ".join(
[f"<strong>{name}</strong>" for name in lines.mapped("name")]
),
msg = Markup(
_(
"Contract line Un-canceled: %s",
"<br/>- ".join(
[f"<strong>{name}</strong>" for name in lines.mapped("name")]
),
)
)
contract.message_post(body=msg)
for rec in self:
Expand Down Expand Up @@ -777,13 +788,15 @@ def renew(self):
else:
new_line = rec._renew_extend_line(date_end)
res |= new_line
msg = _(
"""Contract line for <strong>%(product)s</strong>
msg = Markup(
_(
"""Contract line for <strong>%(product)s</strong>
renewed: <br/>
- <strong>Start</strong>: %(new_date_start)s
<br/>
- <strong>End</strong>: %(new_date_end)s
"""
)
) % {
"product": rec.name,
"new_date_start": date_start,
Expand Down
Loading
Loading