bcquality/microsoft/knowledge/data-modeling/code-must-not-change-workdate.md
Michael Dieringer 3842ef7138 Address second round of Jesper Schulz-Wedde's review on PR #157
- log-writes-must-survive-rollback.good.al: fixed invalid trigger
  OnRun(var Rec: ...) declaration; Rec is implicit when TableNo is set.
- exposed-objects-must-be-in-a-permission-set.md: distinguished the three
  exposure mechanisms (page/query web service or API, codeunit published
  as a web service, [ServiceEnabled] bound action on a page) and their
  actual permission targets (page/query "..." = X vs codeunit "..." = X).
- code-must-not-change-workdate.md: scoped from an absolute "never" to
  "not as a side effect of unrelated logic" - verified real WorkDate(x)
  setter usage in BCApps demo-data generators and test codeunits.
- bcpt-scenarios-must-be-app-specific.md: SingleInstance and
  StartScenario/EndScenario reframed as context-dependent patterns, not
  mandatory requirements - BCPT Create Customer uses neither.
- test-feature-scenario-tags.good.al/.bad.al: replaced the invented
  LibrarySales.CreateCustomerWithPrice/"Item Price Mgt." calls with a real,
  verified price-list-line test using Library - Sales/Library - Inventory/
  Library - Price Calculation.
- page-design-must-match-bc-page-type-conventions.md: scoped the missing
  UsageCategory anti-pattern to pages intended as searchable entry points.
- defensive-vs-offensive-code-must-match-blast-radius.md/.good.al/.bad.al:
  replaced the VAT registration number "low blast radius" example with a
  genuinely cosmetic field (customer home page URL).
- source-organized-by-feature-not-object-type.md: anti-pattern reframed as
  inconsistency with a repo's own convention, not the object-type scheme
  itself.
- pictures-must-use-media-not-blob.md: removed leftover "image variants"
  wording contradicting the already-corrected MediaSet description.

Proactively fixed while sweeping all fixtures for invented APIs:
- given-blocks-must-cover-full-precondition-chain.bad.al: PostSalesOrder
  called with wrong arity and referenced an undeclared variable.
- ui-test-codeunit-naming.good.al/.bad.al: replaced the same fake
  "Item Price Mgt."/TestPage "Item Price" with real Library - Sales calls
  and the real Customer Card TestPage.

Worklist completeness: added review-skill cues for the 12 of 18 new rules
that had none (al-appsource-review.md, al-data-modeling-review.md,
al-error-handling-review.md, al-security-review.md, al-style-review.md x3,
al-testing-review.md x2, al-ui-review.md, al-upgrade-review.md,
al-web-services-review.md), and fixed test-feature-scenario-tags' cue,
which only matched the compliant (tagged) shape instead of the anti-pattern
(untagged/generic-named test).

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-09-21 22:43:46 +02:00

2.6 KiB

bc-version domain keywords technologies countries application-area
all
data-modeling
workdate
session-setting
user-control
side-effect
al
w1
all

Application code must not change the WorkDate

Description

The work date is a per-user session setting the user controls from the client (the date shown in the top-right corner, used to default posting dates and date filters). Business logic unrelated to that setting must not call WorkDate(NewDate) as a side effect of doing something else — that silently changes what the user sees and defaults to for the rest of their session, a surprising, hard-to-trace behavior change the user never asked for and has no visibility into. This is not a blanket ban on the setter itself: BCApps' own demo-data generators legitimately save the current work date, set a specific one to backdate the data they create, and restore it afterward (see CreateDemoEDocsBE.Codeunit.al's WorkDate(SampleInvoiceDate) / WorkDate(SavedWorkDate) pair), and test codeunits routinely set WorkDate deliberately to control the date context a test runs under (hundreds of calls across BCApps' test suite, for example SustainabilityPostingTest.Codeunit.al). Both are the code's actual purpose, not a side effect of something unrelated.

This is a call-direction distinction for the read side: reading the current work date via WorkDate (or WorkDate() with no argument) is always fine.

Best Practice

Read the work date to default a value. Only write to it when changing it is the operation being performed — implementing the user's own work-date/settings action, or a test or demo-data routine that deliberately establishes a date context (saving and restoring the prior value if the routine must leave the session as it found it). Business logic that exists to do something else must never write WorkDate as an incidental side effect; if a calculation needs a specific date, pass or compute that date as a local variable instead.

See sample: code-must-not-change-workdate.good.al.

Anti Pattern

Setting the work date from within a codeunit, report, or page action whose purpose is unrelated to the user's date preference — for example, a posting or calculation routine that calls WorkDate(SomeDate) to make its own logic simpler. This changes session state the user owns for the duration of a call that was never about the work date, and never restores it. This is a different case from a test or demo-data routine explicitly declaring a date context: the anti-pattern is unrelated logic silently mutating state it does not own, not the setter form itself.

See sample: code-must-not-change-workdate.bad.al.