Transfer order shipped
Fires when an inventory transfer order transitions into the Shipped status, signaling that goods have physically left the source warehouse and are now in transit to the destination warehouse. Downstream systems can use this moment to start in-transit tracking, notify the receiving site, or update supply/demand visibility.
| Event class | MGCLatticeTransferOrderShippedBusinessEvent |
| Contract class | MGCLatticeTransferOrderShippedBusinessEventContract |
| Category | Inventory management |
| Module | ModuleAxapta::Inventory |
| Label IDs | TransferOrderShippedBusinessEventName / TransferOrderShippedBusinessEventDescription |
| Trigger | InventTransferUpdShip.run() -> status delta into InventTransferStatus::Shipped (cancellation-safe) |
Trigger / Hook
- Extension class:
MGCLatticeInventTransferUpdShip_Class_Extension,[ExtensionOf(classStr(InventTransferUpdShip))] - CoC method signature:
void run()(no parameters) - Hooked on the shipment update process method rather than the
InventTransferTable.update()table write, so entity/import writes and set-based updates of the transfer order don't fire it. - Firing condition:
- The pre-update status is captured via
InventTransferTable::find(transferId).TransferStatusBEFOREnext run(), then the committed record is re-read afterward. The transfer order id comes from the publicthis.parmParmBuffer(), read as anInventTransferParmTable(the protectedinventTransferTablemember has no accessor). - The event fires only on a genuine transition into
Shipped:origStatus != InventTransferStatus::Shipped && transferTable.TransferStatus == InventTransferStatus::Shipped- a delta guard that suppresses cancellations and partial shipments. run()is also invoked for shipment cancellation (InventTransferMultiShipCancel.updateShipment()), so the status-delta guard is mandatory and cancellation-safe.
- The pre-update status is captured via
- Enablement gate:
BusinessEventsConfigurationReader::isBusinessEventEnabled(classStr(MGCLatticeTransferOrderShippedBusinessEvent))must return true before the event is sent. - The tts boundary lives in the parent
InventTransferUpd.run()(status set inupdateInventTransferOrder()), so firing afternextis post-commit. When the shipment update runs inside a larger transaction, the event is written to the business events commit log inside it and is delivered only if that transaction commits.
void run()
{
// The activation check is a cached lookup and comes first, so an environment that has not activated the
// event pays no database read. next run() is always called.
boolean eventEnabled = BusinessEventsConfigurationReader::isBusinessEventEnabled(classStr(MGCLatticeTransferOrderShippedBusinessEvent));
InventTransferId transferId;
InventTransferStatus origStatus;
if (eventEnabled)
{
// parmParmBuffer() is typed as the ParmBuffer map; assign to the concrete parm table to reach TransferId.
InventTransferParmTable parmTable = this.parmParmBuffer();
transferId = parmTable.TransferId;
origStatus = InventTransferTable::find(transferId).TransferStatus;
}
next run();
if (eventEnabled)
{
InventTransferTable transferTable = InventTransferTable::find(transferId);
if (origStatus != InventTransferStatus::Shipped && transferTable.TransferStatus == InventTransferStatus::Shipped)
{
MGCLatticeTransferOrderShippedBusinessEvent::newFromInventTransferTable(transferTable).send();
}
}
}
Payload Contract
| JSON field | Display label | Type / EDT | Source | Notes |
|---|---|---|---|---|
legalEntity | Legal Entity | DataAreaId | _inventTransferTable.DataAreaId | Company / data area of the transfer order |
transferOrderId | Transfer Order ID | InventTransferId | _inventTransferTable.TransferId | Transfer order number |
fromWarehouseId | From Warehouse | InventLocationId | _inventTransferTable.InventLocationIdFrom | Source (shipping) warehouse |
toWarehouseId | To Warehouse | InventLocationId | _inventTransferTable.InventLocationIdTo | Destination (receiving) warehouse |
transferStatus | Transfer Status | int | enum2int(_inventTransferTable.TransferStatus) | Integer value of the InventTransferStatus enum (0 = Created, 1 = Shipped, 2 = Received); 1 at fire time |
transferStatusName | Transfer Status Name | str | enum2Symbol(enumNum(InventTransferStatus), enum2int(_inventTransferTable.TransferStatus)) | Symbolic enum name string, e.g. "Shipped" |
crossReferences | Cross references | List of MGCLatticeCrossReferenceEventContract | MGCLatticeCrossReferenceEventHelper::listFor(InventTransferTable) | Keys external systems hold for the transfer order: one { externalSystem, externalKey } object per external system, ordered by external system, historical keys included; [] when it has none. See Cross-reference keys. |
Every Lattice event also carries the Lattice ordering and correlation envelope, inherited from MGCLatticeBusinessEventContractBase: eventSequence (Int64 - increasing across the whole catalog and all companies, allocated when the event is raised inside the business transaction rather than when it commits; it orders the events of one business record, gaps are permanent and normal, and it must not be used to re-sequence the whole stream), correlationId (String, always Scope:Key - groups emissions that belong to one logical run; Event:<GUID> unique to this emission when the event belongs to no run) and schemaVersion (Int - the payload shape version; 1 for this contract). D365 F&O also adds its standard envelope fields to every business event payload: BusinessEventId, BusinessEventLegalEntity, ContextRecordSubject, ControlNumber, EventId, EventTime, EventTimeIso8601, InitiatingUserAADObjectId, MajorVersion, MinorVersion, and ParentContextRecordSubjects. Do not rely on the order of members in the delivered JSON.
Example Payload
{
"BusinessEventId": "MGCLatticeTransferOrderShippedBusinessEvent",
"ControlNumber": 558102,
"EventId": "A3F5C2E1-9B4D-4F7A-8C12-6D2E9F0A1B34",
"EventTime": "\/Date(1790251798000)\/",
"EventTimeIso8601": "2026-09-24T12:09:58.1526419Z",
"MajorVersion": 0,
"MinorVersion": 0,
"BusinessEventLegalEntity": "USMF",
"ContextRecordSubject": "",
"InitiatingUserAADObjectId": "{7B2FB6C2-B492-47EC-DB77-363DECAB6C0E}",
"ParentContextRecordSubjects": [],
"eventSequence": 48391,
"correlationId": "Event:960ef52e-03af-4b0a-82c8-2145da9bb204",
"schemaVersion": 1,
"legalEntity": "usmf",
"transferOrderId": "TRF-000042",
"fromWarehouseId": "13",
"toWarehouseId": "24",
"transferStatus": 1,
"transferStatusName": "Shipped",
"crossReferences": [
{
"externalSystem": "WMS",
"externalKey": "TO-557201"
}
]
}
Activation
In the D365FO client, go to System administration > Setup > Business events > Business events catalog. Find this event by name ("Transfer order shipped") or by filtering the Category column to Inventory management. Select it and use Activate to bind it to a configured endpoint (for example, an Azure Service Bus queue, Event Grid topic, or HTTPS webhook).
Related Events
- Transfer Order Received - sibling event sent from
MGCLatticeInventTransferUpdReceive_Class_Extension.run()when the status transitions toReceived. - Transfer order picking list created - raised when a picking list is generated for the transfer order, before it ships.
- Warehouse Load Shipped - the load-level shipping moment for warehouse-managed transfers.