Skip to main content

External Events

Goods Received publishes one event, when a delivery — or part of one — is approved. Nothing is published while a delivery is being created or counted, so an event always means "a store has accepted these quantities".

Read more about how to subscribe in External Events.

The event source

Event sourcegoods-received
Name in the catalogueGRC: Goods received
Versionv1
Content typeapplication/json
Published whenA delivery, a container, or a single line is approved

Inside Hii Retail the same event feeds Stock services, which is how an approved delivery becomes stock. Subscribing externally does not change or delay that.

The payload

The full schema is on Goods received completed format, and is also published as a JSON Schema you can validate against — the event source entry in the External Events catalogue links it. What the fields mean:

The delivery

FieldRequiredMeaning
tenantIdThe tenant
businessUnitIdThe receiving store
uniqueIdHii Retail's id for the delivery. Stable across every event for that delivery
deliveryNoteIdThe shipper's delivery note number
shipperIdWho sent the goods
shipperTypebusinessPartnerId — an external supplier or warehouse — or businessUnitId — another store
deliveryDateThe delivery date stated on the delivery note — when the goods were expected, not when they arrived or were approved
submittedAtWhen the store approved. This is the event's own timestamp
submittedByThe user who approved
commentFree text from the delivery note. May be null
transportationAmountDelivery-level transport cost. May be null
customsAmountDelivery-level customs cost. May be null
linesThe lines this approval covered

shipperType tells you whether this was a purchase or an internal movement. A delivery from another store is goods moving inside the customer's own estate; treating it as a supplier receipt will overstate purchases. Branch on shipperType before booking anything financial.

The lines

FieldRequiredKindMeaning
lineIdThe line's id within the delivery
itemIdThe item
expectedQuantityLevelWhat the delivery note said
approvedQuantityLevelWhat is accepted, as it now stands
deltaQuantityDeltaThe change in approved quantity since this line was last published
unitPriceLevelPrice per unit. May be null — meaning unpriced, not free
currencyCodeThree-letter currency of unitPrice. May be null
reasonCodesLevelThe line's complete current set of reasons, each with a signed quantity
orderIdThe purchase order
orderLineIdThe purchase order line
containersThe line's container chain, innermost first — its own container, then each enclosing one outwards
batchNumberBatch, if the shipper supplied one
bestBeforeDateBest-before date, if supplied
expirationDateExpiration date, if supplied
serialNumbersSerial numbers as expected — not the accepted subset

🛑 deltaQuantity is a change; approvedQuantity and reasonCodes are totals. Adding up reason code quantities across events double-counts every correction. The rules and a worked example are in Delta versus level — read it before writing a consumer.

Message attributes

Set on every message, and usable for server-side filtering so a subscriber only receives what it cares about:

AttributeValue
Tenant-IdThe tenant
Business-Unit-IdThe receiving store
Delivery-IdSame as uniqueId in the payload
Shipper-IdWho sent the goods
Shipper-TypebusinessPartnerId or businessUnitId
Correlation-IdTraces back to the request that caused this. Your own value if you supplied one on import

Filtering on Business-Unit-Id is the usual way to feed a per-store integration, and on Shipper-Type to separate supplier receipts from inter-store movements.

Consuming the events

One delivery, many events

🛑 An event describes one approval, not one delivery. A five-line delivery approved line by line produces five events, each carrying only the lines that approval covered. A consumer that treats the first event as the delivery's complete content will act on a fifth of it.

Accumulate by uniqueId, and expect more events for a delivery you have already seen — including days later, when a correction is recorded.

Duplicates are possible

Delivery is at least once. The same event can arrive more than once, and a consumer must be idempotent.

A safe deduplication key is uniqueId + submittedAt for the event, or uniqueId + lineId + submittedAt at line level. A redelivery of the same approval carries identical values; a genuine new approval of the same line has a later submittedAt.

⚠ Do not deduplicate on uniqueId + lineId alone. That is exactly the pair a legitimate correction reuses, so you would discard the correction.

Do not rely on arrival order

External subscribers get no ordering guarantee. Events for the same delivery can arrive out of order, and a correction can overtake the approval it corrects.

Order by submittedAt within a uniqueId. Two practical consequences:

  • If you keep a current approved quantity per line, ignore an event whose submittedAt is older than the one you have recorded. Applying it would revert a correction.
  • If you accumulate deltaQuantity, order matters less — the sum is the same — but you must still not apply the same event twice, so deduplication is not optional.

Event time versus expected time

deliveryDate and submittedAt answer different questions:

UseFor
submittedAtSequencing events, and "when did we learn this?"
deliveryDateAccounting period, supplier performance, "when were the goods due?"

🛑 deliveryDate can fall on either side of submittedAt, by any amount. It is an expectation recorded by whoever sent the delivery note, not an observation:

  • Earlier — goods received on Friday and approved on Monday, or a paper note entered days late.
  • Later — goods that arrived ahead of schedule and were approved before the date the note promised.

Only one bound exists anywhere in the service: a delivery created in the store, header-first, is rejected if its date is more than 14 days in the past. There is no upper bound, and GS1 imports are not bounded at all — an imported note carries whatever date the sending system put on it.

So never order events by deliveryDate, never assume it precedes submittedAt, and never assume the two fall in the same reporting period.

New fields mean a new version

The schema does not permit undeclared fields, so a new field cannot appear on v1. If Goods Received needs to publish something new, it will publish a new version and the catalogue will list it. You will not silently start receiving fields you have not seen.

Reason code ids are open, and enums may grow. Reason codes are the customer's own values, so treat them as opaque strings and never switch exhaustively on them. Handle a shipperType you do not recognise by falling through to a safe default rather than rejecting the message.

Nullable is not the same as absent

unitPrice and currencyCode are always present on a line but may be null — the source system did not supply a price. That is not the same as a price of zero.

🛑 Do not coerce a null price to zero for anything financial. A null price means the delivery carries no cost information for that line and the cost has to come from somewhere else — a purchase order, a price list, or the item master. Booking it as free goods understates cost of goods sold.

The optional fields marked ➖ above may be absent or null. Assume any of them can be missing on any line.