bcquality/microsoft/knowledge/data-modeling/new-price-source-must-add-candidate-and-trigger-recalculation.md
Michael Dieringer 31206f6616 Fix four merge-critical blockers from Jesper's review; add 4 more patterns
Addresses microsoft/BCQuality#175 review feedback:
- Extend al-data-modeling-review's entry gate/relevance scope and token
  list to recognize document actions, Navigate subscribers, Report
  Selection registration, price-calculation/price-source extensibility,
  TransferFields posting-cascade mirroring, and barcode font-provider
  usage - previously excluded before any worklist cue could run.
- Fix document-print-and-email-actions-call-report-selections-directly:
  permit the legitimate stateless DocumentSendingProfile.TrySendToPrinter/
  TrySendToEMail path; rework the bad fixture to load a configured
  profile instead of demonstrating a trivial blank-record no-op.
- Fix extend-report-selection-usage-for-new-document-types: scope to the
  applicable single counterparty (ReportSelectionHandlerCZZ partitions
  strictly; only genuinely two-sided usages like Compensation need both),
  and add the page-facing usage-enum map/validate events alongside the
  filter-event subscription for full Document Layouts support.
- Fix a stale field-citation in custom-document-dispatch-must-not-bypass-
  report-selections (Custom Report Layout Code is field 7, not part of
  the 19-26 email-configuration range).
- Add deterministic positive/clean evaluation coverage (review-fixtures.json
  additionalArticles + Test-ReviewFixtures.ps1 support) so all 9 new
  good/bad pairs are actually exercised, not just present.
- Add 4 new patterns: activate-new-price-calculation-handler-via-
  onfindsupportedsetup, extend-price-source-type-must-sync-document-
  subset-enum, new-price-source-must-add-candidate-and-trigger-
  recalculation, report-barcodes-must-use-barcode-module-and-production-
  font-name.

All claims verified against live microsoft/BCApps source and Microsoft
Learn. Validators: frontmatter 0/0, review-fixtures 52 cases/17 domains
PASSED, knowledge-index 309 articles PASSED.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-09-24 06:33:04 +02:00

3.6 KiB

bc-version domain keywords technologies countries application-area
all
data-modeling
price-calculation
price-source
onafteraddsources
recalculation
pricing
al
w1
all

A new price source needs both a calculation candidate and a recalculation trigger

Description

Making a custom field usable as a price source on a sales line is two separate, independent pieces of wiring, and doing only one produces a line that looks like it's using the new source without ever actually being priced by it. codeunit "Sales Line - Price" publishes OnAfterAddSources(SalesHeader: Record "Sales Header"; SalesLine: Record "Sales Line"; PriceType: Enum "Price Type"; var PriceSourceList: Codeunit "Price Source List") — subscribing here and calling PriceSourceList.Add(SourceType, SourceNo) makes the source a candidate the calculation considers. But nothing about that subscription causes the price to be recalculated when the source field's value changes on an existing line. That's the second, separate piece: Sales Line's own procedure UpdateUnitPriceByField(CalledByFieldNo: Integer) must be called from the source field's own trigger — the same way Microsoft's own Location example is wired from a Sales Line validation event, not from the price source registration itself.

Add the source without wiring recalculation, and the failure hides easily: a new line still prices correctly, because the field already holds its value when calculation first runs on insert. The gap only shows up when someone changes the source field's value on an existing line — the price silently keeps its old value until something unrelated happens to trigger recalculation.

Best Practice

Wire both halves together whenever a field becomes a price source: an OnAfterAddSources subscriber that adds it via PriceSourceList.Add, and a trigger on the field itself (its own OnValidate, or a matching OnAfterValidate integration event) that calls SalesLine.UpdateUnitPriceByField(SalesLine.FieldNo(<TheField>)).

See sample: new-price-source-must-add-candidate-and-trigger-recalculation.good.al.

Anti Pattern

Subscribing to OnAfterAddSources to register a custom field as a price source, without also triggering recalculation from that field's own validation. The field is a genuine, working calculation candidate — new lines price correctly — but editing the field on an existing line leaves the unit price stale, with nothing to indicate why.

See sample: new-price-source-must-add-candidate-and-trigger-recalculation.bad.al.

Source

BCApps (src/Layers/W1/BaseApp/): Sales/Pricing/SalesLinePrice.Codeunit.al (local procedure OnAfterAddSources(SalesHeader: Record "Sales Header"; SalesLine: Record "Sales Line"; PriceType: Enum "Price Type"; var PriceSourceList: Codeunit "Price Source List")); Pricing/Source/PriceSourceList.Codeunit.al (procedure Add(SourceType: Enum "Price Source Type"; SourceNo: Code[20])); Sales/Document/SalesLine.Table.al (procedure UpdateUnitPriceByField(CalledByFieldNo: Integer)).

Microsoft Learn, "Extending Price Calculations" (Location example): "To recalculate the price, we can subscribe to events that pass the sales line by reference... We'll call the UpdateUnitPriceByLocationCode() method, which is a simplified version of the UpdateUnitPriceByField() method... To add the location in the source list for price calculations, we'll subscribe to the OnAfterAddSources event of Codeunit 'Sales Line - Price,' and add the Location Code as a source." (https://learn.microsoft.com/dynamics365/business-central/dev-itpro/developer/devenv-extending-best-price-calculations)