Zum Inhalt

So entwickeln Sie in Smart Processing (Entwicklerdokumentation)

Status: 01.08.2026 • Lesezeit: ~8 Minuten

Smart Processing bietet Erweiterungspunkte, an denen Sie benutzerdefinierte Logik implementieren können, um Ihre eigenen Prozesse zu starten oder die Handhabung an Kundenanforderungen anzupassen. Dieses Dokument erklärt das zentrale Entwicklungskonzept: Arbeiten Sie durch die aktive Sitzung und lösen Sie benutzerdefinierte Codeunits im entsprechenden Prozess unter Verwendung der Sitzungsdaten aus.

1. Kernkonzept: Arbeiten Sie durch die aktive Verarbeitungssitzung

Smart Processing führt eine „aktive Entwurfssitzung“ aus, während ein Dokument verarbeitet wird. Während dieser Zeit wird der relevante Zustand zentral verwaltet (Dokumentkontext, Vorlagenkontext, übereinstimmende Zeilen, Abweichungen und Zwischendaten, die von der UI und der Verarbeitungspipeline verwendet werden).

Als Entwickler wird der empfohlene Ansatz sein:

  • Lesen Sie den aktuellen Verarbeitungszustand aus der Sitzung.
  • Führen Sie Ihre Logik unter Verwendung des bereitgestellten Kontexts und der Referenzen aus.
  • Schreiben Sie Ergebnisse/Zustände zurück in die Sitzung, wenn der Workflow dies benötigt.
  • Verlassen Sie sich auf die Plattform, um die UI und die Verarbeitung durch die Sitzungsarchitektur synchron zu halten.

Dies macht Anpassungen stabil und vermeidet inkonsistente Zustände zwischen dem, was die Benutzer sehen, und dem, was Ihre Logik geändert hat.


2. Das zentrale Objekt: „SIM_DI Process Session SI“

Die Codeunit „SIM_DI Process Session SI“ ist der zentrale Einstiegspunkt für den Zugriff auf und die Manipulation der derzeit aktiven Smart Processing-Sitzung. Sie fungiert als Hub, um relevante Laufzeitdaten (eingehendes Dokument, Prozessvorlage, Zeilenpuffer, Abweichungsmetadaten) zu erhalten und Ergebnisse zurück an die Pipeline zu kommunizieren (zum Beispiel, welcher Header verarbeitet wurde).

Übersicht über öffentliche Funktionen (aus den bereitgestellten Beispielen)

Die folgende Tabelle listet die öffentlichen Funktionen auf, die tatsächlich in Ihrem Beispielcode verwendet werden. Wenn eine Funktion in mehreren Varianten (Überladungen) existiert, sind Parameter, die möglicherweise nicht in jeder Variante vorhanden sind, als optional gekennzeichnet.

Funktion Was sie tut Parameter (optional gekennzeichnet) Gibt zurück
InitSession Initialisiert die Sitzung mit einem eingehenden Dokument und löst die zugehörige Prozessvorlage auf. InboundDocument keine
InitSession Initialisiert die Sitzung mit einem eingehenden Dokument und einer ausdrücklich bereitgestellten Prozessvorlage (und löscht zuerst die vorherigen Sitzungsdaten). InboundDocument, ProcessTemplate keine
GetInboundDocument Ruft das eingehende Dokument ab, das in der aktuellen Sitzung gespeichert ist. var InboundDocument keine
GetProcessTemplate Ruft die Prozessvorlage ab, die in der aktuellen Sitzung gespeichert ist. var ProcessTemplate keine
ClearProcessSession Löscht den gesamten Sitzungszustand einschließlich eingehendem Dokument, Prozessvorlage, Zuordnungsschlüsseln und Prozessdaten. - keine
ClearProcessData Löscht nur Verarbeitungsdaten (temporäre Header/Zeilen/Anmerkungen, zugeordnete Nachschlageeinträge, verarbeitete Header-ID) und löscht übereinstimmende Daten. - keine
SetProcessedHeaderRecordId Speichert die Datensatzreferenz des erstellten/übereinstimmenden Ziel-Dokument-Headers in der Sitzung für die nachgelagerte Verarbeitung (z. B. Abschluss/Archivierung). RecordId keine
GetProcessedHeaderRecordId Gibt die gespeicherte Referenz des verarbeiteten Header-Datensatzes zurück und löscht sie anschließend aus der Sitzung. - RecordId
SetDocumentHeaderVariableRef Speichert eine Kopie der bereitgestellten temporären Header-Variable in der Sitzung (wird als Referenzstil-Sitzungsheaderzustand verwendet). var TempDocumentHeader (temporär) keine
GetDocumentHeaderVariable Kopiert die aktuellen temporären Headerdaten der Sitzung in die bereitgestellte temporäre Datensatzvariable. var TempDocumentHeader (temporär) keine
SetDocumentLineVariableRef Speichert eine Kopie der bereitgestellten temporären Zeilenvariable in der Sitzung (wird als Referenzstil-Sitzungszeilenzustand verwendet). var TempDocumentLine (temporär) keine
GetDocumentLineVariable Kopiert die aktuellen temporären Zeilendaten der Sitzung in die bereitgestellte temporäre Datensatzvariable. var TempDocumentLine (temporär) keine
SetRemarkVariableRef Speichert eine Kopie der bereitgestellten temporären Anmerkungsvariable in der Sitzung (wird als Referenzstil-Sitzungsanmerkungszustand verwendet). var DocumentRemark (temporär) keine
RemoveDocumentRemark Entfernt eine Anmerkung aus der Sitzung basierend auf Zeilennummer, Feldnummer und Validierungsbereich. LineNo, FieldNo, FieldValidationArea keine
GetRemarks Kopiert alle Sitzungsanmerkungen in die bereitgestellte temporäre Anmerkungsdatensatzvariable. var DocumentRemark (temporär) keine
RemoveMappedLookupRecord Entfernt die zugeordnete Nachschlageeintragsreferenz für ein gegebenes Vorlagenfeldreferenz. TemplateFieldRecordId keine
GetMappedLookupRecord Ruft die zugeordnete Nachschlageeintragsreferenz für ein gegebenes Vorlagenfeldreferenz ab (sofern verfügbar). TemplateFieldRecordId, var MappedRecordId Boolean
GetDevitatingLinesJsonObject Gibt das JSON-Objekt zurück, das abweichende Zeilen und deren nicht übereinstimmende Felder beschreibt (einschließlich des Namens der Übereinstimmung und der Abweichungsdetails). - JsonObject
GetExecuteCodeunitRefRecordIds Ruft Datensatzreferenzen ab, die für die Ausführung durch eine bestimmte Codeunit zugewiesen sind (typischerweise übereinstimmende Zeilen zur Verarbeitung). ExecuteCodeunitNo, var RecordIds Boolean
GetMatchedDocumentHeader Ruft die Referenz des übereinstimmenden Dokumentheaderdatensatzes für einen gegebenen Namen der Übereinstimmung ab (sofern verfügbar). MatchingSetupName, var MatchedDocumentHeaderRecordId Boolean

Hinweis: Dies sind alle öffentlichen Funktionen, die aus Ihren Beispielen bestätigt werden können. Wenn Sie den vollständigen Codeunit-Quellcode bereitstellen, werde ich diese Tabelle erweitern, sodass sie jede öffentliche Funktion enthält.


3. Anpassungen im Übereinstimmungsprozess

Ein häufiges Erweiterungsszenario besteht darin, benutzerdefinierte Codeunits während der Übereinstimmung auszulösen – insbesondere wenn der Benutzer den Entwurf abschließt und der Ziel-Headerdatensatz erstellt wurde.

Ein wichtiges Detail dieses Mechanismus ist, dass die benutzerdefinierte Codeunit mit dem Headerdatensatz des übereinstimmenden Dokumenttyps (zum Beispiel einem Einkaufsheader) ausgeführt wird. Dieser Datensatz wird zu Ihrem stabilen Anker: Sie können Kontext daraus ableiten, Zeilen erstellen oder importieren, verwandte Datensätze aktualisieren und dann das Ergebnis zurück an die Sitzung übergeben, damit die Plattform ihren Workflow fortsetzen kann.


4. Beispiel für benutzerdefinierte Prozess-Codeunits (Code unverändert)

Beispiel 1: Importieren von übereinstimmenden Empfangszeilen nach der Headererstellung

codeunit 5673320 "SIM_DI Purch.-Get Receipts"
{
    Description = 'Diese Codeunit wird als Matching-Import-Codeunit verwendet, um Einkaufsreceiptszeilen zu empfangen und Einkaufsrechnungszeilen basierend auf den übereinstimmenden Dokumentzeilen zu erstellen.';
    TableNo = "Purchase Header"; // <-- Dies ist der erstellte Header nach Abschluss der Verarbeitung
    Access = Internal;

    var
        GlobalCodeunitSIMDIProcessSessionSI: Codeunit "SIM_DI Process Session SI";
        GlobalCodeunitGetReceipts: Codeunit "Purch.-Get Receipt";
        GlobalNoPurchaseReceiptLinesFoundLbl: Label 'Es gibt keine Einkaufsreceiptszeilen, die verarbeitet werden können. Bitte überprüfen Sie die Einkaufsreceiptszeilen und prüfen Sie, ob sie bereits in Rechnung gestellt wurden.';

    trigger OnRun()
    var
        LocalRecordPurchRcptLine: Record "Purch. Rcpt. Line";
        LocalMatchedLineRecordId: RecordId;
        LocalToExecuteRecordIds: List of [RecordId];
    begin
        // Empfangen Sie die übereinstimmenden Zeilen, die ausgeführt werden müssen
        GlobalCodeunitSIMDIProcessSessionSI.GetExecuteCodeunitRefRecordIds(Codeunit::"SIM_DI Purch.-Get Receipts", LocalToExecuteRecordIds);

        // Filtern Sie die Einkaufsreceiptszeilen basierend auf den eingehenden übereinstimmenden Zeilen
        foreach LocalMatchedLineRecordId in LocalToExecuteRecordIds do
            if LocalRecordPurchRcptLine.Get(LocalMatchedLineRecordId) then
                LocalRecordPurchRcptLine.Mark(true);

        // Holen Sie sich die Einkaufsreceiptszeilen, die nicht in Rechnung gestellt wurden
        LocalRecordPurchRcptLine.MarkedOnly(true);
        LocalRecordPurchRcptLine.SetFilter("Qty. Rcd. Not Invoiced", '<>0');

        // Wenn keine Einkaufsreceiptszeilen gefunden werden, werfen Sie einen Fehler
        if LocalRecordPurchRcptLine.IsEmpty() then
            Error(GlobalNoPurchaseReceiptLinesFoundLbl);

        // Führen Sie den Prozess aus, um Empfangszeilen zu erhalten
        GlobalCodeunitGetReceipts.SetPurchHeader(Rec);
        GlobalCodeunitGetReceipts.CreateInvLines(LocalRecordPurchRcptLine);
        Commit();
    end;
}

Dieses Beispiel zeigt einen typischen, übereinstimmungsgetriebenen Prozess: Die Sitzung stellt die Liste der „auszuführenden Datensätze“ bereit, und Ihre Codeunit führt einen Import basierend auf genau diesen übereinstimmenden Referenzen durch.


Beispiel 2: Aktualisieren von abweichenden Feldern, wenn die Abweichung akzeptiert wird

codeunit 5673350 "SIM_DI Update Deviating Fields"
{
    Description = 'Diese Codeunit kann verwendet werden, um die verknüpften Dokumentzeilen mit den eingehenden Werten für die Felder zu aktualisieren, die während des Übereinstimmungsprozesses abweichend waren und bei denen die Abweichung akzeptiert wurde.';
    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;
        LocalRecordSIMDIProcessTemplate: Record "SIM_DI Process Template";
        LocalRecordSIMDIInboundDocument: Record "SIM_DI Inbound Document";
        LocalCodeunitSIMDILogManagement: Codeunit "SIM_DI Log Management";
        LocalDocumentHeaderRecordId: RecordId;
        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;
        LocalDevitatingFieldUpdateLbl: Label 'Die Zeile %1 wurde aktualisiert.\Das Feld "%2" wurde von Wert "%3" auf Wert "%4" geändert.', Comment = '%1 = Zeilennr., %2 = Feldname, %3 = Alter Wert, %4 = Neuer Wert';
    begin
        /// Empfangen Sie die Daten aus der Verarbeitungssitzung
        LocalDevitatingLinesJsonObject := GlobalCodeunitSIMDIProcessSessionSI.GetDevitatingLinesJsonObject();
        GlobalCodeunitSIMDIProcessSessionSI.GetDocumentLineVariable(TempLocalRecordSIMDITempDocumentLine);
        GlobalCodeunitSIMDIProcessSessionSI.GetInboundDocument(LocalRecordSIMDIInboundDocument);
        GlobalCodeunitSIMDIProcessSessionSI.GetProcessTemplate(LocalRecordSIMDIProcessTemplate);

        // Iterieren Sie über die 
        foreach LocalRecordIdText in LocalDevitatingLinesJsonObject.keys() do begin
            if not Evaluate(LocalRecordId, LocalRecordIdText) then continue;
            if not LocalRecordRef.Get(LocalRecordId) then continue;

            // Holen Sie sich den aktuellen Zeilendatensatz
            LocalDevitatingLinesJsonObject.Get(LocalRecordIdText, LocalJsonToken);
            // Holen Sie sich den Namen der Übereinstimmung
            LocalMatchingSetupNameText := GlobalCodeunitSIMCOREJSON.GetJsonPathValueText(LocalJsonToken.AsObject(), 'MatchingSetupName');

            // Holen Sie sich die nicht übereinstimmenden Felder als JSON-Objekt
            if not LocalJsonToken.AsObject().Get('MissMatchedFields', LocalJsonToken) then continue;
            LocalLineMatchingValidationFieldsJsonObject := LocalJsonToken.AsObject();
            // Iterieren Sie über die nicht übereinstimmenden Felder und aktualisieren Sie die verknüpfte Dokumentzeile mit dem eingehenden Wert, wenn die Abweichung akzeptiert wird
            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;

                // Aktualisieren Sie das Feld mit dem eingehenden Wert
                GlobalCodeunitSIMCOREEvaluate.EvaluateVariable(
                   LocalNewValueText,
                   Format(LocalFieldRef.Type),
                   LocalValueVariant,
                   false
               );
                LocalFieldRef.Validate(LocalValueVariant);
                LocalRecordRef.Modify();

                // Fügen Sie einen Logeintrag hinzu
                LocalCodeunitSIMDILogManagement.AddInformationInTheLog(
                    StrSubstNo(LocalDevitatingFieldUpdateLbl, Format(LocalRecordIdText), LocalFieldRef.Caption, LocalOldValueText, LocalNewValueText),
                    'SIM_DI Update Deviating Fields',
                    LocalRecordSIMDIInboundDocument."Entry No."
                );
            end;

            // Merken Sie sich die Dokumentheaderdatensatz-ID, um das Dokument später zu archivieren
            GlobalCodeunitSIMDIProcessSessionSI.GetMatchedDocumentHeader(LocalMatchingSetupNameText, LocalDocumentHeaderRecordId);
        end;

        // Setzen Sie die übereinstimmende Dokumentheaderdatensatz-ID in die Sitzung
        GlobalCodeunitSIMDIProcessSessionSI.SetProcessedHeaderRecordId(LocalDocumentHeaderRecordId);
        Commit();
    end;
}

Dieses Beispiel veranschaulicht die „sitzungsgetriebene Abweichungsbehandlung“: Die Sitzung liefert einen strukturierten Abweichungsdatensatz; Ihr Code wendet akzeptierte Abweichungen an, dokumentiert, was sich geändert hat, und meldet den verarbeiteten Header zurück in die Sitzung.


5. Zusätzliche Anpassungspunkte

Es gibt weitere Stellen, an denen benutzerdefinierte Codeunits integriert werden können, aber das leitende Prinzip bleibt konstant: Verwenden Sie die Sitzung, um den aktiven Verarbeitungszustand abzurufen und workflow-relevante Ergebnisse zurückzuschreiben. Dies hält Ihre Erweiterungen konsistent mit dem Smart Processing-Fluss und verhindert UI-/Prozessdrift.