Skip to main content

Scenario: Maintain sales prices and ask for the effective price

Goal. Your pricing system (PIM, CPQ, ERP master) owns customer prices. You push each price to D365 as a sales price trade agreement, with a clean timeline: a price with a later start date replaces the current one from that date, and a repeated push changes nothing. Then you ask D365 which price is effective for a customer, item, date and quantity, exactly as a sales order line would price it.

When to use. Price synchronization into D365 and price checks before an order is sent, for example when your web shop shows the D365 price or when you want to know the price an order line will get in Sales order to invoice. The service sets sales prices only (not discounts or purchase prices), and only from today forward.

Prerequisites​

  1. Security (see Security model):
    • MGCLatticeSalesTradeAgreementServiceMaintain grants setSalesPrice and setSalesPriceBatch; MGCLatticeIntegrationOperatorRole and MGCLatticeIntegrationAdministratorRole hold it.
    • MGCLatticePriceInquiryServiceView grants resolveSalesPrice, and MGCLatticeTradeAgreementEntityView grants read access to the trade agreement entities (LatticeSalesPriceAgreements). Both are in MGCLatticeIntegrationServiceQueryDuty, which all three integration roles hold, including MGCLatticeIntegrationReadOnlyRole.
  2. D365 configuration in the legal entity:
    • a trade agreement journal name whose default relation is Sales price (Sales and marketing > Setup > Prices and discounts > Trade agreement journal names); the service posts every price through such a journal;
    • the price search activated for the account and item levels you use, for example customer and item (Sales and marketing > Setup > Prices and discounts > Activate price/discount);
    • the customer (USMF US-001), the item (USMF D0001) and the currency (USD).
  3. Lattice setup. setSalesPrice needs a valid license (an expired or invalid one answers LicenseNotValid); resolveSalesPrice is a read and is never refused for the license. To name the customer or item by your own key, set up an external system as in Cross-reference keys.

Steps​

Both services are in MGCLatticeAccountsReceivableServiceGroup: POST https://<your-environment>/api/services/MGCLatticeAccountsReceivableServiceGroup/<service>/<operation>, with the request in _request and an OAuth 2.0 client-credentials token. Neither request has a RequestId: resolveSalesPrice is a read, and setSalesPrice is idempotent by content (a repeated price answers Unchanged). Always send LegalEntityId; without it the calling user's current company is used.

  1. Push the first price for a customer and item. setSalesPrice on MGCLatticeSalesTradeAgreementService:

    {
    "_request": {
    "CorrelationId": "6f5e4d3c-2b1a-4098-8e7d-6c5b4a3f2e1d",
    "LegalEntityId": "USMF",
    "AccountCode": 0,
    "AccountRelation": "US-001",
    "ItemCode": 0,
    "ItemRelation": "D0001",
    "CurrencyCode": "USD",
    "UnitPrice": 42.50,
    "FromDate": "2026-10-07",
    "FindNext": true
    }
    }

    AccountCode and ItemCode are numbers: 0 Table (one customer or one item), 1 GroupId (a price group or item group), 2 All; a name such as "Table" is refused with HTTP 400. UnitId defaults to the item's sales unit, PriceUnit to 1 and FromQty to 0 (all quantities); SiteId and WarehouseId make the price dimension-specific. FromDate may not be before today; an empty ToDate means open-ended. UnitPrice must be greater than zero, unless you send "AllowZeroPrice": true for a free item.

    Send FindNext: true for a key that has no open-ended price yet, such as a first price: without it the service treats the timeline as not simple and answers RejectedComplex. With it, the price is posted as a plain new row and existing rows are not touched.

    The result is MGCLatticeSetSalesPriceResult: Success true, Outcome 1, OutcomeName Created, JournalNum (the posted trade agreement journal), CreatedAgreementRecId (the new price row), AppliedFromDate, AppliedToDate and AppliedUnitPrice.

  2. Push later changes. Send the same key (account, item, currency, unit, quantity break, site and warehouse) with the new values; FindNext is not needed while the key has one open-ended price. The service decides:

    You sendOutcome (Outcome)What D365 gets
    The same FromDate as the open-ended price, another UnitPriceEditedInPlace (2)The existing row's price is changed.
    A later FromDateSuperseded (3)One journal caps the prior row at the day before FromDate (SupersededAgreementRecId, SupersededNewToDate) and adds the new open-ended row.
    Exactly the current price (same FromDate, UnitPrice, PriceUnit, open-ended)Unchanged (5)Nothing is posted.
    An earlier FromDate than the current price, or a key with no open-ended price or severalRejectedComplex (4)Nothing is posted, unless you send FindNext: true.

    For example, "UnitPrice": 44.00 with "FromDate": "2026-10-07" edits the price in place, and "UnitPrice": 45.00 with "FromDate": "2026-11-01" supersedes it: the 44.00 price then ends on 2026-10-31.

  3. Push many prices at once (optional). setSalesPriceBatch takes { "_requests": [ ... ] }, each item a setSalesPrice request. Each item is posted in its own journal and does not affect the others. The answer has Success (true only when every item succeeded), TotalCount, SuccessCount, FailureCount, the message N of M sales prices were applied. and one Results entry per item, in request order.

  4. Ask for the effective price. resolveSalesPrice on MGCLatticePriceInquiryService:

    {
    "_request": {
    "LegalEntityId": "USMF",
    "CustAccount": "US-001",
    "ItemId": "D0001",
    "Qty": 10,
    "PriceDate": "2026-11-05",
    "CurrencyCode": "USD"
    }
    }

    The service runs the standard D365 sales price search for the customer (its trade agreements and price group) and falls back to the item's base sales price. It answers Success, PriceFound, UnitPrice, PriceUnit (the quantity the price is for), SourceAgreementRecId (the trade agreement row that won; 0 means the item's base price), AppliedCurrencyCode, AppliedDate and AppliedQty. Qty defaults to 1, PriceDate to today, CurrencyCode to the customer's currency and UnitId to the item's sales unit. With the prices of step 2, and no other agreement for this customer and item that wins the search, this request answers 45.00, and with "PriceDate": "2026-10-15" it answers 44.00. It writes nothing.

How to verify​

  • The result: Success true and the OutcomeName you expected; JournalNum and CreatedAgreementRecId name what was posted (an Unchanged result names the existing row and no journal).

  • The price rows: LatticeSalesPriceAgreements lists the sales price trade agreements; SysRecId is the CreatedAgreementRecId of the result:

    GET https://<your-environment>/data/LatticeSalesPriceAgreements?cross-company=true&$filter=dataAreaId eq 'USMF' and AccountRelation eq 'US-001' and ItemRelation eq 'D0001'&$select=SysRecId,Amount,PriceUnit,CurrencyCode,UnitSymbol,FromDate,ToDate
  • The effective price: resolveSalesPrice for a date in each period returns the expected UnitPrice, with SourceAgreementRecId equal to the row that applies.

  • In D365: Sales and marketing > Prices and discounts > Trade agreement journals lists the posted journal, named Lattice sales price push.

  • Business events, when activated: MGCLatticeTradeAgreementPriceCreatedBusinessEvent, MGCLatticeTradeAgreementPriceModifiedBusinessEvent and MGCLatticeTradeAgreementPriceSupersededBusinessEvent for what the journal changed, and the scheduled Effective and Expired events as the dates are reached (events).

When it goes wrong​

setSalesPrice never throws: every refusal answers Success false, an Outcome of RejectedComplex or Failed, an ErrorCode and the reasons in Errors.

ErrorCodeTypical causeWhat to do
ValidationFailed (1)A missing member (An item relation is required., A currency is required., A from date is required.); a back-dated price (The from date ... is in the past; sales-price pushes must be forward-dated.); a ToDate before FromDate; a zero or negative UnitPrice without AllowZeroPrice; an unknown LegalEntityId; an external key sent with a group or All code, or one that does not resolveCorrect the request. Nothing was posted.
DocumentNotFound (2)With code 0, the customer or item does not exist (Customer US-999 was not found in company usmf.)Check the id and LegalEntityId.
BusinessRule (4)RejectedComplex: The price timeline for this key is not a single open-ended price, so no price was created. Set FindNext to accept a plain create.; or the journal line failed D365's trade agreement validation (unit, currency, item or account), or the journal did not post (Trade agreement journal ... was created but did not post; its validation may have blocked it.)For RejectedComplex, send FindNext: true when a plain new row is what you want, or review the key's prices first. A journal that did not post is deleted again; fix the cause and push again.
ConfigurationMissing (11)No trade agreement journal name with default relation Sales priceCreate one (see Prerequisites).
PreviewUnsupported (10)PreviewMode true: the operation has no previewUse resolveSalesPrice to see the current price instead.
LicenseNotValid (12)The Lattice license is expired or invalidRenew the license, then push again.
TransientRuntime (6)A runtime faultPush the same request again; an applied price answers Unchanged.

In a batch, a missing item answers its own Failed result with ValidationFailed; an empty list answers Success false with At least one request item is required. and no results.

resolveSalesPrice answers ValidationFailed when the customer or item is missing, the legal entity is unknown or an external key does not resolve, DocumentNotFound when the customer or item does not exist in the company, BusinessRule when the pricing engine refused, and TransientRuntime for a runtime fault. When no trade agreement and no base price exist, it answers Success true with PriceFound false and the message No trade-agreement or base sales price is defined for this item. If it returns an older price than you pushed, check the dates and that the price search is activated for that account and item level.

Reference​