mirror of
https://github.com/microsoft/BCQuality.git
synced 2026-08-07 18:06:53 +01:00
Rules derived from BC MCP API page development experience: - api-page-flowfields-must-be-calcfields: FlowFields return empty on API pages unless explicitly CalcFields'd in OnAfterGetRecord - stored-derived-fields-must-not-be-exposed-directly: Stored fields updated only via OnValidate triggers can be stale; recalculate live in OnAfterGetRecord - api-page-key-fields-must-be-editable-on-insert: ODataKeyFields with Editable=false are rejected as unknown properties on POST - api-page-least-privilege-write-access: Create dedicated minimal pages per write concern rather than widening general-purpose pages - agent-must-not-write-business-process-status: Agents must only write developer-tracking fields; business status fields affect invoicing/time registration Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
44 lines
1.7 KiB
Markdown
44 lines
1.7 KiB
Markdown
# CURABIS MCP: Stored Derived Fields Must Be Recalculated in OnAfterGetRecord
|
|
|
|
## Core Principle
|
|
|
|
A stored field whose value is derived from other fields via `OnValidate` triggers can be stale. When the source data changes (e.g., new time entries posted), the stored derived field is not updated automatically — it only recalculates when a specific trigger fires. Exposing such a field directly via an API page returns a value that may be hours, days, or weeks out of date.
|
|
|
|
## Pattern to Avoid
|
|
|
|
```al
|
|
// WRONG: Exposes the stored snapshot — may be stale
|
|
field(timeLeft; Rec."Time left") { }
|
|
```
|
|
|
|
`"Time left"` is recalculated only when `"Estimated time"` is validated. If new time entries are posted, the stored value does not update.
|
|
|
|
## Correct Pattern
|
|
|
|
Recalculate in `OnAfterGetRecord` using a page variable:
|
|
|
|
```al
|
|
trigger OnAfterGetRecord()
|
|
begin
|
|
Rec.CalcFields("Elapsed time (Chargeable)");
|
|
TimeLeftCalc := Rec."Estimated time" - Rec."Elapsed time (Chargeable)";
|
|
end;
|
|
|
|
var
|
|
TimeLeftCalc: Decimal;
|
|
|
|
// In layout:
|
|
field(timeLeft; TimeLeftCalc) { } // live value
|
|
field(elapsedTime; Rec."Elapsed time (Chargeable)") { } // source FlowField
|
|
```
|
|
|
|
## Requirements
|
|
|
|
- Identify stored fields whose value is computed from other fields via triggers
|
|
- Do not expose them directly in API pages
|
|
- Recalculate from the authoritative source (FlowField or live query) in `OnAfterGetRecord`
|
|
- Expose both the recalculated result and the source FlowField so the consumer can verify
|
|
|
|
## Verification
|
|
|
|
Inspect the source table for any field with `FieldClass = Normal` whose value is set inside an `OnValidate` trigger on another field. If that field is exposed on an API page, verify it is recalculated in `OnAfterGetRecord` rather than read from `Rec` directly.
|