Return order received
Fires when returned goods are registered on a sales return order line - the moment the line's return status changes to Registered, which is when D365 records the arrival and the disposition code. RMA, refund and quality systems can use it to start inspection, replacement or refund processing as soon as the goods are in. One event is sent per return order line; when only part of a line's expected quantity is registered, D365 first splits the line and the event describes the registered part.
| Event class | MGCLatticeReturnOrderReceivedBusinessEvent |
| Contract class | MGCLatticeReturnOrderReceivedBusinessEventContract |
| Category | Sales orders |
| Module | ModuleAxapta::SalesOrder |
| Label IDs | ReturnOrderReceivedBusinessEventName / ReturnOrderReceivedBusinessEventDescription |
| Trigger | Return registration (SalesLine.returnLineRegisterUpdate() / SalesReturnNonStockedRegistrationHelper.updateOnRegistration()) -> ReturnStatus changes to Registered |
Trigger / Hook
Process hooks on the two registration methods - the only standard code that sets ReturnStatusLine::Registered:
- Stocked return lines:
MGCLatticeSalesLine_Table_Extension,[ExtensionOf(tableStr(SalesLine))], wrappingpublic void returnLineRegisterUpdate(InventQtyRegistered _qtyRegistered, PdsCWQtyRegistered _cwQtyRegistered)- the arrival registration step, called fromreturnLineArrivalUpdate()for a positive registered quantity. - Non-stocked return lines:
MGCLatticeSalesReturnNonStockedRegistrationHelper_Class_Extension,[ExtensionOf(classStr(SalesReturnNonStockedRegistrationHelper))], wrappingpublic void updateOnRegistration(). - Firing condition: each wrapper captures the line's return status before
next; afterwardsMGCLatticeReturnLifecycleEventHandlers::raiseReturnOrderReceived()reads the line again by its lot ID and sends the event when the status moved into Registered (isReturnRegistration). A line that was already Registered does not fire again, and canceling a registration (a negative registered quantity) raises nothing. - Both methods split the line first when less than the expected return quantity is registered; the event describes the registered line as the registration left it.
- Other writers: sales lines written by data entities, imports or other updates do not raise the event, even when they set the return status.
- Timing:
send()writes to the business events commit log inside the registration transaction and delivery happens asynchronously after commit, so a registration that is rolled back never delivers. - Enablement gate:
BusinessEventsConfigurationReader::isBusinessEventEnabled(classStr(MGCLatticeReturnOrderReceivedBusinessEvent)), checked before the line is read again.
// MGCLatticeSalesLine_Table_Extension
public void returnLineRegisterUpdate(InventQtyRegistered _qtyRegistered, PdsCWQtyRegistered _cwQtyRegistered)
{
ReturnStatusLine previousStatus = this.ReturnStatus;
next returnLineRegisterUpdate(_qtyRegistered, _cwQtyRegistered);
MGCLatticeReturnLifecycleEventHandlers::raiseReturnOrderReceived(this.InventTransId, previousStatus);
}
// MGCLatticeSalesReturnNonStockedRegistrationHelper_Class_Extension
public void updateOnRegistration()
{
boolean eventEnabled = BusinessEventsConfigurationReader::isBusinessEventEnabled(classStr(MGCLatticeReturnOrderReceivedBusinessEvent));
InventTransId inventTransId = this.parmSalesLine().InventTransId;
ReturnStatusLine previousStatus;
if (eventEnabled)
{
previousStatus = SalesLine::findInventTransId(inventTransId).ReturnStatus;
}
next updateOnRegistration();
if (eventEnabled)
{
MGCLatticeReturnLifecycleEventHandlers::raiseReturnOrderReceived(inventTransId, previousStatus);
}
}
Payload Contract
One row per [DataMember], in declaration order. initialize() reads the registered SalesLine, looks up its SalesTable header for the RMA number and its inventory dimensions for the warehouse.
| JSON field | Display label | Type / EDT | Source | Notes |
|---|---|---|---|---|
legalEntity | Legal Entity | DataAreaId | SalesLine.DataAreaId | Company of the return order, as stored (lowercase, for example usmf). |
rmaNumber | RMA Number | ReturnItemNum | SalesTable.ReturnItemNum (header of the line's order) | Return merchandise authorization number. |
salesOrderId | Sales Order ID | SalesId | SalesLine.SalesId | Return order number. |
itemId | Item ID | ItemId | SalesLine.ItemId | Returned item. |
quantity | Quantity | SalesQty | SalesLine.SalesQty | Line quantity in the sales unit at registration. Return lines carry negative quantities, so this is normally negative (for example -2.00). |
dispositionCode | Disposition Code | ReturnDispositionCodeId | SalesLine.ReturnDispositionCodeId | Disposition code recorded at registration. |
warehouseId | Warehouse ID | InventLocationId | SalesLine.inventDim().InventLocationId | The receiving warehouse: the warehouse on the return line's inventory dimensions, which is where the goods are registered. |
crossReferences | Cross references | List of MGCLatticeCrossReferenceEventContract | MGCLatticeCrossReferenceEventHelper::listFor(SalesLine) | Keys external systems hold for the return order line that was received: 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 sent inside the business transaction; it orders the events of one record, but two transactions can commit in the opposite order of their values, and gaps are normal and permanent - see the events overview), correlationId (String - always Scope:Key: Event: followed by a GUID unique to this emission, unless the event joins a 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": "MGCLatticeReturnOrderReceivedBusinessEvent",
"ControlNumber": 5637148741,
"EventId": "2B9F4E61-8D07-4C3A-B5E2-9A6C1F0D8E37",
"EventTime": "\/Date(1790329263000)\/",
"EventTimeIso8601": "2026-09-25T09:41:03.5933659Z",
"MajorVersion": 0,
"MinorVersion": 0,
"BusinessEventLegalEntity": "USMF",
"ContextRecordSubject": "",
"InitiatingUserAADObjectId": "{A8868484-2F5C-4377-D677-36773EDA2E39}",
"ParentContextRecordSubjects": [],
"eventSequence": 48391,
"correlationId": "Event:ff895e38-fd22-40c2-8cb4-e01bc0750577",
"schemaVersion": 1,
"legalEntity": "usmf",
"rmaNumber": "RMA-000017",
"salesOrderId": "001207",
"itemId": "D0001",
"quantity": -2.00,
"dispositionCode": "11",
"warehouseId": "13",
"crossReferences": [
{
"externalSystem": "SHOP",
"externalKey": "RMA-20931-1"
}
]
}
Activation
In the D365FO client, go to System administration > Setup > Business events > Business events catalog. Find this event by name ("Return order received") or by filtering the Category column to Sales orders. 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
- Sales return order created - the return order this line belongs to was created.
- Return credit note posted - the credit note posted for the return order after the goods are received.
- Sales order invoice posted - also fires when the return order's credit note is posted.
- Sales order line changed - also raised by return-line updates that change quantity, price or requested dates.