So entwickeln Sie in Smart Processing (Entwicklerdokumentation)
Status: 01.08.2026 • Lesezeit: ~10 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 der öffentlichen Funktionen
Die folgende Tabelle listet alle öffentlichen, nicht veralteten Funktionen von "SIM_DI Process Session SI" auf. 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 |
AddProcessedHeaderRecordId |
Fügt eine verarbeitete Dokument-Header-Datensatz-ID zur Sitzungsliste hinzu. Unterstützt Mehrfachdokument-Übereinstimmung. Doppelte Einträge werden stillschweigend ignoriert. | RecordId |
keine |
GetProcessedHeaderRecordIds |
Gibt die Liste aller verarbeiteten Dokument-Header-Datensatz-IDs zurück, die während des aktuellen Übereinstimmungslaufs gesammelt wurden. | - | List of [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 |
GetMatchedLineData |
Kopiert alle übereinstimmenden Zeilendateneinträge in den bereitgestellten temporären Datensatz. Jeder Eintrag enthält: Dokumentzeilen-Nr., Name der Übereinstimmung, Ausführungs-Codeunit-Nr. und die übereinstimmenden Header/Zeilen-RecordIds. | var TempMatchedLineData (temporär) |
keine |
3. Anpassungen im Übereinstimmungsprozess
Dies ist nur erforderlich, wenn die Codeunit als benutzerdefinierter Prozess verwendet wird (wenn die Option „Erstellung bei Übereinstimmung überspringen" in der Übereinstimmungseinrichtung aktiv ist).
Der Übereinstimmungsprozess in Smart Processing wurde neu gestaltet, um gleichzeitige Ausführung mehrerer Übereinstimmungseinrichtungen zu unterstützen. Jede eingehende Dokumentzeile kann nun unabhängig einem anderen Zieldokument-Header und einer anderen Zeile zugeordnet werden (Mehrfach-Header- und Mehrfach-Zeilen-Übereinstimmung). Das bedeutet, dass Zeile 1 eines eingehenden Dokuments einer Bestellung zugewiesen werden kann, während Zeile 2 zu einer völlig anderen geht.
Ein häufiges Erweiterungsszenario besteht darin, benutzerdefinierte Codeunits während dieses Übereinstimmungsschritts auszulösen – insbesondere wenn der Benutzer den Entwurf abschließt und ein oder mehrere Ziel-Header erstellt oder identifiziert wurden.
Die benutzerdefinierte Codeunit wird weiterhin mit dem Header-Datensatz des übereinstimmenden Dokumenttyps als Rec ausgeführt (zum Beispiel ein Einkaufs-Header). Dieser Datensatz ist Ihr stabiler Anker für diesen bestimmten Header. Um zu bestimmen, welche eingehenden Zeilen diesem Header zugeordnet wurden (und welche Ausführungs-Codeunit den Lauf ausgelöst hat), verwenden Sie GetMatchedLineData aus der Sitzung und filtern nach "Execution Codeunit No.".
Sobald Ihre Codeunit die Verarbeitung eines Headers abgeschlossen hat, registrieren Sie ihn über AddProcessedHeaderRecordId, damit die Plattform alle verarbeiteten Header korrekt archivieren und abschließen kann. Da mehrere Header im gleichen Lauf verarbeitet werden können, ist jede Ausführungs-Codeunit dafür verantwortlich, ihren eigenen verarbeiteten Header zur Sitzungsliste hinzuzufügen.
4. Beispiel für benutzerdefinierte Prozess-Codeunits
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
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");
// Filtern Sie die Einkaufsreceiptszeilen basierend auf den eingehenden übereinstimmenden Zeilen
if TempLocalRecordSIMDIMatchedLineData.FindSet() then
repeat
if LocalRecordPurchRcptLine.Get(TempLocalRecordSIMDIMatchedLineData."Matched Line RecordId") then
LocalRecordPurchRcptLine.Mark(true);
until TempLocalRecordSIMDIMatchedLineData.Next() = 0;
// 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;
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 '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';
LocalNoLinesUpdatedLbl: Label 'Es wurden keine Zeilen aktualisiert, da keine Abweichungen akzeptiert wurden oder die eingehenden Werte mit den vorhandenen Werten identisch sind.';
begin
/// Empfangen Sie die Daten aus der Verarbeitungssitzung
LocalDevitatingLinesJsonObject := GlobalCodeunitSIMDIProcessSessionSI.GetDevitatingLinesJsonObject();
GlobalCodeunitSIMDIProcessSessionSI.GetDocumentLineVariable(TempLocalRecordSIMDITempDocumentLine);
GlobalCodeunitSIMDIProcessSessionSI.GetMatchedLineData(TempLocalRecordSIMDIMatchedLineData);
GlobalCodeunitSIMDIProcessSessionSI.GetInboundDocument(LocalRecordSIMDIInboundDocument);
// Iterieren Sie über die abweichenden Zeilen
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."
);
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;
}
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. Benutzerdefinierte Nachschlagevalidierungs-Codeunits
Benutzerdefinierte Nachschlage-Codeunits validieren Vorlagenfelder gegen Stammdaten. Konfigurieren Sie dies über Nachschlagevalidierung verwenden = Benutzerdefinierte Codeunit in den Vorlagenfeld-Einstellungen.
Vertrag: TableNo = "SIM_DI Lookup Codeunit"
Eingabefelder:
- Parameter – Operationstyp: 'Lookup' (Benutzer klickt auf Nachschlagen) oder 'Validate' (automatische Validierung)
- Input Value – Zu validierender Wert oder Suchbegriff
- Templ. Field RecordId / Temp. Document Line RecordId – Kontextreferenzen
Ausgabefelder:
- Lookup Return Value – Validierter Rückgabewert
- Lookup Validation Success – true wenn gefunden, false wenn fehlgeschlagen
- Lookup Error Message – Fehlertext bei fehlgeschlagener Validierung
- Lookup Table No. – Verwendete Stammdatentabelle (nur bei Fehlern erforderlich)
Implementierungsmuster
Ihre Codeunit muss:
- Beide Betriebsmodi behandeln über
case Rec.Parameter - Kontext aus der Sitzung abrufen mit
GetProcessTemplate()undGetDocumentLineVariable() - Validierungslogik implementieren: zuerst exakte Übereinstimmung, dann Suchfilter als Fallback
- Alle Ausgabefelder setzen und
Rec.Modify()aufrufen - Verwandte Felder synchronisieren (z. B. Beschreibung aktualisieren, wenn Nr. validiert wird) mit dem
ChangeAdditionalField()-Muster - Klare Fehlermeldungen zurückgeben, wenn die Validierung fehlschlägt
Wichtige Punkte:
- Für den Modus 'Lookup':
Page.RunModal()öffnen und ausgewählten Wert zurückgeben - Für den Modus 'Validate': Übereinstimmenden Datensatz suchen, Rückgabewert und Fehlermeldung setzen
- Immer
Lookup Validation Success,Lookup Return ValueundLookup Error Messageaktualisieren - Separaten RecordRef für Vorab-Validierung verwenden, um die Seitenquelle nicht zu verändern
- Für typabhängige Validierung: Typ aus der Dokumentzeile abrufen, dynamisch Tabelle/Felder zuordnen
Vollständiges Beispiel
Das folgende Beispiel demonstriert alle Muster einschließlich Typauflösung, bidirektionaler Feldsynchronisierung, zweiphasiger Validierung und Fehlerbehandlung:
codeunit 5673301 "SIM_DI LookUp No."
{
TableNo = "SIM_DI Lookup Codeunit";
Description = 'Diese Codeunit wird verwendet, um die Nummer des Artikels, Sachkontos, der Ressource, des Anlageguts und des Zuordnungskontos nachzuschlagen.';
Access = Public;
var
GlobalRecordSIMDIProcessTemplate: Record "SIM_DI Process Template";
GlobalCodeunitSIMDISession: Codeunit "SIM_DI Process Session SI";
GlobalTypeNotFilledErrLbl: Label 'Um dieses Feld zu validieren, muss das Feld "Typ" ausgefüllt sein.';
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;
// Vertriebszeilentyp aus der Dokumentzeile auflösen
if not this.ResolveSalesLineType(Rec, LocalSalesLineType) then exit;
// Tabellen- und Feldkonfiguration für den aufgelösten Typ abrufen
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>
/// Löst den Vertriebszeilentyp aus dem Dokumentzeilenkontext auf.
/// </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 'Der Typ "%1" existiert nicht. Bitte verwenden Sie einen gültigen Typ.', Comment = '%1 = Type Text';
LocalInvalidRecordIdErrLbl: Label 'Eine ungültige Datensatz-ID für den Nachschlagevorgabe empfangen.';
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>
/// Gibt die Tabellenreferenz und Feldnummern für den angegebenen Vertriebszeilentyp zurück.
/// </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>
/// Validiert den Eingabewert durch Abgleich mit dem Feld „Nr.".
/// </summary>
local procedure ValidateNo(
var Rec: Record "SIM_DI Lookup Codeunit";
var ParamRecordRef: RecordRef;
ParamNoFieldNo: Integer;
ParamDescFieldNo: Integer): RecordId
var
LocalRemarkMessageLbl: Label 'Die %2 "%1" konnte nicht gefunden werden.', Comment = '%1 = Field Value, %2 = Table';
begin
// Exakte Übereinstimmung im Feld „Nr." versuchen
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;
// Suchfilter im Feld „Nr." versuchen
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>
/// Erstellt eine Nachschlageseite zur Anzeige von Datensätzen und ermöglicht dem Benutzer die Auswahl eines Datensatzes.
/// Vorab-Auswahl eines Datensatzes durch Validierung des Eingabewerts gegen das Feld „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
// Separaten RecordRef für Validierung verwenden, um das Original für die Seite sauber zu halten
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>
/// Ändert den Wert eines zusätzlichen Felds in der Dokumentzeile basierend auf dem Nachschlageergebnis.
/// Wird verwendet, um das Beschreibungsfeld nach dem Nachschlagen der Nummer zu setzen.
/// </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;
}
Weitere 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.