Ga naar inhoud

Hoe te Ontwikkelen in Slimme Verwerking (Ontwikkelaarsdocumentatie)

Status: 01.08.2026 • Leestijd: ~10 minuten

Slimme Verwerking biedt uitbreidingspunten waar je aangepaste logica kunt implementeren om je eigen processen te starten of de afhandeling aan te passen aan klantvereisten. Dit document legt het belangrijkste ontwikkelingsconcept uit: werk door de actieve sessie en trigger aangepaste codeunits in het bijbehorende proces met behulp van de sessiegegevens.

1. Kernconcept: Werk Door de Actieve Verwerkingssessie

Slimme Verwerking draait een “actieve conceptsessie” terwijl een document wordt verwerkt. Gedurende die tijd wordt de relevante status centraal onderhouden (documentcontext, sjablooncontext, gematchte regels, afwijkingen en tussenliggende gegevens die door de UI en verwerkingspipeline worden gebruikt).

Als ontwikkelaar is de aanbevolen aanpak:

  • Lees de huidige verwerkingscontext uit de sessie.
  • Voer je logica uit met behulp van de verstrekte context en referenties.
  • Schrijf resultaten/status terug in de sessie wanneer de workflow dit nodig heeft.
  • Vertrouw op het platform om de UI en verwerking in overeenstemming te houden via de sessiearchitectuur.

Dit maakt aanpassingen stabiel en voorkomt inconsistente status tussen wat gebruikers zien en wat je logica heeft veranderd.


2. Het Centrale Object: “SIM_DI Verwerkingssessie SI”

De codeunit “SIM_DI Verwerkingssessie SI” is het centrale toegangspunt voor het verkrijgen en manipuleren van de momenteel actieve Slimme Verwerkingssessie. Het fungeert als de hub om relevante runtime-gegevens te verkrijgen (inbound document, proces-sjabloon, regelbuffers, afwijkingsmetadata) en om resultaten terug te communiceren naar de pipeline (bijvoorbeeld, welke header is verwerkt).

Overzicht van openbare functies

De volgende tabel geeft alle openbare, niet-verouderde functies van "SIM_DI Process Session SI" weer. Als een functie in meerdere varianten (overloads) bestaat, zijn parameters die mogelijk niet in elke variant aanwezig zijn gemarkeerd als optioneel.

"SIM_DI Process Session SI" - Tabel van Openbare Functies

Functie Wat het doet Parameters (optioneel gemarkeerd) Retourneert
InitSession Initialiseert de sessie met een inbound document en lost het bijbehorende proces-sjabloon op. InboundDocument geen
InitSession Initialiseert de sessie met een inbound document en een expliciet verstrekt proces-sjabloon (en wist eerst de vorige sessiegegevens). InboundDocument, ProcessTemplate geen
GetInboundDocument Haalt het inbound document op dat in de huidige sessie is opgeslagen. var InboundDocument geen
GetProcessTemplate Haalt het proces-sjabloon op dat in de huidige sessie is opgeslagen. var ProcessTemplate geen
ClearProcessSession Wis de volledige sessiecontext, inclusief inbound document, proces-sjabloon, mapping-sleutels en procesgegevens. - geen
ClearProcessData Wis alleen verwerkingsgegevens (tijdelijke header/regels/opmerkingen, gemapte lookup-records, verwerkte header-id) en wist bijpassende gegevens. - geen
AddProcessedHeaderRecordId Voegt een verwerkte document-header record-id toe aan de sessielijst. Ondersteunt het matchen van meerdere documenten. Dubbele vermeldingen worden stilzwijgend genegeerd. RecordId geen
GetProcessedHeaderRecordIds Retourneert de lijst van alle verwerkte document-header record-ids die zijn verzameld tijdens de huidige matchingrun. - List of [RecordId]
SetDocumentHeaderVariableRef Slaat een kopie van de verstrekte tijdelijke headervariabele op in de sessie (gebruikt als een referentiestijl sessie headerstatus). var TempDocumentHeader (tijdelijk) geen
GetDocumentHeaderVariable Kopieert de huidige tijdelijke headergegevens van de sessie naar de verstrekte tijdelijke recordvariabele. var TempDocumentHeader (tijdelijk) geen
SetDocumentLineVariableRef Slaat een kopie van de verstrekte tijdelijke regelvariabele op in de sessie (gebruikt als een referentiestijl sessie regelstatus). var TempDocumentLine (tijdelijk) geen
GetDocumentLineVariable Kopieert de huidige tijdelijke regeldgegevens van de sessie naar de verstrekte tijdelijke recordvariabele. var TempDocumentLine (tijdelijk) geen
SetRemarkVariableRef Slaat een kopie van de verstrekte tijdelijke opmerkingvariabele op in de sessie (gebruikt als een referentiestijl sessie opmerkingstatus). var DocumentRemark (tijdelijk) geen
RemoveDocumentRemark Verwijdert een opmerking uit de sessie op basis van regelnummer, veldnummer en validatiegebied. LineNo, FieldNo, FieldValidationArea geen
GetRemarks Kopieert alle sessieopmerkingen naar de verstrekte tijdelijke opmerkingrecordvariabele. var DocumentRemark (tijdelijk) geen
RemoveMappedLookupRecord Verwijdert de gemapte lookup-recordreferentie voor een gegeven sjabloonveldreferentie. TemplateFieldRecordId geen
GetMappedLookupRecord Haalt de gemapte lookup-recordreferentie op voor een gegeven sjabloonveldreferentie (indien beschikbaar). TemplateFieldRecordId, var MappedRecordId Boolean
GetDevitatingLinesJsonObject Retourneert het JSON-object dat afwijkende regels en hun niet-overeenkomende velden beschrijft (inclusief naam van de matching setup en mismatchdetails). - JsonObject
GetMatchedLineData Kopieert alle gematchte regelgegevens naar de opgegeven tijdelijke record. Elke vermelding bevat: documentregel nr., naam van de matching setup, uitvoerings-codeunit nr. en de gematchte header/regel RecordIds. var TempMatchedLineData (tijdelijk) geen

3. Aanpassingen in het Matchingproces

Dit is alleen nodig als de codeunit wordt gebruikt als een aangepast proces (als de optie "Aanmaak overslaan bij match" in de matching setup actief is).

Het matchingproces in Smart Processing is opnieuw ontworpen om gelijktijdige uitvoering van meerdere matching setups te ondersteunen. Elke inkomende documentregel kan nu onafhankelijk worden gematcht aan een andere doeldocument-header en -regel (multi-header en multi-regel matching). Dit betekent dat regel 1 van een inkomend document kan worden toegewezen aan één inkooporder, terwijl regel 2 naar een geheel andere gaat.

Een veelvoorkomend uitbreidingsscenario is het triggeren van aangepaste codeunits tijdens deze matchingstap, vooral wanneer de gebruiker het concept heeft voltooid en een of meer doelheaders zijn aangemaakt of geïdentificeerd.

De aangepaste codeunit wordt nog steeds uitgevoerd met de headerrecord van het gematchte documenttype als Rec (bijvoorbeeld een inkoopheader). Deze record is je stabiele anker voor die specifieke header. Om te bepalen welke inkomende regels aan deze header zijn gematcht (en welke uitvoerings-codeunit de run heeft gestart), gebruik je GetMatchedLineData uit de sessie en filter je op "Execution Codeunit No.".

Zodra je codeunit klaar is met het verwerken van een header, registreer je deze via AddProcessedHeaderRecordId zodat het platform alle verwerkte headers correct kan archiveren en afsluiten. Omdat meerdere headers in dezelfde run kunnen worden verwerkt, is elke uitvoerings-codeunit verantwoordelijk voor het toevoegen van zijn eigen verwerkte header aan de sessielijst.


4. Voorbeeld van Aangepaste Procescodeunits

Voorbeeld 1: Importeer gematchte ontvangstregels na headercreatie

codeunit 5673320 "SIM_DI Aankoop.-Ontvangsten Krijgen"
{
    Description = 'Deze codeunit wordt gebruikt als een matching import codeunit om aankoopontvangstregels te ontvangen en aankoopfactuurregels te creëren op basis van de gematchte documentregels.';
    TableNo = "Aankoop Header"; // <-- Dit is de aangemaakte header na het voltooien van de verwerking
    Access = Internal;

    var
        GlobalCodeunitSIMDIProcessSessionSI: Codeunit "SIM_DI Verwerkingssessie SI";
        GlobalCodeunitGetReceipts: Codeunit "Aankoop.-Ontvangst Krijgen";
        GlobalNoPurchaseReceiptLinesFoundLbl: Label 'Er zijn geen aankoopontvangstregels die kunnen worden verwerkt. Controleer de aankoopontvangstregels en kijk of ze al zijn gefactureerd.';

    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 de aankoopontvangstregels op basis van de binnenkomende gematchte regels
        if TempLocalRecordSIMDIMatchedLineData.FindSet() then
            repeat
                if LocalRecordPurchRcptLine.Get(TempLocalRecordSIMDIMatchedLineData."Matched Line RecordId") then
                    LocalRecordPurchRcptLine.Mark(true);
            until TempLocalRecordSIMDIMatchedLineData.Next() = 0;

        // Verkrijg de aankoopontvangstregels die nog niet zijn gefactureerd
        LocalRecordPurchRcptLine.MarkedOnly(true);
        LocalRecordPurchRcptLine.SetFilter("Qty. Rcd. Not Invoiced", '<>0');

        // Als er geen aankoopontvangstregels worden gevonden, gooi een fout
        if LocalRecordPurchRcptLine.IsEmpty() then
            Error(GlobalNoPurchaseReceiptLinesFoundLbl);

        // Voer het proces uit om ontvangstregels te krijgen
        GlobalCodeunitGetReceipts.SetPurchHeader(Rec);
        GlobalCodeunitGetReceipts.CreateInvLines(LocalRecordPurchRcptLine);
        Commit();
    end;
}

Dit voorbeeld toont een typisch matching-gedreven proces: de sessie biedt de lijst van “records om uit te voeren”, en je codeunit voert een import uit op basis van precies die gematchte referenties.


Voorbeeld 2: Update afwijkende velden wanneer afwijking is geaccepteerd

codeunit 5673350 "SIM_DI Update Afwijkende Velden"
{
    Description = 'Deze codeunit kan worden gebruikt om de gekoppelde documentregels bij te werken met de binnenkomende waarden voor de velden die tijdens het matchingproces afwijkend zijn geweest en waar afwijking is geaccepteerd.';
    Access = Public;

    var
        GlobalCodeunitSIMDIProcessSessionSI: Codeunit "SIM_DI Verwerkingssessie SI";
        GlobalCodeunitSIMCOREEvaluate: Codeunit "SIM_CORE Evalueren";
        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;
        LocalDevitatingLinesJsonObject: JsonObject;
        LocalLineMatchingValidationFieldsJsonObject: JsonObject;
        LocalJsonToken: JsonToken;
        LocalFieldNoInteger: Integer;
        LocalFieldNoText: Text;
        LocalCellIndexInteger: Integer;
        LocalMatchingSetupNameText: Text;
        LocalOldValueText: Text;
        LocalNewValueText: Text;
        LocalValueVariant: Variant;
        LocalUpdatedBoolean: Boolean;
        LocalDevitatingFieldUpdateLbl: Label 'De regel %1 is bijgewerkt.\Het veld "%2" is veranderd van waarde "%3" naar waarde "%4".', Comment = '%1 = Regel Nr., %2 = Veldnaam, %3 = Oude Waarde, %4 = Nieuwe Waarde';
        LocalNoLinesUpdatedLbl: Label 'Er zijn geen regels bijgewerkt omdat er geen afwijkingen zijn geaccepteerd of de inkomende waarden gelijk zijn aan de bestaande waarden.';
    begin
        /// Ontvang de gegevens van de proces sessie
        LocalDevitatingLinesJsonObject := GlobalCodeunitSIMDIProcessSessionSI.GetDevitatingLinesJsonObject();
        GlobalCodeunitSIMDIProcessSessionSI.GetDocumentLineVariable(TempLocalRecordSIMDITempDocumentLine);
        GlobalCodeunitSIMDIProcessSessionSI.GetMatchedLineData(TempLocalRecordSIMDIMatchedLineData);
        GlobalCodeunitSIMDIProcessSessionSI.GetInboundDocument(LocalRecordSIMDIInboundDocument);

        // Itereer over de afwijkende regels
        foreach LocalRecordIdText in LocalDevitatingLinesJsonObject.keys() do begin
            if not Evaluate(LocalRecordId, LocalRecordIdText) then continue;
            if not LocalRecordRef.Get(LocalRecordId) then continue;

            // Verkrijg de huidige regelrecord
            LocalDevitatingLinesJsonObject.Get(LocalRecordIdText, LocalJsonToken);
            // Verkrijg de naam van de matching setup
            LocalMatchingSetupNameText := GlobalCodeunitSIMCOREJSON.GetJsonPathValueText(LocalJsonToken.AsObject(), 'MatchingSetupName');

            // Verkrijg de niet-overeenkomende velden als een json-object
            if not LocalJsonToken.AsObject().Get('MissMatchedFields', LocalJsonToken) then continue;
            LocalLineMatchingValidationFieldsJsonObject := LocalJsonToken.AsObject();
            // Itereer over de niet-overeenkomende velden en werk de gekoppelde documentregel bij met de binnenkomende waarde als afwijking is geaccepteerd
            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;

                // Werk het veld bij met de binnenkomende waarde
                GlobalCodeunitSIMCOREEvaluate.EvaluateVariable(
                   LocalNewValueText,
                   Format(LocalFieldRef.Type),
                   LocalValueVariant,
                   false
               );
                LocalFieldRef.Validate(LocalValueVariant);
                LocalRecordRef.Modify();

                // Voeg logboekvermelding toe
                LocalCodeunitSIMDILogManagement.AddInformationInTheLog(
                    StrSubstNo(LocalDevitatingFieldUpdateLbl, Format(LocalRecordIdText), LocalFieldRef.Caption, LocalOldValueText, LocalNewValueText),
                    'SIM_DI Update Afwijkende Velden',
                    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;
}

Dit voorbeeld illustreert “sessie-gedreven afwijkingsafhandeling”: de sessie levert een gestructureerde afwijkingsdataset; je code past geaccepteerde afwijkingen toe, registreert wat is veranderd en rapporteert de verwerkte header terug in de sessie.


5. Aangepaste Opzoekvalidatie-Codeunits

Aangepaste opzoek-codeunits valideren sjabloonvelden tegen stamgegevens. Configureer via Opzoekvalidatie gebruiken = Aangepaste Codeunit in de sjabloonveldinstellingen.

Contract: TableNo = "SIM_DI Lookup Codeunit"

Invoervelden: - Parameter – Bewerkingstype: 'Lookup' (gebruiker klikt op opzoeken) of 'Validate' (automatische validatie) - Input Value – Te valideren waarde of zoekterm - Templ. Field RecordId / Temp. Document Line RecordId – Contextreferenties

Uitvoervelden: - Lookup Return Value – Gevalideerde retourwaarde - Lookup Validation Successtrue indien gevonden, false indien mislukt - Lookup Error Message – Fouttekst wanneer validatie mislukt - Lookup Table No. – Gebruikte stamgegevenstabel (alleen vereist bij fouten)

Implementatiepatroon

Je codeunit moet:

  1. Beide bewerkingsmodi verwerken via case Rec.Parameter
  2. Context ophalen uit de sessie met GetProcessTemplate() en GetDocumentLineVariable()
  3. Validatielogica implementeren: exacte match eerst, daarna zoekfilter als terugval
  4. Alle uitvoervelden instellen en Rec.Modify() aanroepen
  5. Gerelateerde velden synchroniseren (bijv. Omschrijving bijwerken wanneer Nr. wordt gevalideerd) met het ChangeAdditionalField()-patroon
  6. Duidelijke foutmeldingen retourneren wanneer validatie mislukt

Belangrijke punten:

  • Voor de modus 'Lookup': Page.RunModal() openen en geselecteerde waarde retourneren
  • Voor de modus 'Validate': Overeenkomend record zoeken, retourwaarde en foutmelding instellen
  • Altijd Lookup Validation Success, Lookup Return Value en Lookup Error Message bijwerken
  • Aparte RecordRef gebruiken voor pre-validatie om muteren van de paginabron te vermijden
  • Voor typeafhankelijke validatie: Type ophalen uit documentregel, tabel/velden dynamisch toewijzen

Volledig Voorbeeld

Het onderstaande voorbeeld demonstreert alle patronen inclusief typeoplossing, bidirectionele veldsynchronisatie, tweefasige validatie en foutafhandeling:

codeunit 5673301 "SIM_DI LookUp No."
{
    TableNo = "SIM_DI Lookup Codeunit";
    Description = 'Deze codeunit wordt gebruikt om het nummer van het artikel, grootboekrekening, resource, vast actief en toewijzingsrekening op te zoeken.';
    Access = Public;

    var
        GlobalRecordSIMDIProcessTemplate: Record "SIM_DI Process Template";
        GlobalCodeunitSIMDISession: Codeunit "SIM_DI Process Session SI";
        GlobalTypeNotFilledErrLbl: Label 'Om dit veld te valideren, moet het veld "Type" zijn ingevuld.';

    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;

        // Verkoopregelstype oplossen uit de documentregel
        if not this.ResolveSalesLineType(Rec, LocalSalesLineType) then exit;

        // Tabel- en veldconfiguratie ophalen voor het opgeloste 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>
    /// Lost het verkoopregelstype op uit de documentregelcontext.
    /// </summary>
    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 'Het type "%1" bestaat niet. Gebruik een geldig type.', Comment = '%1 = Type Text';
        LocalInvalidRecordIdErrLbl: Label 'Een ongeldige record-id ontvangen voor opzoeken.';
    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>
    /// Retourneert de tabelreferentie en veldnummers voor het opgegeven verkoopregelstype.
    /// </summary>
    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>
    /// Valideert de invoerwaarde door te matchen tegen het veld Nr.
    /// </summary>
    local procedure ValidateNo(
        var Rec: Record "SIM_DI Lookup Codeunit";
        var ParamRecordRef: RecordRef;
        ParamNoFieldNo: Integer;
        ParamDescFieldNo: Integer): RecordId
    var
        LocalRemarkMessageLbl: Label 'De %2 "%1" kon niet worden gevonden.', Comment = '%1 = Field Value, %2 = Table';
    begin
        // Exacte match op veld Nr. proberen
        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;

        // Zoekfilter op veld Nr. proberen
        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>
    /// Maakt een opzoekpagina om records weer te geven en de gebruiker een selectie te laten maken.
    /// Selecteert vooraf een record door de invoerwaarde te valideren tegen het veld Nr.
    /// </summary>
    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
        // Aparte RecordRef gebruiken voor validatie om het origineel schoon te houden voor de pagina
        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>
    /// Wijzigt de waarde van een aanvullend veld op de documentregel op basis van het opzoekresultaat.
    /// Wordt gebruikt om het omschrijvingsveld in te stellen na het opzoeken van het nummer.
    /// </summary>
    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;
}

Aanvullende Aanpassingspunten

Er zijn meer plaatsen waar aangepaste codeunits kunnen worden geïntegreerd, maar het leidende principe blijft constant: gebruik de sessie om de actieve verwerkingscontext op te halen en om workflow-relevante resultaten terug te schrijven. Dit houdt je extensies consistent met de Slimme Verwerkingsflow en voorkomt UI/procesafwijkingen.