bcquality/custom/knowledge/mcp/stored-derived-fields-must-not-be-exposed-directly.md
Michael Dieringer 935d756f05 Add mcp knowledge category with 5 rules
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>
2026-06-20 13:48:06 +02:00

1.7 KiB

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

// 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:

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.