Understanding the Rule Execution Pipeline: From Trigger to Action

Introduction

QUALIA Rule Engine operates as a sophisticated event-driven system that intercepts data changes in Business Central and evaluates configured business rules in real-time. Understanding the execution pipeline—how a database operation flows through trigger detection, scenario evaluation, condition processing, and action execution—is essential for advanced rule design, performance optimization, and troubleshooting.

This technical deep-dive examines the complete rule execution pipeline, revealing the internal mechanics that transform a simple "Save" button click into comprehensive business logic enforcement.

What you'll learn:

  • Event subscription architecture and trigger detection

  • Multi-stage evaluation process (Scenarios → Conditions → Actions)

  • RecordRef/FieldRef dynamic table access patterns

  • Performance optimization points within the pipeline

  • Transaction management and rollback behavior

  • Execution flow for different trigger types

Part 1: Event Subscription Architecture

GlobalTriggerManagement Integration

QUALIA Rule Engine integrates with Business Central's core event system through GlobalTriggerManagement event subscribers.

Event Subscriber Pattern:

Business Central fires database trigger events whenever records are inserted, modified, or deleted. QUALIA subscribes to six critical events:

[EventSubscriber(ObjectType::Codeunit, Codeunit::GlobalTriggerManagement, 
    OnBeforeOnDatabaseInsert, '', false, false)]
local procedure GlobalTriggerManagement_OnBeforeOnDatabaseInsert(
    RecRef: RecordRef; var IsHandled: Boolean)
begin
    TriggerRuleSet(RecRef, 1);  // TriggerType = 1 (Before Insert)
end;

[EventSubscriber(ObjectType::Codeunit, Codeunit::GlobalTriggerManagement, 
    OnAfterOnDatabaseInsert, '', false, false)]
local procedure GlobalTriggerManagement_OnAfterOnDatabaseInsert(RecRef: RecordRef)
begin
    TriggerRuleSet(RecRef, 11);  // TriggerType = 11 (After Insert)
end;

// Similar patterns for Modify (2, 21) and Delete (3, 31)

Trigger Type Encoding:

Trigger Type

Code

Database Operation

Timing

Before Insert

1

Insert

Before commit

After Insert

11

Insert

After commit

Before Modify

2

Modify

Before commit

After Modify

21

Modify

After commit

Before Delete

3

Delete

Before commit

After Delete

31

Delete

After commit

Why Two Timing Options?

Before Triggers (1, 2, 3):

  • Execute before database commit

  • Can block the operation with error messages

  • Can modify field values via Assign actions

  • Changes affect the transaction being committed

  • Used for validation and pre-population

After Triggers (11, 21, 31):

  • Execute after successful database commit

  • Cannot block the operation (already committed)

  • Cannot modify the committed record

  • Used for notifications, emails, external integrations

  • Record is already in database with committed values

Universal Table Coverage:

Event subscribers intercept every table operation in Business Central:

  • Standard BC tables (Customer, Item, Sales Order, etc.)

  • Custom extension tables

  • Temporary tables (if configured)

  • System tables (if not excluded)

This universal coverage eliminates the need to modify individual table triggers or create custom code for each validation scenario.

Entry Point: TriggerRuleSet Procedure

When an event fires, control passes to the TriggerRuleSet procedure:

procedure TriggerRuleSet(var Curr_RecRef: RecordRef; TriggerType: Integer)

Parameters:

  • Curr_RecRef: RecordRef pointing to the record being inserted/modified/deleted

  • TriggerType: Integer code indicating which trigger fired

Responsibilities:

  1. Old Value Retrieval (for Modify operations):

xRecRef.Open(Curr_RecRef.Number, false, Curr_RecRef.CurrentCompany());
xRecRef.ReadIsolation := xRecRef.ReadIsolation::ReadCommitted;
if xRecRef.Get(Curr_RecRef.RecordId) then;

The system opens a second RecordRef (xRecRef) to retrieve the pre-modification state of the record. This enables change detection and old value placeholder resolution {TableID:FieldID}.

  1. Field-Level Change Detection (Modify only):

if (TriggerType = 2) or (TriggerType = 21) then
    for i := 1 to Curr_RecRef.FieldCount do begin
        Curr_FieldRef := Curr_RecRef.FieldIndex(i);
        xFieldRef := xRecRef.FieldIndex(i);
        if Curr_FieldRef.Value <> xFieldRef.Value then begin
            ApplyValidations(Curr_RecRef, xRecRef, Curr_FieldRef, xFieldRef, TriggerType);
        end;
    end;

For Modify operations, the system loops through all fields and calls ApplyValidations only for fields that actually changed. This optimization prevents unnecessary rule evaluation for unchanged fields.

  1. Full Record Validation (Insert/Delete):

For Insert and Delete operations, all fields with values are processed:

if (TriggerType <> 2) and (TriggerType <> 21) then
    for i := 1 to Curr_RecRef.FieldCount do begin
        Curr_FieldRef := Curr_RecRef.FieldIndex(i);
        if HasValue(Curr_FieldRef) then
            ApplyValidations(Curr_RecRef, xRecRef, Curr_FieldRef, xFieldRef, TriggerType);
    end;

Performance Optimization Note:

The field-level change detection for Modify operations is a critical performance optimization. Without it, every Modify would evaluate rules for all fields, even those that didn't change. By detecting which specific fields changed, the system evaluates only relevant rules.

Example: If a user modifies only the "Phone No." field on a Customer record, only rules configured for the Phone No. field (Field ID 9) are evaluated, not rules for Name, Address, Credit Limit, etc.

Part 2: Validation Set Retrieval and Filtering

ApplyValidations Procedure Entry

The ApplyValidations procedure is the main orchestrator of rule evaluation:

local procedure ApplyValidations(
    var Curr_RecRef: RecordRef;
    var xRecRef: RecordRef;
    Curr_FieldRef: FieldRef;
    xFieldRef: FieldRef;
    TriggerType: Integer)

First Task: Retrieve Applicable Validation Sets

ValidationSets.Reset;
ValidationSets.SetRange(TableNo, Curr_RecRef.Number);
ValidationSets.SetRange(FieldNo, Curr_FieldRef.Number);
ValidationSets.SetRange(TriggerType, TriggerType);
ValidationSets.SetRange(Disable, false);
if ValidationSets.FindSet() then

Filtering Logic:

Filter

Purpose

TableNo

Only validation sets configured for this table (e.g., 18 for Customer)

FieldNo

Only validation sets for the specific field that changed

TriggerType

Only validation sets configured for this trigger (Before Insert, After Modify, etc.)

Disable

Exclude validation sets that have been disabled

This filtering dramatically narrows the rule evaluation scope. If 500 validation sets exist in the system but only 3 apply to "Customer table, Credit Limit field, Before Modify trigger," only those 3 are evaluated.

User Group and Rule Group Filtering

After retrieving applicable validation sets, the system applies user-based filtering:

BusinessRulesGroup.Reset;
BusinessRulesGroup.SetRange("Validation Set No.", ValidationSets."Validation Set No.");
if BusinessRulesGroup.FindSet() then
    repeat
        UserGroupMembers.Reset();
        UserGroupMembers.SetRange("User Security ID", UserSecurityId());
        UserGroupMembers.SetRange("User Group Code", BusinessRulesGroup."Rule Group Code");
        if UserGroupMembers.FindFirst() then
            UserGroupMatched := true;
    until (BusinessRulesGroup.Next() = 0) or UserGroupMatched;

User Group Logic:

  1. If validation set has NO rule groups assigned → Applies to ALL users

  2. If validation set has rule groups assigned → Applies ONLY to users in those groups

  3. User must be in AT LEAST ONE assigned rule group for rules to execute

Use Case Example:

Validation Set: "Sales Order Discount Approval"

  • Rule Group: "Sales Managers" → Only sales managers see discount approval rules

  • Rule Group: "Sales Reps" → Sales reps don't see these rules

When a sales rep modifies a sales order, this validation set is skipped entirely due to user group filtering.

Performance Impact:

User group filtering happens early in the pipeline, before any complex evaluations. This prevents unnecessary rule processing for users who shouldn't be subject to certain rules.

Part 3: Scenario Evaluation (First Gate)

Two-Tier Validation Model

QUALIA uses a unique two-tier model:

Tier 1: Scenarios (Optional pre-filters)

  • Must ALL evaluate to TRUE for processing to continue

  • Act as early filters to prevent unnecessary condition evaluation

  • Optimize performance by short-circuiting evaluation

Tier 2: Conditions (Main validation logic)

  • Evaluated only if all scenarios pass

  • At least ONE condition must be TRUE for actions to execute

  • Contain the core business logic

Processing Flow:


Scenario Evaluation Logic

if CheckScenarios(Curr_RecRef, xRecRef, Curr_FieldRef, xFieldRef, ValidationSets) then begin
    // Proceed to conditions
end;

Inside CheckScenarios:

local procedure CheckScenarios(...) : Boolean
var
    ScenarioResult: Boolean;
begin
    Validations.Reset();
    Validations.SetRange("Validation Set No.", Par_ValidationSets."Validation Set No.");
    Validations.SetRange(Type, Validations.Type::Scenario);
    if not Validations.FindSet() then
        exit(true);  // No scenarios = automatic pass
    
    repeat
        // Evaluate each scenario
        ScenarioResult := EvaluateCondition(...);
        if not ScenarioResult then
            exit(false);  // ANY scenario failure = entire validation set fails
    until Validations.Next() = 0;
    
    exit(true);  // All scenarios passed
end;

Key Behavior:

  • If NO scenarios exist → Automatically pass and proceed to conditions

  • If scenarios exist → ALL must evaluate to TRUE

  • First scenario failure → Immediate exit, skip all conditions

Performance Optimization Strategy:

Place restrictive filters in scenarios to eliminate unnecessary condition evaluation:

Example: Large Customer Credit Check

Scenario (fast filter):

[18:59]

Conditions (complex logic):

[37:125] > [18:59] * 0.9
// Current order amount > 90% of credit limit

AND EXISTS(SalesLine WHERE [Outstanding Amount]

If credit limit is only $5,000, the scenario fails immediately and the complex condition evaluation is skipped entirely. This saves significant processing time for the majority of customers who don't meet the threshold.

Placeholder Resolution During Scenarios

When evaluating scenario conditions, the system must resolve placeholders like [18:59]:

local procedure FillValues(
    var MessageString: Text;
    Par_validations: Record QUA_Validations;
    var Curr_RecRef: RecordRef;
    Curr_FieldRef: FieldRef;
    xFieldRef: FieldRef)

Placeholder Resolution Process:

  1. Current Table Fields [TableID:FieldID]:

    • Open FieldRef on Curr_RecRef

    • Retrieve value directly

    • Convert to text for substitution

  2. Linked Table Fields (via Source References):

    • Retrieve Source Reference configuration

    • Apply filters to linked table

    • Navigate to linked record

    • Extract field value

  3. Old Values {TableID:FieldID}:

    • Use xRecRef instead of Curr_RecRef

    • Retrieve pre-modification value

  4. Aggregate Functions MIN(), MAX(), SUM():

    • Apply filters to record set

    • Calculate aggregate value

    • Replace function call with result

  5. Mathematical Expressions [18:59] * 0.9:

    • Resolve all placeholders first

    • Parse mathematical expression

    • Evaluate arithmetic

    • Replace with calculated result

Resolution Order Matters:

The system resolves placeholders in a specific order to handle nested expressions:

  1. Special placeholders (USERID, TODAY, WORKDATE)

  2. Old value placeholders {}

  3. Linked table navigations

  4. Current table fields []

  5. Aggregate functions

  6. Mathematical expressions

This order ensures that expressions like SUM([Line Amount]) * 1.1 resolve correctly.

Part 4: Condition Evaluation (Second Gate)

Conditions as Primary Logic

After all scenarios pass (or if no scenarios exist), the system evaluates conditions:

Validations.Reset();
Validations.SetRange("Validation Set No.", ValidationSets."Validation Set No.");
Validations.SetRange(Type, Validations.Type::Condition);
Validations.SetRange(Disable, false);
if Validations.FindSet() then
    repeat
        if EvaluateCondition(...) then begin
            ConditionPassed := true;
            // Execute actions for this condition
            ExecuteActions(...);
        end;
    until Validations.Next() = 0;

Key Differences from Scenarios:

Aspect

Scenarios

Conditions

Must ALL pass

YES

NO (any ONE can pass)

Typical use

Pre-filtering

Core validation logic

Failure behavior

Stop entire validation set

Continue to next condition

Action execution

Never

Always (if condition TRUE)

Multiple Condition Behavior:

If validation set has 3 conditions:

  • Condition 1 evaluates TRUE → Actions execute

  • Condition 2 evaluates TRUE → Actions execute again

  • Condition 3 evaluates FALSE → No actions, continue

Each condition is independent. Multiple conditions passing means actions execute multiple times (potentially with different placeholder values resolved for each condition).

Why Allow Multiple Conditions?

Use Case: Tiered Approval Routing

Condition 1: Amount > $1,000 → Email to Manager Condition 2: Amount > $10,000 → Email to Director Condition 3: Amount > $100,000 → Email to VP

If amount is $50,000:

  • Condition 1 TRUE → Email Manager

  • Condition 2 TRUE → Email Director

  • Condition 3 FALSE → Don't email VP

Result: Both Manager and Director receive emails (tiered escalation).

Condition Evaluation Engine

The EvaluateCondition function performs the actual comparison logic:

local procedure EvaluateCondition(
    var Curr_RecRef: RecordRef;
    var xRecRef: RecordRef;
    Curr_FieldRef: FieldRef;
    xFieldRef: FieldRef;
    Validation: Record QUA_Validations) : Boolean

Evaluation Process:

  1. Retrieve Validation Formula:

ValidationFormula.Get(Validation."Validation Set No.", Validation."Validation Rule No.");
Message := ValidationFormula.GetFormula();
  1. Resolve Placeholders:

FillValues(Message, Validation, Curr_RecRef, Curr_FieldRef, xFieldRef);

After placeholder resolution, formula transforms:

Before: [18:59]

  1. Apply Logical Operators:

The system supports multiple comparison operators:

Operator

Meaning

Example

is

Equals

[18:3] is 'JOHN'

is not

Not equals

[18:3] is not ''

>

Greater than

[18:59] > 50000

<

Less than

[18:59] < 10000

>=

Greater than or equal

[18:59] >= 50000

<=

Less than or equal

[18:59] <= 10000

is in

Value in list

[18:21] is in ('SALES', 'MARKETING')

is not in

Value not in list

[18:21] is not in ('INTERNAL')

..

Range

[18:59] in 10000..50000

|

Multiple values

[18:3] is 'JOHN'|'JANE'|'JACK'

*

Wildcard

[18:3] is 'A*'

  1. Evaluate Boolean Expression:

After operator application, the formula becomes a boolean expression:

The system evaluates this expression and returns TRUE or FALSE.

Complex Expressions with AND/OR:

QUALIA supports complex logical expressions:

[18:59] > 50000 AND [18:2] is not '' AND [18:9]

The expression parser evaluates left-to-right with AND/OR precedence:

  • AND has higher precedence than OR

  • Parentheses can group expressions (planned future enhancement)

Part 5: Action Execution

Action Retrieval and Execution

When a condition evaluates TRUE, the system executes configured actions:

Actions.Reset();
Actions.SetRange("Validation Set No.", Validation."Validation Set No.");
Actions.SetRange("Validation Rule No.", Validation."Validation Rule No.");
Actions.SetRange(Disable, false);
if Actions.FindSet() then
    repeat
        ExecuteAction(Actions, Curr_RecRef, xRecRef, Curr_FieldRef, xFieldRef);
    until Actions.Next() = 0;

Action Types and Execution:

Each action type has a specific execution handler:

1. Error Message (Type = Error Message)

if Actions."Action Type" = Actions."Action Type"::"Error Message" then begin
    ErrorMessageDetails.Get(Actions."Validation Set No.", Actions."Action No.");
    Message := ErrorMessageDetails.GetMessage();
    FillValues(Message, ...);  // Resolve placeholders
    Error(Message);  // Throws error, blocks transaction
end;

Critical Behavior: Error() immediately terminates processing and rolls back the entire database transaction. The record is NOT saved.

2. Message (Type = Message)

if Actions."Action Type" = Actions."Action Type"::Message then begin
    MessageDetails.Get(Actions."Validation Set No.", Actions."Action No.");
    Message := MessageDetails.GetMessage();
    FillValues(Message, ...);
    Message(Message);  // Shows dialog, allows continuation
end;

Critical Behavior: Message() displays a dialog but does NOT block the transaction. User clicks OK and the record saves.

3. Confirmation (Type = Confirmation)

if Actions."Action Type" = Actions."Action Type"::Confirmation then begin
    ConfirmationDetails.Get(Actions."Validation Set No.", Actions."Action No.");
    Message := ConfirmationDetails.GetMessage();
    FillValues(Message, ...);
    if Confirm(Message, false) then begin
        // Execute "Yes" actions
        ExecuteConfirmationActions(ConfirmationSelection::Yes, ...);
    end else begin
        // Execute "No" actions
        ExecuteConfirmationActions(ConfirmationSelection::No, ...);
    end;
end;

Critical Behavior: User choice determines which secondary actions execute. "No" selection can contain error messages that block the transaction.

4. Email (Type = Email)

if Actions."Action Type" = Actions."Action Type"::Email then begin
    EmailDetails.Get(Actions."Validation Set No.", Actions."Action No.");
    ToRecipients := EmailDetails.GetToRecipients();
    Subject := EmailDetails.GetSubject();
    Body := EmailDetails.GetBody();
    FillValues(ToRecipients, ...);
    FillValues(Subject, ...);
    FillValues(Body, ...);
    SendEmail(ToRecipients, Subject, Body, ...);
end;

Critical Behavior: Email execution is asynchronous. The transaction completes regardless of email success/failure. Email errors are logged but don't block the operation.

5. Assign (Type = Assign)

if Actions."Action Type" = Actions."Action Type"::Assign then begin
    AssignDetails.Get(Actions."Validation Set No.", Actions."Action No.");
    AssignDetailsLine.SetRange("Validation Set No.", Actions."Validation Set No.");
    AssignDetailsLine.SetRange("Action No.", Actions."Action No.");
    if AssignDetailsLine.FindSet() then
        repeat
            TargetFieldRef := Curr_RecRef.Field(AssignDetailsLine."Field No.");
            ValueToAssign := AssignDetailsLine.GetValue();
            FillValues(ValueToAssign, ...);
            TargetFieldRef.Value := ValueToAssign;
        until AssignDetailsLine.Next() = 0;
end;

Critical Behavior: Assign actions modify the Curr_RecRef directly BEFORE database commit. The modified values are what gets saved to the database.

Action Execution Order:

Actions execute in the order defined by the "Action No." field (typically 10, 20, 30, etc.). This ordering is critical when actions depend on previous actions:

Example:

  • Action 10: Assign discount percentage

  • Action 20: Email notification referencing the newly assigned discount

  • Action 30: Confirmation asking user to approve

Action 20's email will include the discount value assigned by Action 10 because placeholder resolution happens after the assignment.

Transaction Management

Before Triggers (1, 2, 3) Transaction Behavior:


After Triggers (11, 21, 31) Transaction Behavior:


Performance Consideration:

Before triggers can delay user experience if complex rules take time to evaluate. After triggers execute asynchronously and don't block the UI, but they can't prevent invalid data from being saved.

Best Practice:

  • Validations that must block bad data → Before triggers

  • Notifications and integrations → After triggers

  • Assign actions → Before triggers only (After triggers can't modify committed records)

Part 6: Validation Log Recording

Audit Trail Creation

After every rule evaluation, the system records execution details to the Validation Log:

ValidationLog.Init();
ValidationLog."Entry No." := GetNextEntryNo();
ValidationLog."User ID" := UserId();
ValidationLog."Validation Set No." := ValidationSets."Validation Set No.";
ValidationLog."Validation Rule No." := Validation."Validation Rule No.";
ValidationLog."Table ID" := Curr_RecRef.Number;
ValidationLog."Field ID" := Curr_FieldRef.Number;
ValidationLog."Trigger Type" := TriggerType;
ValidationLog."Scenario Result" := ScenarioResult;
ValidationLog."Condition Result" := ConditionResult;
ValidationLog."Action Executed" := ActionExecuted;
ValidationLog."Execution Timestamp" := CurrentDateTime;
ValidationLog."Error Occurred" := ErrorOccurred;
ValidationLog."Error Message" := ErrorMessage;
ValidationLog.Insert();

Log Entry Contents:

Field

Purpose

Entry No.

Unique sequential log entry identifier

User ID

Which user triggered the rule

Validation Set No.

Which validation set was evaluated

Validation Rule No.

Which specific scenario/condition

Table ID

Which table was being modified

Field ID

Which field triggered the evaluation

Trigger Type

Before/After Insert/Modify/Delete

Scenario Result

TRUE/FALSE (did scenarios pass?)

Condition Result

TRUE/FALSE (did condition pass?)

Action Executed

Which actions ran

Execution Timestamp

When did evaluation occur

Error Occurred

Did an error action fire?

Error Message

What was the error text?

Log Usage Scenarios:

Troubleshooting:

  • Why didn't my rule fire? → Check if scenario failed

  • Why did user get error? → View error message and condition

Performance Analysis:

  • Which rules are slowest? → Analyze execution timestamps

  • Which rules fire most frequently? → Count log entries by validation set

Compliance Auditing:

  • Prove certain validations are enforcing policy

  • Demonstrate when rules were active/inactive

  • Show historical rule evaluation results

Log Retention and Cleanup:

Validation log can grow large over time. The system includes cleanup functionality:

Report 72777800 "Delete Extra Records"
// Deletes validation log entries older than specified date

Typical retention policy: Keep logs for 90-180 days depending on compliance requirements.

Part 7: Performance Optimization Points

Optimization Opportunities in the Pipeline

1. Scenario Optimization (Earliest Possible Exit)

Scenarios provide the earliest opportunity to skip unnecessary processing:

BAD (No scenarios, slow conditions):
Condition: SUM(Sales Line Amount) > [Customer Credit Limit] AND [Customer Blocked] is not 'Yes'
// Calculates sum for EVERY customer modification

GOOD (Scenario filters, fast exit):
Scenario: [Customer Blocked] is not 'Yes' AND [Customer Credit Limit] > 0
Condition: SUM(Sales Line Amount) > [Customer Credit Limit]

Performance Gain: Scenarios eliminate 80-90% of unnecessary condition evaluations.

2. User Group Filtering (Second Exit Point)

Assign rule groups to validation sets that apply to specific user roles:


Performance Gain: User group filtering prevents 80-95% of unnecessary rule evaluations for role-specific rules.

3. Field-Level Trigger Specificity

Configure validation sets for specific fields instead of entire tables:


Performance Gain: Field-specific rules reduce evaluation by 90-99% (typical record modifications change 1-2 fields out of 100+).

4. Linked Table Reduction

Minimize the number of source references (linked tables):

INEFFICIENT:
Source Reference 1: Sales Header
Source Reference 2: Customer
Source Reference 3: Salesperson
Source Reference 4: Payment Terms
Source Reference 5: Shipping Agent
// 5 table navigations for every evaluation

EFFICIENT:
Source Reference 1: Sales Header
// Access Customer data through Sales Header relationship in conditions
// [36:3] then [18:59]

Performance Gain: Each source reference adds 10-50ms per evaluation. Reducing from 5 to 2 source references can improve performance by 50-150ms per rule.

5. Aggregate Function Filter Specificity

Always apply restrictive filters to aggregate functions:

INEFFICIENT:
SUM([Sales Line:Amount])
// Sums ALL sales lines in database (potentially millions)

EFFICIENT:
SUM([Sales Line:Amount] WHERE [Document No.] = [Sales Header:No.]

Performance Gain: Proper filtering reduces aggregate calculations from seconds to milliseconds.

Execution Time Benchmarks

Typical Execution Times:

Operation

Time

Event subscription trigger

<1ms

Validation set retrieval

1-5ms

User group filtering

1-3ms

Scenario evaluation (simple)

2-10ms

Condition evaluation (simple)

2-10ms

Condition with 1 source reference

10-30ms

Condition with aggregates (well-filtered)

20-50ms

Condition with aggregates (poorly filtered)

500-5000ms

Email action execution

50-200ms

Error action execution

1-5ms

Total (optimized rule)

10-100ms

Total (unoptimized rule)

500-5000ms

Performance Target:

Well-designed rules should complete in under 100ms. Users perceive delays over 200ms as sluggish.

Part 8: Execution Flow Diagrams

Complete Pipeline Visualization

┌─────────────────────────────────────────────────────────────────┐
│  USER ACTION: Clicks "Save" on Customer Card                    │
└────────────────┬────────────────────────────────────────────────┘
                 │
                 ▼
┌─────────────────────────────────────────────────────────────────┐
│  BUSINESS CENTRAL: Processes Customer.Modify()                  │
│  • Validates field types and lengths                            │
│  • Checks table permissions                                     │
│  • Begins database transaction                                  │
└────────────────┬────────────────────────────────────────────────┘
                 │
                 ▼
┌─────────────────────────────────────────────────────────────────┐
│  EVENT: GlobalTriggerManagement.OnBeforeOnDatabaseModify        │
│  • Fires before database commit                                 │
│  • Passes RecordRef to all subscribers                          │
└────────────────┬────────────────────────────────────────────────┘
                 │
                 ▼
┌─────────────────────────────────────────────────────────────────┐
│  QUALIA: TriggerRuleSet (TriggerType = 2)                      │
│  • Retrieves old record state (xRecRef)                        │
│  • Loops through all fields                                     │
│  • Identifies changed fields                                    │
│  • Calls ApplyValidations for each changed field               │
└────────────────┬────────────────────────────────────────────────┘
                 │
                 ▼
┌─────────────────────────────────────────────────────────────────┐
│  FILTERING STAGE 1: Validation Set Retrieval                    │
│  • Filter by: Table ID = 18 (Customer)                         │
│  • Filter by: Field ID = 59 (Credit Limit)                     │
│  • Filter by: Trigger Type = 2 (Before Modify)                 │
│  • Filter by: Disable = false                                   │
│  Result: 3 validation sets found                                │
└────────────────┬────────────────────────────────────────────────┘
                 │
                 ▼
┌─────────────────────────────────────────────────────────────────┐
│  FILTERING STAGE 2: User Group Check                            │
│  FOR EACH validation set:                                       │
│    • Check assigned rule groups                                 │
│    • Check if current user in any rule group                    │
│    • If NO groups assigned → Include (applies to all)          │
│    • If user in group → Include                                 │
│    • If user not in any group → Exclude                         │
│  Result: 2 validation sets applicable to this user              │
└────────────────┬────────────────────────────────────────────────┘
                 │
                 ▼
┌─────────────────────────────────────────────────────────────────┐
│  EVALUATION LOOP: FOR EACH Validation Set                       │
└────────────────┬────────────────────────────────────────────────┘
                 │
                 ▼
┌─────────────────────────────────────────────────────────────────┐
│  STAGE 1: Scenario Evaluation                                   │
│  • Retrieve all scenarios for validation set                    │
│  • If NO scenarios → Proceed to conditions                      │
│  • If scenarios exist:                                          │
│    ├─ Evaluate Scenario 1:                                      │
│    │  └─ Resolve placeholders: [18:59] > 50000                 │
│    │  └─ Evaluate: 75000 > 50000 → TRUE                        │
│    ├─ Evaluate Scenario 2:                                      │
│    │  └─ Resolve placeholders: [18:39] is ''                   │
│    │  └─ Evaluate: 'BLOCKED' is '' → FALSE                     │
│    └─ RESULT: NOT ALL scenarios passed                          │
│       └─ STOP: Skip this validation set entirely                │
└─────────────────────────────────────────────────────────────────┘
                 │
                 ▼
        ┌────────────────┐
        │ NEXT VALIDATION│
        │      SET       │
        └────────┬───────┘
                 │
                 ▼
┌─────────────────────────────────────────────────────────────────┐
│  STAGE 1: Scenario Evaluation (Validation Set #2)              │
│  • Retrieve all scenarios                                       │
│  ├─ Evaluate Scenario 1: [18:59] > 10000                       │
│  │  └─ 75000 > 10000 → TRUE                                    │
│  ├─ Evaluate Scenario 2: [18:2] is not ''                      │
│  │  └─ 'John Haddock Insurance Co.' is not '' → TRUE          │
│  └─ RESULT: ALL scenarios passed                               │
│     └─ PROCEED to condition evaluation                          │
└────────────────┬────────────────────────────────────────────────┘
                 │
                 ▼
┌─────────────────────────────────────────────────────────────────┐
│  STAGE 2: Condition Evaluation                                  │
│  • Retrieve all conditions for validation set                   │
│  ├─ Evaluate Condition 1:                                       │
│  │  ├─ Formula: [36:125] > [18:59] * 0.9                      │
│  │  ├─ Resolve [36:125]: Navigate to Sales Header via Source   │
│  │  │  Reference, get Outstanding Amount = 82,000              │
│  │  ├─ Resolve [18:59]: Current record Credit Limit = 75,000  │
│  │  ├─ Calculate: 75,000 * 0.9 = 67,500                       │
│  │  ├─ Evaluate: 82,000 > 67,500 → TRUE                       │
│  │  └─ CONDITION PASSED → Execute actions                      │
│  └─ Evaluate Condition 2:                                       │
│     ├─ Formula: [18:21] is 'EXTERNAL'                          │
│     ├─ Resolve [18:21]

Error Flow (Transaction Rollback)

┌─────────────────────────────────────────────────────────────────┐
│  ... (Same flow as above through Scenario and Condition) ...   │
└────────────────┬────────────────────────────────────────────────┘
                 │
                 ▼
┌─────────────────────────────────────────────────────────────────┐
│  CONDITION EVALUATION:                                           │
│  • Formula: [18:1]

Summary and Key Takeaways

This deep-dive revealed the complete rule execution pipeline:

  • Event subscription architecture with GlobalTriggerManagement integration for universal table coverage

  • Field-level change detection for Modify operations to optimize performance

  • Two-stage filtering (validation sets → user groups) to narrow evaluation scope

  • Two-tier validation model (scenarios → conditions) with short-circuit evaluation

  • Placeholder resolution system supporting current values, old values, linked tables, and aggregates

  • Action execution framework with transaction control (Error blocks, Message allows)

  • Comprehensive audit logging for troubleshooting and compliance

Performance optimization opportunities:

  • Scenarios for early filtering (80-90% evaluation reduction)

  • User group assignment for role-specific rules (80-95% reduction)

  • Field-specific validation sets (90-99% reduction)

  • Aggregate function filter specificity (seconds to milliseconds)

Best practices:

  • Use Before triggers for validation and Assign actions

  • Use After triggers for notifications and integrations

  • Place restrictive logic in scenarios

  • Apply user groups to role-specific validation sets

  • Configure validation sets for specific fields, not entire tables

  • Always filter aggregate functions aggressively

Related topics:

  • Blog 006: Understanding Trigger Events (foundational concepts)

  • Blog 007: Scenarios vs. Conditions (two-tier model)

  • Blog 041: Performance Tuning (optimization techniques)

  • Blog 093: Rule Execution Pipeline (this technical deep-dive)

This blog is part of the QUALIA Rule Engine series for Microsoft Dynamics 365 Business Central. Follow along as we explore business rule automation patterns.

Get Your FREE Dynamics 365 Demo

Transform your business operations with Microsoft Dynamics 365 Business Central

Experience the transformative power of Microsoft Dynamics 365 Business Central for yourself! Request a free demo today and see how our solutions can streamline your operations and drive growth for your business.

Our team will guide you through a personalized demonstration tailored to your specific needs. This draft provides a structured approach to presenting Qualia Tech's offerings related to Microsoft Dynamics 365 Business Central while ensuring that potential customers understand the value proposition clearly.

Certified partners with

Certified partners with

Areas Of Interest

Please read and confirm the following:

*Note: Fields marked with * are mandatory for processing your request.

*Note: Fields marked with * are mandatory for processing your request.

© 2024 Qualia. All rights reserved

QUALIA Technik GmbH

info@qualiatechnik.de

17, Heinrich-Erpenbach-Str. 50999 Köln

© 2024 Qualia. All rights reserved

QUALIA Technik GmbH

info@qualiatechnik.de

17, Heinrich-Erpenbach-Str. 50999 Köln

© 2024 Qualia. All rights reserved

QUALIA Technik GmbH

info@qualiatechnik.de

17, Heinrich-Erpenbach-Str. 50999 Köln

© 2024 Qualia. All rights reserved

QUALIA Technik GmbH

info@qualiatechnik.de

17, Heinrich-Erpenbach-Str. 50999 Köln