Skip to main content

Implementation of a new, generic document class WIP

SQUEEZE → Core → Your processing

This guide is deliberately focused and pipeline-oriented: It shows how document data is downloaded from SQUEEZE, processed in queues, and persisted in DEXPRO Core—and where a third-party extension comes into play.

Term: In this guide, “SQUEEZE app” refers to the Business Central extension that is connected to the SQUEEZE API and controls imports to DEXPRO Core.

Assumptions:

  • Third-party developers typically work with published icons, not with the entire source code.
  • The SQUEEZE app handles downloading + import queue + creation of the core document as well as the rudimentary creation of the SQUEEZE document, taking field mapping into account, as soon as setup is complete.
  • Your extension takes over the business logic once the core document exists: create or extend source records, validate them, generate “Processed JSON,” and optionally create your own target object or document.

1) The end-to-end process SQUEEZE → Core

Once the document class and setup are configured, the SQUEEZE app's import pipeline typically runs as follows:

  1. DXP Document Class extend and bind your implementation: DXP ISource Document.
  2. Configure settings for this Document class‑Setup.
    • When validating DXP SQZ Document Class ID, the SQUEEZE app automatically retrieves the required setup values from SQUEEZE (e.g., DXP SQZ Export Interface ID and DXP SQZ Line Table ID) and fills them in.
  3. The SQUEEZE app job calls DXP SQZ API Mgt.DownloadDocumentData(). Can also be called via “Download documents”.
    • New entries are created in the DXP SQZ import queue (document ID + target client).
  4. The SQUEEZE app calls DXP SQZ Import Queue Mgt.TransferImportQueueEntries().
    • The orchestration code unit DXP Sqz Create Document is executed for the entries in the import queue.
  5. DXP SQZ API Mgt.CreateCoreAndSQUEEZEDocument(DocClass, DocumentId) loads the complete JSON.
    • DXP Document Mgt.AddDocument(DocClass, DocumentId, RawJson, …) is called up.
    • Result: A DXP Document‑Record exists. Status = ImportedJSON Raw is filled.
  6. The SQUEEZE app calls your implementation ISourceDocument.CreateSource(Document).
    • Your implementation begins here.

After step 6, you will have:

  • A Core‑Container‑Document (DXP Document).
  • A SQUEEZE document in the SQUEEZE app.
  • Your source records should exist once CreateSource has completed successfully.

2) What you implement (Interfaces)

Source contract, i.e., squeeze page: DXP ISource Document

You implement this to:

  • Create your editable source data sets from DXP Document.JSON Raw
  • Validate source data records
  • Generate “Processed JSON” and transfer it to core processing

Target contract, i.e., target document: DXP IDocument Processing

You implement this if Core is to create/update its own target data set (your own table/document) from the “Processed JSON.”


3) Schritt 1 — Dokumentklasse definieren und Quell‑Implementierung zuweisen

Erstellen Sie eine Enum‑Extension und binden Sie Ihre Source‑Implementierung:

enumextension 50100 "MY Document Class Ext." extends "DXP Document Class"
{
    value(50100; "MY Generic Document")
    {
        Caption = 'My Generic Document';
        Implementation = "DXP ISource Document" = "MY Source Document Impl.";
    }
}

4) Schritt 2 — Dokumentenklassen‑Einrichtung konfigurieren (damit die Importwarteschlange gefüllt wird)

Konfigurieren Sie in der Dokumentenklassen‑Einrichtung für Ihre neue Dokumentklasse die SQUEEZE‑spezifischen Felder.

Wichtig:

  • Das Validieren von DXP SQZ Document Class ID triggert das automatische Laden der erforderlichen Konfiguration aus SQUEEZE (z. B. DXP SQZ Export Interface ID und DXP SQZ Line Table ID).
  • Falls der Abruf fehlschlägt, werden diese API‑abgeleiteten Felder wieder geleert.

Ergebnis: Die SQUEEZE‑App kann DownloadDocumentData() ausführen und Einträge in DXP SQZ Import Queue für Ihre Klasse anlegen.


5) Schritt 3 — Feldzuordnung sicherstellen (heruntergeladene Zuordnung + Ihre Standardwerte)

Die SQUEEZE‑App kann den Feldkatalog des angeschlossenen Squeeze Mandanten herunterladen und in Core‑Mapping‑Tabellen persistieren (pro Dokumentklasse).

Ihre Rolle als Dokumentklassen‑Implementierer:

  • Standard‑Feldzuordnung „Squeeze Field Name → AL Field No.“ via InitFieldMapping(...) bereitstellen.
  • Alternativ Felder über die Zuordnungs‑Pages mappen (Benutzerdefinierte Feldzuordnung).

5.1 InitFieldMapping implementieren (eigenständiges Beispiel)

codeunit 50101 "MY Source Document Impl." implements "DXP ISource Document"
{
    procedure InitFieldMapping(var HeaderMapping: Dictionary of [Text, Integer]; var LineMapping: Dictionary of [Text, Integer])
    var
        Header: Record "DXP SQZ Document Header";
        Line: Record "DXP SQZ Document Line";
    begin
        Clear(HeaderMapping);
        Clear(LineMapping);

        // Header fields
        HeaderMapping.Add('vendorNo', Header.FieldNo("Buy-from Vendor No."));
        HeaderMapping.Add('documentDate', Header.FieldNo("Document Date"));
        HeaderMapping.Add('postingDate', Header.FieldNo("Posting Date"));
        HeaderMapping.Add('reference', Header.FieldNo("Document Reference"));

        // Line fields
        LineMapping.Add('description', Line.FieldNo(Description));
        LineMapping.Add('quantity', Line.FieldNo(Quantity));
        LineMapping.Add('unitPrice', Line.FieldNo("Direct Unit Cost"));
    end;

    // other interface methods omitted here...
}

5.2 Validierungs‑Schalter beim Zuweisen von Feldern: App‑Feld validieren

Wenn die SQUEEZE‑App oder Ihre Quell‑Erstelllogik JSON‑Werte in „App-/Quellfelder“ schreibt, können Sie steuern, ob der OnValidate‑Trigger ausgeführt wird.

In DXP Custom Field Mapping:

  • Validate App Field = App‑Feld während des Assignments validieren.
  • Validate (Ziel) = Validierung beim Transfer in das Ziel‑Feld ausführen.

Nutzen Sie das z. B. wenn Erweiterungsfelder Business‑Logik in OnValidate haben oder wenn Sie bewusst trigger‑frei (performanter) zuweisen wollen.


6) Schritt 4 — Source‑Datensätze in CreateSource erstellen

CreateSource(Document) wird von der SQUEEZE‑App unmittelbar nach DXP Document Mgt.AddDocument(...) aufgerufen, sobald der Core‑Container‑Datensatz existiert.

Minimale Verantwortlichkeiten:

  1. DXP Document.JSON Raw lesen.
  2. SQUEEZE‑Source‑Header/Lines erstellen (DXP SQZ Document Header / DXP SQZ Document Line).
  3. Core‑Dokument mit dem SQUEEZE‑Source‑Header via UpdateDocumentAfterTransfer verknüpfen.

Optional (empfohlen, wenn Sie Gleichwertigkeit der Funktionen zu den Standard‑SQUEEZE‑Dokumentklassen möchten): 4) Anhänge herunterladen, wenn DXP Download Attachments aktiv ist. 5) Automatische Validierung ausführen, wenn für das Dokument aktiviert.

6.1 Beispiel: SQUEEZE‑Header + Lines mit den generischen Helpern erstellen

Was die Helper machen (damit Sie es nicht doppelt implementieren):

  • SqzDocumentMgt.CreateSQUEEZEDocHeader(Document, DocHeader, RawJson)

    • Erstellt genau einen Datensatz in DXP SQZ Document Header für das Core‑Dokument (DXP Document).
    • Setzt Identifikationsfelder wie Core Document No. und die SQUEEZE‑API Document ID.
    • Weist Header‑Felder aus JSON anhand der konfigurierten Zuordnungen zu:
      • DXP Field Mapping (Feldzuordnung)
      • DXP Custom Field Mapping (Benutzerdefinierte Feldzuordnung)
    • Respektiert DXP Custom Field Mapping.Validate App Field und entscheidet damit, ob beim Zuweisen der OnValidate‑Trigger ausgeführt wird.
    • Führt das Integration‑Event OnBeforeModifySQUEEZEDocumentHeader(...) als Extension‑Hook aus.
    • Für die eingebauten Standard‑SQUEEZE‑Klassen wird zusätzlich Standard‑Post‑Processing ausgeführt (z. B. alternative IBAN/USt‑ID‑Ermittlung, Lieferantenaktualisierung, Standard‑Buchungsdatum, Duplikatsprüfung).
  • SqzDocumentMgt.CreateSQUEEZEDocLine(DocHeader, RawJson)

    • Erstellt die DXP SQZ Document Line‑Datensätze basierend auf den JSON‑Rows.
    • Weist Line‑Felder nach dem gleichen Mapping‑Prinzip zu (Standard + Custom; optional mit Validierungstriggern).
    • Befüllt Core Document No. auf den Lines.
    • Für Standard‑SQUEEZE‑Klassen wird zusätzlich Standard‑Line‑Post‑Processing angewendet (z. B. Übertragung erkannter Werte).
    • Persistiert line‑bezogene Hilfsdaten (z. B. Koordinaten/Metadaten, die UI und Matching‑Logik nutzen).
procedure CreateSource(Document: Record "DXP Document"): Boolean
var
    DocClassSetup: Record "DXP Document Class Setup";
    AutoValidationMgt: Codeunit "DXP Automatic Validation Mgt.";
    JsonHelper: Codeunit "DXP Json Helper";
    DocMgt: Codeunit "DXP Document Mgt.";
    SqzDocumentMgt: Codeunit "DXP SQZ Document Mgt.";
    SqzApiMgt: Codeunit "DXP SQZ API Mgt.";
    RawJson: JsonObject;
    DocHeader: Record "DXP SQZ Document Header";
    AutomaticValidation: Boolean;
begin
    if (not Document."JSON Raw".HasValue()) or (Document.Status <> Document.Status::Imported) then
        exit(false);

    // 1) Unverarbeitetes JSON lesen
    RawJson := JsonHelper.JObjectFromBlob(Document.RecordId(), Document.FieldNo("JSON Raw"));

    // 2) SQUEEZE‑Source‑Dokument mit den generischen Hilfsfunktionen erstellen
    //    (Feldzuordnung + optionales Validierungs‑Trigger‑Verhalten wird im Helper behandelt)
    SqzDocumentMgt.CreateSQUEEZEDocHeader(Document, DocHeader, RawJson);
    SqzDocumentMgt.CreateSQUEEZEDocLine(DocHeader, RawJson);

    // 3) Core → SQUEEZE‑Source‑Header verknüpfen
    DocMgt.UpdateDocumentAfterTransfer(Document."No.", Document.Status::Transferred, DocHeader."API Document ID", DocHeader.RecordId());

    // 4) Optional: Anhänge herunterladen und speichern
    //    (gesteuert über die Dokumentenklassen‑Einrichtung)
    if DocClassSetup.Get(DocHeader."Document Class") then
        if DocClassSetup."DXP Download Attachments" then
            SqzApiMgt.DownloadAndSaveAttachments(DocHeader);

    // 5) Optional: Automatische Validierung
    //    Wenn aktiviert, führt die Validierung die Plausibilitätsprüfungen aus und erstellt das „Processed JSON“.
    AutomaticValidation := AutoValidationMgt.AutomaticValidationActivated(DocHeader."No.");
    if AutomaticValidation then
        SqzDocumentMgt.ValidateSQUEEZEDocument(DocHeader, AutomaticValidation);

    exit(true);
end;

7) Schritt 5 — Validierung + Übergabe „Processed JSON“

7.1 Validierung: IsSourceDataPlausible

Nutzen Sie das Core‑Plausibility‑Framework, damit Fehler konsistent angezeigt werden:

procedure IsSourceDataPlausible(Source: Variant; var TempPlausibilityCheckEntry: Record "DXP Plausibility Check Entry" temporary): Boolean
var
    PlausibilityCheck: Codeunit "DXP Plausiblity Check Mgt.";
    Header: Record "DXP SQZ Document Header";
begin
    if not Source.IsRecord then
        exit(false);

    Header := Source;
    TempPlausibilityCheckEntry.DeleteAll();

    PlausibilityCheck.FieldIsEmpty(Header, Header.FieldNo("Buy-from Vendor No."), 0, false, false);
    PlausibilityCheck.CheckPostingDate(Header."Posting Date", Header.FieldNo("Posting Date"));
    PlausibilityCheck.GetPlausibilityCheckEntries(TempPlausibilityCheckEntry);

    exit(TempPlausibilityCheckEntry.IsEmpty());
end;

7.2 „Processed JSON“ bauen und an Core übergeben: CreateProcessedJsonFromSource

„Processed JSON“ wird von Core in DXP Document.JSON Processed gespeichert und an Ihre Zielverarbeitung übergeben.

procedure CreateProcessedJsonFromSource(Source: Variant)
var
    CoreTokenMgt: Codeunit "DXP Core Token Mgt.";
    DocMgt: Codeunit "DXP Document Mgt.";
    JsonHelper: Codeunit "DXP Json Helper";
    DocTransferMgt: Codeunit "DXP Document Transfer Mgt.";
    Header: Record "DXP SQZ Document Header";
    Line: Record "DXP SQZ Document Line";
    HeaderJObj: JsonObject;
    LinesJArr: JsonArray;
    LineJObj: JsonObject;
begin
    Header := Source;

    HeaderJObj.Add(CoreTokenMgt.GetVendorNoTok(), Header."Buy-from Vendor No.");
    HeaderJObj.Add(CoreTokenMgt.GetDocDateTok(), Header."Document Date");
    HeaderJObj.Add(CoreTokenMgt.GetDocReferenceTok(), Header."Document Reference");

    // Benutzerdefinierte Felder (Erweiterungsfelder) aus den Source‑Tabellen hinzufügen
    HeaderJObj.Add(CoreTokenMgt.GetCustomFieldsTok(), JsonHelper.Rec2JsonFieldArray(Header, true, true, Header."Document Class", Enum::"DXP Field Type"::Header));

    Line.SetRange("Document No.", Header."No.");
    if Line.FindSet() then
        repeat
            Clear(LineJObj);
            LineJObj.Add(CoreTokenMgt.GetDescriptionTok(), Line.Description);
            LineJObj.Add(CoreTokenMgt.GetQtyTok(), Line.Quantity);
            LineJObj.Add(CoreTokenMgt.GetCustomFieldsTok(), JsonHelper.Rec2JsonFieldArray(Line, true, true, Header."Document Class", Enum::"DXP Field Type"::Line));
            LinesJArr.Add(LineJObj);
        until Line.Next() = 0;

    DocTransferMgt.AddDocLinesToHeaderJson(HeaderJObj, LinesJArr);

    // An Core‑Zielverarbeitung übergeben
    DocMgt.UpdateDocument(
        Header."Core Document No.",
        Enum::"DXP Document Status"::Transferred,
        Enum::"DXP Target Document Process"::"MY Create Custom Document",
        Enum::"DXP Next process step"::"Standard document",
        HeaderJObj);
end;

8) Schritt 6 — Eigenes Ziel‑Dokument erstellen

Wenn Core Ihren eigenen Ziel‑Datensatz erstellen soll, erweitern Sie DXP Target Document Process und implementieren Sie DXP IDocument Processing:

enumextension 50102 "MY Target Doc Process Ext." extends "DXP Target Document Process"
{
    value(50102; "MY Create Custom Document")
    {
        Caption = 'Create My Custom Document';
        Implementation = "DXP IDocument Processing" = "MY Custom Doc Creation";
    }
}

codeunit 50103 "MY Custom Doc Creation" implements "DXP IDocument Processing"
{
    procedure ProcessStandardDocument(JObject: JsonObject): RecordId
    var
        CoreTokenMgt: Codeunit "DXP Core Token Mgt.";
        DocTransferMgt: Codeunit "DXP Document Transfer Mgt.";
        JsonHelper: Codeunit "DXP Json Helper";
        MyDoc: Record "MY Custom Document";
        TargetVariant: Variant;
    begin
        MyDoc.Init();
        MyDoc.Validate("Vendor No.", JsonHelper.ValAsTxt(JObject, CoreTokenMgt.GetVendorNoTok(), false));
        MyDoc.Validate("Document Date", JsonHelper.ValAsDate(JObject, CoreTokenMgt.GetDocDateTok(), false));
        MyDoc.Insert(true);

        // Überträgt Metadaten sowie benutzerdefinierte/zusätzliche Felder anhand der Core‑Zuordnungen in den Ziel‑Datensatz.
        TargetVariant := MyDoc;
        DocTransferMgt.ProcessAdditionalInformation(JObject, TargetVariant);
        MyDoc := TargetVariant;

        exit(MyDoc.RecordId());
    end;
}

Hinweis: Die Übertragung von Metadaten sowie benutzerdefinierten/zusätzlichen Feldern ist im obigen Beispiel bereits enthalten und basiert auf den Core‑Zuordnungstabellen.


9) Fehlerbehebung (schnelle Prüfungen)

  • Keine Einträge in DXP SQZ Import Queue → Dokumentenklassen‑Einrichtung für SQUEEZE ist unvollständig.
  • Core‑DXP Document existiert, aber kein Source‑Datensatz → Ihr CreateSource(Document) ist fehlgeschlagen oder gibt false zurück.
  • Source existiert, aber Ziel wird nicht erstellt → CreateProcessedJsonFromSource wird nicht aufgerufen oder DXP Target Document Process ist nicht konfiguriert.
  • Falsches Trigger‑Verhalten beim Zuweisen → DXP Custom Field Mapping.Validate App Field und Validate prüfen.
    • Validate steuert, ob die Validierung des Ziel‑Feldes (OnValidate) ausgeführt wird.

10) Was Core automatisch macht

Wenn Sie DXP Document Mgt.UpdateDocument(...) aufrufen:

  • Core speichert das „Processed JSON“ in DXP Document.JSON Processed.
  • Core setzt DXP Document.Status auf den übergebenen Status.
  • Core verknüpft/aktualisiert DXP Document.Linked-to Record Id mit dem Datensatz, den Ihre DXP IDocument Processing‑Implementierung zurückgibt.
  • Core überträgt Core‑Attachments auf den zurückgegebenen Datensatz (und hängt typischerweise das „Processed JSON“ als .json‑Attachment an).