Skip to content

Delivery Manifests

Who this is for

Warehouse team. You'll use this when a sales order is confirmed and you need to physically pick, pack, and ship the devices.

What you'll accomplish: scan the devices into their packing box, which automatically picks them on the manifest, posts COGS, creates the customer invoice, and (for consignment devices) generates settlement reports.

Before you start: the sales order must be confirmed and have devices allocated. The delivery manifest and packing box are both auto-created at confirmation time.


The flow

Since v2.26, delivery is a single scan per device. The packing box is the one place you scan — no separate pick step.

SO confirmed  →  Scan each IMEI into packing box  →  Mark Ready to Ship  →  Mark Shipped  →  Everything auto-posts
                 (one scan picks + packs)

Three automatic things happen the moment the box is marked shipped:

  • COGS entry posts (DR COGS / CR Valuation)
  • Customer invoice is generated and posted
  • Settlement reports are created for consignment devices (owner + consignee, paired)

Delivery manifests list


Step 1 — Find the order to pack

When a sales order is confirmed, Odoo auto-creates:

  • A delivery manifest (type delivery), pre-populated with the allocated IMEIs
  • A packing box (if the order isn't inter-company) with the allocations pre-assigned

You can jump to either from the SO's smart buttons (IMEI Manifest or Packing Boxes).

Which do I open — the manifest or the packing box?

Start with the packing box — that's where you'll scan. The manifest auto-updates from the box. You can open the manifest afterward to confirm picking is 100%, but you don't need to touch it to scan.

On a delivery manifest, you'll see a banner pointing you to the linked packing box and a Pack & Scan button that takes you there:

Manifest form


Step 2 — Scan devices into the packing box

Open the packing box (from the SO, via the Packing Boxes smart button, or from Inventory → Device Inventory → Packing Boxes).

Packing box form actions

Use the barcode scanner or the box's scan-to-pack UI. For each device:

  1. Scan the IMEI.
  2. The box's line is confirmed as packed.
  3. The matching manifest line automatically flips to received (picked).
  4. The progress counters on both documents tick up.

Packing box scanner UI

What each scan does

  • If the IMEI matches an allocation on this order — it's packed and picked in one step.
  • If the IMEI isn't on this order but could be (e.g. you swapped in an equivalent device last-minute) — the box auto-allocates it via _handle_unexpected_scan() so you don't get stuck. The allocation is added to the SO and the manifest.
  • If the IMEI is already on another open order — the scan is rejected with a clear error. Resolve the conflict before re-scanning.

Scanning the same device twice

Second scan gets rejected. Each IMEI can be packed into at most one box.

QC/cost exceptions at shipment

Packing can scan devices that were allocated with a manager override, but Mark Shipped re-checks sale readiness before posting accounting. If a packed device is not QC Complete or has zero/missing cost and the allocation does not carry an override reason, shipment is blocked. Go back to the SO allocation, complete QC/fix cost, or have a manager approve the exception before shipping.


Step 3 — Mark the packing box ready, then shipped

When the box has every expected device scanned, click Mark Ready to Ship. The box moves to Ready to Ship and the Mark Shipped button appears. Then click Mark Shipped.

That's the completion event. The system then:

  1. Sets the box state to Shipped.
  2. Marks each device as Sold on its stock.lot, stamping the sale date and SO reference.
  3. Posts the COGS journal entry (DR COGS / CR Valuation).
  4. Generates and posts the Customer Invoice.
  5. For each consignment device, creates the paired Owner Settlement Report (Axis Mobile) and Consignee Settlement Report (Reyder). Both are auto-confirmed, which triggers the vendor bill for Axis's owner amount.
  6. Marks the delivery manifest as Done.

Packing box shipped

From this point on, sales, warehouse, and accounting all see the order in its finished state. Accounting's next step is to mark the settlement reports as paid once the vendor bill clears.


GL entries posted on ship

The COGS entry posts to the Device Stock Journal:

Account DR CR
Device COGS (expense) sum of purchase_cost
Device Valuation (asset) sum of purchase_cost

The customer invoice (posted to the standard sales journal) follows normal Odoo invoicing — product revenue credited, AR debited, with the customer's tax settings applied.

See Accounting Reference for the full breakdown of every entry including the settlement-report vendor bill.


Inter-company orders

When the customer on the SO is the other Reyder company (inter-company), the flow is slightly different:

  • Marking the box shipped generates a mirror receiving manifest in the destination company's books so the devices land as receipts on the other side.
  • The COGS entry is posted in the selling company.
  • The customer invoice is an inter-company out_invoice; Odoo's inter-company mirroring automatically creates the matching draft in_invoice (vendor bill) in the destination company for your accounting team to post.

See Inter-Company Transfers for the full inter-company flow.


Smart buttons on the manifest

Button What it opens
Received Devices List of devices that have been scanned/picked
Packing Boxes The packing box(es) associated with this order
Journal Entries The COGS move and any related GL entries
Sale Order The originating SO
Purchase Order For receiving manifests only — the originating PO

Common problems

Mark Shipped button is disabled

If the box is still Draft or Packing, first scan every expected device and click Mark Ready to Ship. Mark Shipped appears only once the box is in Ready to Ship. If the box is missing devices, the box shows packed / expected. Pack the remaining devices, or if a device is genuinely unavailable, go to the SO's Device Allocation tab and remove that allocation first — then the box expected-count drops and you can ship.

I scanned a device that the system says belongs to another order

That IMEI is allocated to a different open SO. Options: (a) deallocate it from that SO first if it was allocated in error, or (b) allocate a different IMEI to this order. The scanner won't let you double-book.

The manifest shows 100% picked but the box isn't shipped yet

Correct — those are different states. Scanning picks the manifest line and packs into the box. Mark Ready to Ship confirms the box is complete; Mark Shipped is the separate accounting/shipment confirmation that triggers COGS/invoice/settlement. If you want the manifest done, ship the box.

The customer invoice came out wrong

The invoice is generated from the SO lines, not the manifest. If the SO line had the wrong price or quantity, cancel the invoice, fix the SO, and re-post. The COGS entry is based on the actual purchase_cost of the shipped IMEIs and doesn't change with invoice edits.

I need to un-ship (reverse the delivery)

That's not a casual operation — it means reversing COGS, credit-noting the invoice, and deleting or reversing the settlement reports. Talk to accounting first and then use the device-by-device return flow rather than trying to un-ship in bulk.


Under the hood

Technical details for developers

Unified delivery flow (v2.26 change)

Before v2.26, picking and packing were separate — scan to pick on the manifest, then scan again to pack into a box. Commit ec1cff7 unified them: the packing box is now the single scan surface. The manifest no longer has its own barcode UI for delivery; its "Start Picking" / "Barcode Scanner" buttons hide when a packing box exists.

Models

  • device.manifest with manifest_type='delivery'
  • device.manifest.line — one per allocated IMEI, status expected → received
  • packing.box — grouping container; state draft → packing → ready → shipped
  • packing.box.line — one per packed IMEI

Scan → pick + pack

packing_box.scan_device(imei) is the entry point. It:

  1. Acquires pg_try_advisory_xact_lock on the lot ID.
  2. Resolves the IMEI to a stock.lot.
  3. Validates allocation: looks for a sale.order.line.device with lot_id == lot on this order. If missing and allocation-by-swap is possible, calls _handle_unexpected_scan() which creates the allocation.
  4. Creates a packing.box.line row.
  5. Calls _quick_pick(lot) on the matching delivery-manifest line — flips status to received and links stock_lot_id.

Mark shipped

packing_box.action_mark_shipped() orchestrates:

  1. _validate_delivery_quantities() — catches over-delivery
  2. _process_customer_delivery() on the delivery manifest:
  3. stock.lot.action_mark_sold() per device → device_status='sold', stamps sale date
  4. _create_cogs_accounting_entry()account.move, stored as manifest.cogs_move_id
  5. _create_settlement_reports() (consignment only) → paired owner + consignee consignment.settlement.report. Uses .sudo() to cross the company boundary for the owner-side write.
  6. _create_customer_invoice() → calls sale.order._create_invoices() and auto-posts; stored as manifest.customer_invoice_id
  7. For inter-company orders, _create_buyer_receiving_manifest() mirrors the manifest in the destination company.
  8. manifest.state = 'done'
  9. box.state = 'shipped'

Each of the accounting steps is idempotent — if called twice (e.g. after a retry), the existing moves are reused via *_move_id checks.

Key fields — device.manifest

Field Notes
manifest_type delivery for outbound, receiving for inbound
sale_order_id Link to the originating SO
state draft / in_progress / done
cogs_move_id COGS journal entry
customer_invoice_id Customer invoice
account_move_id Inventory receipt GL (used on receiving manifests)
expected_count / received_count / progress_percent Picking progress