
Subscription Billing & Recurring Invoices
Recurring invoicing on Odoo Community: plans, subscriptions, a nightly billing job, and a record of every period billed so it cannot bill twice.
Available for Odoo 16.0, Odoo 17.0, Odoo 18.0, Odoo 19.0. Technical name bambooforge_subscription.
Subscription Billing & Recurring Invoices
Odoo's subscriptions are Enterprise. Community can raise an invoice, and that is where it stops — so anybody billing monthly on Community is either duplicating a sale order twelve times a year, or running a spreadsheet next to Odoo and remembering.
What it does
Plans — every day, week, month or year, any number of them, billed in advance or in arrears. Three are installed to start with.
Subscriptions — a customer, a plan, the lines to bill, a start date and optionally an end date. Draft, running, paused, closed.
A nightly job that bills whatever has come due, and a button for when you cannot wait for it.
What it is worth — the amount per period, and the same amount folded into a month, so plans of different lengths add up to one figure.
The hard part is not the recurrence
It is that a job which runs twice must not bill the customer twice.
And jobs do run twice. A retry after a timeout. Two crons overlapping because the first was slow. An administrator pressing Bill what is due while the scheduler is halfway through the list.
Nothing here relies on a lock, on crons not overlapping, or on nobody pressing anything.
Every period billed is a row. A unique index on the subscription and the period's start date refuses the second one. If two runs race, one of them fails to insert and rolls back, and the customer gets one invoice.
The row is written in the same transaction as the invoice it belongs to, so there is never a moment when an invoice exists that no period claims — which is the shape of every duplicate-billing bug worth having an opinion about.
The index is created in init() rather than declared as an _sql_constraint, because Odoo 18.0 ignores those.
The nightly job
It commits one subscription at a time. A failure on the fortieth does not undo the thirty-nine before it; the one that failed is written up on its own chatter and tried again tomorrow.
Committing as it goes is only safe because of the index: a re-run cannot bill a period that already has a row, so there is nothing to lose by having kept the earlier ones.
What it refuses
Deleting a billed period, in any group. It is the record that stops a customer being invoiced twice for the same month, and a tidy-up that removes one puts the duplicate back on the table.
Posting invoices by default. Off unless you turn it on per subscription: an invoice nobody has looked at is an invoice nobody can defend when the customer telephones.
Catching up for ever. A subscription that has been dormant stops after sixty invoices in one run and says so on its chatter, rather than quietly producing two hundred.
Starting with nothing to bill, ending before it starts, or a plan set to bill less than once per period.
Dates
A period runs from its first day to its last — a monthly period starting on the 1st ends on the 30th or the 31st, and an invoice line that read "1 to 1" would be wrong at both ends. The invoice carries the period's first day when the plan bills in advance, and its last day when it bills in arrears. Each line says which period it pays for, because a customer looking at twelve identical invoices needs to know which month each one is.
Monthly on the 31st does not skip February.
Series
Community 16.0, 17.0, 18.0 and 19.0.
Screens



