Sales agreement confirmed
Fires when a sales agreement is confirmed in D365FO. Confirmation writes an AgreementHeaderHistory record (of subtype SalesAgreementHeaderHistory), a snapshot capturing the agreed customer, the default effective and expiration dates, and the sales agreement number. This event lets downstream systems react to a newly committed customer sales agreement (e.g. updating CRM, pricing engines, or fulfillment forecasts).
| Event class | MGCLatticeSalesAgreementConfirmedBusinessEvent |
| Contract class | MGCLatticeSalesAgreementConfirmedBusinessEventContract |
| Category | Sales orders |
| Module | ModuleAxapta::SalesOrder |
| Label IDs | SalesAgreementConfirmedBusinessEventName / SalesAgreementConfirmedBusinessEventDescription |
| Trigger | AgreementConfirm_Sales.createHeaderHistory() -> after a sales agreement header history snapshot is created |
Trigger / Hook
- Extension class:
MGCLatticeAgreementConfirm_Sales_Class_Extension,[ExtensionOf(classStr(AgreementConfirm_Sales))] - CoC method signature:
protected recId createHeaderHistory() - Type discrimination is structural: the hook sits on the sales confirmation engine (
AgreementConfirm_Sales), so only sales agreements raise this event. Purchase agreement confirmations runAgreementConfirm_Purchand raise Purchase agreement confirmed instead. - Firing condition: after
next createHeaderHistory(), when the event is activated (checked first, a cached lookup), the returnedheaderHistoryRecIdis resolved withAgreementHeaderHistory::find(headerHistoryRecId)and the event fires only when that history record was actually created (agreementHeaderHistory.RecId). - When it fires: the Confirm action on a sales agreement runs
AgreementConfirm_Sales.startConfirm(), which (whenmustConfirm()returns true, as it always does in the standard application) callsAgreementConfirm.createHistory()and from therecreateHeaderHistory(). Every confirmation writes a new history snapshot, so re-confirming an agreement raises the event again. Pro forma confirmation (AgreementProformaConfirm) only prints the confirmation document and creates no history, so it does not raise the event. - Transaction:
createHeaderHistory()runs inside thettsbegin/ttscommitofAgreementConfirm.createHistory().send()writes the event to the business events commit log inside that transaction and delivery happens after commit, so a confirmation that is rolled back never delivers. - Enablement gate:
BusinessEventsConfigurationReader::isBusinessEventEnabled(classStr(MGCLatticeSalesAgreementConfirmedBusinessEvent))must return true before the event is sent.
protected recId createHeaderHistory()
{
recId headerHistoryRecId = next createHeaderHistory();
if (headerHistoryRecId
&& BusinessEventsConfigurationReader::isBusinessEventEnabled(classStr(MGCLatticeSalesAgreementConfirmedBusinessEvent)))
{
AgreementHeaderHistory agreementHeaderHistory = AgreementHeaderHistory::find(headerHistoryRecId);
if (agreementHeaderHistory.RecId)
{
MGCLatticeSalesAgreementConfirmedBusinessEvent::newFromAgreementHeaderHistory(agreementHeaderHistory).send();
}
}
return headerHistoryRecId;
}
Payload Contract
One row per [DataMember], in declaration order. The contract is built from the AgreementHeaderHistory record, down-cast to SalesAgreementHeaderHistory inside initialize().
| JSON field | Display label | Type / EDT | Source | Notes |
|---|---|---|---|---|
legalEntity | Legal Entity | DataAreaId | CompanyInfo.DataArea of SalesAgreementHeaderHistory.SellingLegalEntity; SalesAgreementHeaderHistory.CustomerDataAreaId when no selling legal entity is set | The company the agreement belongs to. Agreement headers and their history are shared across companies and have no company column of their own, so the company comes from the agreement's selling legal entity. Always in lower case (for example usmf), although CompanyInfo.DataArea stores it in upper case. |
salesAgreementId | Sales Agreement ID | str | SalesAgreementHeaderHistory.SalesNumberSequence | Read from the SalesAgreementHeaderHistory down-cast; the sales agreement number. |
confirmationId | Confirmation ID | str | AgreementHeaderHistory.ConfirmationNumber | Number of this confirmation; each confirmation of the same agreement gets the next number. |
customerAccount | Customer Account | CustAccount | SalesAgreementHeaderHistory.CustAccount | Read from the SalesAgreementHeaderHistory down-cast. |
effectiveDate | Effective Date | TransDate | AgreementHeaderHistory.DefaultAgreementLineEffectiveDate | Default agreement-line effective date from the base record; serialized in the \/Date(milliseconds)\/ form. |
expirationDate | Expiration Date | TransDate | AgreementHeaderHistory.DefaultAgreementLineExpirationDate | Default agreement-line expiration date from the base record; an agreement without an expiration date carries the D365 empty date 1900-01-01 (\/Date(-2208988800000)\/). |
crossReferences | Cross references | List of MGCLatticeCrossReferenceEventContract | MGCLatticeCrossReferenceEventHelper::listForAgreement(AgreementHeaderHistory.AgreementHeader, legalEntity) | Keys external systems hold for the sales agreement: one { externalSystem, externalKey } object per external system, ordered by external system, historical keys included; [] when it has none. See Cross-reference keys. Read in the selling legal entity. |
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 sent inside the business transaction; it orders the events of one record, but two transactions can commit in the opposite order of their values, and gaps are normal and permanent - see the events overview), correlationId (String - always Scope:Key: Event: followed by a GUID unique to this emission, unless the event joins a 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": "MGCLatticeSalesAgreementConfirmedBusinessEvent",
"ControlNumber": 5637148874,
"EventId": "1CB42B9D-D542-478F-9743-0D383B91C833",
"EventTime": "\/Date(1790155664000)\/",
"EventTimeIso8601": "2026-09-23T09:27:44.6927061Z",
"MajorVersion": 0,
"MinorVersion": 0,
"BusinessEventLegalEntity": "USMF",
"ContextRecordSubject": "",
"InitiatingUserAADObjectId": "{6709D7DD-A639-B47A-97EA-F1BC25EDA6ED}",
"ParentContextRecordSubjects": [],
"eventSequence": 48102,
"correlationId": "Event:004f4939-65ec-4b3e-b9b9-c3d1529c5da5",
"schemaVersion": 1,
"legalEntity": "usmf",
"salesAgreementId": "SAG-000002",
"confirmationId": "2",
"customerAccount": "US-004",
"effectiveDate": "\/Date(1790812800000)\/",
"expirationDate": "\/Date(1822262400000)\/",
"crossReferences": [
{
"externalSystem": "CRM",
"externalKey": "SA-2026-114"
}
]
}
Activation
In the D365FO client, go to System administration > Setup > Business events > Business events catalog. Find the event by name ("Sales agreement confirmed") or by filtering the Category column to Sales orders, then activate it against the desired endpoint (Azure Service Bus, Event Grid, Event Hubs, etc.).
Related Events
- Purchase agreement confirmed - sibling event raised from the parallel
MGCLatticeAgreementConfirm_Purch_Class_Extension.createHeaderHistory()hook on the purchase confirmation engine. - Sales order confirmed
- Sales quotation confirmed