Knowledge Base - Cloud Connect

Using Custom Handlers

CPQ Event Controller — Partner Event Subscription Guide

The CPQ Event Controller publishes a set of integration events that allow partners to extend and customize behavior during different stages of CPQ record processing. This includes initialization, validation, insertion, deletion, updating, configuration processing, and value changes.

This document provides a comprehensive overview of how partners can subscribe to the events exposed by codeunit 14124410 "CPQ Event Controller".

Each section includes:

  • A short description of when the event is triggered

  • A minimal AL subscriber snippet that can be copied into your extension

  • The correct event signature to ensure proper compilation and subscription

Codeunit Skeleton

It is recommended to keep all CPQ-related event subscribers in a single dedicated codeunit. Below is a basic structure that can be used to register all event subscriptions:

Example Event Subscriber Codeunit

codeunit 60000 "My CPQ Event Subscribers"
{
    Subtype = EventSubscriber;
 
    // Add your subscriber methods here
}

onAfterInitRecord

Triggered after the record has been initialized with template and key values. Use this to set default values before any processing starts.

Example: Subscribing to onAfterInitRecord

[EventSubscriber(ObjectType::Codeunit, Codeunit::"CPQ Event Controller", 'onAfterInitRecord', '', false, false)]
local procedure OnAfterInitRecord(var MainRecord: RecordRef; var ChangedFields: Boolean)
begin
    // Implementation
end;

onBeforeStartProcess

Triggered before CPQ processing starts. Use this to adjust field values before the standard logic runs.

 [EventSubscriber(ObjectType::Codeunit, Codeunit::"CPQ Event Controller", 'onBeforeStartProcess', '', false, false)] 
local procedure OnBeforeStartProcess(var MainRecord: RecordRef; var ChangedFields: Boolean) 
begin
     // Implementation 
end;    

onBeforeValidate

Triggered before input values are validated. Use this to normalize or modify values prior to validation.

 [EventSubscriber(ObjectType::Codeunit, Codeunit::"CPQ Event Controller", 'onBeforeValidate', '', false, false)] 
local procedure OnBeforeValidate(var MainRecord: RecordRef; var ChangedFields: Boolean) 
begin
     // Implementation 
end;

OnAfterProcessRecord

Triggered after the record has been processed.

 [EventSubscriber(ObjectType::Codeunit, Codeunit::"CPQ Event Controller", 'OnAfterProcessRecord', '', false, false)] 
local procedure OnAfterProcessRecord(var MainRecord: RecordRef) 
begin
     // Implementation 
end;

onBeforeInsert

Triggered before the record is inserted into the database.

 [EventSubscriber(ObjectType::Codeunit, Codeunit::"CPQ Event Controller", 'onBeforeInsert', '', false, false)] 
local procedure OnBeforeInsert(var MainRecord: RecordRef) begin
     // Implementation 
end;

OnAfterInsert

Triggered after the record has been inserted into the database.

 [EventSubscriber(ObjectType::Codeunit, Codeunit::"CPQ Event Controller", 'OnAfterInsert', '', false, false)] 
local procedure OnAfterInsert(var MainRecord: RecordRef) 
begin
     // Implementation 
end;

onBeforeDelete

Triggered before the record is deleted.

 [EventSubscriber(ObjectType::Codeunit, Codeunit::"CPQ Event Controller", 'onBeforeDelete', '', false, false)] 
local procedure OnBeforeDelete(var MainRecord: RecordRef) 
begin
     // Implementation 
end;    

OnAfterDelete

Triggered after the record is deleted.

 [EventSubscriber(ObjectType::Codeunit, Codeunit::"CPQ Event Controller", 'OnAfterDelete', '', false, false)] 
local procedure OnAfterDelete(var MainRecord: RecordRef) 
begin
     // Implementation 
end;

onBeforeUpdate

Triggered before the record is updated.

 [EventSubscriber(ObjectType::Codeunit, Codeunit::"CPQ Event Controller", 'onBeforeUpdate', '', false, false)] 
local procedure OnBeforeUpdate(var MainRecord: RecordRef) 
begin
     // Implementation 
end;

OnAfterUpdate

Triggered after the record has been updated.

 [EventSubscriber(ObjectType::Codeunit, Codeunit::"CPQ Event Controller", 'OnAfterUpdate', '', false, false)] 
local procedure OnAfterUpdate(var MainRecord: RecordRef) 
begin
     // Implementation 
end;

onBeforeProcessConfiguration

Triggered before configuration values are processed (read-only).

 [EventSubscriber(ObjectType::Codeunit, Codeunit::"CPQ Event Controller", 'onBeforeProcessConfiguration', '', false, false)] 
local procedure OnBeforeProcessConfiguration(ConfigValues: Dictionary of [Text, Text]) 
begin
     // Implementation 
end;

OnAfterProcessConfiguration

Triggered after configuration values have been processed (read-only).

 [EventSubscriber(ObjectType::Codeunit, Codeunit::"CPQ Event Controller", 'OnAfterProcessConfiguration', '', false, false)] 
local procedure OnAfterProcessConfiguration(ConfigValues: Dictionary of [Text, Text]) 
begin
     // Implementation 
end;

insertBeforeValidate

Indicates whether a record should be inserted before field validation. Currently, the result parameter is not var, so this event can only be used for observation.

 [EventSubscriber(ObjectType::Codeunit, Codeunit::"CPQ Event Controller", 'insertBeforeValidate', '', false, false)] 
local procedure InsertBeforeValidate(TableId: Integer; result: Boolean) begin
     // Implementation 
end;

validateLast

Indicates whether a foreign key field should be validated last. Currently, the result parameter is not var, so this event can only be used for observation.

 [EventSubscriber(ObjectType::Codeunit, Codeunit::"CPQ Event Controller", 'validateLast', '', false, false)] 
local procedure ValidateLast(TableId: Integer; FieldId: Integer; result: Boolean) 
begin
     // Implementation 
end;

OnAfterSetValue

Triggered after a field value has been written to the record buffer.

 [EventSubscriber(ObjectType::Codeunit, Codeunit::"CPQ Event Controller", 'OnAfterSetValue', '', false, false)] 
local procedure OnAfterSetValue(var MainRecord: RecordRef; FieldId: Integer; value: Text) 
begin
     // Implementation 
end;

Best Practices

  • Keep all CPQ event subscribers in a single, clearly named codeunit (e.g., "My CPQ Event Subscribers").

  • Avoid placing heavy business logic directly in event subscribers. Instead, delegate to dedicated functions or codeunits.

  • Use MainRecord.GetTable() and SetTable() when modifying typed records.

  • When changing fields in events that support it, set ChangedFields := true to ensure changes are recognized.

  • Use Validate instead of direct assignment to trigger standard Business Central logic.

  • Keep naming consistent: use On<EventName> for local procedure names.