Batch job status changed
Fires when the status of a batch job is changed through the job record - for example when an operator withholds, releases, or cancels a job from the Batch jobs page - unless the job matches one of your batch job event filters, which filter out the jobs your subscribers don't need. It carries the previous and new status, the caption, legal entity, start and end times, and task count. Read the firing condition below before relying on it to detect job completion - the batch server's own start and finish transitions don't raise it.
| Event class | MGCLatticeBatchJobStatusChangedBusinessEvent |
| Contract class | MGCLatticeBatchJobStatusChangedBusinessEventContract |
| Category | System administration |
| Module | ModuleAxapta::SystemAdministration |
| Label IDs | BatchJobStatusChangedBusinessEventName / BatchJobStatusChangedBusinessEventDescription |
| Trigger | BatchJob.update() -> Status differs from orig().Status and MGCLatticeBatchJobEventFilter::excludes() is false |
Trigger / Hook
- Extension class:
MGCLatticeBatchJob_Table_Extension, decorated with[ExtensionOf(tableStr(BatchJob))]. - CoC method signature:
public void update(). - Firing condition: after
next update(), the event sends when all three hold: the newStatusdiffers fromorig().Status; the event is activated; and no enabledMGCLatticeBatchJobEventFilterrow matches the job (see Filter setup). One status change raises at most one event. - Company:
BatchJobis shared, so the activation check, the filter lookup and the send run in the job's company (BatchJob.Company; the session company when the job has none or the company no longer exists). The event is published when that company has activated it, andBusinessEventLegalEntityis the job's company. - Which status changes reach this hook: only changes written through the
BatchJobrecord'supdate()method.- Raised: Change status on the Batch jobs page (
BatchChangeStatus) - to Withhold (from Waiting, Ended, Error, or Canceled), to Waiting (from Withhold, Ended, Error, or Canceled), or to Canceling (from Executing). - Raised: the platform's clean-up of jobs whose run-as user (the job's
ExecutingByuser, or else its creator) is disabled or removed -BatchRun::serverCleanUpDeadTasks(), Executing -> Canceling and Waiting -> Withhold. It usesupdate_recordsetwithout skipping table methods, and because the platformBatchJobtable overridesupdate(), the statement runs record by record through this hook. - Raised: any other code that sets
BatchJob.Statusand callsupdate()on the record. - Not raised: the batch framework's own scheduling transitions. Waiting -> Executing when a job starts, Executing -> Ended or Error when it finishes, Canceling -> Canceled when a cancellation completes, and the reset of recurring jobs are written with set-based updates under
skipDataMethods(true)(and, for very large jobs, by SQL stored procedures), all of which bypassupdate(). The event therefore never reports those transitions, and a filter row whose On status is Executing, Ended, Error, or Canceled only affects status changes made through the record.
- Raised: Change status on the Batch jobs page (
- Timing:
BatchChangeStatusupdates the job inside a transaction, sosend()writes to the business events commit log and the event is delivered only after that transaction commits. - Enablement gate:
BusinessEventsConfigurationReader::isBusinessEventEnabled(classStr(MGCLatticeBatchJobStatusChangedBusinessEvent)), a cached lookup. It is checked first for the job's company and the session company, before any database read, and again in the job's company before the filter lookup.
public void update()
{
BatchStatus origStatus = this.orig().Status;
next update();
if (origStatus != this.Status
&& (BusinessEventsConfigurationReader::isBusinessEventEnabled(classStr(MGCLatticeBatchJobStatusChangedBusinessEvent), this.Company ? this.Company : curExt())
|| BusinessEventsConfigurationReader::isBusinessEventEnabled(classStr(MGCLatticeBatchJobStatusChangedBusinessEvent))))
{
changecompany(MGCLatticeBusinessEventCompany::resolve(this.Company))
{
if (BusinessEventsConfigurationReader::isBusinessEventEnabled(classStr(MGCLatticeBatchJobStatusChangedBusinessEvent))
&& !MGCLatticeBatchJobEventFilter::excludes(this))
{
MGCLatticeBatchJobStatusChangedBusinessEvent::newFromBatchJob(this, origStatus).send();
}
}
}
}
Filter setup
Filters live in the MGCLatticeBatchJobEventFilter table, maintained at System administration > Setup > Lattice business events > Batch job event filters (menu item MGCLatticeBatchJobEventFilter; access is granted by the MGCLatticeBatchJobEventFilterMaintain privilege). The table is shared across companies, so each row applies to batch jobs in every company. The rows are exclusions: a job that matches an enabled row is filtered out, and its status changes don't raise the event.
| Field | Meaning |
|---|---|
| Enabled | Only rows with Enabled = Yes are evaluated. New rows default to Yes. |
| Description | Free text for your own reference; not used for matching. |
| Caption contains | Case-insensitive substring that the batch job's caption must contain for the row to match. Blank matches any caption. |
| Task class | AOT class name; the row matches only jobs that contain at least one task of this class. Blank matches any job. A name that doesn't resolve to a class never matches. |
| Match any status | Yes = the row matches whatever the new status is. New rows default to Yes. |
| On status | Used only when Match any status = No: the row matches only when the job's new status equals this value. |
Each combination of Caption contains, Task class, Match any status and On status can be entered only once.
How rows combine (MGCLatticeBatchJobEventFilter::excludes() / rowMatches()):
- Within a row, every specified criterion must match (AND); a blank criterion is a wildcard.
- Across rows, any one matching enabled row filters the job out (OR).
- With no enabled rows, nothing is filtered out: every status change that reaches the hook raises the event.
Examples: a row with Caption contains = Invoice and Match any status = Yes stops the event for every job whose caption contains "invoice"; a row with Match any status = No and On status = Withhold (Hold) stops it for jobs being withheld, while their other status changes still raise it.
Payload Contract
One row per [DataMember], in declaration order. Sources are the BatchJob record at its new status, plus the status it held before the update, mapped in initialize().
| JSON field | Display label | Type / EDT | Source | Notes |
|---|---|---|---|---|
batchJobRecId | Batch Job Record ID | int64 | BatchJob.RecId | |
caption | Caption | str | BatchJob.Caption | |
legalEntity | Legal Entity | DataAreaId | BatchJob.Company | Company the job belongs to, in lower case (BatchJob.Company keeps the case it was written with) |
status | Status | int | BatchJob.Status (as integer) | BatchStatus: 0 Hold (Withhold), 1 Waiting, 2 Executing, 3 Error, 4 Finished (Ended), 5 Ready, 6 NotRun, 7 Canceling (Canceling), 8 Canceled, 9 Scheduled |
statusName | Status Name | str | BatchJob.Status (enum symbol) | The symbol, not the UI label - for example "Hold" for Withhold and "Cancelling" for Canceling |
previousStatus | Previous Status | int | BatchJob.orig().Status (as integer) | Same values as status |
previousStatusName | Previous Status Name | str | BatchJob.orig().Status (enum symbol) | |
startDateTime | Start Date Time | StartDateTime | BatchJob.StartDateTime | \/Date(ms)\/ form; an unset value serializes as \/Date(-2208988800000)\/ (1900-01-01) |
endDateTime | End Date Time | EndDateTime | BatchJob.EndDateTime | \/Date(ms)\/ form; unset while a job is running |
taskCount | Task Count | int | Count of Batch rows where BatchJobId = the job's RecId | Number of tasks in the job |
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
An operator cancels a running job from the Batch jobs page:
{
"BusinessEventId": "MGCLatticeBatchJobStatusChangedBusinessEvent",
"ControlNumber": 5637151340,
"EventId": "0B7D4E19-3A62-4C8F-95E1-7F2C8A6D4B30",
"EventTime": "\/Date(1790257287000)\/",
"EventTimeIso8601": "2026-09-24T13:41:27.9887517Z",
"MajorVersion": 0,
"MinorVersion": 0,
"BusinessEventLegalEntity": "USMF",
"ContextRecordSubject": "",
"InitiatingUserAADObjectId": "{577C3196-7236-B014-2312-29B924A43BF7}",
"ParentContextRecordSubjects": [],
"eventSequence": 48766,
"correlationId": "Event:6e2f9a41-0c57-4b3d-a8e6-19d4c7b2f085",
"schemaVersion": 1,
"batchJobRecId": 5637148901,
"caption": "Invoice sales orders - nightly",
"legalEntity": "usmf",
"status": 7,
"statusName": "Cancelling",
"previousStatus": 2,
"previousStatusName": "Executing",
"startDateTime": "\/Date(1790255100000)\/",
"endDateTime": "\/Date(-2208988800000)\/",
"taskCount": 3
}
Activation
In the D365FO client, go to System administration > Setup > Business events > Business events catalog. Find this event by name ("Batch job status changed") 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).
Filter rows are optional. Without any, every batch job status change that reaches the hook raises the event; to leave out jobs your subscribers don't need, add enabled rows at System administration > Setup > Lattice business events > Batch job event filters (see Filter setup).