Skip to main content

Erstellung benutzerdefinierter Dokumente (Individuelle Verarbeitung)

Inhaltsverzeichnis

  1. Überblick
  2. Verstehen des Dokumentenablaufs
  3. JSON-Datenstruktur
  4. 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

x
 
1
┌─────────────────────────────────────────────────────────────────┐
2
│ 1. Dokumentenimport                                             │
3
│    JSON-Rohdaten → DXP Document (Status: Imported)              │
4
└────────────────────────┬────────────────────────────────────────┘
5
6
7
┌─────────────────────────────────────────────────────────────────┐
8
│ 2. Erstellung des Quelldokuments                                │
9
│    ISourceDocument.CreateSource() → Ihre Quelltabellen          │
10
│    (z.B. SQZ Document Header/Lines)  │
11
└────────────────────────┬────────────────────────────────────────┘
12
13
14
┌─────────────────────────────────────────────────────────────────┐
15
│ 3. Validierung & Plausibilitätsprüfungen                        │
16
│    ISourceDocument.IsSourceDataPlausible()                      │
17
│    → Validiert Datenqualität, prüft auf Fehler                  │
18
└────────────────────────┬────────────────────────────────────────┘
19
20
21
┌─────────────────────────────────────────────────────────────────┐
22
│ 4. Verarbeitetes JSON erstellen                                 │
23
│    ISourceDocument.CreateProcessedJsonFromSource()              │
24
│    → Erstellt JSON-Struktur für Zieldokument                    │
25
└────────────────────────┬────────────────────────────────────────┘
26
27
28
┌─────────────────────────────────────────────────────────────────┐
29
│ 5. Core-Dokument mit "Custom Processing" Status aktualisieren   │
30
│    UpdateDocument() → Setzt Status auf "Custom Processing"      │
31
│    → Speichert JSON Processed in DXP Document                   │
32
└────────────────────────┬────────────────────────────────────────┘
33
34
35
┌─────────────────────────────────────────────────────────────────┐
36
│ 6. Ihre benutzerdefinierte Verarbeitung (HIER SETZEN SIE AN!)   │
37
│    Ereignisse abonnieren oder Interface implementieren          │
38
│    → JSON Processed aus DXP Document lesen                      │
39
│    → Ihr BC-Zieldokument erstellen                              │
40
└────────────────────────┬────────────────────────────────────────┘
41
42
43
┌─────────────────────────────────────────────────────────────────┐
44
│ 7. Finale Statusaktualisierung                                  │
45
│    Dokumentstatus → Transferred oder Finished                   │
46
│    Linked-to Record Id → Ihr erstelltes BC-Dokument             │
47
└─────────────────────────────────────────────────────────────────┘
48

Wichtige Statuswerte

StatusBeschreibung
ImportedJSON-Rohdaten empfangen, noch nicht verarbeitet
TransferredQuelldokument erstellt, bereit zur Verarbeitung
Custom ProcessingIhr Zielstatus - Dokument bereit für benutzerdefinierte Verarbeitung
ClosedZieldokument erfolgreich verbucht oder ggfs. bezahlt
DeletedVerarbeitung 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
 
1
{
2
  // ═══════════════════════════════════════════════════════════
3
  // KOPFBEREICH - Dokumentenebene Informationen
4
  // ═══════════════════════════════════════════════════════════
5
  
6
  "Type": "Invoice",                    // Dokumenttyp: Invoice, Credit Memo, Order, usw.
7
  "VendorNo": "VENDOR001",              // Kreditoren-/Kundennummer
8
  "DocumentDate": "2025-10-15",         // Belegdatum
9
  "PostingDate": "2025-10-16",          // Buchungsdatum
10
  "DocumentReference": "INV-2025-001",  // Externe Belegnummer
11
  "OrderNo": "PO-12345",                // Referenz zur Bestellung (falls vorhanden)
12
  "PostingDescription": "Invoice Q4",   // Buchungsbeschreibung
13
  "NetAmount": 1000.00,                 // Nettobetrag (ohne MwSt.)
14
  "TotalAmount": 1190.00,               // Bruttobetrag (inkl. MwSt.)
15
  "TaxAmount": 190.00,                  // Gesamter Steuerbetrag
16
  "Currency": "EUR",                    // Währungscode
17
  
18
  // ───────────────────────────────────────────────────────────
19
  // DIMENSIONEN - Kopfebene Dimensionen als Schlüssel-Wert-Paare
20
  // ───────────────────────────────────────────────────────────
21
  
22
  "Dimensions": {
23
    "DEPARTMENT": "SALES",
24
    "PROJECT": "PROJ001",
25
    "COSTCENTER": "CC-100"
26
  },
27
  
28
  // ───────────────────────────────────────────────────────────
29
  // BENUTZERDEFINIERTE FELDER - Zusätzliche Kopffelder (siehe unten)
30
  // ───────────────────────────────────────────────────────────
31
  
32
  "CustomFields": [
33
    {
34
      "Id": 50100,
35
      "Name": "CustomerNo",
36
      "Value": "CUST001",
37
      "Caption": "Customer Number"
38
    },
39
    {
40
      "Id": 50101,
41
      "Name": "DeliveryTerms",
42
      "Value": "EXW",
43
      "Caption": "Delivery Terms"
44
    }
45
  ],
46
  
47
  // ───────────────────────────────────────────────────────────
48
  // METADATEN - Systemgenerierte Feldzuordnungen
49
  // ───────────────────────────────────────────────────────────
50
  
51
  "Metadata": [
52
    {
53
      "DocumentClass": "DXP Invoice / Credit Memo",
54
      "FieldType": "Header",
55
      "FieldId": 101,
56
      "Value": "Additional Info"
57
    }
58
  ],
59
  
60
  // ═══════════════════════════════════════════════════════════
61
  // ZEILEN-BEREICH - Dokumentzeilen
62
  // ═══════════════════════════════════════════════════════════
63
  
64
  "Lines": [
65
    {
66
      // ─────────────────────────────────────────────────────────
67
      // Zeilen-Grundinformationen
68
      // ─────────────────────────────────────────────────────────
69
      
70
      "Type": "Item",                   // Item, G/L Account, Charge (Item), Fixed Asset
71
      "No": "ITEM001",                  // Artikel-/Kontonummer
72
      "Description": "Product A",       // Zeilenbeschreibung
73
      "VendorItemNo": "VEND-SKU-123",   // Kreditorenartikelnummer
74
      "Quantity": 10.0,                 // Menge
75
      "UnitOfMeasure": "PCS",          // Einheitencode
76
      "DirectUnitCost": 100.00,        // EK-Preis
77
      "LineDiscount": 5.0,             // Zeilenrabatt in Prozent
78
      
79
      // ─────────────────────────────────────────────────────────
80
      // Zeilen-Beträge & Steuern
81
      // ─────────────────────────────────────────────────────────
82
      
83
      "NetAmount": 950.00,             // Zeilennettobetrag
84
      "TotalAmount": 1130.50,          // Zeilenbruttobetrag
85
      "TaxRate": 19.0,                 // Steuersatz in Prozent
86
      "VATBusPostingGroup": "DOMESTIC",
87
      "VATProdPostingGroup": "STANDARD",
88
      "GenBusPostingGroup": "DOMESTIC",
89
      "GenProdPostingGroup": "RETAIL",
90
      
91
      // ─────────────────────────────────────────────────────────
92
      // Bestellreferenz (für Zuordnung)
93
      // ─────────────────────────────────────────────────────────
94
      
95
      "OrderNo": "PO-12345",           // Referenzierte Bestellnummer
96
      "OrderLineNo": 10000,            // Referenzierte Bestellzeilennummer
97
      "ReceiptNo": "RCP-001",          // Referenzierte Wareneingangsnummer
98
      "ReceiptLineNo": 10000,          // Referenzierte Wareneingangszeile
99
      
100
      // ─────────────────────────────────────────────────────────
101
      // Zeilen-Dimensionen
102
      // ─────────────────────────────────────────────────────────
103
      
104
      "Dimensions": {
105
        "DEPARTMENT": "PROD",
106
        "PROJECT": "PROJ001"
107
      },
108
      
109
      // ─────────────────────────────────────────────────────────
110
      // Zeilen-Benutzerdefinierte Felder
111
      // ─────────────────────────────────────────────────────────
112
      
113
      "CustomFields": [
114
        {
115
          "Id": 50200,
116
          "Name": "SerialNo",
117
          "Value": "SN-12345",
118
          "Caption": "Serial Number"
119
        }
120
      ],
121
      
122
      // ─────────────────────────────────────────────────────────
123
      // Zeilen-Metadaten
124
      // ─────────────────────────────────────────────────────────
125
      
126
      "Metadata": [
127
        {
128
          "DocumentClass": "DXP Invoice / Credit Memo",
129
          "FieldType": "Line",
130
          "FieldId": 201,
131
          "Value": "Line-specific metadata"
132
        }
133
      ]
134
    }
135
    // ... weitere Zeilen
136
  ]
137
}
138

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:

xxxxxxxxxx
 
1
codeunit 50100 "My Custom Document Handler"
2
{
3
    [EventSubscriber(ObjectType::Codeunit, Codeunit::"DXP Document Mgt.", 
4
        'OnAfterWriteProcessedJsonToBlob', '', true, true)]
5
    local procedure OnAfterWriteProcessedJsonToBlob(
6
        var Document: Record "DXP Document"; 
7
        var ProcessedJSONObj: JsonObject)
8
    begin
9
        // Nur Custom Processing Status behandeln
10
        if Document.Status <> Document.Status::"Custom Processing" then
11
            exit;
12
        
13
        // Ihr benutzerdefiniertes Dokument erstellen
14
        CreateMyCustomDocument(Document, ProcessedJSONObj);
15
    end;
16
    
17
    local procedure CreateMyCustomDocument(
18
        var Document: Record "DXP Document"; 
19
        ProcessedJSON: JsonObject)
20
    var
21
        CoreTokenMgt: Codeunit "DXP Core Token Mgt.";
22
        JsonHelper: Codeunit "DXP Json Helper";
23
        MyCustomHeader: Record "My Custom Document Header";
24
        MyCustomLine: Record "My Custom Document Line";
25
        LineJArray: JsonArray;
26
        LineJToken: JsonToken;
27
        LineJObj: JsonObject;
28
    begin
29
        // ═══════════════════════════════════════════════════════
30
        // 1. KOPF ERSTELLEN
31
        // ═══════════════════════════════════════════════════════
32
        
33
        MyCustomHeader.Init();
34
        MyCustomHeader."No." := '';  // Wird durch Nummernserie zugewiesen
35
        
36
        // Standard-Kopffelder lesen
37
        MyCustomHeader."Vendor No." := 
38
            JsonHelper.ValAsTxt(ProcessedJSON, CoreTokenMgt.GetVendorNoTok(), false);
39
        MyCustomHeader."Document Date" := 
40
            JsonHelper.ValAsDate(ProcessedJSON, CoreTokenMgt.GetDocDateTok(), false);
41
        MyCustomHeader."Document Reference" := 
42
            JsonHelper.ValAsTxt(ProcessedJSON, CoreTokenMgt.GetDocReferenceTok(), false);
43
        
44
        MyCustomHeader.Insert(true);
45
        
46
        // ═══════════════════════════════════════════════════════
47
        // 2. DIMENSIONEN VERARBEITEN (wenn Ihr Dokument diese unterstützt)
48
        // ═══════════════════════════════════════════════════════
49
        
50
        ProcessHeaderDimensions(ProcessedJSON, MyCustomHeader);
51
        
52
        // ═══════════════════════════════════════════════════════
53
        // 3. BENUTZERDEFINIERTE FELDER VERARBEITEN
54
        // ═══════════════════════════════════════════════════════
55
        
56
        ProcessCustomFields(ProcessedJSON, MyCustomHeader);
57
        
58
        // ═══════════════════════════════════════════════════════
59
        // 4. ZEILEN ERSTELLEN
60
        // ═══════════════════════════════════════════════════════
61
        
62
        LineJArray := JsonHelper.ReadJArrayFromObj(ProcessedJSON, CoreTokenMgt.GetLinesTok());
63
        
64
        foreach LineJToken in LineJArray do begin
65
            LineJObj := LineJToken.AsObject();
66
            
67
            MyCustomLine.Init();
68
            MyCustomLine."Document No." := MyCustomHeader."No.";
69
            MyCustomLine."Line No." := GetNextLineNo(MyCustomHeader."No.");
70
            
71
            // Zeilenfelder lesen
72
            MyCustomLine."Item No." := 
73
                JsonHelper.ValAsTxt(LineJObj, CoreTokenMgt.GetNoTok(), false);
74
            MyCustomLine.Description := 
75
                JsonHelper.ValAsTxt(LineJObj, CoreTokenMgt.GetDescriptionTok(), false);
76
            MyCustomLine.Quantity := 
77
                JsonHelper.ValAsDec(LineJObj, CoreTokenMgt.GetQtyTok(), false);
78
            MyCustomLine."Unit of Measure" := 
79
                JsonHelper.ValAsTxt(LineJObj, CoreTokenMgt.GetUoMTok(), false);
80
            
81
            MyCustomLine.Insert(true);
82
            
83
            // Zeilen-Dimensionen und benutzerdefinierte Felder verarbeiten, falls erforderlich
84
            ProcessLineDimensions(LineJObj, MyCustomLine);
85
            ProcessLineCustomFields(LineJObj, MyCustomLine);
86
        end;
87
        
88
        // ═══════════════════════════════════════════════════════
89
        // 5. CORE-DOKUMENTSTATUS AKTUALISIEREN
90
        // ═══════════════════════════════════════════════════════
91
        
92
        UpdateCoreDocument(Document, MyCustomHeader);
93
    end;
94
    
95
    local procedure ProcessHeaderDimensions(ProcessedJSON: JsonObject; var MyCustomHeader: Record "My Custom Document Header")
96
    var
97
        CoreTokenMgt: Codeunit "DXP Core Token Mgt.";
98
        DocTransferMgt: Codeunit "DXP Document Transfer Mgt.";
99
        DimensionsJObj: JsonObject;
100
        DimSetID: Integer;
101
    begin
102
        // Dimensionen aus JSON lesen
103
        if ProcessedJSON.Contains(CoreTokenMgt.GetDimensionsTok()) then begin
104
            ProcessedJSON.Get(CoreTokenMgt.GetDimensionsTok(), DimensionsJObj);
105
            
106
            // In Dimensionssatz-ID konvertieren
107
            if DocTransferMgt.GetDimSetIdFromJsonObj(DimensionsJObj, DimSetID) then begin
108
                MyCustomHeader."Dimension Set ID" := DimSetID;
109
                MyCustomHeader.Modify(true);
110
            end;
111
        end;
112
    end;
113
    
114
    local procedure ProcessCustomFields(ProcessedJSON: JsonObject; var MyCustomHeader: Record "My Custom Document Header")
115
    var
116
        CoreTokenMgt: Codeunit "DXP Core Token Mgt.";
117
        JsonHelper: Codeunit "DXP Json Helper";
118
        CustomFieldsJArray: JsonArray;
119
        CustomFieldJToken: JsonToken;
120
        CustomFieldJObj: JsonObject;
121
        FieldName: Text;
122
        FieldValue: Text;
123
    begin
124
        // Benutzerdefinierte Felder-Array lesen
125
        if not ProcessedJSON.Contains(CoreTokenMgt.GetCustomFieldsTok()) then
126
            exit;
127
            
128
        CustomFieldsJArray := JsonHelper.ReadJArrayFromObj(ProcessedJSON, CoreTokenMgt.GetCustomFieldsTok());
129
        
130
        foreach CustomFieldJToken in CustomFieldsJArray do begin
131
            CustomFieldJObj := CustomFieldJToken.AsObject();
132
            FieldName := JsonHelper.ValAsTxt(CustomFieldJObj, CoreTokenMgt.GetNameTok(), false);
133
            FieldValue := JsonHelper.ValAsTxt(CustomFieldJObj, CoreTokenMgt.GetValueTok(), false);
134
            
135
            // Zu Ihren benutzerdefinierten Feldern zuordnen
136
            case FieldName of
137
                'CustomerNo':
138
                    MyCustomHeader."Customer No." := CopyStr(FieldValue, 1, 20);
139
                'DeliveryTerms':
140
                    MyCustomHeader."Delivery Terms" := CopyStr(FieldValue, 1, 10);
141
                // Weitere Feldzuordnungen nach Bedarf hinzufügen
142
            end;
143
        end;
144
        
145
        MyCustomHeader.Modify(true);
146
    end;
147
    
148
    local procedure ProcessLineDimensions(LineJObj: JsonObject; var MyCustomLine: Record "My Custom Document Line")
149
    var
150
        CoreTokenMgt: Codeunit "DXP Core Token Mgt.";
151
        DocTransferMgt: Codeunit "DXP Document Transfer Mgt.";
152
        DimensionsJObj: JsonObject;
153
        DimSetID: Integer;
154
    begin
155
        if LineJObj.Contains(CoreTokenMgt.GetDimensionsTok()) then begin
156
            LineJObj.Get(CoreTokenMgt.GetDimensionsTok(), DimensionsJObj);
157
            
158
            if DocTransferMgt.GetDimSetIdFromJsonObj(DimensionsJObj, DimSetID) then begin
159
                MyCustomLine."Dimension Set ID" := DimSetID;
160
                MyCustomLine.Modify(true);
161
            end;
162
        end;
163
    end;
164
    
165
    local procedure ProcessLineCustomFields(LineJObj: JsonObject; var MyCustomLine: Record "My Custom Document Line")
166
    var
167
        CoreTokenMgt: Codeunit "DXP Core Token Mgt.";
168
        JsonHelper: Codeunit "DXP Json Helper";
169
        CustomFieldsJArray: JsonArray;
170
        CustomFieldJToken: JsonToken;
171
        CustomFieldJObj: JsonObject;
172
        FieldName: Text;
173
        FieldValue: Text;
174
    begin
175
        if not LineJObj.Contains(CoreTokenMgt.GetCustomFieldsTok()) then
176
            exit;
177
            
178
        CustomFieldsJArray := JsonHelper.ReadJArrayFromObj(LineJObj, CoreTokenMgt.GetCustomFieldsTok());
179
        
180
        foreach CustomFieldJToken in CustomFieldsJArray do begin
181
            CustomFieldJObj := CustomFieldJToken.AsObject();
182
            FieldName := JsonHelper.ValAsTxt(CustomFieldJObj, CoreTokenMgt.GetNameTok(), false);
183
            FieldValue := JsonHelper.ValAsTxt(CustomFieldJObj, CoreTokenMgt.GetValueTok(), false);
184
            
185
            // Zu Ihren benutzerdefinierten Zeilenfeldern zuordnen
186
            case FieldName of
187
                'SerialNo':
188
                    MyCustomLine."Serial No." := CopyStr(FieldValue, 1, 50);
189
                // Weitere Feldzuordnungen hinzufügen
190
            end;
191
        end;
192
        
193
        MyCustomLine.Modify(true);
194
    end;
195
    
196
    local procedure UpdateCoreDocument(var Document: Record "DXP Document"; MyCustomHeader: Record "My Custom Document Header")
197
    var
198
        DocumentMgt: Codeunit "DXP Document Mgt.";
199
    begin
200
        // Core-Dokument aktualisieren, um es mit Ihrem erstellten Dokument zu verknüpfen
201
        Document.Status := Document.Status::Transferred;
202
        Document."Linked-to Record Id" := MyCustomHeader.RecordId;
203
        Document.Modify(true);
204
        
205
        // Optional: Anhänge von Core zu Ihrem Dokument übertragen
206
        TransferAttachments(Document, MyCustomHeader);
207
    end;
208
    
209
    local procedure TransferAttachments(Document: Record "DXP Document"; MyCustomHeader: Record "My Custom Document Header")
210
    var
211
        DocAttachment: Record "DXP Document Attachment";
212
        MyDocAttachment: Record "Document Attachment";
213
        InStr: InStream;
214
    begin
215
        DocAttachment.SetRange("Document No.", Document."No.");
216
        if DocAttachment.FindSet() then
217
            repeat
218
                MyDocAttachment.Init();
219
                MyDocAttachment.ID := 0;
220
                
221
                DocAttachment."File Content".CreateInStream(InStr);
222
                MyDocAttachment.SaveAttachmentFromStream(
223
                    InStr, 
224
                    MyCustomHeader.RecordId, 
225
                    DocAttachment."File Name");
226
            until DocAttachment.Next() = 0;
227
    end;
228
    
229
    local procedure GetNextLineNo(DocumentNo: Code[20]): Integer
230
    var
231
        MyCustomLine: Record "My Custom Document Line";
232
    begin
233
        MyCustomLine.SetRange("Document No.", DocumentNo);
234
        if MyCustomLine.FindLast() then
235
            exit(MyCustomLine."Line No." + 10000);
236
        exit(10000);
237
    end;
238
}
239

Wichtige Hilfs-Codeunits

CodeunitZweck
DXP Core Token Mgt.Stellt Token-Namen für JSON-Felder bereit (GetVendorNoTok(), GetDocDateTok(), usw.)
DXP Json HelperJSON-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.