Skip to content

9 AL/BC patterns: document distribution, price calculation & barcode extensibility - #175

Open
Michael Dieringer (MichaelDieringer) wants to merge 10 commits into
microsoft:mainfrom
Curabis:community-contribution/document-distribution-batch
Open

Michael Dieringer (MichaelDieringer) wants to merge 10 commits into
microsoft:mainfrom
Curabis:community-contribution/document-distribution-batch

Conversation

@MichaelDieringer

@MichaelDieringer Michael Dieringer (MichaelDieringer) commented Sep 10, 2026 •

Copy link
Copy Markdown
Contributor

Summary

Nine rules about Business Central's document distribution architecture and posting-cascade/pricing extensibility, verified against BCApps source and Microsoft Learn:

  • custom-document-dispatch-must-not-bypass-report-selections — a codeunit that hardcodes a report ID and builds its own email instead of registering through Report Selections loses per-account layout overrides and "Document Layouts" discoverability.
  • document-print-and-email-actions-call-report-selections-directly — a document's own Print/Email actions should go through Report Selections' own procedures, or the legitimate stateless DocumentSendingProfile.TrySendToPrinter/TrySendToEMail path; Document Sending Profile.Send/SendVendor on an actually-configured profile is reserved for a genuine combined Post-and-Send action.
  • extend-find-entries-navigate-for-new-document-types — registering a custom table with Find Entries (page 344 Navigate) requires both OnAfterFindRecords and OnBeforeShowRecords; either alone leaves a result row with nothing to open it.
  • extend-report-selection-usage-for-new-document-types — a new Report Selection Usage value needs the matching single counterparty's full triad (filter event + page-facing usage-enum map/validate events), not both counterparties by default — ReportSelectionHandlerCZZ partitions strictly; only a genuinely two-sided usage (as ReportSelectionHandlerCZC demonstrates for Compensation) needs both.
  • transferfields-mirrored-fields-must-match-type-and-length — a field mirrored across a TransferFields posting cascade (e.g. Sales Header → Sales Invoice Header) must match type and length exactly; a length-only mismatch compiles and posts cleanly until a value finally exceeds the shorter definition.
  • activate-new-price-calculation-handler-via-onfindsupportedsetup — a Price Calculation Handler enumextension needs an OnFindSupportedSetup subscriber that inserts a Price Calculation Setup row with Method and Default := true set, or FindSetup can never select it.
  • extend-price-source-type-must-sync-document-subset-enum — a new Price Source Type value needs a matching value at the same numeric ID in the relevant document-subset enum (Sales/Purchase/Job Price Source Type), or price lists targeting it never resolve.
  • new-price-source-must-add-candidate-and-trigger-recalculation — registering a custom price source via PriceSourceList.Add needs an OnValidate that calls UpdateUnitPriceByField, or changing the source field never recalculates the price.
  • report-barcodes-must-use-barcode-module-and-production-font-name — barcodes should go through the Barcode module's Barcode Font Provider/Barcode Font Provider 2D interface with the purchased production font name, not manual concatenation or an evaluation/demo font.

Each article cites specific BCApps source (file, procedure, and in most cases line number) and, where applicable, Microsoft Learn.

Changes since the last review round

Addressed all four merge-critical items from the 2026-09-15/2026-09-22 reviews:

  1. Entry-gate/relevance scope extended. al-data-modeling-review.md's not-applicable definition, object/token lists, and worklist now explicitly recognize document actions, Navigate subscribers, Report Selection registration, price-calculation/price-source extensibility, TransferFields posting-cascade mirroring, and barcode/font-provider usage — these topics are no longer excluded before the cues that name them can run.
  2. document-print-and-email-actions-call-report-selections-directly reworked. Verified against SalesInvoiceHeader.Table.al (PrintRecords/EmailRecords call TrySendToPrinter/TrySendToEMail on a local, never-Get'd profile) and SalesPostandSend.Codeunit.al (GetDefaultForCustomer + Send is the real Post-and-Send-only path). The stateless helpers are now explicitly permitted; the bad fixture now loads an actually-configured profile via GetDefaultForCustomer before calling Send, so it demonstrates the real anti-pattern instead of a blank-record no-op.
  3. extend-report-selection-usage-for-new-document-types reworked. Verified ReportSelectionHandlerCZZ (strict customer/vendor partition) against ReportSelectionHandlerCZC (genuinely two-sided Compensation case), and the page-facing usage enums (Custom Report Selection Sales / Report Selection Usage Vendor) with their map/validate events. The rule now scopes to the applicable single counterparty and includes the enum+event wiring needed for full Document Layouts support, not just the filter subscription.
  4. Deterministic evaluation coverage added, using the now-established articles[] override convention (matching the finance/scm/query/reporting/style overrides) rather than inventing a parallel mechanism — all 9 new good/bad pairs are exercised by Test-ReviewFixtures.ps1, not just present as files.

Also fixed independently: 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), and converted all 9 articles' sample references to the markdown-link form Knowledge-Retrieval.ps1's Assert-SampleLink requires.

Rebased/merged onto current upstream/main (resolved conflicts in al-data-modeling-review.md and evaluation/README.md, preserving folder-path support and the two new main-side cues).

Test plan

  • validate_frontmatter.py — 0 errors, 0 warnings
  • Test-ReviewFixtures.ps1 — 126 cases across 20 leaf domains PASSED
  • Test-KnowledgeIndex.ps1 — 342 articles / 575 samples round-tripped, PASSED
  • Test-SkillIndex.ps1 — 19 review leaves preserved, PASSED
  • Domain-owning reviewers confirm placement and accuracy per file

🤖 Generated with Claude Code

…ent 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>
@MichaelDieringer

Copy link
Copy Markdown
Contributor Author

@microsoft-github-policy-service agree company="CURABIS ApS"

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Requesting changes on 83f041b6624b965c061e05bc36cbdc1926399be2.

The exact-tree validators pass (frontmatter; deterministic 305-article index; 34 fixtures/17 leaves), but these are necessary before the rules are safe to execute:

  1. Owning-skill reachability is contradictory. al-data-modeling-review still scopes relevance/not-applicable/outcome to setup/master/key/numbering/block/audit surfaces, and its initial tokens omit all five new signal families. The appended worklist cues therefore do not make document actions, Navigate subscribers, report-selection registration, or posting-cascade extensions deterministically reachable. Extend the entry gate/scope and prove each route.
  2. The Document Sending Profile rule and bad fixture do not match current BCApps semantics. Ordinary Sales Invoice Header.PrintRecords/EmailRecords use stateless DocumentSendingProfile.TrySendToPrinter/TrySendToEMail, not direct Report Selections; the article names only Purchase Header as an exception. Conversely, the bad sample calls Send on a blank record. Send does not load the customer's assigned profile, so this sample does not make the outcome depend on that profile—it simply has all sending options at their default No. Permit the stateless helpers and load a configured/default customer profile in the actual anti-pattern.
  3. The Report Selection Usage rule is overbroad and incomplete. Requiring both customer and vendor filter subscribers even for a one-sided document is contradicted by current ReportSelectionHandlerCZZ, which partitions sales usages to the customer page and purchase usages to the vendor page. Also, filter subscribers alone only affect Copy from Report Selection; full Document Layouts display/edit support requires extending the page-facing usage enum and handling its map/validate events, as the cited CZC implementation itself does. Scope to the applicable counterparty and either include those pieces or narrow every claim to the copy action.
  4. Evaluation does not exercise any new rule. The generated data-modeling control still selects check-blocked-in-referencing-code-not-in-master; all five new good/bad pairs are only existence/ranking inputs. Add deterministic positive/clean coverage that demonstrates the corrected owning-skill routes and semantics.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The current head is unchanged from the prior review, and four merge-critical blockers remain:

  1. The five rules are only targeted cues in al-data-modeling-review.md; its entry gate/not-applicable definition can reject document actions, Navigate subscribers, report-selection registration, and posting cascades before those cues run.

  2. document-print-and-email-actions-call-report-selections-directly.md incorrectly rejects supported stateless TrySendToPrinter/TrySendToEMail usage. Its bad sample calls Send on a blank profile, so the no-op is not caused by the assigned customer profile as claimed.

  3. extend-report-selection-usage-for-new-document-types.md requires both customer and vendor subscribers for one-sided documents and claims Document Layout support without the page-facing usage-enum mapping/validation integration. Its good sample contains only the two copy-filter subscribers.

  4. None of the five rules has deterministic positive/clean evaluation coverage; the default data-modeling pair still selects an unrelated alphabetical article.

The conflict with current main is separate, but resolution must preserve current folder-path support.

@JesperSchulz

Copy link
Copy Markdown
Contributor

The remaining work is now a concrete four-item checklist plus the main-branch conflict. Once those are addressed, the next pass can stay tightly focused on merge readiness.

…terns

Addresses microsoft#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>
…READ convention

- Resolve conflicts in al-data-modeling-review.md by keeping both sides'
  additions (folder-path support, InitRecord/Round cues from upstream;
  the 9 document-distribution/pricing/barcode cues from this branch).
- Switch the data-modeling evaluation override from an ad-hoc
  additionalArticles field to upstream's now-established articles[]
  convention (used elsewhere for finance/scm/query/reporting/style),
  removing the redundant parallel code path from Test-ReviewFixtures.ps1.
- Fix all 9 new articles' sample references to the markdown-link READ
  convention required by Knowledge-Retrieval.ps1's Assert-SampleLink
  (plain backticks satisfy validate_frontmatter.py's regex alone but not
  this stricter check - both validators must pass).

Validators: frontmatter 0/0, review-fixtures 126 cases/20 domains PASSED,
knowledge-index 342 articles/575 samples PASSED, skill-index 19 review
leaves PASSED.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
@MichaelDieringer Michael Dieringer (MichaelDieringer) changed the title 5 AL/BC patterns: document distribution (Report Selections, Document Sending Profile, Find Entries, TransferFields) 9 AL/BC patterns: document distribution, price calculation & barcode extensibility Sep 24, 2026
@MichaelDieringer

Copy link
Copy Markdown
Contributor Author

Thanks Jesper — apologies for how long this one sat. All four merge-critical items fixed:

  1. Entry-gate/scope: al-data-modeling-review.md's not-applicable definition, object/token lists, and worklist now explicitly cover document actions, Navigate subscribers, Report Selection registration, price-calculation/price-source extensibility, TransferFields posting-cascade mirroring, and barcode/font-provider usage. Traced each of the 9 slugs through the (extended) gate to confirm none are rejected upstream of their cue.

  2. document-print-and-email-actions-call-report-selections-directly: you were right on both counts. Verified SalesInvoiceHeader.Table.al's PrintRecords/EmailRecords call TrySendToPrinter/TrySendToEMail on a local record that's never Get'd — a legitimate stateless path, now explicitly permitted. And confirmed the bad fixture's blank-record Send call doesn't depend on any assigned profile; reworked it to load a real one via GetDefaultForCustomer first, matching the real anti-pattern.

  3. extend-report-selection-usage-for-new-document-types: confirmed ReportSelectionHandlerCZZ partitions strictly by counterparty (sales → customer page, purchase → vendor page) versus ReportSelectionHandlerCZC, which genuinely needs both for Compensation. Rule now scopes to the applicable single counterparty by default, and added the page-facing usage-enum (Custom Report Selection Sales/Report Selection Usage Vendor) map/validate events alongside the filter subscription — the filter alone only wires "Copy from Report Selection", not full Document Layouts support, as you noted.

  4. Evaluation coverage: noticed review-fixtures.json has since grown a real articles[] override convention (used by finance/scm/query/reporting/style) since this PR was opened — used that instead of inventing a parallel mechanism. All 9 new pairs are now exercised.

Also caught independently: 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-config range), and converted all 9 articles' sample links to the markdown-link form Knowledge-Retrieval.ps1 requires.

Rebased onto current main (conflicts in al-data-modeling-review.md and evaluation/README.md, both resolved keeping both sides — folder-path support and the two new cues preserved).

Also folded in 4 new patterns while I was in here (price-calculation-handler activation, price-source/document-subset enum sync, price-source recalculation trigger, barcode font-provider usage) — same domain, same review skill, seemed better as one coherent PR than a second one right behind it. Title/description updated accordingly.

All local validators pass: frontmatter 0/0, review-fixtures 126/20 domains, knowledge-index 342/575, skill-index 19 leaves.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The original four blockers are addressed, but five merge-critical defects remain in the added price/barcode material:

  1. activate-new-price-calculation-handler-via-onfindsupportedsetup.md and its review cue require every handler setup to be Default := true. Detailed Price Calculation Setup can select a registered non-default handler by code; default is required only for fallback selection.
  2. new-price-source-must-add-candidate-and-trigger-recalculation.good.al calls UpdateUnitPriceByField without first calling PlanPriceCalcByField, so the method exits and the canonical fix recalculates nothing. Use the full planned-field sequence or a dedicated handler recalculation routine.
  3. The barcode article universally requires ValidateInput, but Barcode Font Provider 2D does not expose that API. Split 1D (ValidateInput + EncodeFont) from 2D (EncodeFont).
  4. The same rule rejects all manual delimiters, but *value* can be valid Code 39 encoding when the input/font/checksum requirements permit it. Flag demonstrably invalid or mismatched encoding rather than construction categorically.
  5. Two expected-clean evaluation fixtures are not self-contained: the price-handler fixture references undefined Sample Price Calc - Special, and the Find Entries fixture references undefined sample table/page objects. Add minimal definitions or use existing symbols.

- 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>
@MichaelDieringer

Copy link
Copy Markdown
Contributor Author

Thanks Jesper — all five fixed:

  1. Default := true absoluteness: confirmed against PriceCalculationMgt.Codeunit.al's FindSetup — it's genuinely two-stage: it first tries PriceCalculationDtldSetup.FindSetup (no Default condition at all, matches on Source Group/No./Asset Type/No.), and only falls back to the Enabled/Default/Method/Type/Asset Type filter if that misses. Softened the article and its worklist cue to reflect that Default only governs the fallback branch.

  2. UpdateUnitPriceByField no-op: confirmed — FieldCausedPriceCalculation starts at 0 and UpdateUnitPriceByField exits immediately unless a prior PlanPriceCalcByField call set it to match. Switched the fixture to the public UpdateUnitPrice wrapper (ClearFieldCausedPriceCalculation + PlanPriceCalcByField + UpdateUnitPriceByField in one call), which is also what real BCApps code uses for this exact "recalculate from an unrelated field's OnValidate" shape (ItemReferenceManagement.Codeunit.al).

  3. Barcode 1D/2D split: confirmed Barcode Font Provider 2D has no ValidateInput member — only EncodeFont (and GetSupportedBarcodeSymbologies). Split the article's Best Practice accordingly.

  4. Code 39 delimiter: you're right that * is a legitimate Code 39 start/stop character, not inherently wrong. Reframed the anti-pattern around demonstrable mismatch — verified IDA1DCode39Encoder.Codeunit.al's actual encoded output wraps in (/), not literal *, so a hand-built '*'+value+'*' is mismatched with the paired production font specifically, which is the real defect, not manual construction per se.

  5. Undefined eval-fixture objects: found and fixed both — a "Sample Price Calc - Special" codeunit referenced but never declared (added a real implements "Price Calculation" stub), and a "Sample Posted Document Header" table/page referenced but never declared in the Find Entries fixtures (declared both, self-contained, matching the convention already used elsewhere in this PR).

All validators pass: frontmatter 0/0, review-fixtures 126/20 domains, knowledge-index 342/575, skill-index 19 leaves.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The five article/fixture defects are corrected, but two merge-critical routing contradictions remain in al-data-modeling-review.md:

  1. The price-source cue still requires direct SalesLine.UpdateUnitPriceByField, while the corrected article and good fixture use UpdateUnitPrice. Direct UpdateUnitPriceByField exits unless planning state was established first. Accept UpdateUnitPrice, or the explicit PlanPriceCalcByField + UpdateUnitPriceByField sequence, and add those APIs to routing tokens.
  2. The barcode cue still categorically flags manual delimiters and associates ValidateInput with both provider interfaces. Align it with the corrected article: route only demonstrably invalid/provider-font-mismatched hand encoding; require ValidateInput + EncodeFont for 1D and EncodeFont only for 2D.

The handler-default semantics and both formerly undefined clean fixtures are resolved.

- 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>
@MichaelDieringer

Copy link
Copy Markdown
Contributor Author

Thanks Jesper — both fixed in e246b19 (al-data-modeling-review.md only):

  1. Price-source cue: now accepts SalesLine.UpdateUnitPrice(<field no.>), or the explicit PlanPriceCalcByField(<field no.>) followed by UpdateUnitPriceByField(<same field no.>). A bare UpdateUnitPriceByField without a preceding PlanPriceCalcByField for the same field is explicitly stated not to count, matching the article and good fixture. UpdateUnitPrice and PlanPriceCalcByField added to the routing tokens.
  2. Barcode cue: no longer flags manual delimiters as a category. It routes only demonstrably invalid or provider/font-mismatched hand encoding (e.g. '*' + Value + '*' rendered with the IDAutomation Code 39 font, whose paired 1D provider encoder emits (/)), and explicitly excludes a custom provider paired with a font that genuinely expects literal delimiters. Interface use is now split: ValidateInput + EncodeFont for 1D "Barcode Font Provider", EncodeFont only for 2D "Barcode Font Provider 2D" (absence of ValidateInput on the 2D path is stated not to be a finding). The evaluation/demo font check is unchanged.

Test-ReviewContract.ps1 and Test-ReviewFixtures.ps1 pass.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

One merge-critical evaluation contradiction remains at e246b1942a7ad8960a21487f41d2d9f58c8f3d43. The corrected barcode routing requires a demonstrable provider/font mismatch and explicitly says manual delimiters alone are not a finding, but the registered positive fixture only contains BarcodeText := '*' + "No." + '*'; with no layout, font, or provider evidence. The evaluation therefore forces reviewers either to violate the rule or miss the expected finding. Please make the bad fixture self-contained and unambiguous—for example, show an IDAutomation provider path that omits ValidateInput, or include evaluation-visible evidence of an incompatible font binding.

…teInput

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>
@MichaelDieringer

Copy link
Copy Markdown
Contributor Author

Thanks Jesper — fixed in 4cd41f0.

The bad fixture report-barcodes-must-use-barcode-module-and-production-font-name.bad.al is now self-contained: the hand-built '*' + "No." + '*' string is gone, and it instead shows an Interface "Barcode Font Provider" path with Enum::"Barcode Font Provider"::IDAutomation1D and Code39 that calls EncodeFont without ValidateInput. That maps directly to the cue's "1D path must call both ValidateInput and EncodeFont" clause, with no dependency on layout or font evidence.

Verified against BCApps: IDAutomation1DProvider.Codeunit.al's EncodeFont goes straight to the symbology encoder — only ValidateInput calls IsValidInput — and IDA 1D Code39 Encoder's IsValidInput restricts input to ^[0-9A-Z\-.$\/\+%\*\s]*$, so an Item "No." containing e.g. _ or # is never rejected. The article's Anti Pattern and Source now describe this variant as the one the sample shows.

Test-ReviewContract.ps1 and Test-ReviewFixtures.ps1 pass.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Reviewed 4cd41f08f7618ffa61bc57f1eec7ef1168e849dc. The previous fixture-specific barcode blocker is resolved, but three merge-critical issues remain:

  1. The article/routing still treats manual *value* rendered with an IDAutomation Code 39 font as mismatched because BC's encoder emits parentheses. IDAutomation documents asterisks as valid Code 39 start/stop characters and says parentheses may also be used; Microsoft likewise documents * delimiters. Remove this false example and require concrete incompatibility evidence, or report independently provable validation/checksum/demo-font defects.
  2. The canonical document-dispatch good samples pass a Customer record with Report Selection Usage::S.Invoice. The selected standard invoice report expects Sales Invoice Header, so these examples fail rather than demonstrate working dispatch. Use a compatible record/usage pair or define a matching sample report.
  3. The registered custom-document-dispatch-must-not-bypass-report-selections.bad.al fixture only hardcodes Report.RunModal, while the article and routing define the anti-pattern as hardcoding a report and directly constructing email. Either make the rule disjunctive if each bypass is independently defective, or make the positive fixture demonstrate both conditions.

The fixture/frontmatter/index/contract workflows show action_required with zero jobs at this head, so they did not provide additional validation coverage.

- 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>
Resolve al-data-modeling-review.md by union: scope, not-applicable
definition and targeted cues now cover both this PR's document
distribution/price/barcode topics and main's dimension-wiring,
posting-routine and Item Ledger Entry topics.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@MichaelDieringer

Copy link
Copy Markdown
Contributor Author

Thanks Jesper — all three fixed in aad3991, then merged current main in b6a59be (the branch had gone conflicting after #156/#158/#199 landed).

  1. Barcode * delimiter: you're right, and I'd repeated the error in my last reply. *value* is a documented Code 39 start/stop form (Microsoft Learn's font table; IDAutomation's Code 39 manual, which also gives parentheses as an alternative to keep * out of the human-readable text). The false mismatch example is removed from both the article and the al-data-modeling-review.md cue. The cue now routes only independently provable defects: a source value that can contain out-of-set characters and is never validated, a required checksum never applied, concrete evidence of an incompatible font binding, a 1D "Barcode Font Provider" path missing ValidateInput, or a demo font name. Delimiter choice alone is explicitly never a finding.
  2. Record/usage mismatch: both dispatch articles' samples (custom-document-dispatch-must-not-bypass-report-selections and document-print-and-email-actions-call-report-selections-directly, good and bad) now pass a Sales Invoice Header with "S.Invoice", mirroring BaseApp's own Sales Invoice Header.EmailRecords/PrintRecords (SetSelectionFilter/SetRecFilter, "Bill-to Customer No." as the customer field, GetFullDocumentTypeText for the doc name). Verified that ReportSelectionMgt maps "S.Invoice" to report 1306 "Standard Sales - Invoice".
  3. Conjunctive vs. disjunctive: made the rule disjunctive, since each bypass is independently defective — a hardcoded report ignores the registered report and per-account layout overrides even with no email involved, and a hand-built email ignores the attachment/email-body configuration even if the report came from the registry. Article Description/Anti Pattern and the cue say so explicitly, and the cue is scoped to customer/vendor-facing documents that have (or should have) a Report Selection Usage, so a hardcoded Report.Run of an ordinary list/analysis report is not flagged. The bad fixture shows the hardcoded report alone, which now matches.

Merge conflict in al-data-modeling-review.md resolved by union (scope, not-applicable definition, and cues cover both this PR's topics and main's dimension/posting-routine/ILE topics). On the merged tree: validate_frontmatter.py (0/0), Test-ReviewContract.ps1, Test-ReviewFixtures.ps1 (140 cases), Test-KnowledgeIndex.ps1, Test-SkillIndex.ps1 all pass.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Reviewed b6a59be325df6f7dce9394c8764e19453b59af11. The three prior blockers are resolved: barcode routing now accepts valid Code 39 *value* delimiters and requires independently provable defects; the dispatch samples now pass Sales Invoice Header for S.Invoice; and the custom-dispatch rule/fixture consistently treats either hardcoded report execution or direct email construction as an independent bypass. The merge resolution also preserves current main, and every reported check passes.

One contradictory sentence remains in the canonical good sample at microsoft/knowledge/data-modeling/document-print-and-email-actions-call-report-selections-directly.good.al:27-29: it says DocumentSendingProfile.TrySendToEMail(...) "would be equally correct," then explains that it never gets the customer's assigned profile and instead uses a local hardcoded one. This directly reverses the article's normative rule and could teach an agent to accept the forbidden alternative. Please change "would be equally correct" to "would not be equally correct" (and preferably Get's to gets). No broader change is needed.

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>
…crosoft#198, microsoft#202) into document-distribution-batch

Resolve evaluation/review-fixtures.json semantically: data-modeling
articles list is the union of main's list and this PR's nine articles;
everything else is taken from main unchanged.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@MichaelDieringer

Copy link
Copy Markdown
Contributor Author

Thanks Jesper. I've fixed the wording in 833a002, but I'd suggest not flipping it to "would not be equally correct". The sentence was meant to say TrySendToEMail is acceptable, and the old phrasing made the reason read like a drawback. The fixture now says:

Calling DocumentSendingProfile.TrySendToEMail(...) instead would also be correct, because it never reads the customer's assigned profile: it only uses a local record that it never retrieves with Get, and sets its "E-Mail" option itself. The anti-pattern is Get/GetDefaultForCustomer followed by Send, which makes the outcome depend on that profile.

(Get's is gone as well.)

Why I'd keep TrySendToEMail on the accepted side:

  • The article's Best Practice already names both routes as "equally correct": Report Selections directly, or the stateless TrySendToPrinter/TrySendToEMail/TrySendToPrinterVendor helpers. It reserves Get/GetDefaultForCustomer + Send for Post-and-Send. Flipping the fixture comment would make the canonical sample contradict its own article.
  • BCApps: DocumentSendingProfile.Table.al TrySendToEMail declares no Get. It sets "E-Mail" to Yes (Prompt for Settings) or Yes (Use Default Settings) from ShowDialog, sets "E-Mail Attachment" := PDF, and calls TrySendToEMailGroupedMultipleSelection, which resolves through Report Selections. The customer's assigned profile is never read.
  • BaseApp uses it for exactly this button: Sales Invoice Header.EmailRecords calls DocumentSendingProfile.TrySendToEMail(Usage::"S.Invoice", ...). That's what the posted invoice's Send by Email action runs.
  • Microsoft Learn matches this: Set Up Document Sending Profiles ties a customer's profile to the Post and Send action, and Send documents and emails describes the posted invoice's Send by Email prompting for Yes (Prompt for Settings) / Yes (Use Default Settings), the two values TrySendToEMail sets on its own local record.

If you still read the article's rule differently, I'm happy to tighten the article wording too.

After #157/#159/#161 landed, the branch went into conflict again. I merged current main in 704dc23: evaluation/review-fixtures.json got the union of main's data-modeling articles and this PR's nine, and everything else comes from main unchanged. On the merged tree validate_frontmatter.py (0 errors, plus the two known keyword warnings from #157), Test-ReviewContract.ps1, Test-ReviewFixtures.ps1 (216 cases), Test-KnowledgeIndex.ps1 and Test-SkillIndex.ps1 all pass.

@MichaelDieringer

Copy link
Copy Markdown
Contributor Author

Thanks for the fast re-review — but I'd like to push back on this one specific point before changing it, since the requested edit would introduce a new contradiction rather than remove one.

The good.al comment's claim ("would also be correct... because it never reads the customer's assigned profile") is not an isolated statement — it's restating the article's own Best Practice section verbatim:

For a document's own interactive Print/Email actions, either call the relevant Report Selections procedure directly [...] or call one of Document Sending Profile's stateless TrySendToPrinter/TrySendToEMail/TrySendToPrinterVendor helpers [...]. 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.

The Description section makes the same point independently: "Those three helpers each declare a fresh, local, never-Get'd profile record, hardcode its Printer/"E-Mail" field to a 'Yes' option themselves [...]. The table is a throwaway options carrier here, not the counterparty's configuration."

I re-verified the underlying claim directly against a fresh BCApps main (2026-09-29, commit 9a1ee1911): DocumentSendingProfile.TrySendToEMail (DocumentSendingProfile.Table.al:562-581) sets "E-Mail" on Rec unconditionally and never calls Get/GetDefaultForCustomer — it genuinely never touches the counterparty's assigned profile, confirming the article's claim rather than contradicting it.

If I flip the good.al comment to "would not be equally correct" as requested, it directly contradicts the article's own Best Practice paragraph, which this exact fixture exists to demonstrate — I'd be trading one inconsistency for another.

Could you take another look with the Best Practice section open alongside the fixture? If there's a narrower concern — e.g. the comment reading as "not reading the profile is why it's correct" rather than "not reading the profile is fine for this specific interactive-button purpose, reserved-for-Post-and-Send elsewhere" — I'm happy to sharpen the wording to make that scope explicit without reversing the claim itself. Let me know which you'd prefer.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants