Aggregate measurement refresh completed
Fires when a full-reset refresh of an entity store aggregate measurement finishes, successfully or not. The platform's measurement processor records each completed full refresh on the measurement's BIMeasurementProcessingStatisticsFull row; this event publishes that outcome - the measurement name, status, start and completion times, and duration - so a BI or data pipeline can pick up fresh entity store data, or alert on a failed refresh, instead of polling the processing statistics.
| Event class | MGCLatticeAggregateMeasurementRefreshedBusinessEvent |
| Contract class | MGCLatticeAggregateMeasurementRefreshedBusinessEventContract |
| Category | System administration |
| Module | ModuleAxapta::SystemAdministration |
| Label IDs | AggregateMeasurementRefreshedBusinessEventName / AggregateMeasurementRefreshedBusinessEventDescription |
| Trigger | BIMeasurementProcessingStatisticsFull.update() -> terminal LastExecutionStatus with a new LastProcessed start time |
Trigger / Hook
- Extension class:
MGCLatticeBIMeasurementProcessingStatisticsFull_Table_Extension, decorated with[ExtensionOf(tableStr(BIMeasurementProcessingStatisticsFull))]. - CoC method signature:
public void update(). - Why a table hook: the platform processor (
BIMeasurementProcessor) records a finished full refresh by callingBIMeasurementProcessingStatisticsFull::updateStatistics(), which writes the start time (LastProcessed), duration (LastDuration) and terminal status onto the measurement's statistics row with a record-basedupdate(). There's no separate process method to hook. - Firing condition: after
next update(), the event sends when the newLastExecutionStatusisSucceededorFailedandLastProcesseddiffers from itsorig()value. At the start of processing the platform stamps the rowInProgresswithout touchingLastProcessed, so that write is ignored; the completion write carries the new start time, so the event fires once per measurement per full refresh - including a repeatedSucceeded->Succeededrun. - Scope: full-reset refreshes only. Incremental updates record their results in
BIMeasurementProcessingStatisticsIncremental, which has no execution status and isn't hooked, so they don't raise this event. - Timing:
updateStatistics()performs the update inside its ownttsbegin/ttscommit, sosend()writes to the business events commit log inside that transaction and the event is delivered only after it commits. - The statistics table is shared across companies (
SaveDataPerCompany= No), so the payload carries no legal entity; an entity store refresh isn't company specific. - Company: the statistics row belongs to no company, so activation is checked in the company of the session that writes it (for a scheduled entity store refresh, the batch job's company), and
BusinessEventLegalEntityis that company. Activate the event for all legal entities, or in the company that runs the refresh jobs. - Enablement gate:
BusinessEventsConfigurationReader::isBusinessEventEnabled(classStr(MGCLatticeAggregateMeasurementRefreshedBusinessEvent)), in the writing session's company.
public void update()
{
utcdatetime origProcessed = this.orig().LastProcessed;
next update();
if (MGCLatticeAggregateMeasurementRefreshedBusinessEvent::isTerminalStatus(this.LastExecutionStatus)
&& this.LastProcessed != origProcessed)
{
if (BusinessEventsConfigurationReader::isBusinessEventEnabled(classStr(MGCLatticeAggregateMeasurementRefreshedBusinessEvent)))
{
MGCLatticeAggregateMeasurementRefreshedBusinessEvent::newFromStatistics(this).send();
}
}
}
Payload Contract
One row per [DataMember], in declaration order. Sources are the completed BIMeasurementProcessingStatisticsFull row, mapped in initialize().
| JSON field | Display label | Type / EDT | Source | Notes |
|---|---|---|---|---|
measurementName | Measurement Name | BIAggregateMeasurementName | BIMeasurement.Name, looked up by BIMeasurementProcessingStatisticsFull.BIMeasurement | The aggregate measurement (entity store) that was refreshed |
refreshStatus | Refresh Status | int | BIMeasurementProcessingStatisticsFull.LastExecutionStatus (as integer) | BIMeasurementExecutionStatus: 2 = Succeeded, 3 = Failed |
refreshStatusName | Refresh Status Name | str | LastExecutionStatus (enum symbol) | "Succeeded" or "Failed" |
isSuccessful | Is Successful | boolean | LastExecutionStatus == Succeeded | true / false |
startedDateTime | Started Date Time | utcdatetime | BIMeasurementProcessingStatisticsFull.LastProcessed | UTC time the processing started; serialized in the \/Date(ms)\/ form |
completedDateTime | Completed Date Time | utcdatetime | Derived: LastProcessed plus durationSeconds | The row stores no end time, so this is calculated; \/Date(ms)\/ form |
durationSeconds | Duration Seconds | int | Derived: real2int(LastDuration * 60) | Whole seconds; the platform stores a full refresh's elapsed time in minutes (LastDuration) |
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 doesn't call correlateTo, so correlationId is unique to each emission.
Example Payload
{
"BusinessEventId": "MGCLatticeAggregateMeasurementRefreshedBusinessEvent",
"ControlNumber": 5637150421,
"EventId": "5C0E7B2A-91D4-4F6E-A3B8-2D6F0C9E1A74",
"EventTime": "\/Date(1790215955000)\/",
"EventTimeIso8601": "2026-09-24T02:12:35.0084868Z",
"MajorVersion": 0,
"MinorVersion": 0,
"BusinessEventLegalEntity": "USMF",
"ContextRecordSubject": "",
"InitiatingUserAADObjectId": "{2CAEE5B5-EABB-CA80-0364-1B68C0AAC081}",
"ParentContextRecordSubjects": [],
"eventSequence": 47102,
"correlationId": "Event:d41b6f20-7c3a-4e95-8b1d-0f2a6c9e3b57",
"schemaVersion": 1,
"measurementName": "CustCollectionsBIMeasurements",
"refreshStatus": 2,
"refreshStatusName": "Succeeded",
"isSuccessful": true,
"startedDateTime": "\/Date(1790215205000)\/",
"completedDateTime": "\/Date(1790215955000)\/",
"durationSeconds": 750
}
Activation
In the D365FO client, go to System administration > Setup > Business events > Business events catalog. Find this event by name ("Aggregate measurement refresh completed") or by filtering the Category column to System administration. 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), for all legal entities or in the company that runs the refresh jobs. No Lattice setup is needed; the event follows whatever full refreshes you run or schedule for your entity store measurements (System administration > Setup > Entity store).