Consignment Agreements¶
Who this is for
The operator (admin) and anyone setting up the Reyder ↔ Axis business relationship. Once an agreement is active and running, sales ops and warehouse don't need to touch it.
What you'll accomplish: create the single agreement that lets Reyder see, sell, and earn commission on Axis Mobile's devices.
What a consignment agreement actually is¶
A formal record in the system that says:
"Axis Mobile owns these devices. They allow Reyder Enterprises to sell them and keep a commission. The commission is X."
Without an active agreement, Reyder can't see Axis's devices, can't allocate them to sales orders, and can't invoice customers for them. The agreement is the switch that connects the two companies.
In practice there's usually just one agreement at a time (Reyder ↔ Axis). You set it up once during onboarding, then leave it alone until the commission terms change.
Step 1 — Open the agreements list¶
Go to Inventory → Device Inventory → Agreements.

You'll see the active agreement (and any historical ones, filtered out by default). Click New to create one, or click an existing row to edit.
Step 2 — Fill in the form¶

Required¶
| Field | What to enter |
|---|---|
| Agreement Name | Descriptive — e.g. Reyder-Axis Consignment 2026 |
| Inventory Owner | The company that owns the phones. For Reyder/Axis, set this to Axis Mobile. |
| Authorized Seller (Consignee) | The company that will sell. Set to Reyder Enterprises. |
| Commission Type | See the commission guide below |
| Commission Rate | Depends on commission type |
Optional¶
| Field | What it does |
|---|---|
| Start Date | When the agreement takes effect (defaults to today) |
| End Date | Expiry date — leave blank for an open-ended agreement |
| Owner Sees Device Status | Can Axis see whether a phone is available / reserved / sold? |
| Owner Sees Commission Earned | Owner-side visibility flag for commission summary information. Owner reports still do not expose customer, sale price, sale date, SO number, or per-device commission lines. |
| Terms & Conditions | Free-text legal language |
Step 3 — Pick a commission type¶
Percentage of sale (most common)¶
Reyder keeps a fixed % of each sale price. Axis gets the rest.
Enter as a decimal, not a whole number
To configure 15%, enter 0.15 in the Commission Rate field — not 15. Odoo's percentage widget will display 0.15 as 15% in the UI, but you must enter it as the decimal.
Entering 15 would be interpreted as 1500%, which is never what you want.
Examples at 15%:
| Sale Price | Commission | Axis Gets |
|---|---|---|
| $800 | $120 | $680 |
| $600 | $90 | $510 |
| $450 | $67.50 | $382.50 |
Fixed amount per device¶
Reyder keeps a fixed dollar amount per sale, regardless of price. Axis gets the rest.
Enter the dollar amount in Commission Rate — e.g. 50 for $50/device.
The system caps the commission at the sale price (it won't go negative on a cheap device):
| Sale Price | Fixed Commission | Axis Gets |
|---|---|---|
| $800 | $50 | $750 |
| $300 | $50 | $250 |
| $40 | $40 (capped) | $0 |
None¶
No commission. Axis receives the full sale price. Used for internal transfers or special cases where Reyder waives the fee.
Step 4 — Activate the agreement¶
Click Activate.

The moment you activate, Reyder can see Axis's devices. Go to All Devices and you should see Axis's inventory show up for Reyder users. If it doesn't, the agreement isn't active — check the state badge at the top of the form.
Agreement states¶
| State | What it means | Can Reyder sell Axis devices? |
|---|---|---|
| Draft | Created but not in force | No |
| Active | Operational — normal state | Yes |
| Suspended | Temporarily paused | No |
| Terminated | Permanently ended | No |
Use Suspend to temporarily halt sales (e.g. during a dispute). Use Terminate when ending the business relationship.
Changing the commission rate mid-agreement¶
You can edit the Commission Rate directly on the active agreement — no need to terminate and recreate. Save the form and the new rate applies to future allocations from that moment forward.
What the rate change does NOT affect:
- Already-allocated devices keep the commission rate they were allocated at.
- Already-sold devices and their settlement reports are historical and unchanged.
- Open/confirmed SOs with allocated devices keep their original commission.
To apply a new rate to an open order, de-allocate and re-allocate the devices — the re-allocation pulls the current rate.
Smart buttons on the agreement¶
The form header shows three live counters:
| Counter | What it shows |
|---|---|
| Consigned Devices | Axis devices currently Available — Reyder's saleable inventory |
| Sold via Consignment | Axis devices that Reyder has sold |
| Pending Settlement | Sold devices whose settlement hasn't been marked paid yet |
Click any of them to see the underlying device list.
Common problems¶
I can't see Axis's devices as a Reyder user
Check the agreement state — if it's not Active, Reyder can't see Axis inventory. Also check that you're logged in with a Reyder user (not Axis), since the record rule is company-scoped. Admin is a member of both, so test with a Reyder-only user to verify.
Commission isn't being calculated on new allocations
Most likely causes: (a) no active agreement between the owner and consignee, (b) the device's owner_company_id is set to Reyder (so it's not a consignment device — Reyder owns it directly, no commission applies).
??? question '"Only one agreement allowed between the same companies" error on create' There's already an agreement between Reyder and Axis (even a Terminated one — the unique constraint doesn't care about state). Find the existing row and either edit it or delete it first. Edit is usually preferred — you retain the history.
I changed the commission rate but old orders didn't update
That's by design. Historical allocations keep the rate they were created with to preserve accounting integrity. For an open order, de-allocate and re-allocate the devices to pick up the new rate.
What's the difference between Suspended and Terminated?
Suspended is reversible — click Activate to resume. Terminated is a hard stop; you'd create a new agreement if you wanted to resume (and the unique-pair constraint requires you to delete the terminated one first, or edit it instead of recreating).
Under the hood¶
Technical details for developers
Model: device.consignment.agreement
State methods:
action_activate()—state='active'+_recompute_device_consignees()action_suspend()—state='suspended'+ recomputeaction_terminate()—state='terminated'+ recomputeaction_reset_draft()—state='draft'+ recompute
Commission calculation:
agreement.calculate_commission(sale_price, currency=None)
# → (commission_amount, owner_amount) tuple
Uses odoo.tools.float_round with currency rounding. Returns (0.0, 0.0) for non-positive sale_price.
Device visibility mechanism:
stock.lot.consignee_company_ids is a stored, computed Many2many populated by _compute_consignee_companies(). For each device:
- Query all active agreements where
owner_company_id == lot.owner_company_id - Collect
consignee_company_idvalues - Store on the lot
Batch-optimized to do one search per set of owner companies, not per-device (avoids N+1).
Record rule in security/security.xml:
This is what opens cross-company visibility — a Reyder user (company_ids=[1]) sees Axis's lots because consignee_company_ids contains Reyder's id.
unlink() behavior: captures owner_company_ids before deletion, calls super().unlink(), then recomputes. Without this, deleted agreements would leave stale consignee_company_ids entries.
Constraints:
UNIQUE(owner_company_id, consignee_company_id)— one agreement per ordered pairCHECK(owner_company_id != consignee_company_id)— no self-consignment_check_dates()— end_date must be after start_date
get_active_agreement() class method:
@api.model
def get_active_agreement(self, owner_company_id, consignee_company_id):
return self.search([
('owner_company_id', '=', owner_company_id),
('consignee_company_id', '=', consignee_company_id),
('state', '=', 'active'),
'|', ('date_start', '=', False), ('date_start', '<=', fields.Date.today()),
'|', ('date_end', '=', False), ('date_end', '>=', fields.Date.today()),
], limit=1)
Used by the allocation wizard and by _create_settlement_reports() to find the applicable agreement at computation time.
commission_rate storage gotcha:
Stored as fields.Float(digits=(16, 4)). Odoo 19's auto percentage widget multiplies by 100 for display — so 0.15 displays as 15%. calculate_commission() does sale_price * self.commission_rate directly (no /100 needed).