Skip to main content

External item changed

Fires when a customer or vendor external item description is created or updated in D365FO. An external item description is a CustVendExternalItem record that maps your item number to the item number and text a specific customer or vendor (or a customer or vendor external item group) uses. Downstream systems can use this event to keep EDI mapping tables, B2B portals, and trading-partner catalogs in step with the cross-references maintained in D365FO. The same event is also emitted in bulk by the external item resync described below.

Event classMGCLatticeExternalItemChangedBusinessEvent
Contract classMGCLatticeExternalItemChangedBusinessEventContract
CategoryInventory management
ModuleModuleAxapta::Inventory
Label IDsExternalItemChangedBusinessEventName / ExternalItemChangedBusinessEventDescription
TriggerCustVendExternalItem Inserted and Updated data events (every insert and every update), plus the on-demand external item resync

Trigger / Hook​

  • Handler class: MGCLatticeExternalItemChangedEventHandlers, with two static data event handlers:
    • [DataEventHandler(tableStr(CustVendExternalItem), DataEventType::Inserted)] on public static void CustVendExternalItem_onInserted(Common _sender, DataEventArgs _e), which sends the event with isUpdate = false.
    • [DataEventHandler(tableStr(CustVendExternalItem), DataEventType::Updated)] on public static void CustVendExternalItem_onUpdated(Common _sender, DataEventArgs _e), which sends the event with isUpdate = true.
  • Firing condition: every insert and every update of a CustVendExternalItem record, whatever the source (the external item descriptions page, a data entity or import, or code), and for every module type (customer, vendor, customer group, vendor group). There's no field-change filter, so any update sends an event, even one that changes nothing a subscriber cares about. Deleting a cross-reference doesn't send an event.
  • Transaction: the handlers run inside the insert or update. When that write is part of a larger transaction, the event is written to the business events commit log inside it and is delivered only if the transaction commits.
  • Enablement gate: both handlers check BusinessEventsConfigurationReader::isBusinessEventEnabled(classStr(MGCLatticeExternalItemChangedBusinessEvent)) before building the event.
  • Bulk resync (second send site): the action menu item MGCLatticeProcessAllExternalItems ("Publish all external item business events (resync)") asks for confirmation and then runs MGCLatticeBulkEventPublisher for the ExternalItem entity type in a sandbox session (it isn't a batch job). MGCLatticeBulkEventSourceExternalItem.publishAll() sends the event for every CustVendExternalItem record in the current legal entity, always with isUpdate = false, and the publisher pauses for 250 ms after every 500 events. Before reading any record, the resync checks that the event is published in the current legal entity (business events framework on, event activated). If not, it stops with "The business event MGCLatticeExternalItemChangedBusinessEvent is not activated in legal entity <company>, so no events were published. Activate it in the business events catalog and run the job again." The finish message counts only events actually published. The menu item is at System administration > Periodic tasks > Lattice business events > Publish all external item business events (resync), and the MGCLatticeProcessAllExternalItemsExecute privilege grants it to the integration administrator role through the batch maintenance duty (MGCLatticeBatchMaintenanceDuty).
[DataEventHandler(tableStr(CustVendExternalItem), DataEventType::Updated)]
public static void CustVendExternalItem_onUpdated(Common _sender, DataEventArgs _e)
{
CustVendExternalItem custVendExternalItem = _sender as CustVendExternalItem;

if (BusinessEventsConfigurationReader::isBusinessEventEnabled(classStr(MGCLatticeExternalItemChangedBusinessEvent)))
{
MGCLatticeExternalItemChangedBusinessEvent::newFromCustVendExternalItem(custVendExternalItem, true).send();
}
}

The resync loop in MGCLatticeBulkEventSourceExternalItem.publishAll():

while select custVendExternalItem
{
MGCLatticeExternalItemChangedBusinessEvent businessEvent = MGCLatticeExternalItemChangedBusinessEvent::newFromCustVendExternalItem(custVendExternalItem, false);

businessEvent.send();

_publisher.onPublished(businessEvent, custVendExternalItem.ExternalItemId);
}

Payload Contract​

One row per [DataMember], in declaration order. All sources are fields on the triggering CustVendExternalItem record, mapped in initialize().

JSON fieldDisplay labelType / EDTSourceNotes
legalEntityLegal EntityDataAreaIdCustVendExternalItem.dataAreaIdCompany of the cross-reference
isUpdateIs UpdatebooleanSet by the senderfalse from the Inserted handler and from the resync; true from the Updated handler. Serialized as true/false
itemIdItem NumberItemIdCustVendExternalItem.ItemIdYour internal item number
externalItemIdExternal Item NumberExternalItemIdCustVendExternalItem.ExternalItemIdThe customer's or vendor's item number
externalItemTextExternal Item TextExternalItemFreeTxtCustVendExternalItem.ExternalItemTxtThe customer's or vendor's item text
descriptionDescriptionDescriptionCustVendExternalItem.Description
accountRelationAccount NumberCustVendRelCustVendExternalItem.CustVendRelationCustomer or vendor account, or the external item group for the group module types
moduleTypeModule Typeintenum2int(CustVendExternalItem.ModuleType)ModuleInventPurchSalesVendCustGroup value: 3 = Vend, 4 = Cust, 5 = VendGroup, 6 = CustGroup
moduleTypeNameModule Type Namestrenum2Symbol(enumNum(ModuleInventPurchSalesVendCustGroup), ...)Symbol of the module type, for example Cust
inventDimIdInventory Dimension IDInventDimIdCustVendExternalItem.InventDimIdInventory dimension combination the cross-reference applies to
abcCategoryABC Categoryintenum2int(CustVendExternalItem.ABCCategory)ABC enum as an integer: 0 = None, 1 = A, 2 = B, 3 = C
abcCategoryNameABC Category Namestrenum2Symbol(enumNum(ABC), abcCategory)Enum symbol: None, A, B or C

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": "MGCLatticeExternalItemChangedBusinessEvent",
"ControlNumber": 5637146214,
"EventId": "3C9E5B7A-1F24-4D8B-9A63-7E0C2D4F8B15",
"EventTime": "\/Date(1790244131000)\/",
"EventTimeIso8601": "2026-09-24T10:02:11.2191931Z",
"MajorVersion": 0,
"MinorVersion": 0,
"BusinessEventLegalEntity": "USMF",
"ContextRecordSubject": "",
"InitiatingUserAADObjectId": "{83564AF5-3123-8327-8F6E-9E02A4377FFF}",
"ParentContextRecordSubjects": [],
"eventSequence": 48213,
"correlationId": "Event:626670ca-8973-4d17-886d-cd83ed9dbac7",
"schemaVersion": 1,
"legalEntity": "usmf",
"isUpdate": true,
"itemId": "D0001",
"externalItemId": "CX-44871",
"externalItemText": "Mid-range speaker, black",
"description": "Contoso Retail catalog number",
"accountRelation": "US-003",
"moduleType": 4,
"moduleTypeName": "Cust",
"inventDimId": "AllBlank",
"abcCategory": 0,
"abcCategoryName": "None"
}

Activation​

In the D365FO client, go to System administration > Setup > Business events > Business events catalog. Find this event by name ("External item changed") 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).

No other setup is needed for the insert and update events. To seed a new subscriber with every existing cross-reference, activate the event first and then have an integration administrator run the resync menu item MGCLatticeProcessAllExternalItems in each legal entity; it sends one event per record, marked isUpdate = false.