| Availability |
Odoo Online
Odoo.sh
On Premise
|
| Odoo Apps Dependencies |
•
Discuss (mail)
• Sales (sale_management) • Invoicing (account) • Purchase (purchase) |
| Community Apps Dependencies | Show |
| Lines of code | 10413 |
| Technical Name |
eh_log_warehouse_3pl |
| License | LGPL-3 |
| Website | https://www.erpheritage.com.au/ |
| Availability |
Odoo Online
Odoo.sh
On Premise
|
| Odoo Apps Dependencies |
•
Discuss (mail)
• Sales (sale_management) • Invoicing (account) • Purchase (purchase) |
| Community Apps Dependencies | Show |
| Lines of code | 10413 |
| Technical Name |
eh_log_warehouse_3pl |
| License | LGPL-3 |
| Website | https://www.erpheritage.com.au/ |
Warehouse 3PL Billing
Bill storage and handling from an audit trail, not a spreadsheet.
Why this module
Warehouse 3PL Billing
Every charge traces to a movement
Receipts and picks emit rows into an append-only movement log. Rows are immutable, undeletable, and created only through the put-away and picking paths. The billing run reads that log and nothing else for handling charges, so a disputed invoice line maps back to a dated, locked event.
Pallet-days from frozen snapshots
A nightly cron freezes pallets-on-hand per client per location into immutable snapshot rows. Storage billing sums those snapshots through the client's chosen basis (average, peak, or first-of-month), which is faster and more stable than recomputing on-hand from movement history at billing time.
Yours to keep, no renewal
LGPL-3 source on disk. No activation key, no phone-home, no recurring licence. The billing draft is a standard sale order, so it inherits Odoo's quotation approval flow rather than inventing a parallel one. Read it, extend it, run it on Community.
Day in the life
From dock to invoice in one trail
Goods arrive against a client. The operator marks the receipt arrived, inspects, assigns destination locations, and completes put-away, which stamps movement rows. Through the month, picks ship to the client's customers and emit their own movements. Each night a snapshot freezes pallets-on-hand per location. On the client's billing day the monthly cron raises a draft run for the prior month: handling in and handling out are counted from movements, storage is summed from snapshots through the rate card, a monthly minimum tops up any shortfall, and the operator reviews the draft sale order before confirming. A storage statement PDF backs the bill.
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.
Movement rows cannot be edited or deleted once written. The locked field set blocks any change to type, client, product, quantity, pallet count, or timestamp. Corrections are made by posting a new movement, never by rewriting history, so the billing source stays defensible.
The nightly snapshot cron captures yesterday so day-end activity is fully reflected. Re-running it for a date that already has rows is skipped, not overwritten, and a unique constraint on date plus client plus location prevents duplicate pallet-days corrupting a storage bill.
The monthly cron creates one draft run per client whose billing day matches today, for the prior calendar month. A client that already has a non-cancelled run for that period is skipped, so a manual cron trigger cannot double-bill.
Re-running compute on a draft run clears its prior lines and rebuilds them from the current movement and snapshot data, so a run started early picks up late activity. Posting writes the sale order and locks the run; posted and cancelled runs are terminal.
Rate lines carry a minimum billable quantity, and the rate card carries a monthly minimum that tops up any shortfall. Storage basis can be set to peak for punitive surge-pricing contracts or first-of-month for flat arrangements.
Movements, snapshots, and billing lines all reject direct ORM creates: only the receipt, pick, snapshot-cron, and compute paths hold the context key that lets a row through. State on receipts, picks, and runs can only change via the action buttons, not a raw write.
What is inside
Built to do the job, end to end.
- Facility, zone, location hierarchy. Three addressable levels: facility (one building, carrying a customs status of bonded, free zone, transit, or domestic), zone (a purpose-tagged section), and location (the bin). A composite facility/zone/location code prints on labels and pick lists, and each bin tracks a live pallet count derived from the movement log.
- 3PL clients and rate cards. Each client points at one rate card and a billing day from 1 to 28. The rate card holds one line per chargeable service across eight types: storage per pallet per day, handling in, handling out, pick line, three value-added services, and a monthly minimum. Lines can be constrained to a specific product, with a product-agnostic fallback.
- Receipts and picks. Inbound receipts run expected, arrived, inspecting, put-away, closed; outbound picks run planned, picking, packed, shipped, closed. Both enforce a closed-form transition table, require destination or source locations before they emit movements, and lock their lines once closed. Receipts can link to a freight job; picks can link to a sale order.
- Movement log and snapshots. An append-only ledger of every receipt and pick, immutable once written, plus a nightly immutable snapshot of pallets-on-hand per client per location. The log is the single source of truth for handling charges; the snapshot history is the single source for storage charges.
- Monthly billing run. Per client per period, the engine counts handling pallets from movements, sums storage pallet-days from snapshots through the client's basis, applies rate-line minimums, tops up to any monthly minimum, and emits a draft sale order for review. A daily cron sweeps clients on their billing day for the prior month.
- Documents and access. Goods Receipt Note, Pick List, and Storage Statement PDFs on the suite's clean report layout. Company-isolation record rules on all ten models, three roles (user, manager, auditor), per-model sequences, and demo data for facilities, rate cards, clients, receipts, and picks.
Honest about the edges
What this does not do, so nothing surprises you.
- This is a billing and audit-trail layer, not a replacement for Odoo's internal stock engine. It models bonded space let to clients and the activity charged against it; it does not run host-warehouse inventory valuation or replenishment.
- Customs status on facilities and purpose on zones are classification and reporting fields. The module does not file customs declarations, validate HS codes, or enforce a declaration link at movement time. Treat them as segregation and labelling metadata.
- There is no dangerous-goods classification, container or seal tracking, EDI messaging, demurrage, or carrier tracking-event ingest in this module. Multi-leg freight routing and shipment legs live in the freight module of the suite.
- Movement types for internal transfer and adjustment exist in the data model, but the wired paths that emit movements are receipt put-away and pick picking. Plan ad-hoc corrections as reversing receipts or picks.
- Billing produces a draft sale order for an operator to review and confirm. It does not auto-post invoices, and there is no client-facing portal; clients do not log in to see their own stock in this module.
- Storage charging is pallet-based and snapshot-based. Volumetric, weight-based, or temperature-tier storage pricing is not modelled.
Odoo 19 3PL billing, bonded warehouse Odoo, third party logistics Odoo, warehouse storage billing, pallet day storage charge, handling in handling out billing, 3PL rate card, goods receipt note Odoo, pick list Odoo, storage statement, warehouse movement audit log, monthly billing run, Odoo Community warehouse 3PL, value added services billing, freight forwarding warehouse
Please log in to comment on this module