Customer hold status changed
Fires when a customer's hold status - CustTable.Blocked, the Invoicing and delivery on hold field - changes to a different value during a CustTable update, for example when a customer is placed on hold for all transactions, blocked for invoicing only, or released from hold. It is the same field the vendor twin Vendor hold status changed reports; it is not a credit management hold. The payload carries the customer identity, the new and the previous hold status (each as an integer and a symbol name), and the customer's credit limit, so downstream order-management, e-commerce and credit-control systems can react in near real time.
| Event class | MGCLatticeCustomerHoldStatusChangedBusinessEvent |
| Contract class | MGCLatticeCustomerHoldStatusChangedBusinessEventContract |
| Category | Accounts receivable |
| Module | ModuleAxapta::Customer |
| Label IDs | CustomerCreditHoldChangedBusinessEventName / CustomerCreditHoldChangedBusinessEventDescription |
| Trigger | CustTable.update() -> orig().Blocked != this.Blocked |
Trigger / Hook
- Extension class
MGCLatticeCustTable_Table_Extension,[ExtensionOf(tableStr(CustTable))]. - CoC method signature:
void update(boolean _updateSmmBusRelTable, boolean _updateParty). - Firing condition: when the event is activated (checked first, a cached lookup), the method captures
this.orig().Blockedbefore callingnext update(...), then compares it againstthis.Blockedafter the update. The event is sent only whenorigBlocked != this.Blocked- any change between two values counts (for exampleInvoicetoAll, not only on/off hold). The sameupdate()method independently sendsMGCLatticeCustomerUpdatedBusinessEventon every update, so a Blocked change raises both events, the Customer updated event first. - Not covered: a customer that is created already blocked (inserts do not raise this event), and writes that bypass
CustTable.update()(doUpdate(), or set-based updates run withskipDataMethods(true)). - Timing:
send()runs immediately after the update and writes the event to the business events commit log within whatever transaction the caller has open; if that transaction is rolled back, the event is rolled back with it and never delivered. Delivery happens asynchronously after commit. - Enablement gate:
BusinessEventsConfigurationReader::isBusinessEventEnabled(classStr(MGCLatticeCustomerHoldStatusChangedBusinessEvent))must return true (the event is activated for the current legal entity); when it is not, the original buffer is not read at all.
void update(boolean _updateSmmBusRelTable, boolean _updateParty)
{
// The activation check is a cached lookup; read the original buffer only when the event is on.
boolean holdEventEnabled = BusinessEventsConfigurationReader::isBusinessEventEnabled(classStr(MGCLatticeCustomerHoldStatusChangedBusinessEvent));
CustVendorBlocked origBlocked = holdEventEnabled ? this.orig().Blocked : this.Blocked;
next update(_updateSmmBusRelTable, _updateParty);
if (BusinessEventsConfigurationReader::isBusinessEventEnabled(classStr(MGCLatticeCustomerUpdatedBusinessEvent)))
{
MGCLatticeCustomerUpdatedBusinessEvent::newFromCustTable(this).send();
}
if (holdEventEnabled && origBlocked != this.Blocked)
{
MGCLatticeCustomerHoldStatusChangedBusinessEvent::newFromCustTable(this, origBlocked).send();
}
}
Payload Contract
One row per [DataMember], in declaration order. All sources are read from the updated CustTable record in initialize().
| JSON field | Display label | Type / EDT | Source | Notes |
|---|---|---|---|---|
legalEntity | Legal Entity | DataAreaId | CustTable.DataAreaId | Company / data area of the customer record, in lower case (for example usmf). |
customerAccount | Customer Account | CustAccount | CustTable.AccountNum | Customer account number. |
customerName | Customer Name | str | CustTable.name() | Resolved via the name() display method (DirParty name lookup). |
blockedStatus | Blocked Status | int | enum2int(CustTable.Blocked) | The new value of the CustVendorBlocked enum as an integer: 0 = No, 1 = Invoice, 2 = All, 3 = Payment, 4 = Requisition, 5 = Never, 6 = PurchOrder. |
blockedStatusName | Blocked Status Name | str | enum2Symbol(enumNum(CustVendorBlocked), enum2int(CustTable.Blocked)) | Symbol of the new value (for example No, Invoice, All, Never). |
previousBlockedStatus | Previous Blocked Status | int | enum2int(CustTable.orig().Blocked) | The value before the update, same integers as blockedStatus. |
previousBlockedStatusName | Previous Blocked Status Name | str | enum2Symbol(enumNum(CustVendorBlocked), ...) | Symbol of the value before the update. |
creditLimitMST | Credit Limit MST | AmountMST | CustTable.CreditMax | Customer credit limit, in the legal entity's accounting currency (CreditMax is EDT CustCreditMaxMST). |
crossReferences | Cross references | List of MGCLatticeCrossReferenceEventContract | MGCLatticeCrossReferenceEventHelper::listFor(CustTable) | Keys external systems hold for the customer: 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, not 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; an event that joins no run carries Event: followed by a lower-case 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.
Example Payload
{
"BusinessEventId": "MGCLatticeCustomerHoldStatusChangedBusinessEvent",
"ControlNumber": 5637148502,
"EventId": "1F2A4C6E-8B0D-42F1-9A3C-7E5D1B9F0C44",
"EventTime": "\/Date(1790247806000)\/",
"EventTimeIso8601": "2026-09-24T11:03:26.6507400Z",
"MajorVersion": 0,
"MinorVersion": 0,
"BusinessEventLegalEntity": "USMF",
"ContextRecordSubject": "",
"InitiatingUserAADObjectId": "{9BC563FE-C60E-E236-CEC3-7D55FC1F98F1}",
"ParentContextRecordSubjects": [],
"eventSequence": 48228,
"correlationId": "Event:d41f8a06-3e27-4b9c-a5d8-6c1e2f90b347",
"schemaVersion": 1,
"legalEntity": "usmf",
"customerAccount": "US-027",
"customerName": "Cave Wholesale Bikes",
"blockedStatus": 2,
"blockedStatusName": "All",
"previousBlockedStatus": 0,
"previousBlockedStatusName": "No",
"creditLimitMST": 50000.00,
"crossReferences": [
{
"externalSystem": "CRM",
"externalKey": "ACC-000481"
}
]
}
Activation
In the D365FO client, go to System administration > Setup > Business events > Business events catalog. Find the event by its name ("Customer hold status changed") or by filtering the Category column to Accounts receivable. 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
- Customer updated - sibling event fired from the same
CustTable.update()method on every update. - Customer created - sibling event fired from
CustTable.insert()in the same extension. - Customer credit limit exceeded - fires when a credit limit check finds the customer's exposure over the limit, independent of the Blocked status.
- Collection case changed - collections cases opened or closed for the customer.
- Vendor hold status changed - the Accounts payable counterpart for vendor holds.