Sales order line changed
Fires when a saved sales order line's quantity, unit price, requested receipt date or requested ship date changes. The payload carries the before and after values of all four fields, so order-management, warehouse and customer-notification systems can act on the delta - re-plan a shipment, re-quote, or tell the customer - without diffing the order themselves. It fills the modification gap between the order-level events such as Sales order confirmed and Sales order canceled.
| Event class | MGCLatticeSalesOrderLineChangedBusinessEvent |
| Contract class | MGCLatticeSalesOrderLineChangedBusinessEventContract |
| Category | Sales orders |
| Module | ModuleAxapta::SalesOrder |
| Label IDs | SalesOrderLineChangedBusinessEventName / SalesOrderLineChangedBusinessEventDescription |
| Trigger | SalesLine updated (DataEventType::Updated) -> SalesQty, SalesPrice, ReceiptDateRequested or ShippingDateRequested changed |
Trigger / Hook
- Handler class:
MGCLatticeSalesOrderLineChangedEventHandlers, methodSalesLine_onUpdated, decorated with[DataEventHandler(tableStr(SalesLine), DataEventType::Updated)]. - Method signature:
public static void SalesLine_onUpdated(Common _sender, DataEventArgs _e). - Firing condition: the handler compares the updated line with
orig()and sends the event when at least one ofSalesQty,SalesPrice,ReceiptDateRequestedorShippingDateRequesteddiffers. One event per updated line; changes to any other field (discounts, warehouse, confirmed dates, and so on) do not raise it. The previous values come fromorig(), which still holds the pre-update record whileUpdatedhandlers run. - Scope: every
SalesLineupdate qualifies, whoever makes it - users, data entities, or D365's own processing. There is no order-type filter, so return order lines (which carry negative quantities) and other sales order types raise it too. Creating or deleting a line does not. - Writes that bypass the table's update pipeline (
doUpdate(), or set-based updates run withskipEvents(true)) do not raise it. - Timing:
send()writes to the business events commit log inside the updating transaction and delivery happens asynchronously after commit, so an update that is rolled back never delivers. - Enablement gate:
BusinessEventsConfigurationReader::isBusinessEventEnabled(classStr(MGCLatticeSalesOrderLineChangedBusinessEvent)), evaluated together with the change check.
[DataEventHandler(tableStr(SalesLine), DataEventType::Updated)]
public static void SalesLine_onUpdated(Common _sender, DataEventArgs _e)
{
SalesLine salesLine = _sender as SalesLine;
SalesLine origLine = salesLine.orig();
boolean changed =
salesLine.SalesQty != origLine.SalesQty
|| salesLine.SalesPrice != origLine.SalesPrice
|| salesLine.ReceiptDateRequested != origLine.ReceiptDateRequested
|| salesLine.ShippingDateRequested != origLine.ShippingDateRequested;
if (changed && BusinessEventsConfigurationReader::isBusinessEventEnabled(classStr(MGCLatticeSalesOrderLineChangedBusinessEvent)))
{
MGCLatticeSalesOrderLineChangedBusinessEvent::newFromSalesLine(
salesLine,
origLine.SalesQty,
origLine.SalesPrice,
origLine.ReceiptDateRequested,
origLine.ShippingDateRequested).send();
}
}
Payload Contract
One row per [DataMember], in declaration order. Current values come from the updated SalesLine; previous values are the orig() values passed in by the handler. All four before/after pairs are always populated - a pair that did not change carries the same value twice.
| JSON field | Display label | Type / EDT | Source | Notes |
|---|---|---|---|---|
legalEntity | Legal Entity | DataAreaId | SalesLine.DataAreaId | Company of the order, as stored (lowercase, for example usmf). |
salesOrderId | Sales Order ID | SalesId | SalesLine.SalesId | Sales order number. |
itemId | Item Number | ItemId | SalesLine.ItemId | Item on the line. |
inventTransId | Inventory Transaction Id | InventTransId | SalesLine.InventTransId | Lot ID that uniquely identifies the line; use it as the line key. |
lineNumber | Line Number | LineNum | SalesLine.LineNum | Line number (a real number in D365, for example 1.0). |
previousQuantity | Previous Quantity | SalesQty | SalesLine.orig().SalesQty | Quantity before the change, in the sales unit. |
currentQuantity | Current Quantity | SalesQty | SalesLine.SalesQty | Quantity after the change. |
previousPrice | Previous Price | SalesPrice | SalesLine.orig().SalesPrice | Unit price before the change, in currencyCode. |
currentPrice | Current Price | SalesPrice | SalesLine.SalesPrice | Unit price after the change. |
previousReceiptDate | Previous Receipt Date | TransDate | SalesLine.orig().ReceiptDateRequested | Requested receipt date before the change (date, \/Date(milliseconds)\/ form). |
currentReceiptDate | Current Receipt Date | TransDate | SalesLine.ReceiptDateRequested | Requested receipt date after the change. |
previousShippingDate | Previous Shipping Date | TransDate | SalesLine.orig().ShippingDateRequested | Requested ship date before the change. |
currentShippingDate | Current Shipping Date | TransDate | SalesLine.ShippingDateRequested | Requested ship date after the change. |
currencyCode | Currency Code | CurrencyCode | SalesLine.CurrencyCode | Currency of the prices. |
crossReferences | Cross references | List of MGCLatticeCrossReferenceEventContract | MGCLatticeCrossReferenceEventHelper::listFor(SalesLine) | Keys external systems hold for the sales order line: 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": "MGCLatticeSalesOrderLineChangedBusinessEvent",
"ControlNumber": 5637148768,
"EventId": "7E0D3B95-4A26-4C81-9F3E-1B5C8D2A6F07",
"EventTime": "\/Date(1790331738000)\/",
"EventTimeIso8601": "2026-09-25T10:22:18.4541612Z",
"MajorVersion": 0,
"MinorVersion": 0,
"BusinessEventLegalEntity": "USMF",
"ContextRecordSubject": "",
"InitiatingUserAADObjectId": "{673F0A8D-8247-957C-91F0-D9F769A5BE50}",
"ParentContextRecordSubjects": [],
"eventSequence": 48405,
"correlationId": "Event:b321e91a-3341-49f1-b988-d04cc11f79cc",
"schemaVersion": 1,
"legalEntity": "usmf",
"salesOrderId": "000745",
"itemId": "D0001",
"inventTransId": "USMF-001234567",
"lineNumber": 1.0,
"previousQuantity": 10.00,
"currentQuantity": 12.00,
"previousPrice": 185.00,
"currentPrice": 185.00,
"previousReceiptDate": "\/Date(1791158400000)\/",
"currentReceiptDate": "\/Date(1791763200000)\/",
"previousShippingDate": "\/Date(1790812800000)\/",
"currentShippingDate": "\/Date(1791504000000)\/",
"currencyCode": "USD",
"crossReferences": [
{
"externalSystem": "SHOP",
"externalKey": "WEB-1004871-2"
}
]
}
Activation
In the D365FO client, go to System administration > Setup > Business events > Business events catalog. Find this event by name ("Sales order line changed") 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 order confirmed - confirmation posting for the order.
- Sales order canceled - cancellation of the order.
- Return order received - also raised from
SalesLineupdates, for return lines reaching Registered. - Sales price trade agreement changed - sales price agreements that feed line prices.