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.