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)
xxxxxxxxxx{
// ═══════════════════════════════════════════════════════════
// 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,00, // Nettobetrag (ohne MwSt.)
"TotalAmount": 1190.00,00, // Bruttobetrag (inkl. MwSt.)
"TaxAmount": 190.00,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,50100,
"Name": "CustomerNo",
"Value": "CUST001",
"Caption": "Customer Number"
},
{
"Id": 50101,50101,
"Name": "DeliveryTerms",
"Value": "EXW",
"Caption": "Delivery Terms"
}
],
// ───────────────────────────────────────────────────────────
// METADATEN - Systemgenerierte Feldzuordnungen
// ───────────────────────────────────────────────────────────
"Metadata": [
{
"DocumentClass": "DXP Invoice / Credit Memo",
"FieldType": "Header",
"FieldId": 101,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,0, // Menge
"UnitOfMeasure": "PCS", // Einheitencode
"DirectUnitCost": 100.00,00, // EK-Preis
"LineDiscount": 5.0,0, // Zeilenrabatt in Prozent
// ─────────────────────────────────────────────────────────
// Zeilen-Beträge & Steuern
// ─────────────────────────────────────────────────────────
"NetAmount": 950.00,00, // Zeilennettobetrag
"TotalAmount": 1130.50,50, // Zeilenbruttobetrag
"TaxRate": 19.0,0, // Steuersatz in Prozent
"VATBusPostingGroup": "DOMESTIC",
"VATProdPostingGroup": "STANDARD",
"GenBusPostingGroup": "DOMESTIC",
"GenProdPostingGroup": "RETAIL",
// ─────────────────────────────────────────────────────────
// Bestellreferenz (für Zuordnung)
// ─────────────────────────────────────────────────────────
"OrderNo": "PO-12345", // Referenzierte Bestellnummer
"OrderLineNo": 10000,10000, // Referenzierte Bestellzeilennummer
"ReceiptNo": "RCP-001", // Referenzierte Wareneingangsnummer
"ReceiptLineNo": 10000,10000, // Referenzierte Wareneingangszeile
// ─────────────────────────────────────────────────────────
// Zeilen-Dimensionen
// ─────────────────────────────────────────────────────────
"Dimensions": {
"DEPARTMENT": "PROD",
"PROJECT": "PROJ001"
},
// ─────────────────────────────────────────────────────────
// Zeilen-Benutzerdefinierte Felder
// ─────────────────────────────────────────────────────────
"CustomFields": [
{
"Id": 50200,50200,
"Name": "SerialNo",
"Value": "SN-12345",
"Caption": "Serial Number"
}
],
// ─────────────────────────────────────────────────────────
// Zeilen-Metadaten
// ─────────────────────────────────────────────────────────
"Metadata": [
{
"DocumentClass": "DXP Invoice / Credit Memo",
"FieldType": "Line",
"FieldId": 201,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:
xxxxxxxxxxcodeunit
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.