Sales Orders¶
Who this is for
Sales ops taking a customer order. You'll use this every time a customer wants a specific device or set of devices.
What you'll accomplish: take a customer order, pin specific IMEIs to it so the warehouse knows exactly what to ship, and confirm it — which automatically spins up the delivery manifest and packing box.
Before you start: the devices you want to allocate need to exist in the system as Available, QC Complete, and with a purchase cost greater than zero. A Sales Manager or Inventory Manager can include QC/cost exceptions with a required override reason. If a device is already Reserved or Sold, it's taken. Set the customer sale price on the sales order line before allocating; the allocation wizard uses that line price.
Step 1 — Create the quotation¶
From the home, click Sales, then New.

- Pick the Customer (e.g. AnyMobile Retail).
- On the Order Lines tab, add a line for the device product you're selling.
- Set Quantity. You don't have to pick specific IMEIs yet — that happens in Step 2.
Save the quotation.
What is a device product?
A device product is a product record that represents a model, e.g. iPhone 14 Pro 256GB Excellent. It doesn't represent a specific phone — that's the job of the individual IMEI. The product line says "three iPhone 14 Pros"; the allocation says "these three IMEIs."
Optional line-level filters¶
If you want to constrain what the allocation wizard shows, set these on the order line:
- Required storage — e.g.
256GB - Required grade — e.g.
Excellent - Required color — e.g.
Black - Required lock status — e.g.
Unlocked
The allocation wizard will then only show devices matching the filter.
Step 2 — Allocate specific IMEIs¶
Click the Device Allocation tab on the SO form.

Known UX issue — first allocation on a new SO needs a bootstrap
The Device Allocation tab is hidden when there are zero allocations (the tab has invisible="total_devices == 0"). The Allocate button is inside that tab. There is no header button or action-menu shortcut that launches the allocation wizard from a zero-allocation SO — the wizard requires an sale.order.line.device context that doesn't exist yet.
Until this is fixed, first-allocation on a fresh SO requires admin/support help to seed one allocation. Once one allocation exists, the tab becomes visible and all subsequent allocations work through the Allocate button on each line row. Admins should use Admin Support Runbook — Device Allocation tab missing.
In the Allocate Devices wizard¶

Once the tab is visible, each order-line row in the tab has an Allocate (+) icon. Clicking it opens the wizard scoped to that line. The wizard lists available devices — narrow with the filters at the top (Storage / Grade / Color / Lock Status). For each device you want to pin to the order:
- Tick the row.
- Confirm the sale order line has the right Unit Price.
- Click Allocate.
Each row you allocate creates a link between the order line and that specific IMEI:
- The device's status flips from
AvailabletoReserved. - The allocation uses the sales order line unit price as the sale price for that IMEI.
- The commission is calculated automatically if this is a consignment device (see below).
- If a delivery manifest already exists for this order, the IMEI is added to it.
Allocating QC/cost exceptions¶
By default, the wizard only shows sale-ready devices: Available, not retail-reserved, QC Complete, and with purchase_cost > 0.
Managers see an extra Include QC/Cost Exceptions checkbox. Turning it on shows devices that are otherwise available but not QC complete or missing cost. If any selected device has those issues, the Override Reason is required before allocation succeeds.

The override does not bypass every rule. It only covers QC status and zero/missing cost. The device still must be available, visible to the selling company, not retail-reserved, not already allocated to another open order, and the sales order line must have a positive unit price.
Commission — how it's computed¶
For a device owned by Axis Mobile being sold by Reyder (which is most devices here), the system finds the active consignment agreement between Axis (owner) and Reyder (consignee) and uses its commission rate.
| Field | Value |
|---|---|
| Commission rate | From the agreement — stored as a decimal (0.15 = 15%) |
| Commission amount | unit_price × commission_rate |
| Owner amount | unit_price − commission_amount — what Axis receives |
Reyder keeps the commission; Axis gets the owner amount.
Commission isn't showing?
If the order isn't pulling a commission rate, the most likely cause is there's no active consignment agreement between the owner and the consignee. Check Inventory → Device Inventory → Agreements and confirm the Reyder-Axis Consignment row is Active.
Step 3 — Confirm the order¶
Click Confirm at the top.

Three things happen automatically the moment you confirm:
- Delivery manifest — a manifest of type delivery is created, pre-populated with the IMEIs you allocated. Warehouse uses this to pick.
- Packing box — a packing box is created for the scan-to-pack flow. The warehouse scans each IMEI into the box and the shipment moves forward.
- Inter-company SO (if applicable) — if the customer is the other Reyder company (an inter-company order), Odoo automatically generates the mirror PO on the other side.
You'll see three new smart buttons at the top of the SO header: Invoices, IMEI Manifest, Packing Boxes. Each one jumps to the related document.
The Device Allocation tab becomes the order-level overview: allocated quantity, device value, warning flags, and the packing-box status.

What if I need to cancel?¶
Click Cancel on the SO. Everything backs out cleanly:
- All device reservations release (Reserved → Available).
- All
sale.order.line.deviceallocations go toCancelled. - Any draft delivery manifest attached to this order is cancelled.
You don't need to manually un-allocate devices first.
Common problems¶
I can't find the IMEI I wanted to allocate in the wizard
Check its current status in All Devices. If it's Reserved or Sold, it's already on another order. If it's retail-reserved, it belongs to the retail flow and won't appear here. If it's Pending QC, In QC, QC Failed, or has zero cost, a manager must tick Include QC/Cost Exceptions and enter an override reason. Non-managers only see sale-ready devices.
The wizard shows exception devices but allocation still fails
The override only covers QC and cost readiness. It will not override a missing/zero SO line price, an unavailable device, another open allocation, wrong company visibility, or a duplicate IMEI on the same order.
The commission looks wrong
Check the active consignment agreement's Commission Rate. If it's stored as 0.15, it displays as 15% — that's correct. If the UI is showing something else, the agreement may be wrong. Go to Agreements, open the row, update the rate, save. Re-allocate the device to pick up the new rate (existing allocations are not recomputed retroactively).
I confirmed the SO but I don't see a delivery manifest
Check the IMEI Manifest smart button — it may have been created with zero lines if you confirmed before allocating any devices. Re-open the manifest and either add the devices there or cancel the SO, allocate, and re-confirm.
The order is highlighted orange in the list
That's a visual indicator that this order contains consignment devices (owned by someone other than the selling company). It's informational, not an error — the commission flow is active for these orders.
Under the hood¶
Technical details for developers
Models
sale.order— extended withis_device_order,total_devices,consignment_device_count,total_commission,delivery_manifest_ids,packing_box_ids,packing_state,is_inter_companysale.order.line.device— one record per IMEI allocation. Note the field issale_order_line_id, notsale_line_id.
Allocation record
| Field | Notes |
|---|---|
sale_order_line_id |
Many2one to sale.order.line |
lot_id |
Many2one to stock.lot (the IMEI) |
unit_price |
Sale price for this specific IMEI |
unit_cost |
Related purchase_cost from the lot |
commission_rate |
Decimal — 0.15 = 15%; Odoo's percentage widget renders this as 15% |
commission_amount |
unit_price × commission_rate |
owner_amount |
unit_price − commission_amount |
state |
draft / reserved / confirmed / delivered / cancelled |
is_consignment |
Computed: True when lot.owner_company_id != sale_order.company_id |
Locking & uniqueness
- Allocation uses
pg_try_advisory_xact_lockon the lot ID to prevent two concurrent users allocating the same IMEI. UNIQUE(sale_order_id, lot_id)DB constraint onsale.order.line.device— the same IMEI cannot appear twice on the same SO.
Confirmation flow
sale.order.action_confirm() is overridden:
- If
is_inter_company, create the mirror PO via_create_inter_company_po(). - If
is_device_order, create the delivery manifest via_create_delivery_manifest()and pre-populate lines fromdevice_line_ids. - If
is_device_orderand not inter-company, create a packing box via_create_packing_box(). - Call
super().action_confirm()for normal Odoo stock moves / email confirmations.
Cancellation
action_cancel() iterates device_line_ids:
- Sets
lot.device_status = 'available' - Sets
state = 'cancelled' - Cancels draft delivery manifests via
delivery_manifest_ids.filtered(lambda m: m.state == 'draft').unlink()