Skip to main content

Sales quotation confirmed

Fires when a sales quotation is confirmed and a quotation confirmation journal (CustQuotationConfirmJour) record is created. This marks the moment a customer quotation transitions from an open offer into a confirmed commitment - confirming a quotation also creates the sales order from it - letting downstream systems (CRM, pricing, fulfillment) react to the agreed quotation.

Event classMGCLatticeSalesQuotationConfirmedBusinessEvent
Contract classMGCLatticeSalesQuotationConfirmedBusinessEventContract
CategorySales orders
ModuleModuleAxapta::SalesOrder
Label IDsSalesQuotationConfirmedBusinessEventName / SalesQuotationConfirmedBusinessEventDescription
TriggerSalesQuotationEditLinesForm_Sales_Confir.createJournal() -> when a confirmation journal is created (journal().RecId guard)

Trigger / Hook​

  • Extension class: MGCLatticeSalesQuotationEditLinesForm_Sales_Confir_Class_Extension, [ExtensionOf(classStr(SalesQuotationEditLinesForm_Sales_Confir))]
  • CoC method signature: public boolean createJournal() (no parameters, returns boolean)
  • Firing condition: ret = next createJournal(), then the event sends only when ret && this.journal().RecId - i.e. a confirmation journal was actually created (once per confirmed quotation). Quotation confirmation uses a different framework from the FormletterService form letters: the driver SalesQuotationEditLinesForm.run() wraps each quotation in its own ttsbegin/ttscommit around createJournal(), and the journal row is written by writeJournal() (during the same step the sales order is created from the quotation, unless the quotation is owned by the Dynamics 365 Sales integration). Hooking createJournal() is the true business moment; the table write is only its side effect.
  • Scope: only sales quotations. Project quotations are confirmed by SalesQuotationEditLinesForm_Proj_Confir, a separate class this hook does not extend, so confirming a project quotation does not raise this event.
  • Pro forma: quotation confirmation has no pro forma posting path, so every confirmation journal is a real confirmation.
  • Transaction: the hook runs inside the per-quotation transaction, before commit. This is transactionally safe because BusinessEventsBase.send() only writes to the commit log inside the current transaction and actual delivery is asynchronous post-commit, so a tts-aborted confirmation never delivers.
  • Enablement gate: BusinessEventsConfigurationReader::isBusinessEventEnabled(classStr(MGCLatticeSalesQuotationConfirmedBusinessEvent)) must return true before the event is built and sent.
  • The factory MGCLatticeSalesQuotationConfirmedBusinessEvent::newFromCustQuotationConfirmJour(this.journal(), this.salesQuotationTable().QuotationId) passes the just-created journal record and the number of the quotation being confirmed (the journal does not store it) into the contract.
/// <summary>
/// Triggers the sales quotation confirmed business event when a confirmation journal is created.
/// </summary>
/// <returns>true if the journal was created; otherwise false.</returns>
public boolean createJournal()
{
boolean ret;

ret = next createJournal();

if (ret
&& this.journal().RecId
&& BusinessEventsConfigurationReader::isBusinessEventEnabled(classStr(MGCLatticeSalesQuotationConfirmedBusinessEvent)))
{
MGCLatticeSalesQuotationConfirmedBusinessEvent::newFromCustQuotationConfirmJour(this.journal(), this.salesQuotationTable().QuotationId).send();
}

return ret;
}

Payload Contract​

One row per [DataMember], in declaration order. Source record: CustQuotationConfirmJour, mapped in initialize(), plus the number of the confirmed quotation passed in by the hook.

JSON fieldDisplay labelType / EDTSourceNotes
legalEntityLegal EntityDataAreaIdCustQuotationConfirmJour.DataAreaIdCompany/legal entity of the confirmation journal
quotationIdQuotation IDQuotationIdBaseSalesQuotationTable.QuotationId of the quotation being confirmedThe quotation number
confirmationIdConfirmation IDQuotationConfirmIdCustQuotationConfirmJour.QuotationConfirmIdThe quotation confirmation number, allocated from the quotation confirmation number sequence
salesOrderIdSales Order IDSalesIdCustQuotationConfirmJour.SalesIdThe sales order the confirmation created; empty when no sales order was created (a quotation owned by the Dynamics 365 Sales integration)
customerAccountCustomer AccountCustAccountCustQuotationConfirmJour.OrderAccountOrder/customer account on the quotation
confirmationDateConfirmation DateTransDateCustQuotationConfirmJour.QuotationDateConfirmation date entered when confirming; serialized in the \/Date(milliseconds)\/ form
crossReferencesCross referencesList of MGCLatticeCrossReferenceEventContractMGCLatticeCrossReferenceEventHelper::listForSalesQuotation(quotationId, CustQuotationConfirmJour.DataAreaId)Keys external systems hold for the sales quotation that was confirmed: one { externalSystem, externalKey } object per external system, ordered by external system, historical keys included; [] when it has none. See Cross-reference keys. The keys are the quotation's, not the journal's.

Note: the confirmationDate field is exposed by the parm method parmTransDate (the parm method name differs from the JSON field name confirmationDate).

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": "MGCLatticeSalesQuotationConfirmedBusinessEvent",
"ControlNumber": 5637148712,
"EventId": "B8D30403-8C8D-456A-BD82-04DFD19EB07C",
"EventTime": "\/Date(1790083089000)\/",
"EventTimeIso8601": "2026-09-22T13:18:09.7238571Z",
"MajorVersion": 0,
"MinorVersion": 0,
"BusinessEventLegalEntity": "USMF",
"ContextRecordSubject": "",
"InitiatingUserAADObjectId": "{F6921A98-944E-C44E-A49A-36372D62B034}",
"ParentContextRecordSubjects": [],
"eventSequence": 48031,
"correlationId": "Event:70aa29d9-4e71-4062-8077-5a2d9b1707d3",
"schemaVersion": 1,
"legalEntity": "usmf",
"quotationId": "000077",
"confirmationId": "CQC-000002",
"salesOrderId": "001791",
"customerAccount": "US-004",
"confirmationDate": "\/Date(1790035200000)\/",
"crossReferences": [
{
"externalSystem": "CRM",
"externalKey": "QUO-2026-0412"
}
]
}

Activation​

In the D365FO client, go to System administration > Setup > Business events > Business events catalog. Locate the event by name ("Sales quotation confirmed") or filter the Category column to Sales orders, select it, and activate it to a target endpoint (for example Azure Service Bus, Event Grid, or an HTTPS endpoint) for the desired legal entities.