See it in action
A deep discount blocks confirmation, the manager approves, and only then does the order go through.
Deep discounts should be a decision, not an accident
A few points of discount given away too freely is profit gone for good. Set the limit your reps can approve themselves, and make everything above it pass a manager — without slowing down normal deals. Approval can now be triggered three ways: a line discount over your threshold, the order's overall gross margin falling below your floor, or any line priced below the product's cost.
Over-discount, under margin, or below cost — confirmation stops
A clear Approval Pending state, manager Approve / Refuse buttons, and the whole decision on the chatter — whichever rule triggered it.
Everything you need to govern discounts and margins
Your threshold
Set the maximum self-service discount as a simple percentage, per company.
Margin-floor approval
Set a minimum overall gross margin per company. Fall below it and the order needs sales-manager sign-off, even if no single line looks like a "deep discount".
Below-cost detection
Optionally require approval the moment any line is priced under the product's cost — the fastest way to lose money on a deal.
Currency & UoM-aware margin
Margin is computed in the company currency from each product's standard cost, converting both currency and unit of measure so mixed-currency, mixed-unit orders are compared correctly.
Hard block
Quotations above the threshold, under the margin floor, or below cost cannot be confirmed until a manager approves — not just a warning.
Request approval
Salespeople request approval in one click; a to-do lands with the team leader, naming the exact reason (discount, margin or below-cost).
Approve or refuse
Managers approve or refuse from the order, with clear ribbons showing the state.
Self-clearing
Fix the lines so the order is back under the discount threshold and above the margin floor, and the block lifts automatically.
Manager-only
Only Sales Managers can approve — reps can't wave through their own over-discounted or below-margin orders, even by direct write.
Audit trail
Every request, approval and refusal is logged on the order's chatter, with the reason attached.
Highest-line logic
Uses the deepest line discount, so one aggressive line still triggers review.
Everywhere
The check sits on the confirm action, so it holds in the back office and the portal alike.
Up and running in three steps
Set your limits
Discount threshold, margin floor, below-cost block — all in Settings.
Reps sell
Normal deals flow through.
Over the line?
Deep discount, thin margin or below cost — confirmation waits for approval.
Manager decides
Approve or refuse, logged.
Real screens, real data
Every screenshot below is the actual app running on Odoo 19 — no mockups.
Set your limits
Under Sales > Pricing: the maximum discount reps can give on their own, the minimum gross margin, and whether below-cost lines are blocked.
Approved and cleared to confirm
Once a manager approves, the order shows Discount Approved and confirms like any other.
Keep your margins where you set them
Install in seconds. No Enterprise, no external libraries, no lock-in. Works on Odoo 19 Community and Enterprise.
Full manual included. The app ships with a complete in-app documentation page (Apps > this module > Documentation) covering the threshold, the workflow and FAQs. Questions? bambooforge.labs@gmail.com
BambooForge Discount Approval
Stop margin erosion from over-discounting. Set the maximum discount your salespeople can give on their own — anything above it must be approved by a sales manager before the order can be confirmed.
This page is the complete manual. Follow it top to bottom and you can install, set your threshold and run the workflow without contacting support.
Overview
- You set a discount threshold (a percentage) per company.
- When a quotation has a line discount above that threshold, it cannot be confirmed until a Sales Manager approves it.
- Salespeople request approval; managers approve or refuse, with the whole decision logged on the order's chatter.
- Lower the discount back under the threshold and the order confirms normally — no approval needed.
Requirements
- Odoo 17.0 with Sales (sale_management). Installing this app pulls it in automatically.
No Enterprise modules and no external Python libraries are required.
Installation
- Copy bambooforge_discount_approval into your addons path.
- Update the apps list (Apps > Update Apps List).
- Search for Discount Approval and click Install.
Setting the threshold
Go to Settings > Sales > Pricing > Discount Approval and set Needs approval above to the maximum self-service discount (for example 15%). The threshold is per company.
The workflow
- A salesperson builds a quotation. If any line's discount exceeds the threshold, an Approval Pending state applies and confirming is blocked.
- The salesperson clicks Request Discount Approval — a to-do activity is raised for the team leader and the order shows To Approve.
- A Sales Manager opens the order and clicks Approve Discount (or Refuse Discount). Both are logged on the chatter.
- Once approved, the order confirms normally. If refused, it stays blocked until the discount is reduced or re-approved.
If the discount is lowered below the threshold at any point, the block clears automatically.
Frequently asked questions
- Who can approve?
- Any user in the Sales / Administrator (Sales Manager) group. Salespeople cannot approve their own discounts.
- Does it look at the order's overall discount or each line?
- It uses the highest line discount on the order, so a single deep-discounted line still triggers approval.
- Can I set different thresholds per company?
- Yes — the threshold is stored per company.
- What if a manager builds the quotation?
- A manager can approve and confirm in one go, since they hold the approval right.
- Does it block other ways of confirming?
- Confirmation is blocked wherever it runs, including the portal and back-office, because the check is on the confirm action itself.
Support
Questions or ideas: bambooforge.labs@gmail.com.
Troubleshooting
Order stuck at "To Approve" with no one to approve it
- Cause
- Requesting approval schedules a to-do activity for the sales team leader (team_id.user_id) or, failing that, the salesperson — but only a user in the Sales / Administrator group (sales_team.group_sale_manager) can actually approve. If the team has no leader, or the leader is not a Sales Manager, the activity lands on someone who cannot press Approve Discount.
- Diagnosis
- Open the order: the Approval Pending ribbon shows and Discount Approval reads To Approve. Check the activity assignee against the members of the Sales / Administrator group.
- Resolution
- Have any Sales Manager open the order and click Approve Discount (or Refuse Discount) — the buttons are visible only to that group. Optionally set the team's Team Leader to a Sales Manager so future requests route to the right person.
"Only a Sales Manager can approve or refuse a discount"
- Cause
- The Approve Discount / Refuse Discount buttons are visible only to the Sales / Administrator group, and both the action and the underlying write are guarded — a salesperson (or an RPC call) attempting to set the state to approved or refused is rejected with this error.
- Diagnosis
- Confirm the acting user's groups under Settings > Users. Salespeople hold Sales / User only.
- Resolution
- Grant the approver the Sales / Administrator group, or route the approval to an existing Sales Manager. This is by design: it prevents self-approval by a salesperson.
Confirmation blocked: "needs sales-manager approval"
- Cause
- action_confirm refuses to confirm while approval_required is set and the order is not yet approved. This fires on every confirm path (back office and portal), listing the trigger in the Approval Reason.
- Resolution
- Either reduce what triggered it (bring the top line discount back under the threshold, or lift the margin), or click Request Discount Approval and have a Sales Manager approve. Reducing the discount below the threshold clears the block automatically.
Discount is over the limit but no approval is requested
- Cause
- Approval keys off the highest single line discount versus the per-company Discount Approval Threshold. An order-level discount spread across lines, a threshold set too high, or a threshold of 0 (which approves everything above 0%) can all leave approval_required unset.
- Diagnosis
- In developer mode inspect max_line_discount, discount_approval_threshold and approval_required on the order. Verify the threshold under Settings > Sales > Pricing for the order's company.
- Resolution
- Set the threshold on the correct company (it is per company) and apply discounts on the lines so max_line_discount reflects them.
Margin floor or below-cost check never triggers
- Cause
- Gross margin is computed from each product's cost (standard price) in the company currency. When no line carries a cost, the order is treated as full (100%) margin and gives no signal, so neither the Minimum Gross Margin floor nor Block Below-Cost Lines can fire. A Minimum Gross Margin left at 0 also disables the floor check entirely.
- Diagnosis
- Check bf_margin_percent and bf_below_cost on the order, and confirm the products have a non-zero Cost. Confirm Minimum Gross Margin and Block Below-Cost Lines are set for the company.
- Resolution
- Maintain product costs, set the margin floor above 0, and enable Block Below-Cost Lines if below-cost deals should require approval.
Approval clears itself after the order is edited
- Cause
- Changing the order lines revisits the decision: if the edit removes the trigger, the state falls back to No Approval Needed; if an already-approved order still needs approval, it drops back to To Approve so it is reviewed again.
- Resolution
- This is intended — margin and discount are re-evaluated on every line change. Re-request and re-approve after material edits.
Upgrading & version compatibility
This build targets Odoo 19.0. Each Odoo major series (17.0, 18.0, 19.0) has its own dedicated build of this module — always install the build that matches your Odoo version. Mixing a build with a different Odoo series is not supported.
Patch upgrades (same series, e.g. 19.0.1.0.0 → later)
Back up your database and filestore first.
Replace the module folder with the newer build.
Restart Odoo with the module updated:
./odoo-bin -c your.conf -u bambooforge_discount_approval -d your_db
Odoo applies any schema/data changes automatically. Your existing records and configuration are preserved.
Cross-version migration (e.g. Odoo 17 → 18)
Upgrading Odoo itself is a database migration handled by Odoo's standard upgrade tooling. When you migrate the database to the next Odoo series, install the matching build of this module for that series. Data created by this module carries over with the database migration.
Uninstallation
You can remove this module at any time from Apps → (this module) → Uninstall, or from the command line. Uninstalling is clean and reversible by reinstalling — but note what is and is not deleted.
What is removed
- The fields, views and analysis definitions this module added.
- The menus, actions, views and reports this module installed.
What is preserved
- Your standard Odoo data (partners, products, sales, invoices, inventory moves) is not deleted — this module only reads and annotates it; the underlying records stay.
- Attachments and chatter messages on standard records are kept.
As always, take a database backup before uninstalling in production.
Changelog
19.0.1.0.0
Current release for Odoo 19.0. This build includes:
- Require manager approval when an order's gross margin falls below a configurable floor or a line is priced below cost.
- On top of your existing discount-percentage threshold. Stop margin erosion, with a clean request-approve-refuse workflow and a guard against self-approval.
Feature additions and fixes ship as new builds on the Odoo Apps store; this page and the module's version reflect the current published release. Always keep the build matched to your Odoo series (see Upgrading & version compatibility).
Odoo Proprietary License v1.0 This software and associated files (the "Software") may only be used (executed, modified, executed after modifications) if you have purchased a valid license from the authors, typically via Odoo Apps, or if you have received a written agreement from the authors of the Software (see the COPYRIGHT file). You may develop Odoo modules that use the Software as a library (typically by depending on it, importing it and using its resources), but without copying any source code or material from the Software. You may distribute those modules under the license of your choice, provided that this license is compatible with the terms of the Odoo Proprietary License (For example: LGPL, MIT, or proprietary licenses similar to this one). It is forbidden to publish, distribute, sublicense, or sell copies of the Software or modified copies of the Software. The above copyright notice and this permission notice must be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
Please log in to comment on this module