Use Validate() instead of direct field assignment

Properties
PC0037 Warning Design Code Fix Ignore Obsolete

When a value is assigned to a record field with :=, the AL runtime writes the value directly without executing the field’s OnValidate trigger. Any event subscribers attached to that trigger — from Microsoft, partners, or per-tenant extensions — are silently skipped.

On the Sales Line table, for instance, the OnValidate trigger on "Unit Price" recalculates "Line Amount" from Quantity * "Unit Price". A direct assignment to "Unit Price" leaves "Line Amount" stale — the posted invoice carries the wrong total.

Use Validate() to assign field values so the full validation chain executes regardless of which extensions are installed.

Example

procedure SetUnitPrice(var SalesLine: Record "Sales Line"; NewPrice: Decimal)
begin
    SalesLine."Unit Price" := NewPrice; // Use Validate() instead of direct field assignment [PC0037]
    SalesLine.Modify(true);
end;

Replace the direct assignment with Validate():

procedure SetUnitPrice(var SalesLine: Record "Sales Line"; NewPrice: Decimal)
begin
    SalesLine.Validate("Unit Price", NewPrice);
    SalesLine.Modify(true);
end;

Code fix

The code fix replaces Record."Field" := Value with Record.Validate("Field", Value) preserving the original value expression as the second argument.

Exceptions

The rule is not raised when:

  • The record variable is declared as temporary and the table is a normal (persisted) table. A real table used as a scratch buffer may carry OnValidate logic with side effects the caller never intended, so bypassing it is a legitimate choice.
  • The table is an externally-backed integration table (TableType = CRM, CDS, ExternalSQL, Exchange or MicrosoftGraph). Their OnValidate only runs for AL-side changes, never for changes coming from the external system, so Validate() would suggest a guarantee that does not exist.

Tables with TableType = Temporary

Tables declared with TableType = Temporary are in scope, whether or not the variable also carries the temporary keyword (the keyword is redundant on such a table). Whoever writes an OnValidate trigger on an inherently temporary table knows it is temporary, so that logic is designed to run — in the Base Application such triggers modify other records, raise integration events and ask for confirmation. Like any other table, a TableType = Temporary table can also be extended with OnBeforeValidate/OnAfterValidate triggers from another app. This mirrors PC0027 , which does not warn about Validate() on TableType = Temporary records.

For trigger-less buffer tables where the warning adds nothing, disable the rule for that block or in a ruleset.

Use a pragma when you have a documented reason to bypass validation:

#pragma warning disable PC0037 // Justify why we don't execute the validation on the "Unit Price" field
SalesLine."Unit Price" := NewPrice;
#pragma warning restore PC0037

See also