Skip to main content

Vendor ASN line changed

Fires when a vendor's advanced shipping notice (ASN) line is recorded, that is, when a purchase WHSASNItem row (ASN / item information, the packing-structure lines of an inbound load) is inserted, and again when the quantity an existing purchase ASN line announces changes. It gives receiving warehouses, yard and transport systems, and 3PLs an early signal of announced freight (purchase order, shipment, load, item, quantity, unit and batch expiry) before the product receipt is posted. ASN lines that D365 generates for outbound sales and transfer loads do not raise it.

Event classMGCLatticeVendorASNLineChangedBusinessEvent
Contract classMGCLatticeVendorASNLineChangedBusinessEventContract
CategoryAccounts payable
ModuleModuleAxapta::Vendor
Label IDsVendorASNSubmittedBusinessEventName / VendorASNSubmittedBusinessEventDescription
TriggerWHSASNItem.insert() -> new purchase ASN line (Module == Purch); WHSASNItem.update() -> announced quantity of a purchase ASN line changed

Trigger / Hook​

  • Extension class: MGCLatticeWHSASNItem_Table_Extension, decorated with [ExtensionOf(tableStr(WHSASNItem))].
  • CoC method signatures: public void insert() and public void update().
  • Purchase ASN lines only: both methods raise the event only for rows with Module == WHSModule::Purch, the same range D365's inbound load packing structure entities use. Standard warehouse outbound processing also writes WHSASNItem rows, at shipment confirmation for customers set up to generate ASNs (Sales) and for transfer order loads (Transfer); those do not raise the event, even though an outbound ASN line for an intercompany order carries the intercompany purchase order number.
  • New line: after next insert() completes, every inserted purchase ASN line raises one event with isNewLine = true. An ASN with several lines raises several events; group them by shipmentId / loadId.
  • Quantity change: D365 keeps one ASN line per license plate, item, load line and inventory dimensions. When more quantity is announced for such a line (for example when the packing structure is extended on the page), D365 adds it to the existing line instead of inserting a new one, and an import through the inbound packing structure entities that updates an existing line replaces its quantity. After next update(), the event is raised again when the line's quantity differs from its quantity before the update, with isNewLine = false and previousQuantity set to the quantity before the change. Both quantities are in the line's current unit: when the update also changes the unit, the previous quantity is converted the way WHSASNItem.update() itself converts it, so a unit conversion that leaves the amount unchanged raises nothing. Updates that do not change the quantity (for example receiving against the ASN) raise nothing.
  • Inserts and updates that bypass the table methods (doInsert(), doUpdate(), or set-based statements with data methods skipped) do not raise it.
  • Timing: the event is sent inside the transaction that writes the ASN line. send() writes it to the business events commit log in that transaction and it is delivered after commit, so an ASN import that rolls back never delivers the event.
  • Enablement gate: guarded by BusinessEventsConfigurationReader::isBusinessEventEnabled(classStr(MGCLatticeVendorASNLineChangedBusinessEvent)), so the event only sends when activated in the business events catalog.
public void insert()
{
next insert();

if (this.Module == WHSModule::Purch
&& BusinessEventsConfigurationReader::isBusinessEventEnabled(classStr(MGCLatticeVendorASNLineChangedBusinessEvent)))
{
MGCLatticeVendorASNLineChangedBusinessEvent::newFromASNItem(this).send();
}
}

public void update()
{
WHSASNItem before = this.orig();

next update();

if (this.Module != WHSModule::Purch
|| !BusinessEventsConfigurationReader::isBusinessEventEnabled(classStr(MGCLatticeVendorASNLineChangedBusinessEvent)))
{
return;
}

Qty previousQuantity = MGCLatticeVendorASNLineChangedBusinessEvent::previousQuantityInCurrentUnit(before, this);

if (previousQuantity != this.Qty)
{
MGCLatticeVendorASNLineChangedBusinessEvent::newForQuantityChange(this, previousQuantity).send();
}
}

Payload Contract​

One row per [DataMember], in declaration order. Values are read from the inserted or updated WHSASNItem row in the contract's initialize().

JSON fieldDisplay labelType / EDTSourceNotes
legalEntityLegal EntityDataAreaIdWHSASNItem.DataAreaIdCompany ID of the ASN line, in lower case (for example usmf)
purchaseOrderIdPurchase OrderPurchIdWHSASNItem.PurchIdPurchase order of the ASN line
shipmentIdShipment IDWHSShipmentIdWHSASNItem.ShipmentIdWarehouse shipment
loadIdLoad IDWHSLoadIdWHSASNItem.LoadIdWarehouse load
itemIdItem NumberItemIdWHSASNItem.ItemIdItem number
inventTransIdInventory Transaction IDInventTransIdWHSASNItem.InventTransIdLot ID of the purchase order line the ASN line belongs to
quantityQuantityrealWHSASNItem.QtyQuantity the line announces now, in unitId
previousQuantityPrevious QuantityrealWHSASNItem.orig().Qty, converted to the current unitThe quantity the line announced before this change; 0 for a new line. The change is quantity - previousQuantity
isNewLineIs New LinebooleanSet by the triggertrue when the ASN line was just created; false when the event reports a quantity change on a line announced earlier. Written as true/false
unitIdUnitstrWHSASNItem.UOMUnit of measure symbol
descriptionDescriptionstrWHSASNItem.DescriptionASN line description
batchExpirationDateBatch Expiration DatedateWHSASNItem.BatchExpirationDate"\/Date(ms)\/" form; the empty date (1900-01-01, "\/Date(-2208988800000)\/") when no batch expiration applies
changedDateTimeChanged Date TimeutcdatetimeDateTimeUtil::utcNow() when the contract is builtServer time at which the line was created or its quantity changed; not a field of the ASN. "\/Date(ms)\/" form

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; it orders the events of one record, gaps are normal and permanent, see the README), correlationId (String - always Scope:Key; groups emissions that belong to one logical run and defaults to Event: plus a GUID unique to this emission) 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.

This event does not join a run: correlationId is unique to each emission (lines of the same ASN do not share it).

Example Payload​

{
"BusinessEventId": "MGCLatticeVendorASNLineChangedBusinessEvent",
"ControlNumber": 588214,
"EventId": "6A1F0C93-4D27-4B8E-A5C1-2E97B3D0F648",
"EventTime": "\/Date(1790245231000)\/",
"EventTimeIso8601": "2026-09-24T10:20:31.6077578Z",
"MajorVersion": 0,
"MinorVersion": 0,
"BusinessEventLegalEntity": "USMF",
"ContextRecordSubject": "",
"InitiatingUserAADObjectId": "{4A7E1674-7225-13A5-F8B4-3B6EE69B1F66}",
"ParentContextRecordSubjects": [],
"eventSequence": 48377,
"correlationId": "Event:4e8b2d61-9a3c-4f17-b0d5-7c1e6a2f9b40",
"schemaVersion": 1,
"legalEntity": "usmf",
"purchaseOrderId": "00000050",
"shipmentId": "USMF-000005",
"loadId": "USMF-000007",
"itemId": "A0001",
"inventTransId": "012491",
"quantity": 24,
"previousQuantity": 0,
"isNewLine": true,
"unitId": "Pcs",
"description": "HDMI 6' Cables",
"batchExpirationDate": "\/Date(-2208988800000)\/",
"changedDateTime": "\/Date(1790245231000)\/"
}

If 12 more pieces are later added to the same line, the event is raised again for the line with "quantity": 36, "previousQuantity": 24 and "isNewLine": false.

Activation​

In the D365FO client, go to System administration > Setup > Business events > Business events catalog. Find this event by name ("Vendor ASN line changed") or by filtering the Category column to Accounts payable. 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). No other setup is required.