openepcis_connector_events: visibility events

Every validated transfer becomes an EPCIS 2.0 ObjectEvent, packing an AggregationEvent under an SSCC, a return a TransactionEvent, and a wrong report an error declaration. Three settings decide what is reported — the company switch, the operation type, the location — and an outbox keeps the warehouse from ever waiting on the repository. Partners' events come back into an inbox that shows and, where two settings agree, posts.

Master data says what a product is. An EPCIS event says what became of one of them — received, packed, shipped, sold, returned, turned into something else — and together they are what a scanned serial number can answer. This addon reports Odoo's warehouse operations as those events, and reads back what partners report about the same goods.

Moduleopenepcis_connector_events
Depends onopenepcis_connector_stock; the Python package epcis_event_hash_generator
InstallsBy hand — reporting movements to a shared repository is a decision, not a default
Talks toThe EPCIS 2.0 repository — a different service from the resolver, usually a different host
Sourceopenepcis-odoo · LGPL-3

What is reported

What happened in OdooEventBusiness step
A transfer is validatedObjectEvent, OBSERVEFrom the operation type: receiving, shipping, storing, …
Goods are put into a packageAggregationEvent, ADD, parent = the package's SSCCpacking
A package is emptied — by picking from it or by UnpackAggregationEvent, DELETEunpacking
Cases go onto a pallet (Odoo 19, nested packages)AggregationEvent, children = SSCCspacking
Goods come back on a returnTransactionEvent, DELETE — releases the shipment they went out onreceiving, disposition returned
A manufacturing order is finishedTransformationEvent — see manufacturing eventscommissioning
A report was wrongThe same event again, with an errorDeclaration of did_not_occur—

Four decisions inside every event are worth knowing, because they are decisions rather than transcription:

  • A lot is not a serial number. A serial identifies one unit and goes into the epcList as an SGTIN. A lot identifies a set and goes into the quantityList as an LGTIN. Untracked goods are a set too — the trade item itself — so a warehouse that tracks nothing still produces useful events.
  • Only published products appear. An identifier that resolves nowhere is not worth putting into a repository: a scan would find an event and no product. What is not published is left out, and the event still describes what did move.
  • The read point is not optional, although EPCIS allows it to be. An event that says something happened without saying where is half an answer. A transfer whose locations carry no GLN is reported in its chatter instead, where somebody can fix the location.
  • The Origin field becomes the business transaction, so downstream readers match on what the warehouse actually typed. A delivery with no reference is named after the transfer.

Three settings, each where the fact lives

The company switch. Settings → General Settings → OpenEPCIS → Report visibility events, with the repository's address and your GS1 company prefix. Test capture checks that the offline token the connector already holds is also allowed to capture — publishing master data and capturing events are two rights, and a token can hold one without the other.

Figure 1: The switch, the repository address, and the company prefix — which is used for SSCCs and nothing else

The prefix is what SSCCs are minted from. Unlike a GTIN, an SSCC is not drawn from a registry: a company allocates its own and only has to make sure it never repeats one, so the addon mints them locally from a sequence, at the moment a package comes into being.

The operation type carries the why. Odoo knows a transfer is "incoming"; it does not know GS1 calls that receiving with the goods in_progress. Two fields on every operation type hold the business step and the disposition, pre-filled from Odoo's own codes and the sequence prefix, and never overwritten once somebody has chosen otherwise. A third flag, Report as EPCIS events, lets you leave an operation type out.

Figure 2: Business step and disposition, seeded from the operation's codes

Operation typeOdoo codeBusiness stepDisposition
Receiptsincomingreceivingin_progress
Pick / Pack / Quality Controlinternalpicking / packing / inspectingin_progress
Storageinternalstoringsellable_accessible
Delivery Ordersoutgoingshippingin_transit
Manufacturingmrp_operationcommissioningactive

The location carries the where. EPCIS wants an SGLN, so a location needs a GLN, and Odoo's locations have none. They do have a tree: set the GLN on the warehouse, the loading bay or the shop floor, and everything underneath inherits it by walking up. One number makes a whole site answerable, and because Odoo's own GS1 barcode nomenclature binds AI 414 to a location, the GLN written here is also what a scanner reads off a GS1 location label.

The outbox

Validating a transfer must never wait for a network. Pressing Validate writes rows into an outbox and returns; a scheduled action delivers them every minute.

Figure 3: One warehouse day as twelve rows

Delivery is two answers, kept apart. Accepted means the repository took the document; Captured means it stored it. Capture is asynchronous — the repository validates afterwards and may still refuse — so a queue that deleted its row on the 202 would report success for events thrown away minutes later. A row carries its capture job and is asked again on the next run. After five attempts it stops asking and says it cannot be confirmed.

Each row shows two identifiers that do different jobs. The expected event ID is the canonical CBV event hash over everything the event asserts; anyone holding the event can recompute it, which is what lets two companies name the same observation without agreeing on anything. Odoo computes it only to recognise its own events coming back and sends the document without it — there must be exactly one canonicalisation, and it is the repository's. The idempotency key is private bookkeeping, derived from the database, the transfer and the identifiers, so the same movement cannot enter the outbox twice. It never leaves Odoo.

Taking a report back

An event cannot be pulled out of a repository; EPCIS withdraws it with an error declaration. The addon sends the original event again with errorDeclaration.reason = did_not_occur. Declaration fields are excluded from the hash, so the correction carries the same identity and the repository knows which event is meant without Odoo ever learning its name. The original stays on the row, greyed out and marked as corrected.

The inbox

Events also come in: what partners' systems have said about our lots and packages. A scheduled action reads the repository every five minutes from a watermark — the repository's own write clock, so an outage costs one run rather than a reconciliation — and shows what it finds on the lot, on the package, and where Odoo's own traceability report stops at the company boundary.

A partner's event is an observation, not a document. Odoo's stock is a valued figure that ends up in the books, and posting an observation into it would make the closing depend on a third party's data quality. So the inbox never moves stock. Three settings under What we read back keep it manageable: which events to keep (everything the credential may see, only identifiers under our own prefix, or only what can be placed in this database), events per run, and pages per run — the brake that stops a day of catch-up from becoming one unbounded transaction.

The one exception

An incoming event may advance a transfer that is already open, reserved and waiting for exactly this confirmation — a supplier confirming his own despatch, a customer booking a receipt. It never creates a document, never creates stock, never reopens something closed, never touches quantities. The worst a wrong event can do is validate a transfer a day early: visible, attributable, reversible like any other mistaken validation.

Two settings have to agree before even that happens, so that neither is a single point of trust:

  • The operation type says what an incoming event may do at all — Ignore, Show only (the default), Propose a posting, or Post automatically.
  • The partner says for whom. May move our transfers on the contact, next to the GLN that identifies him in the events, off by default.

And before any of it runs for real, it runs as a rehearsal: while the operation type's Observe only flag is set, the whole decision is made and written to the log and the last step is skipped. Somebody reads a week of that log, then clears the flag.

See it end to end

A warehouse day walks through a receipt, a pallet, a delivery, a return, a manufacturing order and a withdrawal with the event each one produces, copied from the outbox of the instance in the screenshots.

Last updated: