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
68 changes: 40 additions & 28 deletions subscription_oca/README.rst
Original file line number Diff line number Diff line change
Expand Up @@ -47,9 +47,21 @@ Usage
To make a subscription:

1. Go to *Subscriptions > Configuration > Subscription templates*.
2. Create the templates you consider, choosing the billing frequency:
daily, monthly... and the method of creating the invoice and/or
order.
2. Create the templates you consider, choosing the billing frequency
(daily, monthly...) and how the recurring document is generated. The
invoicing behaviour is configured through three independent options:

- *Create sale order*: when enabled, a confirmed sale order is
generated before the invoice (required for the subscription to
show up in Sales analysis reports); when disabled, the invoice is
created directly and a warning reminds you that it will not appear
there.
- *Invoice status*: leave the generated invoice in *Draft* or post
it automatically (*Posted*).
- *Send invoice by email*: only available for posted invoices; when
enabled, the selected *Invoice Email* template is sent
automatically.

3. Go to *Subscription > Subscriptions*.
4. Create a subscription and indicate the start date. When the
*Subscriptions Management* cron job is executed, the subscription
Expand All @@ -58,9 +70,9 @@ To make a subscription:
the execution date matches the next invoice date. Additionally, you
can manually change the subscription status and create an invoice by
using the *Create Invoice* button. This action creates just an
invoice even if the subscription template has the *Sale Order &
Invoice* option selected, because the *Invoicing mode* option is
triggered through the cron job.
invoice even if the subscription template has *Create sale order*
enabled, because the configured invoicing options are only applied by
the cron job.
5. The cron job will also end the subscription if its end date has been
reached. The end date honours the template recurrence (days, weeks,
months or years) and the interval, instead of always assuming months.
Expand All @@ -74,21 +86,21 @@ To create subscriptions with the sale of a product:

Invoice and delivery addresses:

- Each subscription has an *Invoice address* and a *Delivery address*.
They default to the customer's corresponding addresses and can be
overridden per subscription.
- These addresses are propagated to the recurring invoices (the invoice
is addressed to the invoice address and records the delivery one) and
to the sale orders generated by the subscription.
- The fields are shown when the *Customer Addresses* setting (group
*Display Delivery / Invoice addresses*) is enabled.
- Each subscription has an *Invoice address* and a *Delivery address*.
They default to the customer's corresponding addresses and can be
overridden per subscription.
- These addresses are propagated to the recurring invoices (the invoice
is addressed to the invoice address and records the delivery one) and
to the sale orders generated by the subscription.
- The fields are shown when the *Customer Addresses* setting (group
*Display Delivery / Invoice addresses*) is enabled.

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

- Refactor all the onchanges that have business logic to computed
write-able fields when possible. Keep onchanges only for UI purposes.
- Add tests.
- Refactor all the onchanges that have business logic to computed
write-able fields when possible. Keep onchanges only for UI purposes.
- Add tests.

Bug Tracker
===========
Expand All @@ -112,22 +124,22 @@ Authors
Contributors
------------

- Carlos Martínez <carlos@domatix.com>
- Carolina Ferrer <carolina@domatix.com>
- `Ooops404 <https://www.ooops404.com>`__:
- Carlos Martínez <carlos@domatix.com>
- Carolina Ferrer <carolina@domatix.com>
- `Ooops404 <https://www.ooops404.com>`__:

- Ilyas <irazor147@gmail.com>
- Ilyas <irazor147@gmail.com>

- `Sygel <https://www.sygel.es>`__:
- `Sygel <https://www.sygel.es>`__:

- Harald Panten
- Valentin Vinagre
- Alberto Martínez
- Harald Panten
- Valentin Vinagre
- Alberto Martínez

- Dennis Sluijk <d.sluijk@onestein.nl>
- `IKU Solutions <https://www.iku.solutions>`__:
- Dennis Sluijk <d.sluijk@onestein.nl>
- `IKU Solutions <https://www.iku.solutions>`__:

- Yan Chirino <yan.chirino@iku.solutions>
- Yan Chirino <yan.chirino@iku.solutions>

Maintainers
-----------
Expand Down
2 changes: 1 addition & 1 deletion subscription_oca/__manifest__.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
{
"name": "Subscription management",
"summary": "Generate recurring invoices.",
"version": "19.0.1.1.3",
"version": "19.0.2.0.0",
"development_status": "Beta",
"category": "Subscription Management",
"website": "https://github.com/OCA/contract",
Expand Down
38 changes: 38 additions & 0 deletions subscription_oca/migrations/19.0.2.0.0/post-migration.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
# Copyright 2026 Domatix
# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl).


def migrate(cr, version):
"""Split the legacy ``invoicing_mode`` selection into orthogonal fields.

Old ``invoicing_mode`` values map to the new fields as follows:

=================== ================= ============== ==============
invoicing_mode create_sale_order invoice_state send_invoice
=================== ================= ============== ==============
draft False draft False
invoice False posted False
invoice_send False posted True
sale_and_invoice True posted False
=================== ================= ============== ==============
"""
cr.execute(
"SELECT 1 FROM information_schema.columns "
"WHERE table_name = 'sale_subscription_template' "
"AND column_name = 'invoicing_mode'"
)
if not cr.fetchone():
return
cr.execute(
"""
UPDATE sale_subscription_template
SET create_sale_order = (invoicing_mode = 'sale_and_invoice'),
invoice_state = CASE
WHEN invoicing_mode IN ('invoice', 'invoice_send', 'sale_and_invoice')
THEN 'posted' ELSE 'draft' END,
send_invoice = (invoicing_mode = 'invoice_send')
"""
)
cr.execute(
"ALTER TABLE sale_subscription_template DROP COLUMN IF EXISTS invoicing_mode"
)
45 changes: 21 additions & 24 deletions subscription_oca/models/sale_subscription.py
Original file line number Diff line number Diff line change
Expand Up @@ -414,33 +414,30 @@ def generate_invoice(self):
invoice_number = ""
message_body = ""
msg_static = self.env._("Created invoice with reference")
if self.template_id.invoicing_mode in ["draft", "invoice", "invoice_send"]:
invoice = self.create_invoice()
if self.template_id.invoicing_mode != "draft":
invoice.action_post()
mail_template = self.template_id.invoice_mail_template_id
self.env["account.move.send"]._generate_and_send_invoices(
invoice, mail_template=mail_template, sending_methods=["email"]
)
invoice_number = invoice.name
message_body = (
f"<b>{msg_static}</b> "
f"<a href=# data-oe-model=account.move data-oe-id={invoice.id}>"
f"{invoice_number}"
"</a>"
)

if self.template_id.invoicing_mode == "sale_and_invoice":
template = self.template_id
if template.create_sale_order:
order_id = self.create_sale_order()
order_id.action_confirm()
order_id.action_lock()
new_invoice = order_id._create_invoices()
new_invoice.action_post()
new_invoice.invoice_origin = order_id.name + ", " + self.name
invoice_number = new_invoice.name
message_body = f"<b>{msg_static}</b> \
<a href=# data-oe-model=account.move data-oe-id={new_invoice.id}>\
{invoice_number}</a>"
invoice = order_id._create_invoices()
invoice.invoice_origin = order_id.name + ", " + self.name
else:
invoice = self.create_invoice()
if invoice and template.invoice_state == "posted":
invoice.action_post()
if template.send_invoice:
self.env["account.move.send"]._generate_and_send_invoices(
invoice,
mail_template=template.invoice_mail_template_id,
sending_methods=["email"],
)
invoice_number = invoice.name
message_body = (
f"<b>{msg_static}</b> "
f"<a href=# data-oe-model=account.move data-oe-id={invoice.id}>"
f"{invoice_number}"
"</a>"
)

if not invoice_number:
invoice_number = self.env._("To validate")
Expand Down
22 changes: 14 additions & 8 deletions subscription_oca/models/sale_subscription_template.py
Original file line number Diff line number Diff line change
Expand Up @@ -27,15 +27,21 @@ class SaleSubscriptionTemplate(models.Model):
string="Duration",
default="unlimited",
)
invoicing_mode = fields.Selection(
create_sale_order = fields.Boolean(
string="Create sale order",
help="Generate a confirmed sale order before invoicing. Required for the "
"subscription to appear in Sales analysis reports.",
)
invoice_state = fields.Selection(
selection=[("draft", "Draft"), ("posted", "Posted")],
string="Invoice status",
default="draft",
string="Invoicing mode",
selection=[
("draft", "Draft"),
("invoice", "Invoice"),
("invoice_send", "Invoice & send"),
("sale_and_invoice", "Sale order & Invoice"),
],
required=True,
help="State in which the generated invoice is left.",
)
send_invoice = fields.Boolean(
string="Send invoice by email",
help="Send the invoice by email once posted, using the template below.",
)
code = fields.Char()
recurring_rule_count = fields.Integer(default=1, string="Rule count")
Expand Down
20 changes: 14 additions & 6 deletions subscription_oca/readme/USAGE.md
Original file line number Diff line number Diff line change
@@ -1,9 +1,17 @@
To make a subscription:

1. Go to *Subscriptions \> Configuration \> Subscription templates*.
2. Create the templates you consider, choosing the billing frequency:
daily, monthly... and the method of creating the invoice and/or
order.
2. Create the templates you consider, choosing the billing frequency
(daily, monthly...) and how the recurring document is generated. The
invoicing behaviour is configured through three independent options:
- *Create sale order*: when enabled, a confirmed sale order is
generated before the invoice (required for the subscription to show
up in Sales analysis reports); when disabled, the invoice is created
directly and a warning reminds you that it will not appear there.
- *Invoice status*: leave the generated invoice in *Draft* or post it
automatically (*Posted*).
- *Send invoice by email*: only available for posted invoices; when
enabled, the selected *Invoice Email* template is sent automatically.
3. Go to *Subscription \> Subscriptions*.
4. Create a subscription and indicate the start date. When the
*Subscriptions Management* cron job is executed, the subscription
Expand All @@ -12,9 +20,9 @@ To make a subscription:
the execution date matches the next invoice date. Additionally, you
can manually change the subscription status and create an invoice by
using the *Create Invoice* button. This action creates just an
invoice even if the subscription template has the *Sale Order &
Invoice* option selected, because the *Invoicing mode* option is
triggered through the cron job.
invoice even if the subscription template has *Create sale order*
enabled, because the configured invoicing options are only applied by
the cron job.
5. The cron job will also end the subscription if its end date has been
reached. The end date honours the template recurrence (days, weeks,
months or years) and the interval, instead of always assuming months.
Expand Down
24 changes: 18 additions & 6 deletions subscription_oca/static/description/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -397,9 +397,21 @@ <h2><a class="toc-backref" href="#toc-entry-1">Usage</a></h2>
<p>To make a subscription:</p>
<ol class="arabic simple">
<li>Go to <em>Subscriptions &gt; Configuration &gt; Subscription templates</em>.</li>
<li>Create the templates you consider, choosing the billing frequency:
daily, monthly… and the method of creating the invoice and/or
order.</li>
<li>Create the templates you consider, choosing the billing frequency
(daily, monthly…) and how the recurring document is generated. The
invoicing behaviour is configured through three independent options:<ul>
<li><em>Create sale order</em>: when enabled, a confirmed sale order is
generated before the invoice (required for the subscription to
show up in Sales analysis reports); when disabled, the invoice is
created directly and a warning reminds you that it will not appear
there.</li>
<li><em>Invoice status</em>: leave the generated invoice in <em>Draft</em> or post
it automatically (<em>Posted</em>).</li>
<li><em>Send invoice by email</em>: only available for posted invoices; when
enabled, the selected <em>Invoice Email</em> template is sent
automatically.</li>
</ul>
</li>
<li>Go to <em>Subscription &gt; Subscriptions</em>.</li>
<li>Create a subscription and indicate the start date. When the
<em>Subscriptions Management</em> cron job is executed, the subscription
Expand All @@ -408,9 +420,9 @@ <h2><a class="toc-backref" href="#toc-entry-1">Usage</a></h2>
the execution date matches the next invoice date. Additionally, you
can manually change the subscription status and create an invoice by
using the <em>Create Invoice</em> button. This action creates just an
invoice even if the subscription template has the <em>Sale Order &amp;
Invoice</em> option selected, because the <em>Invoicing mode</em> option is
triggered through the cron job.</li>
invoice even if the subscription template has <em>Create sale order</em>
enabled, because the configured invoicing options are only applied by
the cron job.</li>
<li>The cron job will also end the subscription if its end date has been
reached. The end date honours the template recurrence (days, weeks,
months or years) and the interval, instead of always assuming months.</li>
Expand Down
1 change: 1 addition & 0 deletions subscription_oca/tests/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,3 +4,4 @@
from . import test_subscription_security
from . import test_subscription_recurrence_dates
from . import test_subscription_partner_addresses
from . import test_subscription_invoicing_options
Loading
Loading