Skip to content
Open
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
98 changes: 98 additions & 0 deletions product_contract_recurrence_in_price/README.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,98 @@
====================================
Product Contract Recurrence In Price
====================================

..
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
!! This file is generated by oca-gen-addon-readme !!
!! changes will be overwritten. !!
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
!! source digest: sha256:eb3e8e01c6e7342addae863236c3bce5f99edf1a67d3f35eec1e6f59d91473b0
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!

.. |badge1| image:: https://img.shields.io/badge/maturity-Beta-yellow.png
:target: https://odoo-community.org/page/development-status
:alt: Beta
.. |badge2| image:: https://img.shields.io/badge/licence-AGPL--3-blue.png
:target: http://www.gnu.org/licenses/agpl-3.0-standalone.html
:alt: License: AGPL-3
.. |badge3| image:: https://img.shields.io/badge/github-OCA%2Fcontract-lightgray.png?logo=github
:target: https://github.com/OCA/contract/tree/18.0/product_contract_recurrence_in_price
:alt: OCA/contract
.. |badge4| image:: https://img.shields.io/badge/weblate-Translate%20me-F47D42.png
:target: https://translation.odoo-community.org/projects/contract-18-0/contract-18-0-product_contract_recurrence_in_price
:alt: Translate me on Weblate
.. |badge5| image:: https://img.shields.io/badge/runboat-Try%20me-875A7B.png
:target: https://runboat.odoo-community.org/builds?repo=OCA/contract&target_branch=18.0
:alt: Try me on Runboat

|badge1| |badge2| |badge3| |badge4| |badge5|

This module extends the functionality of ``product_contract`` to add an
option to compute the total amounts on "contract" Sale Order lines,
including every invoicing planned.

For instance, a product worth 10€ invoiced every month for one year
would have a total of 120€ (unit_price \* #invoices) instead of 10€
(unit_price \* quantity).

**Table of contents**

.. contents::
:local:

Usage
=====

To use this module, you need to:

- Go to Sales > Products > Products
- In "Product" form, for contract products, select "Include Recurrence
In Price".

This configuration is also available on each individual Sale Order line,
in the contract configurator (see ``product_contract``).

When "Include Recurrence In Price" is set, the amounts (total, subtotal,
tax total) of the Sale Order line are multiplied by the number of times
the line will be invoiced.

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

Bugs are tracked on `GitHub Issues <https://github.com/OCA/contract/issues>`_.
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
`feedback <https://github.com/OCA/contract/issues/new?body=module:%20product_contract_recurrence_in_price%0Aversion:%2018.0%0A%0A**Steps%20to%20reproduce**%0A-%20...%0A%0A**Current%20behavior**%0A%0A**Expected%20behavior**>`_.

Do not contact contributors directly about support or help with technical issues.

Credits
=======

Authors
-------

* ACSONE SA/NV

Contributors
------------

- Quentin Groulard <quentin.groulard@acsone.eu>

Maintainers
-----------

This module is maintained by the OCA.

.. image:: https://odoo-community.org/logo.png
:alt: Odoo Community Association
:target: https://odoo-community.org

OCA, or the Odoo Community Association, is a nonprofit organization whose
mission is to support the collaborative development of Odoo features and
promote its widespread use.

This module is part of the `OCA/contract <https://github.com/OCA/contract/tree/18.0/product_contract_recurrence_in_price>`_ project on GitHub.

You are welcome to contribute. To learn how please visit https://odoo-community.org/page/Contribute.
1 change: 1 addition & 0 deletions product_contract_recurrence_in_price/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
from . import models
24 changes: 24 additions & 0 deletions product_contract_recurrence_in_price/__manifest__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
# Copyright 2025 ACSONE SA/NV
# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl).

{
"name": "Product Contract Recurrence In Price",
"summary": """
Add an option to include the recurrences
in the total of a Sale Order Line.
""",
"version": "18.0.1.0.0",
"category": "Contract Management",
"license": "AGPL-3",
"author": "ACSONE SA/NV,Odoo Community Association (OCA)",
"website": "https://github.com/OCA/contract",
"depends": ["product_contract"],
"data": [
"views/product_template.xml",
"views/sale_order.xml",
"wizards/product_contract_configurator_views.xml",
],
"assets": {
"web.assets_backend": ["product_contract_recurrence_in_price/static/src/js/*"]
},
}
3 changes: 3 additions & 0 deletions product_contract_recurrence_in_price/models/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
from . import product_template
from . import sale_order_line_contract_mixin
from . import sale_order_line
13 changes: 13 additions & 0 deletions product_contract_recurrence_in_price/models/product_template.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
# Copyright 2025 ACSONE SA/NV
# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl).

from odoo import fields, models


class ProductTemplate(models.Model):
_inherit = "product.template"

include_recurrence_in_price = fields.Boolean(
help="The amounts of the Sale Order Line will be multiplied"
"by the number of times the line will be invoiced."
)
34 changes: 34 additions & 0 deletions product_contract_recurrence_in_price/models/sale_order_line.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
# Copyright 2025 ACSONE SA/NV
# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl).

from odoo import api, models


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

def _prepare_base_line_for_taxes_computation(self, **kwargs):
"""
This is the base method for all line amounts computations.
For 'include_recurrence_in_price' lines, we multiply the quantity by
the number of times the line will be invoiced.
"""
self.ensure_one()
if self.include_recurrence_in_price:
return self.env["account.tax"]._prepare_base_line_for_taxes_computation(
self,
**{
"tax_ids": self.tax_id,
"quantity": self.product_uom_qty * self.recurring_invoicing_number,
"partner_id": self.order_id.partner_id,
"currency_id": self.order_id.currency_id
or self.order_id.company_id.currency_id,
"rate": self.order_id.currency_rate,
**kwargs,
},
)
return super()._prepare_base_line_for_taxes_computation(**kwargs)

@api.depends("recurring_invoicing_number", "include_recurrence_in_price")
def _compute_amount(self):
return super()._compute_amount()
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
# Copyright 2025 ACSONE SA/NV
# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl).

from odoo import _, api, fields, models
from odoo.exceptions import ValidationError


class SaleOrderLineContractMixin(models.AbstractModel):
_inherit = "sale.order.line.contract.mixin"

include_recurrence_in_price = fields.Boolean(
help="The amounts of the Sale Order Line will be multiplied"
"by the number of times the line will be invoiced.",
compute="_compute_product_contract_data",
precompute=True,
store=True,
readonly=False,
)
recurring_invoicing_number = fields.Float(
compute="_compute_recurring_invoice_number",
digits=(16, 1),
help="Number of times a line will be invoiced.",
store=True,
readonly=False,
precompute=True,
)

@api.depends("product_id")
def _compute_product_contract_data(self):
res = super()._compute_product_contract_data()
for rec in self:
rec.include_recurrence_in_price = (
rec.product_id.is_contract
and rec.product_id.include_recurrence_in_price
)
return res

@api.depends("date_start", "date_end", "recurring_interval", "recurring_rule_type")
def _compute_recurring_invoice_number(self):
for rec in self:
if (
not rec.date_start
or not rec.date_end
or not rec.recurring_interval
or not rec.recurring_rule_type
):
rec.recurring_invoicing_number = 0
break
days_total = (rec.date_end - rec.date_start).days
if rec.recurring_rule_type == "dayly":
interval_number = days_total
elif rec.recurring_rule_type == "weekly":
interval_number = days_total / 7
elif rec.recurring_rule_type in ("monthly", "monthlylastday"):
interval_number = days_total / (365.25 / 12)
elif rec.recurring_rule_type == "quarterly":
interval_number = days_total / (365.25 / 4)
elif rec.recurring_rule_type == "semesterly":
interval_number = days_total / (365.25 / 2)
elif rec.recurring_rule_type == "yearly":
interval_number = days_total / 365.25
else:
raise ValidationError(_("Invalid recurring_rule_type."))
rec.recurring_invoicing_number = interval_number / rec.recurring_interval
3 changes: 3 additions & 0 deletions product_contract_recurrence_in_price/pyproject.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
[build-system]
requires = ["whool"]
build-backend = "whool.buildapi"
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
- Quentin Groulard \<<quentin.groulard@acsone.eu>\>
5 changes: 5 additions & 0 deletions product_contract_recurrence_in_price/readme/DESCRIPTION.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
This module extends the functionality of ``product_contract`` to add an option to compute
the total amounts on "contract" Sale Order lines, including every invoicing planned.

For instance, a product worth 10€ invoiced every month for one year would have a total
of 120€ (unit_price * #invoices) instead of 10€ (unit_price * quantity).
10 changes: 10 additions & 0 deletions product_contract_recurrence_in_price/readme/USAGE.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
To use this module, you need to:

- Go to Sales > Products > Products
- In "Product" form, for contract products, select "Include Recurrence In Price".

This configuration is also available on each individual Sale Order line, in the contract
configurator (see ``product_contract``).

When "Include Recurrence In Price" is set, the amounts (total, subtotal, tax total) of the
Sale Order line are multiplied by the number of times the line will be invoiced.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading