* Add 5 AL/BC patterns: document distribution (Report Selections, Document Sending Profile, Find Entries, TransferFields) Five rules about Business Central's document distribution architecture, verified against BCApps source and Microsoft Learn. - custom-document-dispatch-must-not-bypass-report-selections - document-print-and-email-actions-call-report-selections-directly - extend-find-entries-navigate-for-new-document-types - extend-report-selection-usage-for-new-document-types - transferfields-mirrored-fields-must-match-type-and-length Wired into al-data-modeling-review.md's worklist cues. Added a disambiguation note on the TransferFields article distinguishing it from the existing transferfields-skip-type-mismatch-can-drop-data.md (type-mismatch skipping vs. length mismatch, which SkipFieldsNotMatchingType does not affect). Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> * 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> * Fix 5 merge-critical issues from Jesper's 2026-09-24 review round - activate-new-price-calculation-handler-via-onfindsupportedsetup: Default := true is required only for the fallback branch of PriceCalculationMgt's two-stage FindSetup - a handler reachable via a specific Dtld. Price Calculation Setup row needs no Default. Softened the article and its worklist cue accordingly. Also fixed an undefined "Sample Price Calc - Special" codeunit referenced but never declared in the eval fixtures - added a real implementation of interface "Price Calculation" with stub methods. - new-price-source-must-add-candidate-and-trigger-recalculation: the good fixture called UpdateUnitPriceByField directly, which is a silent no-op without a prior PlanPriceCalcByField call (FieldCausedPriceCalculation gating, verified against SalesLine.Table.al). Switched to the public UpdateUnitPrice wrapper, matching real BCApps usage in ItemReferenceManagement.Codeunit.al. - report-barcodes-must-use-barcode-module-and-production-font-name: split the 1D (ValidateInput + EncodeFont) and 2D (EncodeFont only) Barcode Font Provider interfaces, which the article previously conflated. Reframed the Code 39 anti-pattern around demonstrable encoding/checksum mismatch (verified against IDA1DCode39Encoder.Codeunit.al's real '(value)' output) rather than rejecting all manual delimiter use, since '*' is a legitimate Code 39 start/stop character. Also fixed extend-find-entries-navigate- for-new-document-types' eval fixtures, which referenced an undefined "Sample Posted Document Header" table/page - declared both. All claims re-verified against live microsoft/BCApps source. Validators: frontmatter 0/0, review-fixtures 126/20 domains PASSED, knowledge-index 342/575 PASSED, skill-index 19 leaves PASSED. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> * Align price-source and barcode routing cues with corrected articles - Price-source cue now accepts UpdateUnitPrice, or the explicit PlanPriceCalcByField + UpdateUnitPriceByField sequence; bare UpdateUnitPriceByField does not count. Both APIs added to tokens. - Barcode cue no longer flags manual delimiters as a category; routes only demonstrably invalid/provider-font-mismatched hand encoding, and requires ValidateInput + EncodeFont for 1D, EncodeFont only for 2D. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * Make barcode bad fixture self-contained: 1D EncodeFont without ValidateInput The previous bad fixture (literal '*' delimiters, no layout/font/provider evidence) no longer matched the narrowed routing cue. It now shows an IDAutomation 1D provider path that calls EncodeFont without ValidateInput, which is visible in AL alone. Article Anti Pattern and Source updated to describe this variant (verified: IDAutomation 1D Provider's EncodeFont does not call IsValidInput). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * Fix three merge-critical items from Jesper's 2026-09-29 review - Barcode: drop the false claim that '*value*' is mismatched with the IDAutomation Code 39 font; '*' is a documented start/stop form and '(' / ')' an accepted alternative. Cue and article now route only independently provable validation/checksum/font-binding defects. - Dispatch good samples (and matching bad samples) now pass a Sales Invoice Header with the S.Invoice usage, matching the record the selected report (1306 "Standard Sales - Invoice") expects. - custom-document-dispatch rule made disjunctive: a hardcoded report or a hand-built email is each a bypass on its own; scoped to customer/vendor-facing documents. Bad fixture shows the hardcoded report alone. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * Clarify TrySendToEMail comment in print/email good sample Make explicit that TrySendToEMail is also correct *because* it never reads the customer's assigned profile (local record, E-Mail option set by the helper itself), and name Get/GetDefaultForCustomer + Send as the anti-pattern. Matches the article's Best Practice and BaseApp's own Sales Invoice Header.EmailRecords. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> --------- Co-authored-by: Claude Sonnet 5 <noreply@anthropic.com>
5.5 KiB
| bc-version | domain | keywords | technologies | countries | application-area | |||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
|
data-modeling |
|
|
|
|
A document's own Print/Email actions call Report Selections directly; Document Sending Profile is scoped to Post-and-Send
Description
table 60 "Document Sending Profile" is not a general gateway for every
print/email path — it exists specifically for the combined Post and
Send action: "You can set each customer up with a preferred method of
sending sales documents, so that you do not have to select a sending
option every time you choose the Post and Send action" (Microsoft Learn,
"Set Up Document Sending Profiles"). A document's own, ordinary
Print/Email actions are unaffected by any configured profile either
way: the unposted Sales Order's "Print Confirmation"/"Email
Confirmation" (codeunit "Document-Print",
PrintSalesOrder/EmailSalesHeader) and the posted Purch. Inv. Header's PrintRecords call Report Selections literally directly
(PrintWithDialogForCust/SendEmailToCust/PrintWithDialogForVend),
while the posted Sales Invoice Header's PrintRecords/EmailRecords
and the unposted Purchase Header's PrintRecords go through
DocumentSendingProfile.TrySendToPrinter/TrySendToEMail/
TrySendToPrinterVendor instead. Those three helpers each declare a
fresh, local, never-Get'd profile record, hardcode its
Printer/"E-Mail" field to a "Yes" option themselves, and feed it into
SendToPrinter/SendToEMailGroupedMultipleSelection — which resolve
into Report Selections just like the direct route. The table is a
throwaway options carrier here, not the counterparty's configuration.
Only a genuinely configured profile changes the outcome, and that only
happens for the combined Post-and-Send flow: Sales-Post and Send loads
the customer's assigned profile (Get(Customer."Document Sending Profile"), or the tenant default) before Sales Invoice Header.SendProfile → DocumentSendingProfile.Send, which gates
SendToPrinter/SendToEMail/SendToDisk on whatever that record holds.
Whether a document needs outbound distribution isn't determined by
Customer vs. Vendor, but by whether it's genuinely outbound to that
party: a posted Purchase Invoice records what a vendor already billed,
so the posted Purch. Inv. Header has only a bare PrintRecords; a
Purchase Order is still outbound before posting, so the rich
SendProfile/SendRecords/PrintRecords triplet lives there instead.
Best Practice
For a document's own interactive Print/Email actions, either call the
relevant Report Selections procedure directly —
PrintForCust/PrintWithDialogForCust/SendEmailToCust for a
customer-facing document, PrintWithDialogForVend/SendEmailToVendor
for a vendor-facing one — or call one of Document Sending Profile's
stateless TrySendToPrinter/TrySendToEMail/TrySendToPrinterVendor
helpers, using the usage value registered per
extend-report-selection-usage-for-new-document-types.md. Both are
equally correct; neither reads the counterparty's assigned profile.
Reserve a genuine Get/GetDefaultForCustomer/GetDefaultForVendor
lookup and Send/SendVendor for Post-and-Send.
See sample: document-print-and-email-actions-call-report-selections-directly.good.al.
Anti Pattern
Loading the counterparty's actually assigned Document Sending Profile (or the tenant default, via Get/GetDefaultForCustomer/
GetDefaultForVendor — the same lookup Sales-Post and Send performs)
and calling Send/SendVendor on it from a plain, on-demand "Email"
button, instead of ReportSelections.SendEmailToCust/SendEmailToVendor
directly. The button's outcome now silently depends on a profile
configured for Post-and-Send — if its "E-Mail" option is No,
clicking "Email" does nothing observable. A second version of the same
mistake: an email action on a document that only receives from its
counterparty and was never meant to send anything back.
See sample: document-print-and-email-actions-call-report-selections-directly.bad.al.
Source
BCApps DocumentPrint.Codeunit.al (EmailSalesHeader/DoPrintSalesHeader/
PrintSalesOrder → ReportSelections.SendEmailToCust/PrintForCust/
PrintWithDialogForCust directly), SalesInvoiceHeader.Table.al
(PrintRecords/EmailRecords, lines 1453/1528 → TrySendToPrinter/
TrySendToEMail, lines 1462/1541, on a local never-Get'd record),
PurchaseHeader.Table.al (PrintRecords line 6357 →
TrySendToPrinterVendor line 6374; SendProfile line 6387 →
SendVendor line 6403), PurchInvHeader.Table.al (PrintRecords →
ReportSelection.PrintWithDialogForVend directly, no send capability),
SalesPostandSend.Codeunit.al/SalesPost.Codeunit.al
(ConfirmPostAndSend loads Get(Customer."Document Sending Profile")/GetDefault; SendPostedDocumentRecord line 7660 →
SalesInvHeader.SendProfile lines 7680/7699 →
DocumentSendingProfile.Send), DocumentSendingProfile.Table.al (table
60; TrySendToPrinter/TrySendToEMail lines 536/562,
TrySendToPrinterVendor line 552, GetDefaultForCustomer line 195,
Send/SendVendor lines 482/506) — all under src/Layers/W1/BaseApp/.
Microsoft Learn, "Set Up Document Sending Profiles": https://learn.microsoft.com/dynamics365/business-central/sales-how-setup-document-send-profiles