Customer Credit Limit
A hard credit limit that actually blocks the customer invoice from posting, with a manager override captured in an append-only audit log.
Why this module
Customer Credit Limit
It actually blocks the post
The gate fires inside account.move._post for customer invoices and refunds. Over-limit means a UserError, not a warning banner you can ignore. The standard credit_limit field never stops a post. This one does.
Every override is on the record
Override needs a reason on that specific invoice from a member of the configured group. Each one writes an append-only audit row (user, time, exposure, limit, excess, reason). The log refuses write and unlink, so it cannot be edited away later.
Policy where you want it
One default policy per company sets the limit, the warn-or-block mode, and the override group. Override the limit or the whole policy per partner. Choose whether drafts and confirmed orders count toward exposure. Sensible defaults, no surprises.
Day in the life
A controller stops an over-extended customer at the gate.
A repeat customer already sits near its limit. Sales raises another invoice and clicks post. The gate reads the partner's current exposure fresh inside the post transaction, adds this invoice, sees it crosses the limit, and refuses the post with a message naming the exposure, the limit, and the exact excess. The salesperson cannot push it through. The credit controller reviews, decides the order is acceptable on a one-off basis, records the override reason on that invoice, and posts. An append-only log row captures who approved it, when, against what exposure, and why. The next invoice for that customer hits the limit again from scratch. If sale-order inclusion is on, the same unbilled order value had already pushed the partner's badge to Over on its form, so nobody was surprised. Enforcement still happens at invoice post; the sales order itself is not blocked.
Edge cases
The cases most modules quietly ignore.
In the shipped code today, each one a place where a cheaper module silently does the wrong thing.
When drafts count toward exposure, the very invoice being posted (already counted as a draft) is excluded from the exposure read before its own amount is added once, so it is never counted twice.
A customer refund (out_refund) subtracts from exposure rather than inflating it, both in the live badge and at the post-time gate. Crediting a customer lowers what they owe, and the math reflects that.
If a deployment blanks the override group on a policy, the gate falls back to the accounting manager group instead of letting any invoice writer self-approve. A regression test pins this behaviour so the hole cannot reopen.
Exposure aggregates against the commercial partner, so child contacts and branch addresses draw down the parent company's single limit rather than each receiving a fresh limit of their own.
A zero limit means enforcement is off for that partner, so the gate skips rather than blocking every invoice. Leaving a limit unset never accidentally freezes a customer.
An already-posted move is skipped, so re-running the post pipeline cannot re-fire the gate or write a duplicate override log row.
Exactly one default policy is allowed per company, enforced by a constraint, with an onchange warning that fires early so a rejected save does not throw away the rest of the form input.
What is inside
Built to do the job, end to end.
- Per-company credit policy. One policy per company sets the default limit, the enforcement mode (warn only or block on post), the override group, and whether drafts and confirmed sales orders count toward exposure. A constraint allows a single default policy per company.
- Per-partner override. Any partner can carry its own credit limit and its own policy, overriding the company default. When neither is set, the company default policy applies. Partners with no policy at all are simply not gated.
- Post-time enforcement gate. Hooks the customer invoice and refund post pipeline. Block mode raises with the exposure, limit, and excess spelled out until an override reason is present. Warn mode logs the breach to chatter and lets the post through.
- Append-only override log. Each override writes one row capturing user, timestamp, exposure, limit, move amount, excess, and reason, linked to the exact invoice that bypassed the limit. The model refuses write to all but the comment field and refuses unlink outright.
- Live exposure badge. The partner form shows an OK, Warn, or Over status with the computed exposure. Open AR uses the company-currency residual captured at posting time; opt-in draft invoices and confirmed-order components convert at the order date.
Honest about the edges
What this does not do, so nothing surprises you.
- The gate enforces at customer invoice and refund post time only. It does not block sales order confirmation. Including confirmed orders in exposure raises the partner's status to Over so it shows up early, but the order itself still confirms.
- Open AR exposure uses the company-currency residual stored on each line when it was posted, not a live re-conversion at today's rate. Confirmed sale-order amounts are converted at the order date.
- There is no explicit database row lock. Exposure is read fresh inside the post transaction, but whether two simultaneous over-limit posts are serialized depends on your database isolation level rather than a lock taken in code.
- The override group defaults to the general accounting manager role. For true separation, point it at a dedicated credit-officer group; it ships configurable but not separated by default.
- Draft-invoice and confirmed-sale-order exposure are both opt-in per policy and off by default. Out of the box the gate considers only posted, unpaid AR. Sale-order inclusion also requires the sale module to be installed.
- Vendor bills and manual journal entries are out of scope. The gate covers customer receivables only.
- This module does not add a customer portal credit display. It works inside the accounting backend and the partner form.
odoo customer credit limit, odoo credit limit block invoice, accounts receivable credit control odoo 19 community, hard credit limit enforcement odoo, credit limit manager override audit log, per partner credit limit odoo, credit policy per company odoo, customer exposure receivable odoo, block invoice over credit limit, credit hold odoo community
Languages
Available in 19 languages
The interface ships translated out of the box. Switch language in Odoo and the fields, menus, and messages follow.
Please log in to comment on this module
There are no comments yet!