BOM version approved
Fires when the standard approval operation (BomVersionApprove) approves a bill of materials (BOM) version that wasn't approved before. This marks the point at which the version is sanctioned for production planning and costing. The payload carries the item, BOM, approver, validity dates and quantity threshold, so product lifecycle, MES and costing systems can react to an engineering release.
| Event class | MGCLatticeBOMVersionApprovedBusinessEvent |
| Contract class | MGCLatticeBOMVersionApprovedBusinessEventContract |
| Category | Production control |
| Module | ModuleAxapta::ProductionControl |
| Label IDs | BOMVersionApprovedBusinessEventName / BOMVersionApprovedBusinessEventDescription |
| Trigger | BomVersionApprove.run() -> an approval (not a removal of approval) changes the version's Approved flag from No to Yes |
Trigger / Hook
- Extension class:
MGCLatticeBomVersionApprove_Class_Extension, decorated with[ExtensionOf(classStr(BomVersionApprove))]. - CoC method signature:
void run()(no parameters). This is the approval process method, not theBOMVersion.update()table write. - Firing condition: before
next run(), the version'sApprovedflag is read withBOMVersion::findRecId(this.parmRecId()). Afternext run(), if the operation was not a removal of approval (this.parmRemove()is No), the version is re-read, and the event is sent when that record exists and itsApprovedflag changed fromNoYes::NotoNoYes::Yes(MGCLatticeBOMVersionApprovedBusinessEvent::isApprovalTransition()). - Re-approval: the standard operation doesn't stop you from approving a version that is already approved; it saves the approval again with the approver from that run. That doesn't raise the event again. If the approval is removed and the version is approved again later, that new approval raises the event.
- Coverage: every caller of
BomVersionApprove.run()raises the event when it approves a version that wasn't approved. That includes the Approve action on a BOM version, the approval step when an engineering change case is applied (EngChgCaseBOMRouteApplyAction), and the approval action on the BOM version OData entities (BOMBillOfMaterialsVersionODataEntity,BOMBillOfMaterialsVersionODataV2Entity). A version that becomes approved without this operation (for example, when code or an import setsBOMVersion.Approveddirectly) doesn't raise it. When the same run also approves the parent BOM (Approve BOM), no separate event is sent for the BOM. - Transaction:
run()saves the approval inside its ownttsbegin/ttscommit, and the event is sent afterrun()returns.BusinessEventsBase.send()only writes to the commit log inside the current transaction and delivery is asynchronous post-commit, so if the approval runs inside a larger transaction that rolls back, the event is never delivered. - Enablement gate: guarded by
BusinessEventsConfigurationReader::isBusinessEventEnabled(classStr(MGCLatticeBOMVersionApprovedBusinessEvent)), so the event only sends when activated in the business events catalog.
void run()
{
// The activation check is a cached lookup and comes first, so an environment that has not activated the
// event pays no database read. next run() is always called.
boolean eventEnabled = BusinessEventsConfigurationReader::isBusinessEventEnabled(classStr(MGCLatticeBOMVersionApprovedBusinessEvent));
RecId versionRecId = this.parmRecId();
NoYes approvedBefore;
if (eventEnabled)
{
approvedBefore = BOMVersion::findRecId(versionRecId).Approved;
}
next run();
if (eventEnabled && !this.parmRemove())
{
BOMVersion bomVersionLocal = BOMVersion::findRecId(versionRecId);
if (bomVersionLocal.RecId
&& MGCLatticeBOMVersionApprovedBusinessEvent::isApprovalTransition(approvedBefore, bomVersionLocal.Approved))
{
MGCLatticeBOMVersionApprovedBusinessEvent::newFromBOMVersion(bomVersionLocal).send();
}
}
}
Payload Contract
One row per [DataMember], in declaration order. All sources are fields on the re-read BOMVersion record, mapped in initialize().
| JSON field | Display label | Type / EDT | Source | Notes |
|---|---|---|---|---|
legalEntity | Legal Entity | DataAreaId | BOMVersion.DataAreaId | Company the BOM version belongs to. |
itemId | Item ID | ItemId | BOMVersion.ItemId | Item the BOM version produces. |
bomId | BOM ID | BOMId | BOMVersion.BOMId | The BOM. |
bomName | BOM Name | Name | BOMVersion.Name | Name of the BOM version. |
approverPersonnelNumber | Approver Personnel Number | HcmPersonnelNumberId | HcmWorker::find(BOMVersion.Approver).PersonnelNumber | Personnel number of the approving worker; empty when Approver is 0. |
approverName | Approver Name | Name | HcmWorker::find(BOMVersion.Approver).name() | Name of the approving worker; empty when Approver is 0. |
isActive | Is Active | boolean | BOMVersion.Active == NoYes::Yes | Approving doesn't activate a version, so this is false unless the version was already active. |
fromDate | From Date | TransDate | BOMVersion.FromDate | Start of the version's validity. |
toDate | To Date | TransDate | BOMVersion.ToDate | End of the version's validity. |
fromQuantity | From Quantity | ProductFromQuantity | BOMVersion.FromQty | Quantity from which this version applies. |
crossReferences | Cross references | List of MGCLatticeCrossReferenceEventContract | MGCLatticeCrossReferenceEventHelper::listFor(BOMVersion) | Keys external systems hold for the BOM version: 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 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": "MGCLatticeBOMVersionApprovedBusinessEvent",
"ControlNumber": 5637151793,
"EventId": "B099D667-09AE-4B31-8C7F-5FE3F2DAC5BC",
"EventTime": "\/Date(1790169044000)\/",
"EventTimeIso8601": "2026-09-23T13:10:44.8066739Z",
"MajorVersion": 0,
"MinorVersion": 0,
"BusinessEventLegalEntity": "USMF",
"ContextRecordSubject": "",
"InitiatingUserAADObjectId": "{EE6EAF96-160E-F345-DC88-6AFED5E73676}",
"ParentContextRecordSubjects": [],
"eventSequence": 48175,
"correlationId": "Event:35dc55bd-8efa-4b0e-8f02-6785ed703b64",
"schemaVersion": 1,
"legalEntity": "usmf",
"itemId": "D0001",
"bomId": "D0001BOM",
"bomName": "Mid-Range Speaker",
"approverPersonnelNumber": "000234",
"approverName": "Julia Funderburk",
"isActive": false,
"fromDate": "\/Date(1790812800000)\/",
"toDate": "\/Date(1822262400000)\/",
"fromQuantity": 1.00,
"crossReferences": [
{
"externalSystem": "PLM",
"externalKey": "BOMV-4410-B"
}
]
}
Activation
In the D365FO client, go to System administration > Setup > Business events > Business events catalog. Find this event by name ("BOM version approved") or by filtering the Category column to Production control. 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).