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.