Skip to main content

Sales price trade agreement changed

Fires when a sales price trade agreement record - a row in PriceDiscTable (the active price and discount agreements) with relation Price (sales) - is inserted or updated, for any party: a specific customer, a customer price group, or all customers. It is the record-level signal for sales prices: one unclassified event per row written, with an isUpdate flag, whatever process writes the row. The six Sales price agreement events (created, modified, superseded, deleted, effective, expired) are its classified complements: they are raised from trade agreement journal posting, from deletes, and from a scheduled effectivity sweep, and they tell you what kind of change happened. Whether this event also fires during trade agreement journal posting depends on two Accounts receivable parameters - see Trigger / Hook.

Event classMGCLatticeSalesPriceTradeAgreementChangedBusinessEvent
Contract classMGCLatticeSalesPriceTradeAgreementChangedBusinessEventContract
CategorySales orders
ModuleModuleAxapta::SalesOrder
Label IDsSalesPriceTradeAgreementChangedBusinessEventName / SalesPriceTradeAgreementChangedBusinessEventDescription
TriggerPriceDiscTable inserted / updated (DataEventType::Inserted / Updated) -> relation is PriceType::PriceSales

Trigger / Hook​

  • Handler class: MGCLatticeSalesPriceTradeAgreementChangedEventHandlers, with two methods:

    • public static void PriceDiscTable_onInserted(Common _sender, DataEventArgs _e), [DataEventHandler(tableStr(PriceDiscTable), DataEventType::Inserted)] - sends with isUpdate = false.
    • public static void PriceDiscTable_onUpdated(Common _sender, DataEventArgs _e), [DataEventHandler(tableStr(PriceDiscTable), DataEventType::Updated)] - sends with isUpdate = true.
  • Firing condition: the row's relation is PriceType::PriceSales. Purchase prices and all discount relations are ignored. Every account code (one customer, price group, all) and item code qualifies. Updates have no field-change guard: any update to a sales-price row fires, even one that changes neither the amount nor the dates. Deleting a row does not raise this event.

  • Trade agreement journal posting: when a trade agreement journal is posted, D365 (PriceDiscAdmCheckPost) writes the resulting PriceDiscTable rows with set-based statements that always skip the table's data methods and skip its events only when the matching Accounts receivable parameter says so. The parameters are on the Prices tab under Trade agreement integration:

    • Skip price agreement insert alerts - applies to the rows the journal inserts.
    • Skip price agreement update alerts - applies to the rows the journal updates.

    When a parameter is Yes, those rows are written without data events and this event is not raised for them. When it is No, the platform processes the statement row by row (because this handler subscribes to the table's events) and the event is raised once per inserted or updated sales-price row. The parameters' own help text recommends keeping them at Yes unless a customization needs the alerts, because No can slow down journal posting (particularly with dual-write).

  • Other writers: any other code that inserts or updates sales-price rows through the table's insert() / update() also raises the event.

  • Timing: send() writes to the business events commit log inside the writing transaction (for journal posting, the posting transaction) and delivery happens asynchronously after commit, so a rolled-back write never delivers.

  • Enablement gate: BusinessEventsConfigurationReader::isBusinessEventEnabled(classStr(MGCLatticeSalesPriceTradeAgreementChangedBusinessEvent)) in both handlers.

  • How it relates to the Sales price agreement events:

    • Sales price agreement created, modified and superseded are raised by Lattice's extension of trade agreement journal posting, one classified event per posted sales-price journal line, regardless of the skip-alert parameters.
    • Sales price agreement deleted is raised when a sales-price row is deleted.
    • Sales price agreement effective and expired are raised by a scheduled batch sweep as prices reach their start and end dates.
    • With the skip-alert parameters at No, posting a journal raises both this event and the classified events for the same rows; subscribe to whichever level of detail you need.
[DataEventHandler(tableStr(PriceDiscTable), DataEventType::Inserted)]
public static void PriceDiscTable_onInserted(Common _sender, DataEventArgs _e)
{
PriceDiscTable priceDiscTable = _sender as PriceDiscTable;

if (priceDiscTable.Relation != PriceType::PriceSales)
{
return;
}

if (BusinessEventsConfigurationReader::isBusinessEventEnabled(classStr(MGCLatticeSalesPriceTradeAgreementChangedBusinessEvent)))
{
MGCLatticeSalesPriceTradeAgreementChangedBusinessEvent::newFromPriceDiscTable(priceDiscTable, false).send();
}
}

The PriceDiscTable_onUpdated handler is identical except that it passes true.

Payload Contract​

One row per [DataMember], in declaration order. All sources are fields on the inserted or updated PriceDiscTable row, mapped in initialize().

JSON fieldDisplay labelType / EDTSourceNotes
legalEntityLegal EntityDataAreaIdPriceDiscTable.dataAreaIdCompany of the agreement, as stored (lowercase, for example usmf).
isUpdateIs Updatebooleanhandler argumentfalse when the row was inserted, true when it was updated.
priceTypePrice Typeintenum2int(PriceDiscTable.relation)Always 4 (PriceType::PriceSales) for this event.
priceTypeNamePrice Type Namestrenum2Symbol(enumNum(PriceType), ...)Always PriceSales.
accountCodeAccount Codeintenum2int(PriceDiscTable.AccountCode)PriceDiscPartyCodeType: 0 = Table (one customer), 1 = GroupId (customer price group), 2 = All.
accountCodeNameAccount Code Namestrenum2Symbol(enumNum(PriceDiscPartyCodeType), ...)Table, GroupId or All.
partyTypeParty Typestrderived from AccountCode in resolvePartyType()Customer (Table), Group (GroupId) or All.
accountRelationAccount RelationPriceDiscAccountRelationPriceDiscTable.AccountRelationCustomer account or customer price group; empty when the account code is All.
itemCodeItem Codeintenum2int(PriceDiscTable.ItemCode)PriceDiscProductCodeType: 0 = Table (one item), 1 = GroupId (item group), 2 = All. No symbol field is provided.
itemCodeNameItem Code Namestrenum2Symbol(enumNum(PriceDiscProductCodeType), …)Symbol of itemCode: Table, GroupId or All
itemRelationItem RelationPriceDiscItemRelationPriceDiscTable.ItemRelationItem number or item group; empty when the item code is All.
currencyCodeCurrency CodeCurrencyCodePriceDiscTable.CurrencyCurrency of the agreed price.
amountAmountPriceDiscAmountPriceDiscTable.AmountAgreed sales price.
priceUnitPrice UnitPriceUnitPriceDiscTable.PriceUnitNumber of units the price applies to.
quantityAmountFromQuantity Amount FromAmountQtyPriceDiscTable.QuantityAmountFromQuantity from which the price applies.
fromDateFrom DateFromDatePriceDiscTable.FromDateStart of validity (date, \/Date(milliseconds)\/ form).
toDateTo DateToDatePriceDiscTable.ToDateEnd of validity; the empty (null) date when the agreement has no end date.
unitIdUnitUnitOfMeasureSymbolPriceDiscTable.UnitIdUnit the price applies to.
inventDimIdInventory DimensionsInventDimIdPriceDiscTable.InventDimIdInventory dimension combination the price applies to.
priceDiscTableRecIdPrice Disc Table RecIdRecIdPriceDiscTable.RecIdIdentifies the trade agreement line across its create and update events.

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": "MGCLatticeSalesPriceTradeAgreementChangedBusinessEvent",
"ControlNumber": 5637148790,
"EventId": "3A8C1E54-9F27-4B60-8D13-E5B2A7C04F96",
"EventTime": "\/Date(1790336270000)\/",
"EventTimeIso8601": "2026-09-25T11:37:50.3450265Z",
"MajorVersion": 0,
"MinorVersion": 0,
"BusinessEventLegalEntity": "USMF",
"ContextRecordSubject": "",
"InitiatingUserAADObjectId": "{386EC7A6-4F2C-0051-1C5F-2E3A46372EAC}",
"ParentContextRecordSubjects": [],
"eventSequence": 48419,
"correlationId": "Event:ff546327-a694-47eb-a02d-7287d4041c32",
"schemaVersion": 1,
"legalEntity": "usmf",
"isUpdate": false,
"priceType": 4,
"priceTypeName": "PriceSales",
"accountCode": 0,
"accountCodeName": "Table",
"partyType": "Customer",
"accountRelation": "US-003",
"itemCode": 0,
"itemCodeName": "Table",
"itemRelation": "D0001",
"currencyCode": "USD",
"amount": 185.00,
"priceUnit": 1.00,
"quantityAmountFrom": 1.00,
"fromDate": "\/Date(1790812800000)\/",
"toDate": "\/Date(1798675200000)\/",
"unitId": "ea",
"inventDimId": "AllBlank",
"priceDiscTableRecId": 5637145326
}

Activation​

In the D365FO client, go to System administration > Setup > Business events > Business events catalog. Find this event by name ("Sales price trade agreement changed") or by filtering the Category column to Sales orders. 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). To also receive it for rows written by trade agreement journal posting, set Skip price agreement insert alerts and/or Skip price agreement update alerts to No in Accounts receivable parameters (Prices tab, Trade agreement integration), weighing the posting-performance impact noted above; otherwise use the Sales price agreement events for journal postings.