Skip to main content

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 classMGCLatticeSalesAgreementConfirmedBusinessEvent
Contract classMGCLatticeSalesAgreementConfirmedBusinessEventContract
CategorySales orders
ModuleModuleAxapta::SalesOrder
Label IDsSalesAgreementConfirmedBusinessEventName / SalesAgreementConfirmedBusinessEventDescription
TriggerAgreementConfirm_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 run AgreementConfirm_Purch and raise Purchase agreement confirmed instead.
  • Firing condition: after next createHeaderHistory(), when the event is activated (checked first, a cached lookup), the returned headerHistoryRecId is resolved with AgreementHeaderHistory::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 (when mustConfirm() returns true, as it always does in the standard application) calls AgreementConfirm.createHistory() and from there createHeaderHistory(). 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 the ttsbegin/ttscommit of AgreementConfirm.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 fieldDisplay labelType / EDTSourceNotes
legalEntityLegal EntityDataAreaIdCompanyInfo.DataArea of SalesAgreementHeaderHistory.SellingLegalEntity; SalesAgreementHeaderHistory.CustomerDataAreaId when no selling legal entity is setThe 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.
salesAgreementIdSales Agreement IDstrSalesAgreementHeaderHistory.SalesNumberSequenceRead from the SalesAgreementHeaderHistory down-cast; the sales agreement number.
confirmationIdConfirmation IDstrAgreementHeaderHistory.ConfirmationNumberNumber of this confirmation; each confirmation of the same agreement gets the next number.
customerAccountCustomer AccountCustAccountSalesAgreementHeaderHistory.CustAccountRead from the SalesAgreementHeaderHistory down-cast.
effectiveDateEffective DateTransDateAgreementHeaderHistory.DefaultAgreementLineEffectiveDateDefault agreement-line effective date from the base record; serialized in the \/Date(milliseconds)\/ form.
expirationDateExpiration DateTransDateAgreementHeaderHistory.DefaultAgreementLineExpirationDateDefault agreement-line expiration date from the base record; an agreement without an expiration date carries the D365 empty date 1900-01-01 (\/Date(-2208988800000)\/).
crossReferencesCross referencesList of MGCLatticeCrossReferenceEventContractMGCLatticeCrossReferenceEventHelper::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.).