| Availability |
Odoo Online
Odoo.sh
On Premise
|
| Odoo Apps Dependencies |
•
Discuss (mail)
• Invoicing (account) • Sales (sale_management) |
| Lines of code | 3092 |
| Technical Name |
eh_log_base |
| License | LGPL-3 |
| Website | https://www.erpheritage.com.au/ |
| Availability |
Odoo Online
Odoo.sh
On Premise
|
| Odoo Apps Dependencies |
•
Discuss (mail)
• Invoicing (account) • Sales (sale_management) |
| Lines of code | 3092 |
| Technical Name |
eh_log_base |
| License | LGPL-3 |
| Website | https://www.erpheritage.com.au/ |
Logistics Suite Base Engine
The shared foundation every ERP Heritage freight, customs, and transport module is built on, one adapter contract, one credentials helper, one audit log, one set of master data.
Why this module
Logistics Suite Base Engine
Every external call goes through one contract
Regulators, carriers, EDI partners, telematics, and routing APIs all inherit the same adapter base. Timeout, retry with exponential backoff and jitter, circuit breaker, dead-letter, and audit logging are written once, so each concrete adapter only writes its serialize, parse, and transport specifics.
Failures surface with a code you can grep
Missing credentials, missing config, version mismatches, rejected payloads, and timeouts each raise a typed exception carrying a stable [EHL-DOMAIN-NNN] prefix. Support reads logs by code instead of parsing English, and tests assert on the code, not the message text.
An append-only event log across the whole suite
eh.log.event captures state transitions, overrides, adapter outcomes, and approvals with user, company, timestamp, related record, and a JSON context column. Writes after creation are blocked at the ORM level, and retention is tuned per category so compliance evidence outlives operational chatter.
Day in the life
Where the engine shows up in a working day
A developer wires a new carrier integration: they subclass the adapter base, register a PROVIDER_CODE, and point a profile at the sandbox endpoint. The profile defaults to mock so the first test run replays a captured fixture with zero network. When they flip it to sandbox and a credential is absent, the credentials helper raises a typed error naming the environment variable and the config key to set. An operator later opens the adapter message log, sees a run that hit the dead-letter status after the circuit breaker tripped on five consecutive failures, fixes the credential, and clicks replay. Every one of those steps lands a row in the append-only audit log, scoped to their company.
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.
The breaker reads the recent message log, not a process-local counter, so its open state is durable across workers. Once the configured count of consecutive failures, timeouts, or dead-letters is reached, calls short-circuit immediately until the cooldown window elapses.
If a profile declares an API version that does not match the implementing adapter class, the base refuses to dispatch and raises a configuration error rather than silently sending against an unsupported contract.
Credentials stored via the helper are Fernet-encrypted with a key derived from the database UUID plus a per-suite salt. A copied database without the salt cannot decrypt the secrets, which is the intended property. If the cryptography library is absent, encrypted writes refuse loudly while plaintext and environment-variable lookups still work.
Writes to existing audit events are rejected at the ORM layer except for the archive flag the retention cron uses, so the operational trail cannot be edited after the fact. Pruning happens only through the garbage-collection cron, per category.
Authorization, API-key, cookie, and proxy-auth headers are redacted before any request or response is written to the message log, and oversized payloads are truncated to a configurable character limit.
Each call generates a correlation id and can carry a caller-supplied idempotency key, both persisted on the message row so a retried or replayed request can be deduplicated by the provider and traced end to end.
Charge codes and document types with no company assigned are shared across all companies; assigning companies scopes them. Adapter profiles, messages, and audit events carry global isolation rules so a multi-company user only sees rows for their selected companies.
What is inside
Built to do the job, end to end.
- Adapter framework. An abstract BaseAdapter plus a process-level registry keyed by PROVIDER_CODE. The base provides the call orchestrator (serialize, transport, parse), timeout handling, retry with exponential backoff and jitter, a database-backed circuit breaker, dead-letter capture, header redaction, payload truncation, and a mock environment that replays fixtures so CI runs the production code path with no network.
- Adapter profile and message log. eh.log.adapter.profile holds one row per provider, company, and environment with endpoint, auth method, credential reference, timeout, retry, breaker, and rate-limit settings plus live health status. eh.log.adapter.message records each request and response cycle with correlation id, idempotency key, sanitised payloads, latency, retry count, status, and a back-link to the originating record, with a replay action for the dead-letter queue.
- Credentials helper. eh.log.credentials resolves a secret through a documented precedence chain: environment variables first, then an ir.config_parameter slot that may be Fernet-encrypted at rest, then an explicit default. It never logs the resolved value, supports per-company keys, and raises a typed CredentialsMissingError that names exactly where to set the value.
- Typed exception hierarchy. Fourteen typed classes covering KYC, credit, agreement, customs, rating, routing, EDI, adapter auth, adapter timeout, adapter validation, configuration, credentials, job-state, and company-mismatch errors. Each carries a stable [EHL-DOMAIN-NNN] code and splits UserError (self-correctable) from ValidationError (rolls back the transaction).
- Operational audit log. eh.log.event is an append-only feed with category, severity, one-line summary, user, company, related model and record, error code, and a JSON context column. A one-call log() helper lets any model write an event, and a retention cron prunes per category with overrides and approvals kept for seven years by default.
- Master data and UX. Seeded charge codes (ocean, air, road, rail freight, origin and destination handling, surcharges, customs, documentation, inland, warehousing, insurance) and trade document types (HBL, MBL, HAWB, MAWB, CMR, commercial invoice, certificate of origin, dangerous goods declaration, ATA carnet, KYC documents, and more) with expiry awareness. A shared UX mixin adds toast notifications, activity nudges, and state-change chatter, plus a branded paper format and report shell.
Honest about the edges
What this does not do, so nothing surprises you.
- This is an engine, not an operational app. It ships no shipment, booking, quotation, customs declaration, or trip model on its own. Those records live in the freight forwarding, customs, quotation, and road transport modules that depend on this base.
- No concrete provider adapter is bundled. The module provides the adapter contract, profile, registry, and message log, but a working integration with any specific regulator or carrier ships in a separate country or vertical module.
- The customs, rating, routing, EDI, KYC, and credit exception types are defined here as the shared vocabulary; the workflows that raise them are implemented in the operational modules, not in this base.
- Port, airport, dry-port, and container-freight-station reference data is intentionally not seeded here so the base stays globally neutral. Country localisation packs supply it.
- Encrypted-at-rest credential storage requires the Python cryptography library on the server. Without it, plaintext parameters and environment variables still work but the encrypted write path is unavailable.
- Application is set to false in the manifest. It installs as a dependency and exposes a configuration and audit menu rather than a standalone application icon.
odoo 19 logistics, freight forwarding odoo, customs broker odoo, transport management system odoo, tms odoo community, logistics adapter framework, edi integration odoo, sea freight air freight road freight, multimodal logistics odoo, charge code master data, shipping document types odoo, credentials helper encrypted, circuit breaker integration, append only audit log odoo, multi company logistics, supply chain odoo community, 3pl odoo, freight quotation engine base, GCC customs integration framework, logistics suite base
Please log in to comment on this module