Extension Entwicklung In diesem Kapitel werden wichtige Integration Events und Beispiele aufgeführt, die bei der Entwicklung einer Extension für Breeze Interface helfen. Wichtig: Anpassungen dürfen nur durch entsprechend geschulte Berater/Entwickler durchgeführt werden. Diese befinden sich außerdem immer außerhalb des Standard-Supports durch DEXPRO. Leitfaden zur Implementierung einer individuellen Belegerstellung Überblick Diese Dokumentation bietet eine Anleitung zur Implementierung benutzerdefinierter Dokumentenerstellungsprozesse unter Verwendung des Integration Event OnDocumentCreation im DEXPRO Core-Framework. Während die Standardimplementierung Einkaufsbelege (Tablelle 38,39) erstellt, zeigt dieser Leitfaden, wie Sie Ihre eigene Dokumentenerstellungslogik mithilfe der bereitgestellten JSON-Daten implementieren können. Voraussetzungen Folgende Einstellung ist vor der Validierung eines Belegs in der Dokumentenklassen Einrichtung vorzunehmen: Nächster Prozessschritt: Breeze Interface Zielvorlage: Keine Sie haben einen Beleg mit der App DEXPRO Squeeze validiert und in den nächsten Prozessschritt Breeze Interface übertragen? Dann sehen Sie diesen jetzt im Breeze Interface in einer Wartestellung ( WaitingForRetrieval ). An diesem Punkt besteht die Möglichkeit, den validierten Beleg in einen Genehmigungsworkflow zu überführen, um diesen nach erfolgter Genehmigung zurückzumelden und in den Prozess der Zielbelegerstellung zu übergeben - weitere Information zur Breeze Interface API finden sie hier . Sobald Sie den Status des Breeze Belegs in " Processed " geändert haben, wird dieser an den DEXPRO Core zur weiteren Verarbeitung gemeldet - dies ist der Integrationspunkt , an dem Sie ansetzen müssen. Integrationspunkt Der zentrale Integrationspunkt ist das  OnDocumentCreation -Ereignis in Codeunit 70954599 “DXP Doc. Creation Def. Impl.” in der App DEXPRO Core . Dieses Ereignis wird während der Dokumentenverarbeitung ausgelöst und ermöglicht die Implementierung einer benutzerdefinierten Dokumentenerstellungslogik. Ereignis-Signatur [IntegrationEvent(false, false)] local procedure OnDocumentCreation(JObject: JsonObject; var CreatedDocumentRecordId: RecordId) Parameter JObject : Enthält die vollständigen JSON-Daten mit den Dokumentinformationen CreatedDocumentRecordId : Muss mit der RecordId des erstellten Dokuments gesetzt werden Implementierungsleitfaden Schritt 1: Subscriber-Codeunit erstellen Zunächst erstellen Sie eine Codeunit, die das OnDocumentCreation-Ereignis abonniert: codeunit 50100 "Custom Document Creation" { [EventSubscriber(ObjectType::Codeunit, Codeunit::"DXP Doc. Creation Def. Impl.", 'OnDocumentCreation', '', false, false)] local procedure OnDocumentCreation(JObject: JsonObject; var CreatedDocumentRecordId: RecordId) begin // Your implementation here CreateCustomDocument(JObject, CreatedDocumentRecordId); end; } Schritt 2: Dokumentenerstellungslogik implementieren Hier ein Beispiel für die Implementierung der benutzerdefinierten Dokumentenerstellung: local procedure CreateCustomDocument(JObject: JsonObject; var CreatedDocumentRecordId: RecordId) var CustomHeader: Record "Custom Document Header"; CustomLine: Record "Custom Document Line"; JsonHelper: Codeunit "DXP Json Helper"; HeaderJObject: JsonObject; LinesJArray: JsonArray; LineJToken: JsonToken; LineNo: Integer; begin // 1. Create Header CustomHeader.Init(); CustomHeader."Document Type" := GetDocumentType(JObject); CustomHeader."Vendor No." := CopyStr(JsonHelper.ValAsTxt(JObject, 'vendorNo', true), 1, MaxStrLen(CustomHeader."Vendor No.")); CustomHeader."Document Date" := JsonHelper.ValAsDate(JObject, 'docDate', true); CustomHeader."Posting Date" := JsonHelper.ValAsDate(JObject, 'postingDate', true); CustomHeader."Document Reference" := CopyStr(JsonHelper.ValAsTxt(JObject, 'docReference', true), 1, MaxStrLen(CustomHeader."Document Reference")); CustomHeader.Insert(true); // 2. Process Lines LinesJArray := JsonHelper.ReadJArrayFromObj(JObject, 'lines'); LineNo := 10000; foreach LineJToken in LinesJArray do begin CustomLine.Init(); CustomLine."Document No." := CustomHeader."No."; CustomLine."Line No." := LineNo; CustomLine."Item No." := CopyStr(JsonHelper.ValAsTxt(LineJToken.AsObject(), 'no', true), 1, MaxStrLen(CustomLine."Item No.")); CustomLine.Quantity := JsonHelper.ValAsDec(LineJToken.AsObject(), 'qty', true); CustomLine."Unit Price" := JsonHelper.ValAsDec(LineJToken.AsObject(), 'unitPrice', true); CustomLine.Insert(true); LineNo += 10000; end; CreatedDocumentRecordId := CustomHeader.RecordId; end; JSON-Struktur Die JSON-Eingabe folgt dieser Struktur: { "type":"Invoice", "vendorNo":"K00170", "iban":"DE58520503530052599766", "vatRegNo":"DE540026784", "barcode":"123456789", "docDate":"2023-07-13", "serviceDate":"2023-07-13", "postingDate":"2025-01-23", "postingDesc":"Die ist eine Buchungsbeschreibung.", "docReference":"R308154", "orderNo":"B23106029", "netAmount":7894.71, "netAmount2":0.0, "netAmount3":0.0, "taxRate":19.0, "taxRate2":0.0, "taxRate3":0.0, "taxAmount":1499.99, "taxAmount2":0.0, "taxAmount3":0.0, "totalAmount":9394.7, "currency":"EUR", "assignedTo":"BERND.FEDDERSEN", "note":"Die ist eine Bemerkung!", "dimensions":{ "ABTEILUNG":"PROD", "EINKÄUFER":"BF", "KOSTENTRÄGER":"IT" }, "dimensionSetID":27, "customFields":{ "docClass":"DXP Invoice / Credit Memo", "namesAndValues":[ { "name":"Customer_No", "value":"01121212" }, { "name":"Custom_Date", "value":"2025-01-26" } ] }, "orderMatchDifference":"false", "lines":[ { "orderNo":"B23106029", "orderLineNo":10000, "receiptNo":"ELIEF107004", "receiptLineNo":10000, "vendorItemNo":"", "type":"Item", "no":"90002", "description":"JogiTek G 1337 Gaming-Headset", "qty":17.0, "uom":"STÜCK", "unitPrice":99.99, "lineDisc":0.0, "netAmount":1699.83, "totalAmount":2022.8, "taxRate":19.0, "vatBusPostingGroup":"INLAND", "vatProdPostingGroup":"MWST.19", "dimensions":{ "ABTEILUNG":"PROD", "BEREICH":"30", "EINKÄUFER":"BF", "KOSTENTRÄGER":"IT", "VERKAUFSKAMPAGNE":"WINTER" }, "dimensionSetID":53, "genProdPostingGroup":"HANDEL", "genBusPostingGroup":"INLAND", "deferralCode":"", "quantityDifference":"false", "unitPriceDifference":"false", "discountDifference":"false", "receiptDateDifference":"false", "customFields":{ "docClass":"DXP Invoice / Credit Memo", "namesAndValues":[ { "name":"Custom_Field_1", "value":"Text in Zeile 10000" }, { "name":"Custom_Field_2", "value":"2025-01-31" } ] } }, { "orderNo":"B23106029", "orderLineNo":30000, "receiptNo":"ELIEF107004", "receiptLineNo":30000, "vendorItemNo":"", "type":"Item", "no":"90005", "description":"Sumsing Galaxy 23", "qty":12.0, "uom":"STÜCK", "unitPrice":349.99, "lineDisc":0.0, "netAmount":4199.88, "totalAmount":4997.86, "taxRate":19.0, "vatBusPostingGroup":"INLAND", "vatProdPostingGroup":"MWST.19", "dimensions":{ "ABTEILUNG":"PROD", "BEREICH":"50", "EINKÄUFER":"BF", "KOSTENTRÄGER":"IT", "VERKAUFSKAMPAGNE":"WINTER" }, "dimensionSetID":55, "genProdPostingGroup":"HANDEL", "genBusPostingGroup":"INLAND", "deferralCode":"", "quantityDifference":"false", "unitPriceDifference":"false", "discountDifference":"false", "receiptDateDifference":"false", "customFields":{ "docClass":"DXP Invoice / Credit Memo", "namesAndValues":[ { "name":"Custom_Field_1", "value":"Text in Zeile 20000" }, { "name":"Custom_Field_2", "value":"2025-01-30" } ] } }, { "orderNo":"B23106029", "orderLineNo":20000, "receiptNo":"ELIEF107004", "receiptLineNo":20000, "vendorItemNo":"", "type":"Item", "no":"90000", "description":"Y-Phone Ultra Pro 23", "qty":5.0, "uom":"STÜCK", "unitPrice":399.0, "lineDisc":0.0, "netAmount":1995.0, "totalAmount":2374.05, "taxRate":19.0, "vatBusPostingGroup":"INLAND", "vatProdPostingGroup":"MWST.19", "dimensions":{ "ABTEILUNG":"PROD", "BEREICH":"70", "EINKÄUFER":"BF", "KOSTENTRÄGER":"IT", "VERKÄUFER":"JH", "VERKAUFSKAMPAGNE":"WINTER" }, "dimensionSetID":58, "genProdPostingGroup":"HANDEL", "genBusPostingGroup":"INLAND", "deferralCode":"", "quantityDifference":"false", "unitPriceDifference":"false", "discountDifference":"false", "receiptDateDifference":"false", "customFields":{ "docClass":"DXP Invoice / Credit Memo", "namesAndValues":[ { "name":"Custom_Field_1", "value":"Text in Zeile 30000" }, { "name":"Custom_Field_2", "value":"2025-01-29" } ] } } ] } Best practice Fehlerbehandlung : Implementieren Sie eine ordnungsgemäße Fehlerbehandlung für JSON-Parsing und Datenbankoperationen: local procedure CreateCustomDocument(JObject: JsonObject; var CreatedDocumentRecordId: RecordId) var CustomHeader: Record "Custom Document Header"; begin if not DoesJsonHaveRequiredFields(JObject) then Error('Required fields are missing in the JSON payload'); if not TryCreateCustomHeader(JObject, CustomHeader) then Error('Failed to create document header'); // ... rest of the implementation end; Validierung : Implementieren Sie eine ordnungsgemäße Validierung vor der Dokumentenerstellung: local procedure ValidateDocument(JObject: JsonObject): Boolean var Vendor: Record Vendor; VendorNo: Code[20]; begin VendorNo := CopyStr(JsonHelper.ValAsTxt(JObject, 'vendorNo', true), 1, MaxStrLen(VendorNo)); if not Vendor.Get(VendorNo) then Error('Vendor %1 does not exist', VendorNo); // Add more validation as needed exit(true); end; Häufige Szenarien Szenario 1: Verschiedene Dokumenttypen erstellen local procedure GetDocumentType(JObject: JsonObject): Enum "Custom Document Type" var DocType: Text; begin DocType := JsonHelper.ValAsTxt(JObject, 'type', true); case DocType of 'Invoice': exit("Custom Document Type"::Invoice); 'CreditMemo': exit("Custom Document Type"::"Credit Memo"); else Error('Unknown document type: %1', DocType); end; end; Szenario 2: Benutzerdefinierte Felder verarbeiten local procedure ProcessCustomFields(JObject: JsonObject; var CustomDoc: Record "Custom Document Header") var CustomFieldsObj: JsonObject; CustomFieldsArr: JsonArray; FieldToken: JsonToken; begin if not JsonHelper.TokenExists(JObject, 'customFields') then exit; CustomFieldsObj := JsonHelper.ReadJObjectFromObj(JObject, 'customFields'); CustomFieldsArr := JsonHelper.ReadJArrayFromObj(CustomFieldsObj, 'namesAndValues'); foreach FieldToken in CustomFieldsArr do ProcessCustomField(FieldToken.AsObject(), CustomDoc); end; Fehlerbehebung RecordId ist leer : Stellen Sie sicher, dass Sie den CreatedDocumentRecordId-Parameter setzen: CreatedDocumentRecordId := CustomHeader.RecordId; JSON-Parsing-Fehler : Verwenden Sie die JsonHelper-Funktionen für sicheres Parsing: if not JsonHelper.TokenExists(JObject, 'vendorNo') then Error('Vendor number is missing in JSON'); Datenvalidierung : Implementieren Sie eine ordnungsgemäße Datenvalidierung: if JsonHelper.ValAsDec(LineJToken.AsObject(), 'qty', true) <= 0 then Error('Quantity must be greater than 0'); Eigene Zielvorlage entwickeln Diese Seite richtet sich an Entwickler und Partner, die Breeze Interface um eine eigene Zielvorlage (Target Template) erweitern wollen. Sie beschreibt am durchgehenden Beispiel „Rechnung mit Bestellbezug“, wie ein Beleg statt der voreingestellten Zielvorlage eine eigene erhält und welche Verarbeitung beim Setzen des Status Processed ausgeführt wird. Das Beispiel: Eine Rechnung kommt über Squeeze herein und wird wie gewohnt validiert. Ist im Belegkopf eine Bestellnummer eingetragen, soll nicht wie im Standard eine Einkaufsrechnung erstellt werden. Stattdessen soll die im Kopf hinterlegte Bestellung gebucht werden. Welcher Weg ist der richtige? Für eine eigene Belegerstellung gibt es zwei Wege. Sie schließen einander aus – wählen Sie vor Beginn den passenden: Eigene Zielvorlage (diese Seite) Ereignis OnDocumentCreation Einrichtung Zielvorlage Ihre eigene Zielvorlage Keine Plausibilitätsprüfung durch Breeze ja, mit den Schaltern der Zielvorlagen-Einrichtung nein Mehrere Belegarten parallel ja, eine Zielvorlage je Fall nur eine Implementierung für alle Belege Geeignet für Standard-Einkaufsbelege abweichend behandeln – etwa eine Bestellung buchen statt eine Rechnung anzulegen Belege in vollständig eigene Tabellen schreiben Wollen Sie in eigene Tabellen schreiben und benötigen die Prüfungen von Breeze nicht, folgen Sie stattdessen der Seite Leitfaden zur Implementierung einer individuellen Belegerstellung . Für das hier beschriebene Beispiel ist dieser Weg nicht geeignet: Er setzt die Zielvorlage Keine voraus und umgeht damit die Plausibilitätsprüfung, die eine Rechnung mit Bestellbezug gerade braucht. Der Standardablauf Ein Beleg durchläuft von Squeeze bis zum Zielbeleg in Business Central diese Stationen: Squeeze übergibt den Beleg an Core. Core legt einen Datensatz in der Tabelle DXP Document an. Core ermittelt über die Dokumentenklasse ( DXP Document Class ) den nächsten Prozessschritt. Steht dieser auf Breeze Interface , wird ein Breeze-Beleg ( DXP BRZ IF Document ) erzeugt. Beim Anlegen erhält der Breeze-Beleg seine Zielvorlage aus der Einrichtung der Dokumentenklasse – Feld Zielvorlage auf der Seite Dokumentenklassen-Einrichtung . Der Beleg wird im externen Workflow bearbeitet und kommt mit dem Status Processed zurück. Breeze Interface löst daraufhin die zur Zielvorlage hinterlegte Implementierung aus. Diese prüft den Beleg auf Plausibilität und übergibt ihn an Core, das den Zielbeleg erzeugt. Technisch ist die Zielvorlage ein Enum, das ein Interface implementiert: DXP Breeze IF Target Template – das Enum der Zielvorlagen, Extensible = true . DXP BRZIF IDocument Processing – das Interface, das jede Zielvorlage implementiert. Es besitzt genau eine Methode: procedure ProcessDocument(var BREEZEDoc: Record "DXP BRZ IF Document"; JObject: JsonObject) Im Auslieferungszustand sind diese Zielvorlagen enthalten: Wert Zielvorlage Implementierung 0 Keine (None) DXP BRZ Default Processing 1 Einkaufsbeleg (Purchase Document) DXP BRZ IF PInv/Crdt Memo Proc 2 Einkaufsbestellung (Purchase Order) DXP BRZ IF Purch. Order Proc. 3 Auftragsbestätigung (Order Confirmation) DXP BRZ IF Order Conf. Proc. 4 Lieferschein (Delivery Note) DXP BRZ IF Del. Note Proc. {add screenshot of Dokumentenklassen-Einrichtung here} Voraussetzungen Eine eigene Extension mit Abhängigkeit auf DEXPRO Breeze Interface (App-Id 7c431886-108e-44c3-9328-30e2b93edfa6 ) und – über propagateDependencies mitgeliefert – auf DEXPRO Core . Ein eigener Objekt-ID-Bereich. Verwenden Sie nicht den Bereich von Breeze Interface (70954950–70954969) oder Core (70954575–70954624). Ein eigener Objektnamen-Affix, den Ihre AppSourceCop.json vorschreibt. Verwenden Sie für Ihre Objekte niemals das Affix DXP . Es ist für die DEXPRO-Apps reserviert. In den Codebeispielen dieser Seite steht XYZ stellvertretend für Ihr eigenes Affix. Schritt 1 – Zielvorlage als Enum-Erweiterung anlegen Das Enum DXP Breeze IF Target Template ist erweiterbar. Legen Sie einen neuen Wert an und verweisen Sie mit Implementation auf Ihre eigene Codeunit: enumextension 50100 "XYZ Target Template Ext." extends "DXP Breeze IF Target Template" { value(50100; "XYZ Post Purchase Order") { Caption = 'Bestellung buchen'; Implementation = "DXP BRZIF IDocument Processing" = "XYZ Post Purch. Order Proc."; } } Der Enum-Wert wird auf dem Breeze-Beleg und auf dem Core-Beleg gespeichert. Ändern Sie die Nummer nach dem ersten Produktiveinsatz nicht mehr – bereits gespeicherte Belege würden sonst auf eine andere Zielvorlage zeigen. Schritt 2 – Die Standardzielvorlage überschreiben Die Zielvorlage wird beim Anlegen des Breeze-Belegs aus der Dokumentenklassen-Einrichtung gelesen. Um sie fallabhängig zu übersteuern, abonnieren Sie das Ereignis OnAfterFillDocumentInfo der Codeunit DXP BRZ IF Document Mgt. . Es wird gerufen, während der Breeze-Beleg aus dem JSON gefüllt wird – also bevor er eingefügt ist: codeunit 50100 "XYZ Breeze Doc. Subscribers" { [EventSubscriber(ObjectType::Codeunit, Codeunit::"DXP BRZ IF Document Mgt.", 'OnAfterFillDocumentInfo', '', false, false)] local procedure SetTargetTemplateOnOrderReference(var BrzIfDoc: Record "DXP BRZ IF Document"; JsonData: JsonObject) var CoreTokenMgt: Codeunit "DXP Core Token Mgt."; JsonHelper: Codeunit "DXP Json Helper"; OrderNo: Code[20]; begin // Nur Belege umleiten, die im Standard zur Einkaufsrechnung fuehren. if BrzIfDoc."Target Template" <> BrzIfDoc."Target Template"::"Purchase Document" then exit; OrderNo := CopyStr(JsonHelper.ValAsTxt(JsonData, CoreTokenMgt.GetOrderNoTok(), false), 1, MaxStrLen(OrderNo)); if OrderNo = '' then exit; BrzIfDoc."Target Template" := BrzIfDoc."Target Template"::"XYZ Post Purchase Order"; end; } Die Bestellnummer steht im Belegkopf-JSON unter dem Token orderNo . Lesen Sie sie ausschließlich über DXP Core Token Mgt. aus – die Token-Namen sind dort zentral gepflegt. Das zweite Argument von ValAsTxt ist EnsureSuccess . Übergeben Sie hier false : Die Bestellnummer ist ein optionales Feld, und bei true würde ein fehlendes Token einen Laufzeitfehler auslösen. Die Zielvorlage im Core-Beleg nachziehen Core führt im Feld Breeze Interface Zielvorlage der Tabelle DXP Document eine Kopie der Zielvorlage mit. Setzt Ihr Abonnent die Zielvorlage um, aktualisieren Sie dieses Feld mit – sonst zeigt der Core-Beleg weiter die alte Vorlage an. Ihre eigene Verarbeitung aus Schritt 3 erledigt das (siehe CoreDoc.Validate("DXP Breeze IF Target Template", ...) im Beispiel unten). Schritt 3 – Die Verarbeitung implementieren Wird der Breeze-Beleg auf Processed gesetzt, ruft Breeze Interface die zur Zielvorlage hinterlegte Implementierung auf: IDocumentProcessing := Rec."Target Template"; IDocumentProcessing.ProcessDocument(Rec, JsonHelper.JObjectFromBlob(Rec.RecordId, Rec.FieldNo(JSON))); Ihre Codeunit implementiert dafür DXP BRZIF IDocument Processing . Sie ist verantwortlich für: Prüfen des Belegs – im Fehlerfall den Breeze-Beleg auf Status Error setzen und die Meldungen in das JSON schreiben. Übergeben an Core über DXP Document Mgt.UpdateDocument , wenn der Beleg gültig ist. Löschen des Breeze-Belegs nach erfolgreicher Übergabe. Der Core-Beleg bleibt als Nachweis bestehen. codeunit 50101 "XYZ Post Purch. Order Proc." implements "DXP BRZIF IDocument Processing" { procedure ProcessDocument(var BREEZEDoc: Record "DXP BRZ IF Document"; JObject: JsonObject) var CoreDoc: Record "DXP Document"; CoreDocMgt: Codeunit "DXP Document Mgt."; ValidDoc: Boolean; begin ValidDoc := CheckDocument(JObject); if not ValidDoc then begin BREEZEDoc.Validate(Status, "DXP Breeze IF Status"::Error); BREEZEDoc.Modify(false); exit; end; CoreDocMgt.UpdateDocument( BREEZEDoc."Core Document No.", "DXP Document Status"::Transferred, "DXP Target Document Process"::"XYZ Post Purchase Order", JObject); CoreDoc.Get(BREEZEDoc."Core Document No."); CoreDoc.Validate("DXP Breeze IF Target Template", BREEZEDoc."Target Template"); CoreDoc.Modify(true); BREEZEDoc.Delete(true); end; } Als Vorlage für Plausibilitätsprüfungen dient die ausgelieferte Codeunit DXP BRZ IF PInv/Crdt Memo Proc . Nutzen Sie DXP Plausiblity Check Mgt. für die Prüfungen und DXP BRZ IF Target Templ. Mgt. , um die Einrichtung Ihrer Zielvorlage zu lesen – damit greifen die Schalter Betragsprüfung deaktivieren und Buchungsdatum nicht prüfen auch für Ihre Vorlage. Schritt 4 – Den Zielbeleg in Core erzeugen Die Zielvorlage in Breeze bestimmt welche Prüfung läuft. Welcher Zielbeleg entsteht, entscheidet ein zweites Enum in Core: DXP Target Document Process , ebenfalls Extensible = true , mit dem Interface DXP IDocument Processing . Die beiden Enums sind getrennt und müssen beide erweitert werden. Die Breeze-Zielvorlage wählt die Prüfung, der Core-Zielbelegprozess die Belegerstellung. Der Wert wird von Ihrer Verarbeitung aus Schritt 3 an UpdateDocument übergeben. enumextension 50101 "XYZ Target Doc. Process Ext." extends "DXP Target Document Process" { value(50100; "XYZ Post Purchase Order") { Caption = 'Bestellung buchen'; Implementation = "DXP IDocument Processing" = "XYZ Post P. Order Creation"; } } Das Interface DXP IDocument Processing hat eine Methode, die die RecordId des erzeugten Belegs zurückgibt. Core speichert sie im Feld Verknüpfte Datensatz-ID des Core-Belegs: procedure ProcessStandardDocument(JObject: JsonObject): RecordId Ihre Implementierung liest die Bestellnummer aus dem Kopf, holt die Bestellung und bucht sie: codeunit 50102 "XYZ Post P. Order Creation" implements "DXP IDocument Processing" { procedure ProcessStandardDocument(JObject: JsonObject): RecordId var PurchaseHeader: Record "Purchase Header"; PurchInvHeader: Record "Purch. Inv. Header"; CoreTokenMgt: Codeunit "DXP Core Token Mgt."; JsonHelper: Codeunit "DXP Json Helper"; TargetDocumentMgt: Codeunit "DXP Target Document Mgt."; PurchPost: Codeunit "Purch.-Post"; DocRecRef: RecordRef; OrderNo: Code[20]; VendorNo: Code[20]; begin OrderNo := CopyStr(JsonHelper.ValAsTxt(JObject, CoreTokenMgt.GetOrderNoTok(), true), 1, MaxStrLen(OrderNo)); VendorNo := CopyStr(JsonHelper.ValAsTxt(JObject, CoreTokenMgt.GetVendorNoTok(), true), 1, MaxStrLen(VendorNo)); PurchaseHeader.SetRange("Document Type", PurchaseHeader."Document Type"::Order); PurchaseHeader.SetRange("No.", OrderNo); PurchaseHeader.SetRange("Buy-from Vendor No.", VendorNo); PurchaseHeader.FindFirst(); // Rechnungsdaten aus dem JSON in die Bestellung uebernehmen // (Belegdatum, Buchungsdatum, Externe Belegnummer, zu berechnende Mengen) UpdatePurchaseHeaderFromJson(JObject, PurchaseHeader); PurchPost.SetSuppressCommit(true); PurchPost.Run(PurchaseHeader); DocRecRef.Get(PurchaseHeader.RecordId()); TargetDocumentMgt.SetProcessedCompletely(DocRecRef); PurchInvHeader.SetRange("Order No.", OrderNo); if PurchInvHeader.FindLast() then exit(PurchInvHeader.RecordId()); exit(PurchaseHeader.RecordId()); end; } Geben Sie niemals eine leere RecordId zurück. Core prüft den Rückgabewert; bleibt er leer, bricht die Verarbeitung mit einer Fehlermeldung ab. Vollständig verarbeitet kennzeichnen Core erweitert die Tabelle Einkaufskopf um das Feld Vollständig verarbeitet . Setzen Sie es über DXP Target Document Mgt.SetProcessedCompletely , sobald der Beleg fertig verarbeitet ist. Die ausgelieferten Implementierungen tun das ebenfalls; das Feld steuert die Anzeige und die nachgelagerte Verarbeitung. Wenn die Bestellung gebucht wird Beim Buchen entsteht ein neuer Beleg – die gebuchte Einkaufsrechnung –, während die Bestellung je nach Restmenge gelöscht wird. Verweisen Sie deshalb im Rückgabewert auf den gebuchten Beleg, wie im Beispiel oben. Zeigt Verknüpfte Datensatz-ID auf eine gelöschte Bestellung, läuft der Absprung vom Core-Beleg ins Leere. Die Einrichtung der Zielvorlage Zu jeder Zielvorlage gehört ein Datensatz in der Tabelle DXP BRZ IF Target Templ. Setup , sichtbar auf der Seite Zielvorlagen-Einrichtung . Er wird beim ersten Zugriff automatisch angelegt. Diese Schalter stehen zur Verfügung: Betragsprüfung deaktivieren – unterdrückt den Abgleich von Kopf- und Zeilenbeträgen. Buchungsdatum nicht prüfen – unterdrückt die Prüfung des Buchungsdatums. Gesperrten Kreditoren (Zahlung) ignorieren – lässt Belege gesperrter Kreditoren zu. Nicht freigeben – unterdrückt die automatische Freigabe des Zielbelegs. Sollen für Ihre Zielvorlage andere Vorgabewerte gelten als die leeren Standardwerte, setzen Sie sie beim Anlegen. Die Codeunit DXP BRZ IF Target Templ. Mgt. tut das für die ausgelieferten Vorlagen; für eigene Vorlagen abonnieren Sie den OnInsert -Trigger der Tabelle. {add screenshot of Zielvorlagen-Einrichtung here} Checkliste Schritt Objekt Zweck 1 enumextension auf DXP Breeze IF Target Template Die eigene Zielvorlage bekannt machen 2 Abonnent auf OnAfterFillDocumentInfo Die Standardzielvorlage fallabhängig überschreiben 3 codeunit implements DXP BRZIF IDocument Processing Prüfen und an Core übergeben 4 enumextension auf DXP Target Document Process Den eigenen Zielbelegprozess bekannt machen 5 codeunit implements DXP IDocument Processing Den Zielbeleg erzeugen – hier: die Bestellung buchen 6 Dokumentenklassen-Einrichtung Prüfen, dass der nächste Prozessschritt auf Breeze Interface steht Fehlersuche Beobachtung Ursache Der Beleg behält die alte Zielvorlage. Der Abonnent aus Schritt 2 greift nicht. Prüfen Sie, ob die Dokumentenklasse tatsächlich auf die Zielvorlage Einkaufsbeleg zeigt und ob das Token orderNo im Kopf-JSON gefüllt ist. Beim Setzen auf Processed läuft die Standardverarbeitung. Der Implementation -Verweis im Enum fehlt. Ohne ihn greift die DefaultImplementation des Enums, also DXP BRZ Default Processing . Meldung Document creation failed. RecordId is empty. Ihre ProcessStandardDocument -Implementierung hat keine RecordId zurückgegeben. Der Absprung vom Core-Beleg öffnet nichts. Verknüpfte Datensatz-ID zeigt auf einen gelöschten Beleg – geben Sie den gebuchten Beleg zurück, nicht die Bestellung. Der Beleg steht auf Error , ohne dass eine Meldung erscheint. Ihre Verarbeitung setzt den Status, schreibt aber keine Prüfmeldungen in das JSON. Nutzen Sie DXP Document Transfer Mgt.AddErrorsToJson . Verwandte Themen Zielvorlagen-Einrichtung – die Schalter je Zielvorlage Dokumentenklassen-Einrichtung – Zuordnung von Dokumentenklasse, nächstem Prozessschritt und Zielvorlage Breeze Belege – die Belegliste und ihre Status Leitfaden zur Implementierung einer individuellen Belegerstellung – der alternative Weg über OnDocumentCreation für eigene Belegtabellen Breeze Interface API – Rückmeldung des Belegs aus dem externen Workflow Eigene Zielvorlage entwickeln Diese Seite richtet sich an Entwickler und Partner, die Breeze Interface um eine eigene Zielvorlage (Target Template) erweitern wollen. Sie beschreibt am durchgehenden Beispiel „Rechnung mit Bestellbezug“, wie ein Beleg statt der voreingestellten Zielvorlage eine eigene erhält und welche Verarbeitung beim Setzen des Status Processed ausgeführt wird. Das Beispiel: Eine Rechnung kommt über Squeeze herein und wird wie gewohnt validiert. Ist im Belegkopf eine Bestellnummer eingetragen, soll nicht wie im Standard eine Einkaufsrechnung erstellt werden. Stattdessen soll die im Kopf hinterlegte Bestellung gebucht werden. Welcher Weg ist der richtige? Für eine eigene Belegerstellung gibt es zwei Wege. Sie schließen einander aus – wählen Sie vor Beginn den passenden:   Eigene Zielvorlage (diese Seite) Ereignis OnDocumentCreation Einrichtung Zielvorlage Ihre eigene Zielvorlage Keine Plausibilitätsprüfung durch Breeze ja, mit den Schaltern der Zielvorlagen-Einrichtung nein Mehrere Belegarten parallel ja, eine Zielvorlage je Fall nur eine Implementierung für alle Belege Geeignet für Standard-Einkaufsbelege abweichend behandeln – etwa eine Bestellung buchen statt eine Rechnung anzulegen Belege in vollständig eigene Tabellen schreiben Wollen Sie in eigene Tabellen schreiben und benötigen die Prüfungen von Breeze nicht, folgen Sie stattdessen der Seite Leitfaden zur Implementierung einer individuellen Belegerstellung . Für das hier beschriebene Beispiel ist dieser Weg nicht geeignet: Er setzt die Zielvorlage Keine voraus und umgeht damit die Plausibilitätsprüfung, die eine Rechnung mit Bestellbezug gerade braucht. Der Standardablauf Ein Beleg durchläuft von Squeeze bis zum Zielbeleg in Business Central diese Stationen: Squeeze übergibt den Beleg an Core. Core legt einen Datensatz in der Tabelle DXP Document an. Core ermittelt über die Dokumentenklasse ( DXP Document Class ) den nächsten Prozessschritt. Steht dieser auf Breeze Interface , wird ein Breeze-Beleg ( DXP BRZ IF Document ) erzeugt. Beim Anlegen erhält der Breeze-Beleg seine Zielvorlage aus der Einrichtung der Dokumentenklasse – Feld Zielvorlage auf der Seite Dokumentenklassen-Einrichtung . Der Beleg wird im externen Workflow bearbeitet und kommt mit dem Status Processed zurück. Breeze Interface löst daraufhin die zur Zielvorlage hinterlegte Implementierung aus. Diese prüft den Beleg auf Plausibilität und übergibt ihn an Core, das den Zielbeleg erzeugt. Technisch ist die Zielvorlage ein Enum, das ein Interface implementiert: DXP Breeze IF Target Template – das Enum der Zielvorlagen, Extensible = true . DXP BRZIF IDocument Processing – das Interface, das jede Zielvorlage implementiert. Es besitzt genau eine Methode: procedure ProcessDocument(var BREEZEDoc: Record "DXP BRZ IF Document"; JObject: JsonObject) Im Auslieferungszustand sind diese Zielvorlagen enthalten: Wert Zielvorlage Implementierung 0 Keine (None) DXP BRZ Default Processing 1 Einkaufsbeleg (Purchase Document) DXP BRZ IF PInv/Crdt Memo Proc 2 Einkaufsbestellung (Purchase Order) DXP BRZ IF Purch. Order Proc. 3 Auftragsbestätigung (Order Confirmation) DXP BRZ IF Order Conf. Proc. 4 Lieferschein (Delivery Note) DXP BRZ IF Del. Note Proc. {add screenshot of Dokumentenklassen-Einrichtung here} Voraussetzungen Eine eigene Extension mit Abhängigkeit auf DEXPRO Breeze Interface (App-Id 7c431886-108e-44c3-9328-30e2b93edfa6 ) und – über propagateDependencies mitgeliefert – auf DEXPRO Core . Ein eigener Objekt-ID-Bereich. Verwenden Sie nicht den Bereich von Breeze Interface (70954950–70954969) oder Core (70954575–70954624). Ein eigener Objektnamen-Affix, den Ihre AppSourceCop.json vorschreibt. Verwenden Sie für Ihre Objekte niemals das Affix DXP . Es ist für die DEXPRO-Apps reserviert. In den Codebeispielen dieser Seite steht XYZ stellvertretend für Ihr eigenes Affix. Schritt 1 – Zielvorlage als Enum-Erweiterung anlegen Das Enum DXP Breeze IF Target Template ist erweiterbar. Legen Sie einen neuen Wert an und verweisen Sie mit Implementation auf Ihre eigene Codeunit: enumextension 50100 "XYZ Target Template Ext." extends "DXP Breeze IF Target Template" { value(50100; "XYZ Post Purchase Order") { Caption = 'Bestellung buchen'; Implementation = "DXP BRZIF IDocument Processing" = "XYZ Post Purch. Order Proc."; } } Der Enum-Wert wird auf dem Breeze-Beleg und auf dem Core-Beleg gespeichert. Ändern Sie die Nummer nach dem ersten Produktiveinsatz nicht mehr – bereits gespeicherte Belege würden sonst auf eine andere Zielvorlage zeigen. Schritt 2 – Die Standardzielvorlage überschreiben Die Zielvorlage wird beim Anlegen des Breeze-Belegs aus der Dokumentenklassen-Einrichtung gelesen. Um sie fallabhängig zu übersteuern, abonnieren Sie das Ereignis OnAfterFillDocumentInfo der Codeunit DXP BRZ IF Document Mgt. . Es wird gerufen, während der Breeze-Beleg aus dem JSON gefüllt wird – also bevor er eingefügt ist: codeunit 50100 "XYZ Breeze Doc. Subscribers" { [EventSubscriber(ObjectType::Codeunit, Codeunit::"DXP BRZ IF Document Mgt.", 'OnAfterFillDocumentInfo', '', false, false)] local procedure SetTargetTemplateOnOrderReference(var BrzIfDoc: Record "DXP BRZ IF Document"; JsonData: JsonObject) var CoreTokenMgt: Codeunit "DXP Core Token Mgt."; JsonHelper: Codeunit "DXP Json Helper"; OrderNo: Code[20]; begin // Nur Belege umleiten, die im Standard zur Einkaufsrechnung fuehren. if BrzIfDoc."Target Template" <> BrzIfDoc."Target Template"::"Purchase Document" then exit; OrderNo := CopyStr(JsonHelper.ValAsTxt(JsonData, CoreTokenMgt.GetOrderNoTok(), false), 1, MaxStrLen(OrderNo)); if OrderNo = '' then exit; BrzIfDoc."Target Template" := BrzIfDoc."Target Template"::"XYZ Post Purchase Order"; end; } Die Bestellnummer steht im Belegkopf-JSON unter dem Token orderNo . Lesen Sie sie ausschließlich über DXP Core Token Mgt. aus – die Token-Namen sind dort zentral gepflegt. Das zweite Argument von ValAsTxt ist EnsureSuccess . Übergeben Sie hier false : Die Bestellnummer ist ein optionales Feld, und bei true würde ein fehlendes Token einen Laufzeitfehler auslösen. Die Zielvorlage im Core-Beleg nachziehen Core führt im Feld Breeze Interface Zielvorlage der Tabelle DXP Document eine Kopie der Zielvorlage mit. Setzt Ihr Abonnent die Zielvorlage um, aktualisieren Sie dieses Feld mit – sonst zeigt der Core-Beleg weiter die alte Vorlage an. Ihre eigene Verarbeitung aus Schritt 3 erledigt das (siehe CoreDoc.Validate("DXP Breeze IF Target Template", ...) im Beispiel unten). Schritt 3 – Die Verarbeitung implementieren Wird der Breeze-Beleg auf Processed gesetzt, ruft Breeze Interface die zur Zielvorlage hinterlegte Implementierung auf: IDocumentProcessing := Rec."Target Template"; IDocumentProcessing.ProcessDocument(Rec, JsonHelper.JObjectFromBlob(Rec.RecordId, Rec.FieldNo(JSON))); Ihre Codeunit implementiert dafür DXP BRZIF IDocument Processing . Sie ist verantwortlich für: Prüfen des Belegs – im Fehlerfall den Breeze-Beleg auf Status Error setzen und die Meldungen in das JSON schreiben. Übergeben an Core über DXP Document Mgt.UpdateDocument , wenn der Beleg gültig ist. Löschen des Breeze-Belegs nach erfolgreicher Übergabe. Der Core-Beleg bleibt als Nachweis bestehen. codeunit 50101 "XYZ Post Purch. Order Proc." implements "DXP BRZIF IDocument Processing" { procedure ProcessDocument(var BREEZEDoc: Record "DXP BRZ IF Document"; JObject: JsonObject) var CoreDoc: Record "DXP Document"; CoreDocMgt: Codeunit "DXP Document Mgt."; ValidDoc: Boolean; begin ValidDoc := CheckDocument(JObject); if not ValidDoc then begin BREEZEDoc.Validate(Status, "DXP Breeze IF Status"::Error); BREEZEDoc.Modify(false); exit; end; CoreDocMgt.UpdateDocument( BREEZEDoc."Core Document No.", "DXP Document Status"::Transferred, "DXP Target Document Process"::"XYZ Post Purchase Order", JObject); CoreDoc.Get(BREEZEDoc."Core Document No."); CoreDoc.Validate("DXP Breeze IF Target Template", BREEZEDoc."Target Template"); CoreDoc.Modify(true); BREEZEDoc.Delete(true); end; } Als Vorlage für Plausibilitätsprüfungen dient die ausgelieferte Codeunit DXP BRZ IF PInv/Crdt Memo Proc . Nutzen Sie DXP Plausiblity Check Mgt. für die Prüfungen und DXP BRZ IF Target Templ. Mgt. , um die Einrichtung Ihrer Zielvorlage zu lesen – damit greifen die Schalter Betragsprüfung deaktivieren und Buchungsdatum nicht prüfen auch für Ihre Vorlage. Schritt 4 – Den Zielbeleg in Core erzeugen Die Zielvorlage in Breeze bestimmt welche Prüfung läuft. Welcher Zielbeleg entsteht, entscheidet ein zweites Enum in Core: DXP Target Document Process , ebenfalls Extensible = true , mit dem Interface DXP IDocument Processing . Die beiden Enums sind getrennt und müssen beide erweitert werden. Die Breeze-Zielvorlage wählt die Prüfung, der Core-Zielbelegprozess die Belegerstellung. Der Wert wird von Ihrer Verarbeitung aus Schritt 3 an UpdateDocument übergeben. enumextension 50101 "XYZ Target Doc. Process Ext." extends "DXP Target Document Process" { value(50100; "XYZ Post Purchase Order") { Caption = 'Bestellung buchen'; Implementation = "DXP IDocument Processing" = "XYZ Post P. Order Creation"; } } Das Interface DXP IDocument Processing hat eine Methode, die die RecordId des erzeugten Belegs zurückgibt. Core speichert sie im Feld Verknüpfte Datensatz-ID des Core-Belegs: procedure ProcessStandardDocument(JObject: JsonObject): RecordId Ihre Implementierung liest die Bestellnummer aus dem Kopf, holt die Bestellung und bucht sie: codeunit 50102 "XYZ Post P. Order Creation" implements "DXP IDocument Processing" { procedure ProcessStandardDocument(JObject: JsonObject): RecordId var PurchaseHeader: Record "Purchase Header"; PurchInvHeader: Record "Purch. Inv. Header"; CoreTokenMgt: Codeunit "DXP Core Token Mgt."; JsonHelper: Codeunit "DXP Json Helper"; TargetDocumentMgt: Codeunit "DXP Target Document Mgt."; PurchPost: Codeunit "Purch.-Post"; DocRecRef: RecordRef; OrderNo: Code[20]; VendorNo: Code[20]; begin OrderNo := CopyStr(JsonHelper.ValAsTxt(JObject, CoreTokenMgt.GetOrderNoTok(), true), 1, MaxStrLen(OrderNo)); VendorNo := CopyStr(JsonHelper.ValAsTxt(JObject, CoreTokenMgt.GetVendorNoTok(), true), 1, MaxStrLen(VendorNo)); PurchaseHeader.SetRange("Document Type", PurchaseHeader."Document Type"::Order); PurchaseHeader.SetRange("No.", OrderNo); PurchaseHeader.SetRange("Buy-from Vendor No.", VendorNo); PurchaseHeader.FindFirst(); // Rechnungsdaten aus dem JSON in die Bestellung uebernehmen // (Belegdatum, Buchungsdatum, Externe Belegnummer, zu berechnende Mengen) UpdatePurchaseHeaderFromJson(JObject, PurchaseHeader); PurchPost.SetSuppressCommit(true); PurchPost.Run(PurchaseHeader); DocRecRef.Get(PurchaseHeader.RecordId()); TargetDocumentMgt.SetProcessedCompletely(DocRecRef); PurchInvHeader.SetRange("Order No.", OrderNo); if PurchInvHeader.FindLast() then exit(PurchInvHeader.RecordId()); exit(PurchaseHeader.RecordId()); end; } Geben Sie niemals eine leere RecordId zurück. Core prüft den Rückgabewert; bleibt er leer, bricht die Verarbeitung mit einer Fehlermeldung ab. Vollständig verarbeitet kennzeichnen Core erweitert die Tabelle Einkaufskopf um das Feld Vollständig verarbeitet . Setzen Sie es über DXP Target Document Mgt.SetProcessedCompletely , sobald der Beleg fertig verarbeitet ist. Die ausgelieferten Implementierungen tun das ebenfalls; das Feld steuert die Anzeige und die nachgelagerte Verarbeitung. Wenn die Bestellung gebucht wird Beim Buchen entsteht ein neuer Beleg – die gebuchte Einkaufsrechnung –, während die Bestellung je nach Restmenge gelöscht wird. Verweisen Sie deshalb im Rückgabewert auf den gebuchten Beleg, wie im Beispiel oben. Zeigt Verknüpfte Datensatz-ID auf eine gelöschte Bestellung, läuft der Absprung vom Core-Beleg ins Leere. Die Einrichtung der Zielvorlage Zu jeder Zielvorlage gehört ein Datensatz in der Tabelle DXP BRZ IF Target Templ. Setup , sichtbar auf der Seite Zielvorlagen-Einrichtung . Er wird beim ersten Zugriff automatisch angelegt. Diese Schalter stehen zur Verfügung: Betragsprüfung deaktivieren – unterdrückt den Abgleich von Kopf- und Zeilenbeträgen. Buchungsdatum nicht prüfen – unterdrückt die Prüfung des Buchungsdatums. Gesperrten Kreditoren (Zahlung) ignorieren – lässt Belege gesperrter Kreditoren zu. Nicht freigeben – unterdrückt die automatische Freigabe des Zielbelegs. Sollen für Ihre Zielvorlage andere Vorgabewerte gelten als die leeren Standardwerte, setzen Sie sie beim Anlegen. Die Codeunit DXP BRZ IF Target Templ. Mgt. tut das für die ausgelieferten Vorlagen; für eigene Vorlagen abonnieren Sie den OnInsert -Trigger der Tabelle. {add screenshot of Zielvorlagen-Einrichtung here} Checkliste Schritt Objekt Zweck 1 enumextension auf DXP Breeze IF Target Template Die eigene Zielvorlage bekannt machen 2 Abonnent auf OnAfterFillDocumentInfo Die Standardzielvorlage fallabhängig überschreiben 3 codeunit implements DXP BRZIF IDocument Processing Prüfen und an Core übergeben 4 enumextension auf DXP Target Document Process Den eigenen Zielbelegprozess bekannt machen 5 codeunit implements DXP IDocument Processing Den Zielbeleg erzeugen – hier: die Bestellung buchen 6 Dokumentenklassen-Einrichtung Prüfen, dass der nächste Prozessschritt auf Breeze Interface steht Fehlersuche Beobachtung Ursache Der Beleg behält die alte Zielvorlage. Der Abonnent aus Schritt 2 greift nicht. Prüfen Sie, ob die Dokumentenklasse tatsächlich auf die Zielvorlage Einkaufsbeleg zeigt und ob das Token orderNo im Kopf-JSON gefüllt ist. Beim Setzen auf Processed läuft die Standardverarbeitung. Der Implementation -Verweis im Enum fehlt. Ohne ihn greift die DefaultImplementation des Enums, also DXP BRZ Default Processing . Meldung Document creation failed. RecordId is empty. Ihre ProcessStandardDocument -Implementierung hat keine RecordId zurückgegeben. Der Absprung vom Core-Beleg öffnet nichts. Verknüpfte Datensatz-ID zeigt auf einen gelöschten Beleg – geben Sie den gebuchten Beleg zurück, nicht die Bestellung. Der Beleg steht auf Error , ohne dass eine Meldung erscheint. Ihre Verarbeitung setzt den Status, schreibt aber keine Prüfmeldungen in das JSON. Nutzen Sie DXP Document Transfer Mgt.AddErrorsToJson .