| Availability |
Odoo Online
Odoo.sh
On Premise
|
| Odoo Apps Dependencies |
•
Discuss (mail)
• Invoicing (account) • Purchase (purchase) • Sales (sale_management) |
| Community Apps Dependencies | Show |
| Lines of code | 9504 |
| Technical Name |
eh_log_carrier_portal |
| License | LGPL-3 |
| Website | https://www.erpheritage.com.au/ |
| Versions | 16.0 17.0 18.0 19.0 |
Carrier Portal for Odoo 19
Rate-shop across carriers, rank the quotes, book the winner, and poll its status, all from inside Odoo.
Why this module
Carrier Portal for Odoo 19
One request, every matching carrier
A rate-shop request searches active carrier profiles by mode and lane, calls each one, and writes a quote row per offer. A carrier with no lane covering the route is skipped quietly, not errored. The request and its quotes are persisted, so the comparison stays auditable after the operator moves on.
Four ranking strategies, not one
Quotes order by cheapest (price then transit), fastest (transit then price), preferred carrier (flagged carriers first, price tie-break), or balanced (price and transit normalised to a unit scale and summed). The strategy is chosen per request and re-applied on read, so it stays honest about how the winner was picked.
Bookings that cannot drift
Booking state moves only through action buttons (requested, accepted, confirmed, closed, cancelled), with illegal transitions blocked. Quotes lock every field except the archive flag once a price comes back. A cancellation window (24 hours after confirmation by default) closes the door on late cancels instead of silently failing.
Day in the life
A forwarder quotes an ocean shipment, then books it
An operator opens the rate-shop wizard from a freight job, enters origin and destination countries, ready-by date, and cargo weight and volume. The request fans out across the carriers contracted for that lane and mode. Each carrier returns service variants with price, transit days, and a validity date, stored as immutable quote rows. The operator ranks by balanced strategy, picks a quote, and books it through the book wizard. The carrier acknowledges and the booking moves to accepted with a carrier reference. Every thirty minutes the status cron refreshes accepted and confirmed bookings with the latest carrier-side message, recorded against the record. If plans change inside the cancellation window, the cancel action calls the carrier and moves the booking to cancelled; past the window it refuses.
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.
A carrier with lanes configured is skipped from the fan-out when none of its lanes cover the requested origin, destination, and mode. A lane with a blank location code matches any location in its country, so contracts written at country granularity still apply.
The rate-shop wizard refuses an intra-country shop where origin and destination countries are equal unless both location codes are supplied, so an ambiguous domestic route cannot silently fan out.
Rate quotes can only be created by the fan-out (a direct ORM create is blocked) and every field except the archive flag is locked once the carrier returns a price. Re-quoting the same parameters creates a fresh request rather than overwriting history.
Booking state changes only through the action methods. A direct write to state, or a transition the state machine does not allow (for example confirmed straight to requested), raises rather than corrupting the lifecycle.
Cancelling a confirmed booking computes hours elapsed since confirmation; past the 24-hour default window the cancel action raises with the elapsed time, so a too-late cancel is a clear error, not a no-op.
If a carrier rejects a booking or a rate call fails, the failure is caught at the adapter boundary: a failed rate call is logged and skipped from the ranking, and a failed booking raises a clear error naming the carrier rather than leaving a half-written record.
The reference air adapter prices on the higher of actual weight and volumetric weight (volume times the IATA 167 divisor), mirroring real chargeable-weight billing instead of naive actual-weight pricing.
What is inside
Built to do the job, end to end.
- Carrier profile registry. eh.log.carrier.profile holds one carrier integration per company: mode, the provider code (validated at save against the registered adapter set so a typo surfaces before a fan-out), credentials key, endpoint, timeout, and explicit capability flags for rate-shop, book, cancel, track, and schedules.
- Lane filter. eh.log.carrier.lane constrains a carrier to the origin and destination countries (with optional location codes) and the mode it is contracted to serve, with an optional transit-day range and a min-not-over-max guard. The rate-shop honours it; no lanes means the carrier is consulted for any route in its mode.
- Rate request and quotes. eh.log.carrier.rate.request captures route, cargo, and ready-by, fans out, and collects eh.log.carrier.rate.quote rows. Requests carry a draft, shopped, expired state; quotes record service, transit days, price with currency, validity, the carrier reference, and the raw response. Both resist edits after the fact.
- Booking lifecycle. eh.log.carrier.booking turns a chosen quote into an instruction with a requested, accepted, confirmed, closed, cancelled state machine, timestamps for each stage, the carrier booking reference, last-polled status, and links to the source quote and freight job. mail.thread chatter and activities are on the record.
- Adapter base and mock providers. A lightweight in-process CarrierAdapter base defines the message set (rate_shop, book, cancel, track_status, schedules, health_check) and dispatches by method. Two mock adapters ship as working references: a generic ocean carrier (weight-scaled pricing, sailings) and a generic air carrier (IATA chargeable-weight pricing, flights).
- Status cron, settings, security. A 30-minute cron refreshes accepted and confirmed bookings against the carrier. Settings expose a default rank strategy and an auto-book threshold parameter. Per-company record rules isolate every model, with user, manager, and auditor access tiers. 21 tests cover profile, lane, rate-shop, and booking-lifecycle behaviour.
Honest about the edges
What this does not do, so nothing surprises you.
- The two shipped adapters (ocean and air) are mock reference implementations that return deterministic canned data. They prove the rate-shop and booking flows end to end, but no live carrier (no EDI, Cargo-XML, or real carrier API) is wired. A real integration means subclassing the adapter base and implementing the wire-level serialise and parse for your carrier.
- This module reaches out to carriers (rate-shop, book, cancel, poll). It does not ingest inbound tracking events; that is the separate track-and-trace module in the suite.
- There is no customs declaration, HS-code validation, or dangerous-goods classification logic here. The dangerous-goods flag is carried through to the carrier payload as a boolean; it does not validate or restrict cargo.
- Auto-booking is exposed as a configurable threshold setting, but the wizard does not yet act on it; booking is an explicit operator step through the book wizard.
- The cancellation window default (24 hours after confirmation) is fixed in code. Per-carrier contract overrides are not yet configurable.
- The schedules message type exists on the adapter base and mock providers, but schedule and sailing results are not persisted to a model or surfaced in a dedicated view.
- Carrier portal adapter calls run in-process and are not written to the eh.log.adapter.message audit log used by the suite's heavier customs adapters; the credentials secret is held by key and never stored on the profile.
Odoo 19 carrier portal, freight rate shopping Odoo, carrier booking Odoo, ocean freight carrier integration, air freight rate shop, freight forwarder Odoo Community, carrier adapter framework, transit time quote ranking, 3PL carrier connector, logistics booking lifecycle, multi-company freight Odoo, carrier lane management
Languages
Available in 19 languages
The interface ships translated out of the box. Switch language in Odoo and the fields, menus, and messages follow.
Please log in to comment on this module