Erstellung benutzerdefinierter Dokumente (Individuelle Verarbeitung)
Inhaltsverzeichnis
- Überblick
- Verstehen des Dokumentenablaufs
- JSON-Datenstruktur
- Schnellstart: Einfache benutzerdefinierte Verarbeitung
Überblick
Dieses Handbuch erklärt, wie Sie benutzerdefinierte Business Central-Dokumente aus JSON-Daten erstellen, die von DEXPRO Core bereitgestellt werden. Das System empfängt Dokumente mit dem Status "Custom Processing" (Individuelle Verarbeitung), die Drittanbieter-Entwickler in jeden BC-Dokumententyp umwandeln können (Bestellungen, Verkaufsaufträge, Fibu-Buchblätter, Serviceaufträge usw.).
Was Sie lernen werden
- Wie Sie Dokumente abfangen, die für die individuelle Verarbeitung markiert sind
- Die JSON-Struktur mit Dokumentenkopf, Zeilen, Metadaten und benutzerdefinierten Feldern
- Wie Sie Ihr benutzerdefiniertes Dokument erstellen
Verstehen des Dokumentenablaufs
Kernworkflow
┌─────────────────────────────────────────────────────────────────┐
│ 1. Dokumentenimport │
│ JSON-Rohdaten → DXP Document (Status: Imported) │
└────────────────────────┬────────────────────────────────────────┘
│
▼
┌─────────────────────────────────────────────────────────────────┐
│ 2. Erstellung des Quelldokuments │
│ ISourceDocument.CreateSource() → Ihre Quelltabellen │
│ (z.B. SQZ Document Header/Lines) │
└────────────────────────┬────────────────────────────────────────┘
│
▼
┌─────────────────────────────────────────────────────────────────┐
│ 3. Validierung & Plausibilitätsprüfungen │
│ ISourceDocument.IsSourceDataPlausible() │
│ → Validiert Datenqualität, prüft auf Fehler │
└────────────────────────┬────────────────────────────────────────┘
│
▼
┌─────────────────────────────────────────────────────────────────┐
│ 4. Verarbeitetes JSON erstellen │
│ ISourceDocument.CreateProcessedJsonFromSource() │
│ → Erstellt JSON-Struktur für Zieldokument │
└────────────────────────┬────────────────────────────────────────┘
│
▼
┌─────────────────────────────────────────────────────────────────┐
│ 5. Core-Dokument mit "Custom Processing" Status aktualisieren │
│ UpdateDocument() → Setzt Status auf "Custom Processing" │
│ → Speichert JSON Processed in DXP Document │
└────────────────────────┬────────────────────────────────────────┘
│
▼
┌─────────────────────────────────────────────────────────────────┐
│ 6. Ihre benutzerdefinierte Verarbeitung (HIER SETZEN SIE AN!) │
│ Ereignisse abonnieren oder Interface implementieren │
│ → JSON Processed aus DXP Document lesen │
│ → Ihr BC-Zieldokument erstellen │
└────────────────────────┬────────────────────────────────────────┘
│
▼
┌─────────────────────────────────────────────────────────────────┐
│ 7. Finale Statusaktualisierung │
│ Dokumentstatus → Transferred oder Finished │
│ Linked-to Record Id → Ihr erstelltes BC-Dokument │
└─────────────────────────────────────────────────────────────────┘
Wichtige Statuswerte
Status | Beschreibung |
---|---|
Imported | JSON-Rohdaten empfangen, noch nicht verarbeitet |
Transferred | Quelldokument erstellt, bereit zur Verarbeitung |
Custom Processing | Ihr Zielstatus - Dokument bereit für benutzerdefinierte Verarbeitung |
Closed | Zieldokument erfolgreich verbucht oder ggfs. bezahlt |
Deleted | Verarbeitung abgebrochen, Dokument gelöscht |
JSON-Datenstruktur
Überblick
Das "JSON Processed"
Blob in der DXP Document
-Tabelle enthält ein strukturiertes JSON-Objekt mit allen Informationen, die zum Erstellen Ihres Zieldokuments benötigt werden.
JSON-Struktur (Auszug)
{
// ═══════════════════════════════════════════════════════════
// KOPFBEREICH - Dokumentenebene Informationen
// ═══════════════════════════════════════════════════════════
"Type": "Invoice", // Dokumenttyp: Invoice, Credit Memo, Order, usw.
"VendorNo": "VENDOR001", // Kreditoren-/Kundennummer
"DocumentDate": "2025-10-15", // Belegdatum
"PostingDate": "2025-10-16", // Buchungsdatum
"DocumentReference": "INV-2025-001", // Externe Belegnummer
"OrderNo": "PO-12345", // Referenz zur Bestellung (falls vorhanden)
"PostingDescription": "Invoice Q4", // Buchungsbeschreibung
"NetAmount": 1000.00, // Nettobetrag (ohne MwSt.)
"TotalAmount": 1190.00, // Bruttobetrag (inkl. MwSt.)
"TaxAmount": 190.00, // Gesamter Steuerbetrag
"Currency": "EUR", // Währungscode
// ───────────────────────────────────────────────────────────
// DIMENSIONEN - Kopfebene Dimensionen als Schlüssel-Wert-Paare
// ───────────────────────────────────────────────────────────
"Dimensions": {
"DEPARTMENT": "SALES",
"PROJECT": "PROJ001",
"COSTCENTER": "CC-100"
},
// ───────────────────────────────────────────────────────────
// BENUTZERDEFINIERTE FELDER - Zusätzliche Kopffelder (siehe unten)
// ───────────────────────────────────────────────────────────
"CustomFields": [
{
"Id": 50100,
"Name": "CustomerNo",
"Value": "CUST001",
"Caption": "Customer Number"
},
{
"Id": 50101,
"Name": "DeliveryTerms",
"Value": "EXW",
"Caption": "Delivery Terms"
}
],
// ───────────────────────────────────────────────────────────
// METADATEN - Systemgenerierte Feldzuordnungen
// ───────────────────────────────────────────────────────────
"Metadata": [
{
"DocumentClass": "DXP Invoice / Credit Memo",
"FieldType": "Header",
"FieldId": 101,
"Value": "Additional Info"
}
],
// ═══════════════════════════════════════════════════════════
// ZEILEN-BEREICH - Dokumentzeilen
// ═══════════════════════════════════════════════════════════
"Lines": [
{
// ─────────────────────────────────────────────────────────
// Zeilen-Grundinformationen
// ─────────────────────────────────────────────────────────
"Type": "Item", // Item, G/L Account, Charge (Item), Fixed Asset
"No": "ITEM001", // Artikel-/Kontonummer
"Description": "Product A", // Zeilenbeschreibung
"VendorItemNo": "VEND-SKU-123", // Kreditorenartikelnummer
"Quantity": 10.0, // Menge
"UnitOfMeasure": "PCS", // Einheitencode
"DirectUnitCost": 100.00, // EK-Preis
"LineDiscount": 5.0, // Zeilenrabatt in Prozent
// ─────────────────────────────────────────────────────────
// Zeilen-Beträge & Steuern
// ─────────────────────────────────────────────────────────
"NetAmount": 950.00, // Zeilennettobetrag
"TotalAmount": 1130.50, // Zeilenbruttobetrag
"TaxRate": 19.0, // Steuersatz in Prozent
"VATBusPostingGroup": "DOMESTIC",
"VATProdPostingGroup": "STANDARD",
"GenBusPostingGroup": "DOMESTIC",
"GenProdPostingGroup": "RETAIL",
// ─────────────────────────────────────────────────────────
// Bestellreferenz (für Zuordnung)
// ─────────────────────────────────────────────────────────
"OrderNo": "PO-12345", // Referenzierte Bestellnummer
"OrderLineNo": 10000, // Referenzierte Bestellzeilennummer
"ReceiptNo": "RCP-001", // Referenzierte Wareneingangsnummer
"ReceiptLineNo": 10000, // Referenzierte Wareneingangszeile
// ─────────────────────────────────────────────────────────
// Zeilen-Dimensionen
// ─────────────────────────────────────────────────────────
"Dimensions": {
"DEPARTMENT": "PROD",
"PROJECT": "PROJ001"
},
// ─────────────────────────────────────────────────────────
// Zeilen-Benutzerdefinierte Felder
// ─────────────────────────────────────────────────────────
"CustomFields": [
{
"Id": 50200,
"Name": "SerialNo",
"Value": "SN-12345",
"Caption": "Serial Number"
}
],
// ─────────────────────────────────────────────────────────
// Zeilen-Metadaten
// ─────────────────────────────────────────────────────────
"Metadata": [
{
"DocumentClass": "DXP Invoice / Credit Memo",
"FieldType": "Line",
"FieldId": 201,
"Value": "Line-specific metadata"
}
]
}
// ... weitere Zeilen
]
}
Benutzerdefinierte Felder vs. Metadaten
Benutzerdefinierte Felder (Custom Fields):
-
Benutzerdefinierte Felder aus Ihrer App
-
Ggfs. zugeordnet über Custom Field Mapping
-
Können jedem Feld in der Squeeze-Validierung zugeordnet werden
-
Beispiel: Hinzufügen einer "Kundennr." zur Squeeze-Validierung
Metadaten:
-
Systemgenerierte Felder
-
Vom Squeeze-System extrahiert, aber keinem Feld in BC zugeordnet
Schnellstart: Einfache benutzerdefinierte Verarbeitung
Szenario
Sie möchten einen benutzerdefinierten Dokumenttyp (z.B. einen Warenausgang) aus Dokumenten erstellen, die mit dem Status "Custom Processing" markiert sind.
Schritt 1: Integration Event abonnieren
Erstellen Sie eine Codeunit, um Dokumente mit dem Status "Custom Processing" abzufangen:
codeunit 50100 "My Custom Document Handler"
{
[EventSubscriber(ObjectType::Codeunit, Codeunit::"DXP Document Mgt.",
'OnAfterGetDocumentOnBeforeCheckStatusAndNextProcessStep', '', true, true)]
local procedure OnAfterGetDocument(
var Document: Record "DXP Document";
NewStatus: Enum "DXP Document Status";
NextStep: Enum "DXP Next process step";
var ProcessedJSONObj: JsonObject;
var Handled: Boolean)
begin
// Nur Custom Processing Status behandeln
if NewStatus <> NewStatus::"Custom Processing" then
exit;
// Als behandelt markieren, um Standard-Verarbeitung zu verhindern
Handled := true;
// Ihr benutzerdefiniertes Dokument erstellen
CreateMyCustomDocument(Document, ProcessedJSONObj);
end;
local procedure CreateMyCustomDocument(
var Document: Record "DXP Document";
ProcessedJSON: JsonObject)
var
CoreTokenMgt: Codeunit "DXP Core Token Mgt.";
JsonHelper: Codeunit "DXP Json Helper";
MyCustomHeader: Record "My Custom Document Header";
MyCustomLine: Record "My Custom Document Line";
LineJArray: JsonArray;
LineJToken: JsonToken;
LineJObj: JsonObject;
begin
// ═══════════════════════════════════════════════════════
// 1. KOPF ERSTELLEN
// ═══════════════════════════════════════════════════════
MyCustomHeader.Init();
MyCustomHeader."No." := ''; // Wird durch Nummernserie zugewiesen
// Standard-Kopffelder lesen
MyCustomHeader."Vendor No." :=
JsonHelper.ValAsTxt(ProcessedJSON, CoreTokenMgt.GetVendorNoTok(), false);
MyCustomHeader."Document Date" :=
JsonHelper.ValAsDate(ProcessedJSON, CoreTokenMgt.GetDocDateTok(), false);
MyCustomHeader."Document Reference" :=
JsonHelper.ValAsTxt(ProcessedJSON, CoreTokenMgt.GetDocReferenceTok(), false);
MyCustomHeader.Insert(true);
// ═══════════════════════════════════════════════════════
// 2. DIMENSIONEN VERARBEITEN (wenn Ihr Dokument diese unterstützt)
// ═══════════════════════════════════════════════════════
ProcessHeaderDimensions(ProcessedJSON, MyCustomHeader);
// ═══════════════════════════════════════════════════════
// 3. BENUTZERDEFINIERTE FELDER VERARBEITEN
// ═══════════════════════════════════════════════════════
ProcessCustomFields(ProcessedJSON, MyCustomHeader);
// ═══════════════════════════════════════════════════════
// 4. ZEILEN ERSTELLEN
// ═══════════════════════════════════════════════════════
LineJArray := JsonHelper.ReadJArrayFromObj(ProcessedJSON, CoreTokenMgt.GetLinesTok());
foreach LineJToken in LineJArray do begin
LineJObj := LineJToken.AsObject();
MyCustomLine.Init();
MyCustomLine."Document No." := MyCustomHeader."No.";
MyCustomLine."Line No." := GetNextLineNo(MyCustomHeader."No.");
// Zeilenfelder lesen
MyCustomLine."Item No." :=
JsonHelper.ValAsTxt(LineJObj, CoreTokenMgt.GetNoTok(), false);
MyCustomLine.Description :=
JsonHelper.ValAsTxt(LineJObj, CoreTokenMgt.GetDescriptionTok(), false);
MyCustomLine.Quantity :=
JsonHelper.ValAsDec(LineJObj, CoreTokenMgt.GetQtyTok(), false);
MyCustomLine."Unit of Measure" :=
JsonHelper.ValAsTxt(LineJObj, CoreTokenMgt.GetUoMTok(), false);
MyCustomLine.Insert(true);
// Zeilen-Dimensionen und benutzerdefinierte Felder verarbeiten, falls erforderlich
ProcessLineDimensions(LineJObj, MyCustomLine);
ProcessLineCustomFields(LineJObj, MyCustomLine);
end;
// ═══════════════════════════════════════════════════════
// 5. CORE-DOKUMENTSTATUS AKTUALISIEREN
// ═══════════════════════════════════════════════════════
UpdateCoreDocument(Document, MyCustomHeader);
end;
local procedure ProcessHeaderDimensions(ProcessedJSON: JsonObject; var MyCustomHeader: Record "My Custom Document Header")
var
CoreTokenMgt: Codeunit "DXP Core Token Mgt.";
DocTransferMgt: Codeunit "DXP Document Transfer Mgt.";
DimensionsJObj: JsonObject;
DimSetID: Integer;
begin
// Dimensionen aus JSON lesen
if ProcessedJSON.Contains(CoreTokenMgt.GetDimensionsTok()) then begin
ProcessedJSON.Get(CoreTokenMgt.GetDimensionsTok(), DimensionsJObj);
// In Dimensionssatz-ID konvertieren
if DocTransferMgt.GetDimSetIdFromJsonObj(DimensionsJObj, DimSetID) then begin
MyCustomHeader."Dimension Set ID" := DimSetID;
MyCustomHeader.Modify(true);
end;
end;
end;
local procedure ProcessCustomFields(ProcessedJSON: JsonObject; var MyCustomHeader: Record "My Custom Document Header")
var
CoreTokenMgt: Codeunit "DXP Core Token Mgt.";
JsonHelper: Codeunit "DXP Json Helper";
CustomFieldsJArray: JsonArray;
CustomFieldJToken: JsonToken;
CustomFieldJObj: JsonObject;
FieldName: Text;
FieldValue: Text;
begin
// Benutzerdefinierte Felder-Array lesen
if not ProcessedJSON.Contains(CoreTokenMgt.GetCustomFieldsTok()) then
exit;
CustomFieldsJArray := JsonHelper.ReadJArrayFromObj(ProcessedJSON, CoreTokenMgt.GetCustomFieldsTok());
foreach CustomFieldJToken in CustomFieldsJArray do begin
CustomFieldJObj := CustomFieldJToken.AsObject();
FieldName := JsonHelper.ValAsTxt(CustomFieldJObj, CoreTokenMgt.GetNameTok(), false);
FieldValue := JsonHelper.ValAsTxt(CustomFieldJObj, CoreTokenMgt.GetValueTok(), false);
// Zu Ihren benutzerdefinierten Feldern zuordnen
case FieldName of
'CustomerNo':
MyCustomHeader."Customer No." := CopyStr(FieldValue, 1, 20);
'DeliveryTerms':
MyCustomHeader."Delivery Terms" := CopyStr(FieldValue, 1, 10);
// Weitere Feldzuordnungen nach Bedarf hinzufügen
end;
end;
MyCustomHeader.Modify(true);
end;
local procedure ProcessLineDimensions(LineJObj: JsonObject; var MyCustomLine: Record "My Custom Document Line")
var
CoreTokenMgt: Codeunit "DXP Core Token Mgt.";
DocTransferMgt: Codeunit "DXP Document Transfer Mgt.";
DimensionsJObj: JsonObject;
DimSetID: Integer;
begin
if LineJObj.Contains(CoreTokenMgt.GetDimensionsTok()) then begin
LineJObj.Get(CoreTokenMgt.GetDimensionsTok(), DimensionsJObj);
if DocTransferMgt.GetDimSetIdFromJsonObj(DimensionsJObj, DimSetID) then begin
MyCustomLine."Dimension Set ID" := DimSetID;
MyCustomLine.Modify(true);
end;
end;
end;
local procedure ProcessLineCustomFields(LineJObj: JsonObject; var MyCustomLine: Record "My Custom Document Line")
var
CoreTokenMgt: Codeunit "DXP Core Token Mgt.";
JsonHelper: Codeunit "DXP Json Helper";
CustomFieldsJArray: JsonArray;
CustomFieldJToken: JsonToken;
CustomFieldJObj: JsonObject;
FieldName: Text;
FieldValue: Text;
begin
if not LineJObj.Contains(CoreTokenMgt.GetCustomFieldsTok()) then
exit;
CustomFieldsJArray := JsonHelper.ReadJArrayFromObj(LineJObj, CoreTokenMgt.GetCustomFieldsTok());
foreach CustomFieldJToken in CustomFieldsJArray do begin
CustomFieldJObj := CustomFieldJToken.AsObject();
FieldName := JsonHelper.ValAsTxt(CustomFieldJObj, CoreTokenMgt.GetNameTok(), false);
FieldValue := JsonHelper.ValAsTxt(CustomFieldJObj, CoreTokenMgt.GetValueTok(), false);
// Zu Ihren benutzerdefinierten Zeilenfeldern zuordnen
case FieldName of
'SerialNo':
MyCustomLine."Serial No." := CopyStr(FieldValue, 1, 50);
// Weitere Feldzuordnungen hinzufügen
end;
end;
MyCustomLine.Modify(true);
end;
local procedure UpdateCoreDocument(var Document: Record "DXP Document"; MyCustomHeader: Record "My Custom Document Header")
var
DocumentMgt: Codeunit "DXP Document Mgt.";
begin
// Core-Dokument aktualisieren, um es mit Ihrem erstellten Dokument zu verknüpfen
Document.Status := Document.Status::Transferred;
Document."Linked-to Record Id" := MyCustomHeader.RecordId;
Document.Modify(true);
// Optional: Anhänge von Core zu Ihrem Dokument übertragen
TransferAttachments(Document, MyCustomHeader);
end;
local procedure TransferAttachments(Document: Record "DXP Document"; MyCustomHeader: Record "My Custom Document Header")
var
DocAttachment: Record "DXP Document Attachment";
MyDocAttachment: Record "Document Attachment";
InStr: InStream;
begin
DocAttachment.SetRange("Document No.", Document."No.");
if DocAttachment.FindSet() then
repeat
MyDocAttachment.Init();
MyDocAttachment.ID := 0;
DocAttachment."File Content".CreateInStream(InStr);
MyDocAttachment.SaveAttachmentFromStream(
InStr,
MyCustomHeader.RecordId,
DocAttachment."File Name");
until DocAttachment.Next() = 0;
end;
local procedure GetNextLineNo(DocumentNo: Code[20]): Integer
var
MyCustomLine: Record "My Custom Document Line";
begin
MyCustomLine.SetRange("Document No.", DocumentNo);
if MyCustomLine.FindLast() then
exit(MyCustomLine."Line No." + 10000);
exit(10000);
end;
}
Wichtige Hilfs-Codeunits
Codeunit | Zweck |
---|---|
DXP Core Token Mgt. | Stellt Token-Namen für JSON-Felder bereit (GetVendorNoTok(), GetDocDateTok(), usw.) |
DXP Json Helper | JSON-Parsing-Hilfsfunktionen (ValAsTxt(), ValAsDate(), ReadJArrayFromObj(), usw.) |
DXP Document Transfer Mgt. | Dimensionsverarbeitung, Metadatenübertragung, Behandlung benutzerdefinierter Felder |
DXP Document Mgt. | Kern-Dokumentenverwaltung (UpdateDocument(), UpdateDocumentStatus(), usw.) |
Bei Fragen oder Unklarheiten wenden Sie sich bitte an die DEXPRO Solutions GmbH.
No Comments