How to Develop in Smart Processing (Developer Documentation)
Status: 01.08.2026 • Reading time: ~10 minutes
Smart Processing provides extension points where you can implement custom logic to start your own processes or adapt handling to customer requirements. This document explains the key development concept: work through the active session, and trigger custom codeunits in the matching process using the session data.
1. Core Concept: Work Through the Active Processing Session
Smart Processing runs an “active draft session” while a document is being processed. During that time, relevant state is maintained centrally (document context, template context, matched lines, deviations, and intermediate data used by the UI and processing pipeline).
As a developer, the recommended approach is:
- Read the current processing context from the session.
- Perform your logic using the provided context and references.
- Write results/state back into the session when the workflow needs it.
- Rely on the platform to keep UI and processing aligned through the session architecture.
This makes customizations stable and avoids inconsistent state between what users see and what your logic changed.
2. The Central Object: “SIM_DI Process Session SI”
The codeunit “SIM_DI Process Session SI” is the central entry point for accessing and manipulating the currently active Smart Processing session. It acts as the hub to obtain relevant runtime data (inbound document, process template, line buffers, deviation metadata) and to communicate results back to the pipeline (for example, which header was processed).
Public function overview
The following table lists all public, non-obsolete functions of "SIM_DI Process Session SI". If a function exists in multiple variants (overloads), parameters that may not be present in every variant are marked as optional.
| Function | What it does | Parameters (optional marked) | Returns |
|---|---|---|---|
InitSession |
Initializes the session with an inbound document and resolves the related process template. | InboundDocument |
none |
InitSession |
Initializes the session with an inbound document and an explicitly provided process template (and clears previous session data first). | InboundDocument, ProcessTemplate |
none |
GetInboundDocument |
Retrieves the inbound document stored in the current session. | var InboundDocument |
none |
GetProcessTemplate |
Retrieves the process template stored in the current session. | var ProcessTemplate |
none |
ClearProcessSession |
Clears the full session context including inbound document, process template, mapping keys, and process data. | - | none |
ClearProcessData |
Clears only processing data (temporary header/lines/remarks, mapped lookup records, processed header list) and clears matching data. | - | none |
AddProcessedHeaderRecordId |
Adds a processed document header record id to the session list. Supports multi-document matching. Duplicate entries are silently ignored. | RecordId |
none |
GetProcessedHeaderRecordIds |
Returns the list of all processed document header record ids collected during the current matching run. | - | List of [RecordId] |
SetDocumentHeaderVariableRef |
Stores a copy of the provided temporary header variable in the session (used as a reference-style session header state). | var TempDocumentHeader (temporary) |
none |
GetDocumentHeaderVariable |
Copies the session’s current temporary header data into the provided temporary record variable. | var TempDocumentHeader (temporary) |
none |
SetDocumentLineVariableRef |
Stores a copy of the provided temporary line variable in the session (used as a reference-style session line state). | var TempDocumentLine (temporary) |
none |
GetDocumentLineVariable |
Copies the session’s current temporary line data into the provided temporary record variable. | var TempDocumentLine (temporary) |
none |
SetRemarkVariableRef |
Stores a copy of the provided temporary remark variable in the session (used as a reference-style session remark state). | var DocumentRemark (temporary) |
none |
RemoveDocumentRemark |
Removes a remark from the session based on line number, field number, and validation area. | LineNo, FieldNo, FieldValidationArea |
none |
GetRemarks |
Copies all session remarks into the provided temporary remark record variable. | var DocumentRemark (temporary) |
none |
RemoveMappedLookupRecord |
Removes the mapped lookup record reference for a given template field reference. | TemplateFieldRecordId |
none |
GetMappedLookupRecord |
Retrieves the mapped lookup record reference for a given template field reference (if available). | TemplateFieldRecordId, var MappedRecordId |
Boolean |
GetDevitatingLinesJsonObject |
Returns the JSON object describing deviating lines and their mismatched fields (including matching setup name and mismatch details). | - | JsonObject |
GetMatchedLineData |
Copies all matched line data entries into the provided temporary record. Each entry contains: document line no., matching setup name, execution codeunit no., and the matched header/line RecordIds. | var TempMatchedLineData (temporary) |
none |
3. Customizations in the Matching Process
This is only needed if the codeunit is used as a custom process (If the option "Skip Creation on Match" in the matching setup is active).
The matching process in Smart Processing has been redesigned to support simultaneous execution of multiple matching setups. Each incoming document line can now be matched independently to a different target document header and line (multi-header and multi-line matching). This means line 1 of an incoming document can be assigned to one purchase order while line 2 goes to a completely different one.
A common extension scenario is to trigger custom codeunits during this matching step — especially when the user completes the draft and one or more target headers have been created or identified.
The custom codeunit is still executed with the header record of the matched document type as Rec (for example, a purchase header). This record is your stable anchor for that particular header. To determine which incoming lines were matched to this header (and which execution codeunit triggered the run), use GetMatchedLineData from the session and filter by "Execution Codeunit No.".
Once your codeunit has finished processing a header, register it via AddProcessedHeaderRecordId so the platform can archive and finalize all processed headers correctly. Because multiple headers can be processed in the same run, every execution codeunit is responsible for adding its own processed header to the session list.
4. Example Custom Process Codeunits
Example 1: Import matched receipt lines after header creation
codeunit 5673320 "SIM_DI Purch.-Get Receipts"
{
Description = 'This codeunit is used as a matching import codeunit to receive purchase receipt lines and create purchase invoice lines based on the matched document lines.';
TableNo = "Purchase Header"; // <-- This is the created header after completing the processing
Access = Internal;
var
GlobalCodeunitSIMDIProcessSessionSI: Codeunit "SIM_DI Process Session SI";
GlobalCodeunitGetReceipts: Codeunit "Purch.-Get Receipt";
GlobalNoPurchaseReceiptLinesFoundLbl: Label 'There are no purchase receipt lines that can be processed. Please check the purchase receipt lines and check if they already have been invoiced.';
trigger OnRun()
var
TempLocalRecordSIMDIMatchedLineData: Record "SIM_DI Matched Line Data" temporary;
LocalRecordPurchRcptLine: Record "Purch. Rcpt. Line";
begin
GlobalCodeunitSIMDIProcessSessionSI.GetMatchedLineData(TempLocalRecordSIMDIMatchedLineData);
TempLocalRecordSIMDIMatchedLineData.SetRange("Execution Codeunit No.", Codeunit::"SIM_DI Purch.-Get Receipts");
// Filter the purchase receipt lines based on the incoming matched lines
if TempLocalRecordSIMDIMatchedLineData.FindSet() then
repeat
if LocalRecordPurchRcptLine.Get(TempLocalRecordSIMDIMatchedLineData."Matched Line RecordId") then
LocalRecordPurchRcptLine.Mark(true);
until TempLocalRecordSIMDIMatchedLineData.Next() = 0;
// Get the purchase receipt lines that have not been invoiced
LocalRecordPurchRcptLine.MarkedOnly(true);
LocalRecordPurchRcptLine.SetFilter("Qty. Rcd. Not Invoiced", '<>0');
// If no purchase receipt lines are found, throw an error
if LocalRecordPurchRcptLine.IsEmpty() then
Error(GlobalNoPurchaseReceiptLinesFoundLbl);
// Execute the process to get receipt lines
GlobalCodeunitGetReceipts.SetPurchHeader(Rec);
GlobalCodeunitGetReceipts.CreateInvLines(LocalRecordPurchRcptLine);
Commit();
end;
}
This example shows a typical matching-driven process: the session provides the list of “records to execute”, and your codeunit performs an import based on exactly those matched references.
Example 2: Update deviating fields when deviation is accepted
codeunit 5673350 "SIM_DI Update Deviating Fields"
{
Description = 'This codeunit can be used to update the linked document lines with the incoming values for the fields that have been deviating during the matching process and where deviation is accepted.';
Access = Public;
var
GlobalCodeunitSIMDIProcessSessionSI: Codeunit "SIM_DI Process Session SI";
GlobalCodeunitSIMCOREEvaluate: Codeunit "SIM_CORE Evaluate";
GlobalCodeunitSIMCOREJSON: Codeunit "SIM_CORE JSON";
trigger OnRun()
var
TempLocalRecordSIMDITempDocumentLine: Record "SIM_DI Temp. Document Line" temporary;
TempLocalRecordSIMDIMatchedLineData: Record "SIM_DI Matched Line Data" temporary;
LocalRecordSIMDIInboundDocument: Record "SIM_DI Inbound Document";
LocalCodeunitSIMDILogManagement: Codeunit "SIM_DI Log Management";
LocalRecordId: RecordId;
LocalRecordRef: RecordRef;
LocalFieldRef: FieldRef;
LocalRecordIdText: Text;
LocalDeviatingLinesJsonObject: JsonObject;
LocalLineMatchingValidationFieldsJsonObject: JsonObject;
LocalJsonToken: JsonToken;
LocalFieldNoInteger: Integer;
LocalFieldNoText: Text;
LocalCellIndexInteger: Integer;
LocalMatchingSetupNameText: Text;
LocalOldValueText: Text;
LocalNewValueText: Text;
LocalValueVariant: Variant;
LocalUpdatedBoolean: Boolean;
LocalDeviatingFieldUpdateLbl: Label 'The line %1 has been updated.\The field "%2" has been changed from value "%3" to value "%4".', Comment = '%1 = Line No., %2 = Field Name, %3 = Old Value, %4 = New Value';
LocalNoLinesUpdatedLbl: Label 'No Lines have been updated because no deviations were accepted or the incoming values are the same as the existing values.';
begin
/// Receive the data from the process session
LocalDeviatingLinesJsonObject := GlobalCodeunitSIMDIProcessSessionSI.GetDevitatingLinesJsonObject();
GlobalCodeunitSIMDIProcessSessionSI.GetDocumentLineVariable(TempLocalRecordSIMDITempDocumentLine);
GlobalCodeunitSIMDIProcessSessionSI.GetMatchedLineData(TempLocalRecordSIMDIMatchedLineData);
GlobalCodeunitSIMDIProcessSessionSI.GetInboundDocument(LocalRecordSIMDIInboundDocument);
// Iterate over the deviating lines
foreach LocalRecordIdText in LocalDeviatingLinesJsonObject.keys() do begin
if not Evaluate(LocalRecordId, LocalRecordIdText) then continue;
if not LocalRecordRef.Get(LocalRecordId) then continue;
// Get the current line record
LocalDeviatingLinesJsonObject.Get(LocalRecordIdText, LocalJsonToken);
// Get the matching setup name
LocalMatchingSetupNameText := GlobalCodeunitSIMCOREJSON.GetJsonPathValueText(LocalJsonToken.AsObject(), 'MatchingSetupName');
// Get the miss matched fields as a json object
if not LocalJsonToken.AsObject().Get('MissMatchedFields', LocalJsonToken) then continue;
LocalLineMatchingValidationFieldsJsonObject := LocalJsonToken.AsObject();
// Iterate over the miss matched fields and update the linked document line with the incoming value if deviation is accepted
foreach LocalFieldNoText in LocalLineMatchingValidationFieldsJsonObject.keys() do begin
if not Evaluate(LocalFieldNoInteger, LocalFieldNoText) then continue;
LocalLineMatchingValidationFieldsJsonObject.Get(LocalFieldNoText, LocalJsonToken);
if not GlobalCodeunitSIMCOREJSON.GetJsonPathValueBoolean(LocalJsonToken.AsObject(), 'AcceptDeviation') then
continue;
if not Evaluate(LocalCellIndexInteger, GlobalCodeunitSIMCOREJSON.GetJsonPathValueText(LocalJsonToken.AsObject(), 'CellIndex')) then
continue;
LocalFieldRef := LocalRecordRef.Field(LocalFieldNoInteger);
LocalNewValueText := GlobalCodeunitSIMCOREJSON.GetJsonPathValueText(LocalJsonToken.AsObject(), 'IncomingValue');
LocalOldValueText := LocalFieldRef.Value;
// Update the field with the incoming value
GlobalCodeunitSIMCOREEvaluate.EvaluateVariable(
LocalNewValueText,
Format(LocalFieldRef.Type),
LocalValueVariant,
false
);
LocalFieldRef.Validate(LocalValueVariant);
LocalRecordRef.Modify();
// Add log entry
LocalCodeunitSIMDILogManagement.AddInformationInTheLog(
StrSubstNo(LocalDeviatingFieldUpdateLbl, Format(LocalRecordIdText), LocalFieldRef.Caption, LocalOldValueText, LocalNewValueText),
'SIM_DI Update Deviating Fields',
LocalRecordSIMDIInboundDocument."Entry No."
);
TempLocalRecordSIMDIMatchedLineData.SetRange("Matched Line RecordId", LocalRecordId);
if TempLocalRecordSIMDIMatchedLineData.FindFirst() then
GlobalCodeunitSIMDIProcessSessionSI.AddProcessedHeaderRecordId(TempLocalRecordSIMDIMatchedLineData."Matched Header RecordId");
LocalUpdatedBoolean := true;
end;
end;
if not LocalUpdatedBoolean then
Error(LocalNoLinesUpdatedLbl);
Commit();
end;
}
This example illustrates “session-driven deviation handling”: the session supplies a structured deviation dataset; your code applies accepted deviations, records what changed, and reports the processed header back into the session.
5. Custom Lookup Validation Codeunits
Custom lookup codeunits validate template fields against master data. Configure via Use Lookup Validation = Custom Codeunit in template field settings.
Contract: TableNo = "SIM_DI Lookup Codeunit"
Input Fields:
- Parameter – Operation type: 'Lookup' (user clicks lookup) or 'Validate' (automatic validation)
- Input Value – Value to validate or search term
- Templ. Field RecordId / Temp. Document Line RecordId – Context references
Output Fields:
- Lookup Return Value – Validated value to return
- Lookup Validation Success – true if found, false if failed
- Lookup Error Message – Error text when validation fails
- Lookup Table No. – Master data table used (only requiered for errors)
Implementation Pattern
Your codeunit must:
- Handle both operation modes via
case Rec.Parameter - Retrieve context from session using
GetProcessTemplate()andGetDocumentLineVariable() - Implement validation logic: exact match first, then search filter fallback
- Set all output fields and call
Rec.Modify() - Synchronize related fields (e.g., update Description when No. is validated) using
ChangeAdditionalField()pattern - Return clear error messages when validation fails
Key Points:
- For 'Lookup' mode: Open
Page.RunModal()and return selected value - For 'Validate' mode: Find matching record, set return value and error message
- Always update
Lookup Validation Success,Lookup Return Value, andLookup Error Message - Use separate RecordRef for pre-validation to avoid mutating page source
- For type-dependent validation: retrieve Type from document line, map to table/fields dynamically
Complete Example
The example below demonstrates all patterns including type resolution, bidirectional field sync, two-phase validation, and error handling:
codeunit 5673301 "SIM_DI LookUp No."
{
TableNo = "SIM_DI Lookup Codeunit";
Description = 'This codeunit is used to lookup the number of the item, G/L Account, resource, fixed asset, and allocation account.';
Access = Public;
var
GlobalRecordSIMDIProcessTemplate: Record "SIM_DI Process Template";
GlobalCodeunitSIMDISession: Codeunit "SIM_DI Process Session SI";
GlobalTypeNotFilledErrLbl: Label 'To validate this field, the field "Type" must be filled in.';
trigger OnRun()
var
LocalRecordSIMDITemplField: Record "SIM_DI Templ. Field";
LocalRecordRef: RecordRef;
LocalSalesLineType: Enum "Sales Line Type";
LocalNoFieldNo: Integer;
LocalDescFieldNo: Integer;
begin
GlobalCodeunitSIMDISession.GetProcessTemplate(GlobalRecordSIMDIProcessTemplate);
if not LocalRecordSIMDITemplField.Get(Rec."Templ. Field RecordId") then exit;
// Resolve the sales line type from the document line
if not this.ResolveSalesLineType(Rec, LocalSalesLineType) then exit;
// Get the table and field configuration for the resolved type
if not this.GetTypeConfiguration(LocalSalesLineType, LocalRecordRef, LocalNoFieldNo, LocalDescFieldNo) then begin
Rec."Lookup Validation Success" := false;
Rec."Lookup Error Message" := GlobalTypeNotFilledErrLbl;
Rec.Modify();
exit;
end;
case Rec.Parameter of
'Lookup':
if this.CreateLookupPage(Rec, LocalRecordRef, LocalNoFieldNo, LocalDescFieldNo) then begin
Rec."Lookup Validation Success" := true;
Rec."Lookup Return Value" := CopyStr(Format(LocalRecordRef.Field(LocalNoFieldNo).Value), 1, MaxStrLen(Rec."Lookup Return Value"));
Rec.Modify();
this.ChangeAdditionalField(
GlobalRecordSIMDIProcessTemplate."Template Code",
Rec."Temp. Document Line RecordId",
Format(LocalRecordRef.Field(LocalDescFieldNo).Value)
);
end;
'Validate':
this.ValidateNo(Rec, LocalRecordRef, LocalNoFieldNo, LocalDescFieldNo);
end;
end;
/// <summary>
/// Resolves the sales line type from the document line context.
/// </summary>
/// <param name="Rec">The lookup codeunit record containing the context.</param>
/// <param name="ParamSalesLineType">The resolved sales line type.</param>
/// <returns>True if the type was resolved successfully, otherwise false.</returns>
local procedure ResolveSalesLineType(var Rec: Record "SIM_DI Lookup Codeunit"; var ParamSalesLineType: Enum "Sales Line Type"): Boolean
var
TempLocalRecordSIMDITempDocumentLine: Record "SIM_DI Temp. Document Line" temporary;
LocalRecordSalesLine: Record "Sales Line";
LocalTypeText: Text;
LocalTypeNotExistErrLbl: Label 'The type "%1" does not exist. Please use a valid type.', Comment = '%1 = Type Text';
LocalInvalidRecordIdErrLbl: Label 'Received an invalid record id to lookup.';
begin
GlobalCodeunitSIMDISession.GetDocumentLineVariable(TempLocalRecordSIMDITempDocumentLine);
if not TempLocalRecordSIMDITempDocumentLine.Get(Rec."Temp. Document Line RecordId") then begin
Rec."Lookup Validation Success" := false;
Rec."Lookup Error Message" := LocalInvalidRecordIdErrLbl;
Rec.Modify();
exit(false);
end;
LocalTypeText := TempLocalRecordSIMDITempDocumentLine.GetCellValueViaFieldNo(
GlobalRecordSIMDIProcessTemplate."Template Code", LocalRecordSalesLine.FieldNo(Type));
if not Evaluate(ParamSalesLineType, LocalTypeText) then begin
Rec."Lookup Validation Success" := false;
Rec."Lookup Error Message" := StrSubstNo(LocalTypeNotExistErrLbl, LocalTypeText);
Rec.Modify();
exit(false);
end;
exit(true);
end;
/// <summary>
/// Returns the table reference and field numbers for the given sales line type.
/// </summary>
/// <param name="ParamSalesLineType">The sales line type to configure.</param>
/// <param name="ParamRecordRef">The record reference to open for the corresponding table.</param>
/// <param name="ParamNoFieldNo">The field number of the No. field.</param>
/// <param name="ParamDescFieldNo">The field number of the Description/Name field.</param>
/// <returns>True if the type is supported, otherwise false.</returns>
local procedure GetTypeConfiguration(
ParamSalesLineType: Enum "Sales Line Type";
var ParamRecordRef: RecordRef;
var ParamNoFieldNo: Integer;
var ParamDescFieldNo: Integer): Boolean
var
LocalRecordItem: Record "Item";
LocalRecordGLAccount: Record "G/L Account";
LocalRecordResource: Record "Resource";
LocalRecordFixedAsset: Record "Fixed Asset";
LocalRecordAllocationAccount: Record "Allocation Account";
begin
case ParamSalesLineType of
ParamSalesLineType::"Charge (Item)",
ParamSalesLineType::Item:
begin
ParamRecordRef.Open(Database::Item);
ParamNoFieldNo := LocalRecordItem.FieldNo("No.");
ParamDescFieldNo := LocalRecordItem.FieldNo(Description);
end;
ParamSalesLineType::"G/L Account":
begin
ParamRecordRef.Open(Database::"G/L Account");
ParamNoFieldNo := LocalRecordGLAccount.FieldNo("No.");
ParamDescFieldNo := LocalRecordGLAccount.FieldNo(Name);
end;
ParamSalesLineType::Resource:
begin
ParamRecordRef.Open(Database::Resource);
ParamNoFieldNo := LocalRecordResource.FieldNo("No.");
ParamDescFieldNo := LocalRecordResource.FieldNo(Name);
end;
ParamSalesLineType::"Fixed Asset":
begin
ParamRecordRef.Open(Database::"Fixed Asset");
ParamNoFieldNo := LocalRecordFixedAsset.FieldNo("No.");
ParamDescFieldNo := LocalRecordFixedAsset.FieldNo(Description);
end;
ParamSalesLineType::"Allocation Account":
begin
ParamRecordRef.Open(Database::"Allocation Account");
ParamNoFieldNo := LocalRecordAllocationAccount.FieldNo("No.");
ParamDescFieldNo := LocalRecordAllocationAccount.FieldNo(Name);
end;
else
exit(false);
end;
exit(true);
end;
/// <summary>
/// Validates the input value by matching against the No. field.
/// </summary>
/// <param name="Rec">The lookup codeunit record to validate and update.</param>
/// <param name="ParamRecordRef">The record reference to search in.</param>
/// <param name="ParamNoFieldNo">The field number of the No. field.</param>
/// <param name="ParamDescFieldNo">The field number of the Description/Name field.</param>
/// <returns>The RecordId of the found record, or an empty RecordId if not found.</returns>
local procedure ValidateNo(
var Rec: Record "SIM_DI Lookup Codeunit";
var ParamRecordRef: RecordRef;
ParamNoFieldNo: Integer;
ParamDescFieldNo: Integer): RecordId
var
LocalRemarkMessageLbl: Label 'The %2 "%1" could not be located.', Comment = '%1 = Field Value, %2 = Table';
begin
// Try exact match on No. field
if StrLen(Rec."Input Value") <= ParamRecordRef.Field(ParamNoFieldNo).Length then begin
ParamRecordRef.Field(ParamNoFieldNo).SetRange(Rec."Input Value");
Rec."Lookup Validation Success" := ParamRecordRef.FindFirst();
end;
// Try search filter on No. field
if not Rec."Lookup Validation Success" then begin
ParamRecordRef.Reset();
ParamRecordRef.Field(ParamNoFieldNo).SetRange(Rec."Input Value");
Rec."Lookup Validation Success" := ParamRecordRef.FindFirst();
end;
if Rec."Lookup Validation Success" then begin
Rec."Lookup Return Value" := CopyStr(Format(ParamRecordRef.Field(ParamNoFieldNo).Value), 1, MaxStrLen(Rec."Lookup Return Value"));
this.ChangeAdditionalField(
GlobalRecordSIMDIProcessTemplate."Template Code",
Rec."Temp. Document Line RecordId",
Format(ParamRecordRef.Field(ParamDescFieldNo).Value)
);
end else
Rec."Lookup Error Message" := StrSubstNo(LocalRemarkMessageLbl, Rec."Input Value", ParamRecordRef.Caption());
Rec."Lookup Table No." := ParamRecordRef.Number;
Rec.Modify();
if Rec."Lookup Validation Success" then
exit(ParamRecordRef.RecordId);
end;
/// <summary>
/// Creates a lookup page to display records and allow the user to select one.
/// Pre-selects a record by validating the input value against the No. field.
/// </summary>
/// <param name="Rec">The lookup codeunit record.</param>
/// <param name="ParamRecordRef">The record reference for the lookup page.</param>
/// <param name="ParamNoFieldNo">The field number of the No. field.</param>
/// <param name="ParamDescFieldNo">The field number of the Description/Name field.</param>
/// <returns>True if the user selects a record, otherwise false.</returns>
local procedure CreateLookupPage(
var Rec: Record "SIM_DI Lookup Codeunit";
var ParamRecordRef: RecordRef;
ParamNoFieldNo: Integer;
ParamDescFieldNo: Integer): Boolean
var
LocalValidationRecordRef: RecordRef;
LocalRecordId: RecordId;
LocalVariant: Variant;
begin
// Use a separate RecordRef for validation to keep the original clean for the page
LocalValidationRecordRef.Open(ParamRecordRef.Number);
LocalRecordId := this.ValidateNo(Rec, LocalValidationRecordRef, ParamNoFieldNo, ParamDescFieldNo);
if ParamRecordRef.Get(LocalRecordId) then;
LocalVariant := ParamRecordRef;
exit(Page.RunModal(0, LocalVariant) = Action::LookupOK);
end;
/// <summary>
/// Changes the value of an additional field on the document line based on the lookup result.
/// This is used to set the description field after looking up the number, ensuring both fields are populated.
/// </summary>
/// <param name="ParamTemplateCode">The template code of the document.</param>
/// <param name="ParamTempDocumentLineRecordId">The record ID of the temp document line to update.</param>
/// <param name="ParamFieldValueText">The value to set in the additional field.</param>
local procedure ChangeAdditionalField(
ParamTemplateCode: Code[20];
ParamTempDocumentLineRecordId: RecordId;
ParamFieldValueText: Text)
var
TempLocalRecordSIMDITempDocumentLine: Record "SIM_DI Temp. Document Line" temporary;
LocalRecordSIMDITemplField: Record "SIM_DI Templ. Field";
begin
GlobalCodeunitSIMDISession.GetDocumentLineVariable(TempLocalRecordSIMDITempDocumentLine);
if not TempLocalRecordSIMDITempDocumentLine.Get(ParamTempDocumentLineRecordId) then exit;
if not LocalRecordSIMDITemplField.GetViaFieldName(ParamTemplateCode, Enum::"SIM_DI Document Type"::Line, 'Description') then exit;
if LocalRecordSIMDITemplField."Use Lookup Validation" = LocalRecordSIMDITemplField."Use Lookup Validation"::No then exit;
TempLocalRecordSIMDITempDocumentLine.SetCellValue(
TempLocalRecordSIMDITempDocumentLine.GetCellIndex(LocalRecordSIMDITemplField),
CopyStr(ParamFieldValueText, 1, 250)
);
GlobalCodeunitSIMDISession.SetDocumentLines(TempLocalRecordSIMDITempDocumentLine);
end;
}
Additional Customization Points
There are more places where custom codeunits can be integrated, but the guiding principle remains constant: use the session to retrieve the active processing context and to write back workflow-relevant results. This keeps your extensions consistent with the Smart Processing flow and prevents UI/process drift.