# Extension Entwicklung

In diesem Kapitel werden Anwendungsfälle aufgeführt, die bei der Entwicklung einer Extension für Freeze for BC helfen. Dies sind in der Regel Beispiele, die einer Notwendigkeit im Produktivbetrieb eines Partners oder Kunden entwachsen sind.  
  
Wichtig:  
Anpassungen dürfen nur durch entsprechend geschulte Berater/Entwickler durchgeführt werden. Diese befinden sich außerdem immer außerhalb des Standard-Supports.

# Archivieren von Dateien

Freeze archiviert, abhängig von der Einrichtung, in verschiedenen Situationen automatisch (z.B. nach dem Buchen von Verkaufs- oder Einkaufsbelegen). Im Folgenden wird erläutert, welche Prozeduren ein Entwickler aufrufen kann, um das Archivieren einer Mappe inkl. Dateianhänge zu initiieren.

Angenommen, Sie möchten eine neue Archivmappe mit einer oder mehreren Datei(en) archivieren.

Zunächst fügen Sie dem temporären Record des Typs "DXP FRZ Attachment Buffer" die entsprechenden Werte hinzu, die ihren Dateien entsprechend.

Zur Vereinfachung empfehlen wir die Nutzung der folgenden Prozedur:

**codeunit 70954897 "DXP FRZ Attachment Mgt.":**

```PASCAL
procedure AddFileFromStream(var TempFRZAttachmentBuffer: Record "DXP FRZ Attachment Buffer" temporary, var InStr: InStream, FileName: Text)
```

Anschließend rufen sie die folgende Prozedur auf, um einen Eintrag in der Freeze Verarbeitungswarteschlange zu erstellen, der Mappendetails und Anhänge enthält, die zur Erstellung des Archiveintrags verwendet werden.

<div id="bkmrk-codeunit-70954899-%22d"><div>**codeunit 70954899 "DXP Freeze Queue Buffer Mgt.":**</div><div></div></div>```PASCAL
procedure InsertQueueBufferEntry(RecSystemID: Guid, RecTableNo: Integer, var FRZAttachmentBufferTemp: Record "DXP FRZ Attachment Buffer" temporary, ArchiveRecordID: Guid, FrzRecordType: Text[35], ReferenceDate: Date, RecordTitle: Text[250]): Boolean
```

Die Prozeduren sind überladen - wählen Sie die passende Variante für ihr Szenario aus.

Sofern Sie eine eigene Logik implementieren möchten, um nach der Buchung eines Verkaufs- oder Einkaufsbelegs eine Archivierung zu initiieren, deaktivieren Sie die automatische Archivierung in der Freeze Einrichtung, um unerwünschte Redundanzen zu vermeiden.

# Anhang Download als Zip

Diese Dokumentation bietet umfassende Anleitung für externe AL-Entwickler zur effektiven Nutzung der DXP Freeze Anhang-Download-Funktionalität in ihren Business Central Erweiterungen.

## Überblick

Die DXP Freeze Result Management Codeunit bietet zwei Hauptmethoden zum Herunterladen archivierter Anhänge als ZIP-Dateien:

<div id="bkmrk-downloadattachmentsa"><div><div><div><div><div><div></div><div>1. **`DownloadAttachmentsAsZipWithPagination`** - Lädt Anhänge aus Suchanfrage-Ergebnissen mit Paginierung herunter
2. **`DownloadAttachmentsAsZipFromRecord`** - Lädt Anhänge von spezifischen Business Central Datensätzen herunter

</div><div></div></div></div></div></div></div></div>Beide Methoden organisieren Anhänge in strukturierte ZIP-Archive mit umfassenden Metadaten für Audit- und Nachverfolgungszwecke.

## Methode 1: DownloadAttachmentsAsZipWithPagination

### Zweck

Lädt alle Anhänge aus einem Suchanfrage-Ergebnissatz herunter und verarbeitet Ergebnisse seitenweise, um große Datensätze effizient zu handhaben. Die Anhänge jedes Datensatzes werden in individuelle ZIP-Dateien innerhalb eines Haupt-ZIP-Archivs organisiert.

### Verfügbare Überladungen

#### 1. Grundlegende Verwendung

<div id="bkmrk-procedure-downloadat"><div><div><div><div><div><div></div><div>```al
procedure DownloadAttachmentsAsZipWithPagination(SearchQuery: Text; var ZipArchive: Codeunit "Data Compression"; var AttachmentCount: Integer): Boolean

```

</div><div></div></div></div></div></div></div></div>#### 2. Mit Filtern

<div id="bkmrk-procedure-downloadat-0"><div><div><div><div><div><div></div><div>```al
procedure DownloadAttachmentsAsZipWithPagination(SearchQuery: Text; var ZipArchive: Codeunit "Data Compression"; var AttachmentCount: Integer; FilenameFilter: Text; FileExtensionFilter: Text): Boolean

```

</div><div></div></div></div></div></div></div></div>#### 3. Vollständige Kontrolle

<div id="bkmrk-procedure-downloadat-1"><div><div><div><div><div><div></div><div>```al
procedure DownloadAttachmentsAsZipWithPagination(SearchQuery: Text; var ZipArchive: Codeunit "Data Compression"; var AttachmentCount: Integer; FilenameFilter: Text; FileExtensionFilter: Text; StoreApiLink: Text; RecordsPerPage: Integer; SuppressDialog: Boolean): Boolean

```

</div><div></div></div></div></div></div></div></div>#### 4. Vorab gefüllte Datensätze (Erweitert)

<div id="bkmrk-procedure-downloadat-2"><div><div><div><div><div><div></div><div>```al
procedure DownloadAttachmentsAsZipWithPagination(var TempFrzResultQueryHeader: Record "DXP FRZ Query Result Header" temporary; var TempFrzResultRecordHeader: Record "DXP FRZ Record Result Header" temporary; var TempFrzResultRecordField: Record "DXP FRZ Result Record-Field" temporary; var TempFrzAttachmentResult: Record "DXP FRZ Attachment Result" temporary; SearchQuery: Text; var ZipArchive: Codeunit "Data Compression"; var AttachmentCount: Integer; FilenameFilter: Text; FileExtensionFilter: Text; SuppressDialog: Boolean): Boolean

```

</div><div></div></div></div></div></div></div></div>### Parameter

<div id="bkmrk-parameter-typ-beschr"><div><div><div><div><div><div></div><div><div><table><thead><tr><th>Parameter</th><th>Typ</th><th>Beschreibung</th></tr></thead><tbody><tr><td>`SearchQuery`</td><td>Text</td><td>Freeze-Suchanfrage-String. Wenn leer in vorab gefüllter Überladung, verwendet vorhandene Anfrage oder führt Suche erneut aus</td></tr><tr><td>`ZipArchive`</td><td>Codeunit “Data Compression”</td><td>ZIP-Archiv-Objekt, das die heruntergeladenen Dateien enthalten wird</td></tr><tr><td>`AttachmentCount`</td><td>Integer (var)</td><td>Gibt die Gesamtanzahl der heruntergeladenen Anhänge zurück</td></tr><tr><td>`FilenameFilter`</td><td>Text</td><td>Filter für Anhangsdateinamen (z.B. ‘*.pdf’, 'rechnung*’)</td></tr><tr><td>`FileExtensionFilter`</td><td>Text</td><td>Filter für Dateierweiterungen (z.B. ‘pdf’, ‘docx’)</td></tr><tr><td>`StoreApiLink`</td><td>Text</td><td>Optional spezifischer Store-API-Link</td></tr><tr><td>`RecordsPerPage`</td><td>Integer</td><td>Anzahl Datensätze pro Seite (Standard: 100)</td></tr><tr><td>`SuppressDialog`</td><td>Boolean</td><td>Ob Fortschrittsdialog unterdrückt werden soll</td></tr></tbody></table>

</div></div><div></div></div></div></div></div></div></div>### Rückgabewert

<div id="bkmrk-boolean%3A%C2%A0true%C2%A0wenn-a"><div><div><div><div><div><div></div><div>- `Boolean`: `true` wenn Anhänge gefunden und heruntergeladen wurden; `false` andernfalls

</div><div></div></div></div></div></div></div></div>### Verwendungsbeispiele

#### Grundlegender Download

<div id="bkmrk-procedure-downloadse"><div><div><div><div><div><div></div><div>```al
procedure DownloadSearchResults()
var
    ResultMgt: Codeunit "DXP FRZ Result Mgt.";
    ZipArchive: Codeunit "Data Compression";
    FileMgt: Codeunit "File Management";
    TempBlob: Codeunit "Temp Blob";
    AttachmentCount: Integer;
    InStr: InStream;
    OutStr: OutStream;
    SearchQuery: Text;
begin
    SearchQuery := 'rechnung AND 2024';
    
    if ResultMgt.DownloadAttachmentsAsZipWithPagination(SearchQuery, ZipArchive, AttachmentCount) then begin
        // ZIP in Datei speichern
        TempBlob.CreateOutStream(OutStr);
        ZipArchive.SaveZipArchive(OutStr);
        TempBlob.CreateInStream(InStr);
        
        FileMgt.DownloadFromStreamHandler(InStr, '', '', '', 'Suchergebnisse.zip');
        Message('Erfolgreich %1 Anhänge heruntergeladen.', AttachmentCount);
    end else
        Message('Keine Anhänge für die Suchanfrage gefunden.');
end;

```

</div><div></div></div></div></div></div></div></div>#### Mit Filtern

<div id="bkmrk-procedure-downloadpd"><div><div><div><div><div><div></div><div>```al
procedure DownloadPDFInvoices()
var
    ResultMgt: Codeunit "DXP FRZ Result Mgt.";
    ZipArchive: Codeunit "Data Compression";
    AttachmentCount: Integer;
    SearchQuery: Text;
begin
    SearchQuery := 'typ:rechnung';
    
    if ResultMgt.DownloadAttachmentsAsZipWithPagination(
        SearchQuery, 
        ZipArchive, 
        AttachmentCount, 
        '*.pdf', // Nur PDF-Dateien
        'pdf'    // Dateierweiterungsfilter
    ) then begin
        // ZIP-Archiv verarbeiten
        ProcessDownloadedFiles(ZipArchive, AttachmentCount);
    end;
end;

```

</div><div></div></div></div></div></div></div></div>#### Verwendung vorab gefüllter Datensätze

<div id="bkmrk-procedure-downloadfr"><div><div><div><div><div><div></div><div>```al
procedure DownloadFromExistingResults()
var
    ResultMgt: Codeunit "DXP FRZ Result Mgt.";
    TempFrzResultQueryHeader: Record "DXP FRZ Query Result Header" temporary;
    TempFrzResultRecordHeader: Record "DXP FRZ Record Result Header" temporary;
    TempFrzResultRecordField: Record "DXP FRZ Result Record-Field" temporary;
    TempFrzAttachmentResult: Record "DXP FRZ Attachment Result" temporary;
    ZipArchive: Codeunit "Data Compression";
    AttachmentCount: Integer;
begin
    // Angenommen, diese Datensätze sind bereits aus einer vorherigen Suche gefüllt
    PopulateSearchResults(TempFrzResultQueryHeader, TempFrzResultRecordHeader, TempFrzResultRecordField, TempFrzAttachmentResult);
    
    // Download mit vorhandenen Ergebnissen ohne erneute Suchausführung
    if ResultMgt.DownloadAttachmentsAsZipWithPagination(
        TempFrzResultQueryHeader,
        TempFrzResultRecordHeader,
        TempFrzResultRecordField,
        TempFrzAttachmentResult,
        'rechnungssuche', // SearchQuery - wenn leer, wird Suche erneut ausgeführt
        ZipArchive,
        AttachmentCount,
        '',     // Kein Dateinamenfilter
        '',     // Kein Erweiterungsfilter
        true    // Dialog unterdrücken
    ) then begin
        ProcessDownloadedFiles(ZipArchive, AttachmentCount);
    end;
end;

```

</div><div></div></div></div></div></div></div></div>### ZIP-Struktur (Paginierung)

<div id="bkmrk-suchergebnisse.zip-%E2%94%9C"><div><div><div><div><div><div></div><div>```
Suchergebnisse.zip
├── export-metadata.json
├── Rechnung_001_V1_20241201_1430.zip
│   ├── {GUID}_rechnung.pdf
│   └── {GUID}_begleitdokument.docx
├── Bestellung_002_V2_20241202_0900.zip
│   └── {GUID}_bestelldokument.pdf
└── Vertrag_003_V1_20241203_1200.zip
    ├── {GUID}_vertrag.pdf
    └── {GUID}_nachtrag.pdf

```

</div><div></div></div></div></div></div></div></div>## Methode 2: DownloadAttachmentsAsZipFromRecord

### Zweck

Lädt Anhänge von spezifischen Business Central Datensätzen herunter. Die Anhänge jedes ausgewählten Datensatzes werden in individuelle ZIP-Dateien innerhalb eines Haupt-ZIP-Archivs organisiert.

### Verfügbare Überladungen

#### 1. Grundlegende Verwendung

<div id="bkmrk-procedure-downloadat-3"><div><div><div><div><div><div></div><div>```al
procedure DownloadAttachmentsAsZipFromRecord(var SelectedRecord: RecordRef; var ZipArchive: Codeunit "Data Compression"): Boolean

```

</div><div></div></div></div></div></div></div></div>#### 2. Mit Filtern

<div id="bkmrk-procedure-downloadat-4"><div><div><div><div><div><div></div><div>```al
procedure DownloadAttachmentsAsZipFromRecord(var SelectedRecord: RecordRef; var ZipArchive: Codeunit "Data Compression"; FilenameFilter: Text; FileExtensionFilter: Text): Boolean

```

</div><div></div></div></div></div></div></div></div>### Parameter

<div id="bkmrk-parameter-typ-beschr-0"><div><div><div><div><div><div></div><div><div><table><thead><tr><th>Parameter</th><th>Typ</th><th>Beschreibung</th></tr></thead><tbody><tr><td>`SelectedRecord`</td><td>RecordRef (var)</td><td>RecordRef mit den ausgewählten Business Central Datensätzen</td></tr><tr><td>`ZipArchive`</td><td>Codeunit “Data Compression”</td><td>ZIP-Archiv-Objekt, das die heruntergeladenen Dateien enthalten wird</td></tr><tr><td>`FilenameFilter`</td><td>Text</td><td>Filter für Anhangsdateinamen</td></tr><tr><td>`FileExtensionFilter`</td><td>Text</td><td>Filter für Dateierweiterungen</td></tr></tbody></table>

</div></div><div></div></div></div></div></div></div></div>### Rückgabewert

<div id="bkmrk-boolean%3A%C2%A0true%C2%A0wenn-a-0"><div><div><div><div><div><div></div><div>- `Boolean`: `true` wenn Anhänge gefunden und heruntergeladen wurden; `false` andernfalls

</div><div></div></div></div></div></div></div></div>### Verwendungsbeispiele

#### Download von Verkaufsrechnungen

<div id="bkmrk-procedure-downloadin"><div><div><div><div><div><div></div><div>```al
procedure DownloadInvoiceAttachments()
var
    SalesInvoiceHeader: Record "Sales Invoice Header";
    ResultMgt: Codeunit "DXP FRZ Result Mgt.";
    ZipArchive: Codeunit "Data Compression";
    RecordRef: RecordRef;
    HasAttachments: Boolean;
begin
    // Spezifische Rechnungen auswählen
    SalesInvoiceHeader.SetRange("Posting Date", DMY2Date(1, 1, 2024), DMY2Date(31, 12, 2024));
    SalesInvoiceHeader.SetFilter("Sell-to Customer No.", '10000|20000');
    
    if SalesInvoiceHeader.FindSet() then begin
        RecordRef.GetTable(SalesInvoiceHeader);
        
        HasAttachments := ResultMgt.DownloadAttachmentsAsZipFromRecord(RecordRef, ZipArchive);
        
        if HasAttachments then
            SaveZipFile(ZipArchive, 'Rechnungsanhänge.zip')
        else
            Message('Keine Anhänge für die ausgewählten Rechnungen gefunden.');
    end;
end;

```

</div><div></div></div></div></div></div></div></div>#### Download mit Filtern von Seite

<div id="bkmrk-%2F%2F-in-einer-seitener"><div><div><div><div><div><div></div><div>```al
// In einer Seitenerweiterung
action(DownloadAttachmentsFiltered)
{
    Caption = 'Gefilterte Anhänge herunterladen';
    Image = ExportFile;
    
    trigger OnAction()
    var
        ResultMgt: Codeunit "DXP FRZ Result Mgt.";
        ZipArchive: Codeunit "Data Compression";
        RecordRef: RecordRef;
        FilenameFilter: Text;
        FileExtensionFilter: Text;
    begin
        // Filterdialog anzeigen
        if ShowFilterDialog(FilenameFilter, FileExtensionFilter) then begin
            CurrPage.SetSelectionFilter(Rec);
            RecordRef.GetTable(Rec);
            
            if ResultMgt.DownloadAttachmentsAsZipFromRecord(
                RecordRef, 
                ZipArchive, 
                FilenameFilter, 
                FileExtensionFilter
            ) then
                DownloadZipFile(ZipArchive, 'GefilterteAnhänge.zip');
        end;
    end;
}

```

</div><div></div></div></div></div></div></div></div>### ZIP-Struktur (Datensätze)

<div id="bkmrk-datensatzanh%C3%A4nge.zip"><div><div><div><div><div><div></div><div>```
Datensatzanhänge.zip
├── export-metadata.json
├── Sales_Invoice_Header_Unternehmen_SI-001.zip
│   ├── {GUID}_rechnung.pdf
│   └── {GUID}_bedingungen.pdf
├── Sales_Invoice_Header_Unternehmen_SI-002.zip
│   └── {GUID}_rechnung.pdf
└── Sales_Invoice_Header_Unternehmen_SI-003.zip
    ├── {GUID}_rechnung.pdf
    ├── {GUID}_lieferschein.pdf
    └── {GUID}_quittung.jpg

```

</div><div></div></div></div></div></div></div></div>## Metadaten-Struktur

Beide Methoden generieren umfassende Metadaten in `export-metadata.json`:

### Paginierungs-Export-Metadaten

<div id="bkmrk-%7B-%22exportinfo%22%3A-%7B-%22e"><div><div><div><div><div><div></div><div>```json
<span class="token punctuation">{</span>
  <span class="token string">"exportInfo"</span><span class="token punctuation">:</span> <span class="token punctuation">{</span>
    <span class="token string">"exportTimestamp"</span><span class="token punctuation">:</span> <span class="token string">"2024-12-19T10:13:52.248Z"</span><span class="token punctuation">,</span>
    <span class="token string">"exportedBy"</span><span class="token punctuation">:</span> <span class="token string">"USER001"</span><span class="token punctuation">,</span>
    <span class="token string">"searchQuery"</span><span class="token punctuation">:</span> <span class="token string">"typ:rechnung AND jahr:2024"</span><span class="token punctuation">,</span>
    <span class="token string">"totalRecordsFound"</span><span class="token punctuation">:</span> <span class="token number">150</span><span class="token punctuation">,</span>
    <span class="token string">"totalPages"</span><span class="token punctuation">:</span> <span class="token number">15</span><span class="token punctuation">,</span>
    <span class="token string">"exportType"</span><span class="token punctuation">:</span> <span class="token string">"paginated-search"</span><span class="token punctuation">,</span>
    <span class="token string">"description"</span><span class="token punctuation">:</span> <span class="token string">"Freeze Suchanfrage Export"</span>
  <span class="token punctuation">}</span><span class="token punctuation">,</span>
  <span class="token string">"appliedFilters"</span><span class="token punctuation">:</span> <span class="token punctuation">{</span>
    <span class="token string">"filenameFilter"</span><span class="token punctuation">:</span> <span class="token string">"*.pdf"</span><span class="token punctuation">,</span>
    <span class="token string">"fileExtensionFilter"</span><span class="token punctuation">:</span> <span class="token string">"pdf"</span>
  <span class="token punctuation">}</span><span class="token punctuation">,</span>
  <span class="token string">"statistics"</span><span class="token punctuation">:</span> <span class="token punctuation">{</span>
    <span class="token string">"pagesProcessed"</span><span class="token punctuation">:</span> <span class="token number">15</span><span class="token punctuation">,</span>
    <span class="token string">"totalRecordsProcessed"</span><span class="token punctuation">:</span> <span class="token number">150</span><span class="token punctuation">,</span>
    <span class="token string">"recordsWithAttachments"</span><span class="token punctuation">:</span> <span class="token number">120</span><span class="token punctuation">,</span>
    <span class="token string">"recordsWithoutAttachments"</span><span class="token punctuation">:</span> <span class="token number">30</span><span class="token punctuation">,</span>
    <span class="token string">"totalAttachments"</span><span class="token punctuation">:</span> <span class="token number">245</span><span class="token punctuation">,</span>
    <span class="token string">"exportCompletedAt"</span><span class="token punctuation">:</span> <span class="token string">"2024-12-19T10:15:33.021Z"</span>
  <span class="token punctuation">}</span><span class="token punctuation">,</span>
  <span class="token string">"records"</span><span class="token punctuation">:</span> <span class="token punctuation">[</span>
    <span class="token punctuation">{</span>
      <span class="token string">"recordId"</span><span class="token punctuation">:</span> <span class="token string">"{GUID}"</span><span class="token punctuation">,</span>
      <span class="token string">"title"</span><span class="token punctuation">:</span> <span class="token string">"Rechnung REG-2024-001"</span><span class="token punctuation">,</span>
      <span class="token string">"version"</span><span class="token punctuation">:</span> <span class="token number">1</span><span class="token punctuation">,</span>
      <span class="token string">"archivedAt"</span><span class="token punctuation">:</span> <span class="token string">"2024-12-01T09:30:00Z"</span><span class="token punctuation">,</span>
      <span class="token string">"archivedBy"</span><span class="token punctuation">:</span> <span class="token string">"SYSTEM"</span><span class="token punctuation">,</span>
      <span class="token string">"type"</span><span class="token punctuation">:</span> <span class="token string">"Verkaufsrechnung"</span><span class="token punctuation">,</span>
      <span class="token string">"masterId"</span><span class="token punctuation">:</span> <span class="token string">"{GUID}"</span><span class="token punctuation">,</span>
      <span class="token string">"attachmentCount"</span><span class="token punctuation">:</span> <span class="token number">3</span><span class="token punctuation">,</span>
      <span class="token string">"hasAttachments"</span><span class="token punctuation">:</span> <span class="token boolean">true</span><span class="token punctuation">,</span>
      <span class="token string">"zipFile"</span><span class="token punctuation">:</span> <span class="token string">"Rechnung_REG-2024-001_V1_20241201_0930.zip"</span>
    <span class="token punctuation">}</span>
  <span class="token punctuation">]</span>
<span class="token punctuation">}</span>

```

</div><div></div></div></div></div></div></div></div>### Datensatz-Export-Metadaten

<div id="bkmrk-%7B-%22exporttimestamp%22%3A"><div><div><div><div><div><div></div><div>```json
<span class="token punctuation">{</span>
  <span class="token string">"exportTimestamp"</span><span class="token punctuation">:</span> <span class="token string">"2024-12-19T14:30:00Z"</span><span class="token punctuation">,</span>
  <span class="token string">"exportedBy"</span><span class="token punctuation">:</span> <span class="token string">"USER001"</span><span class="token punctuation">,</span>
  <span class="token string">"totalRecordsProcessed"</span><span class="token punctuation">:</span> <span class="token number">25</span><span class="token punctuation">,</span>
  <span class="token string">"description"</span><span class="token punctuation">:</span> <span class="token string">"DXP Freeze Anhänge Export"</span><span class="token punctuation">,</span>
  <span class="token string">"sourceTable"</span><span class="token punctuation">:</span> <span class="token punctuation">{</span>
    <span class="token string">"tableNumber"</span><span class="token punctuation">:</span> <span class="token number">112</span><span class="token punctuation">,</span>
    <span class="token string">"tableName"</span><span class="token punctuation">:</span> <span class="token string">"Sales Invoice Header"</span><span class="token punctuation">,</span>
    <span class="token string">"tableCaption"</span><span class="token punctuation">:</span> <span class="token string">"Geb. Verkaufsrechnung"</span>
  <span class="token punctuation">}</span><span class="token punctuation">,</span>
  <span class="token string">"appliedFilters"</span><span class="token punctuation">:</span> <span class="token punctuation">{</span>
    <span class="token string">"filenameFilter"</span><span class="token punctuation">:</span> <span class="token string">"*.pdf"</span><span class="token punctuation">,</span>
    <span class="token string">"fileExtensionFilter"</span><span class="token punctuation">:</span> <span class="token string">"pdf"</span>
  <span class="token punctuation">}</span><span class="token punctuation">,</span>
  <span class="token string">"statistics"</span><span class="token punctuation">:</span> <span class="token punctuation">{</span>
    <span class="token string">"totalAttachments"</span><span class="token punctuation">:</span> <span class="token number">45</span><span class="token punctuation">,</span>
    <span class="token string">"recordsWithAttachments"</span><span class="token punctuation">:</span> <span class="token number">20</span><span class="token punctuation">,</span>
    <span class="token string">"recordsWithoutAttachments"</span><span class="token punctuation">:</span> <span class="token number">5</span><span class="token punctuation">,</span>
    <span class="token string">"totalZipFiles"</span><span class="token punctuation">:</span> <span class="token number">20</span>
  <span class="token punctuation">}</span><span class="token punctuation">,</span>
  <span class="token string">"records"</span><span class="token punctuation">:</span> <span class="token punctuation">[</span>
    <span class="token punctuation">{</span>
      <span class="token string">"recordId"</span><span class="token punctuation">:</span> <span class="token string">"Sales Invoice Header: Unternehmen, SI-001"</span><span class="token punctuation">,</span>
      <span class="token string">"systemId"</span><span class="token punctuation">:</span> <span class="token string">"{GUID}"</span><span class="token punctuation">,</span>
      <span class="token string">"primaryKey"</span><span class="token punctuation">:</span> <span class="token punctuation">{</span>
        <span class="token string">"fields"</span><span class="token punctuation">:</span> <span class="token punctuation">[</span>
          <span class="token punctuation">{</span>
            <span class="token string">"fieldName"</span><span class="token punctuation">:</span> <span class="token string">"Nr."</span><span class="token punctuation">,</span>
            <span class="token string">"fieldValue"</span><span class="token punctuation">:</span> <span class="token string">"SI-001"</span><span class="token punctuation">,</span>
            <span class="token string">"fieldType"</span><span class="token punctuation">:</span> <span class="token string">"Code"</span>
          <span class="token punctuation">}</span>
        <span class="token punctuation">]</span>
      <span class="token punctuation">}</span><span class="token punctuation">,</span>
      <span class="token string">"hasAttachments"</span><span class="token punctuation">:</span> <span class="token boolean">true</span><span class="token punctuation">,</span>
      <span class="token string">"attachmentCount"</span><span class="token punctuation">:</span> <span class="token number">2</span><span class="token punctuation">,</span>
      <span class="token string">"zipFile"</span><span class="token punctuation">:</span> <span class="token string">"Sales_Invoice_Header_Unternehmen_SI-001.zip"</span>
    <span class="token punctuation">}</span>
  <span class="token punctuation">]</span>
<span class="token punctuation">}</span>

```

</div><div></div></div></div></div></div></div></div>## Leistungsüberlegungen

### Paginierungsmethode

<div id="bkmrk-gro%C3%9Fe-ergebnismengen"><div><div><div><div><div><div></div><div>- **Große Ergebnismengen**: Automatische Handhabung der Paginierung zur effizienten Verarbeitung großer Datensätze
- **Speicherverwaltung**: Verarbeitet eine Seite nach der anderen, bereinigt Speicher zwischen Seiten
- **Fortschrittsverfolgung**: Zeigt Echtzeit-Fortschritt für länger dauernde Operationen
- **Empfohlen für**: Suchanfragen, die Hunderte oder Tausende von Datensätzen zurückgeben können

</div><div></div></div></div></div></div></div></div>### Datensatzmethode

<div id="bkmrk-ausgew%C3%A4hlte-datens%C3%A4t"><div><div><div><div><div><div></div><div>- **Ausgewählte Datensätze**: Verarbeitet nur die spezifisch ausgewählten Datensätze
- **Direkte Verarbeitung**: Kein Paginierungs-Overhead für kleinere Datensätze
- **Stapelverarbeitung**: Effizient für die Verarbeitung spezifischer Datensatzmengen
- **Empfohlen für**: Gezielte Downloads von spezifischen Business Central Datensätzen

</div><div></div></div></div></div></div></div></div>## Fehlerbehandlung

Beide Methoden beinhalten umfassende Fehlerbehandlung:

### Häufige Szenarien

<div id="bkmrk-keine-ergebnisse-gef"><div><div><div><div><div><div></div><div>- **Keine Ergebnisse gefunden**: Gibt `false` zurück, wenn keine Datensätze oder Anhänge gefunden werden
- **Berechtigungsprobleme**: Schließt automatisch Datensätze aus, auf die der Benutzer nicht zugreifen kann
- **API-Fehler**: Zuverlässiger Umgang mit API-Kommunikationsfehlern
- **Leere Filter**: Behandelt leere oder ungültige Filterparameter

</div><div></div></div></div></div></div></div></div>### Best Practices

<div id="bkmrk-%2F%2F-r%C3%BCckgabewert-imme"><div><div><div><div><div><div></div><div>```al
// Rückgabewert immer prüfen
if not ResultMgt.DownloadAttachmentsAsZipWithPagination(SearchQuery, ZipArchive, AttachmentCount) then begin
    Message('Keine Anhänge gefunden oder Download fehlgeschlagen.');
    exit;
end;

// Anhanganzahl validieren
if AttachmentCount = 0 then begin
    Message('Suche abgeschlossen, aber keine Anhänge entsprechen den Kriterien.');
    exit;
end;

// Große Downloads handhaben
if AttachmentCount > 1000 then
    if not Confirm('Dies wird %1 Anhänge herunterladen. Fortfahren?', false, AttachmentCount) then
        exit;

```

</div><div></div></div></div></div></div></div></div>## Integrationsereignisse

Beide Methoden unterstützen Integrationsereignisse zur Anpassung:

### Verfügbare Ereignisse

<div id="bkmrk-onbeforedownloadatta"><div><div><div><div><div><div></div><div>- `OnBeforeDownloadAttachmentsAsZip`: Verhalten vor Download-Start modifizieren
- `OnBeforeProcessAttachmentForZip`: Einzelne Anhänge überspringen oder modifizieren
- `OnAfterGetAttachmentBase64`: Anhanginhalt nach Abruf modifizieren
- `OnAfterAddAttachmentToZip`: Aktionen nach Hinzufügung zum ZIP durchführen
- `OnNoAttachmentsFound`: Szenario ohne Anhänge behandeln

</div><div></div></div></div></div></div></div></div>### Integrationsbeispiel

<div id="bkmrk-%5Beventsubscriber%28obj"><div><div><div><div><div><div></div><div>```al
[EventSubscriber(ObjectType::Codeunit, Codeunit::"DXP FRZ Result Mgt.", 'OnBeforeProcessAttachmentForZip', '', false, false)]
local procedure OnBeforeProcessAttachmentForZip(var TempFrzAttachmentResult: Record "DXP FRZ Attachment Result" temporary; var IsHandled: Boolean)
begin
    // Anhänge größer als 10MB überspringen
    if TempFrzAttachmentResult.Filesize > 10485760 then
        IsHandled := true;
end;

```

</div><div></div></div></div></div></div></div></div>## Dateinamen-Konventionen

### Automatische Bereinigung

Alle Dateinamen werden automatisch mit der `SanitizeFileName`-Methode bereinigt:

<div id="bkmrk-ung%C3%BCltige-zeichen-%28%3C"><div><div><div><div><div><div></div><div>- Ungültige Zeichen (`< > : " / \ | ? *`) werden durch Unterstriche ersetzt
- Leerzeichen werden durch Unterstriche ersetzt
- Maximale Dateinamenlängen werden erzwungen

</div><div></div></div></div></div></div></div></div>### Eindeutige Benennung

<div id="bkmrk-einzelne-dateien%3A-en"><div><div><div><div><div></div><div>- **Einzelne Dateien**: Enthalten Anhang-GUID-Präfix zur Eindeutigkeit
- **ZIP-Dateien**: Enthalten Datensatzinformationen und Zeitstempel
- **Keine Konflikte**: Garantiert eindeutige Namen innerhalb jedes ZIP-Archivs

</div></div></div></div></div></div>

# Freeze Integration in benutzerdefinierte Seiten

## Überblick

Die DEXPRO Freeze Extension bietet Dokumentenarchivierungs-Funktionalität, die es Benutzern ermöglicht, Dokumente, Anhänge und zugehörige Daten für jeden Business Central-Datensatz zu speichern und abzurufen. Dieser Leitfaden erklärt, wie Sie die Freeze-Funktionalität in benutzerdefinierte Seiten integrieren.

## Kernkomponenten

### 1. Quick Freeze Infobox

Die zentrale Komponente ist die **"DXP FRZ Quick Freeze FB"** Seite, die folgendes bietet:

- Drag-and-Drop Datei-Upload-Funktionalität
- Anzeige archivierter Datensätze bezogen auf den aktuellen Datensatz
- Schnellzugriff auf Freeze-Operationen

### 2. Management Codeunits

- **"DXP Extension Mgt."** - Verwaltet Freeze-Warteschlangen-Operationen und Hintergrundverarbeitung
- **"DXP Freeze Search Mgt."** - Verwaltet Suchfunktionalität für archivierte Datensätze
- **"DXP FRZ Result Mgt."** - Verwaltet Ergebnisanzeige und Archiv-Ansicht

### 3. Wichtige Tabellen

- **"DXP Frz Recent Record"** - Speichert aktuelle Freeze-Einträge
- **"DXP Freeze Setup"** - Konfigurationseinstellungen
- **"DXP Freeze Record Type"** - Verfügbare Datensatztypen für die Archivierung

## Implementierungsschritte

### Schritt 1: Infobox hinzufügen

Fügen Sie die Quick Freeze Infobox zum Layout-Bereich Ihrer Seite hinzu:

```PASCAL
layout
{
    addfirst(factboxes)
    {
        part("DXP Quick Archive"; "DXP FRZ Quick Freeze FB")
        {
            ApplicationArea = All;
            SubPageLink = "Record System ID" = field(SystemId),
                         "Record Table No." = const(Database::"Ihr Tabellenname");
            SubPageView = sorting("Entry No.") order(descending);
        }
    }
}

```

**Wichtige Hinweise:**

- Ersetzen Sie `Database::"Ihr Tabellenname"` mit der entsprechenden Tabellenkonstante

### Schritt 2: Navigationsaktionen hinzufügen

Fügen Sie Freeze-bezogene Aktionen zum Aktionsbereich der Seite hinzu:

```PASCAL
actions
{
    addfirst(navigation)
    {
        action("DXP Freeze")
        {
            Caption = 'Freeze öffnen';
            ApplicationArea = All;
            Image = Archive;
            ToolTip = 'Öffnet das Freeze-Archiv und zeigt mit dem ausgewählten Datensatz verknüpfte Anhänge an.';
            ShortcutKey = 'Ctrl+Alt+F';
            trigger OnAction()
            var
                ResultMgt: Codeunit "DXP FRZ Result Mgt.";
            begin
                ResultMgt.SubmitSearchAndShowResults(Rec.RecordId(), FrzSearchMgt.GetSearchCombinationForTable(Rec.SystemId, Rec.RecordId.TableNo()));
            end;
        }
    }
}

```

### Schritt 3: Hervorgehobene Aktionen hinzufügen

```PASCAL
actions
{
    addfirst(Category_Category16)
    {
        actionref("DXP Freeze_Promoted"; "DXP Freeze")
        {
        }
    }
    modify(Category_Category16)
    {
        Caption = 'DEXPRO Freeze', Locked = true;
    }
}

```

### Schritt 4: Seiten-Trigger hinzufügen

```PASCAL
trigger OnAfterGetCurrRecord()
begin
    CurrPage."DXP Quick Archive".Page.SetSourceRecord(Rec.RecordId());
end;

```

### Schritt 5: Variablen deklarieren

```PASCAL
var
    ExtMgt: Codeunit "DXP Extension Mgt.";
    FrzSearchMgt: Codeunit "DXP Freeze Search Mgt.";

```

## Vollständiges Beispiel

Hier ist ein vollständiges Beispiel für eine benutzerdefinierte Belegseite:

```PASCAL
pageextension 50000 "Mein benutzerdef. Beleg Erw." extends "Mein benutzerdefinierter Beleg"
{
    layout
    {
        addfirst(factboxes)
        {
            part("DXP Quick Archive"; "DXP FRZ Quick Freeze FB")
            {
                ApplicationArea = All;
                SubPageLink = "Record System ID" = field(SystemId),
                         "Record Table No." = const(Database::"Ihr Tabellenname");
                SubPageView = sorting("Entry No.") order(descending);
            }
        }
    }
    
    actions
    {
        addfirst(navigation)
        {
            action("DXP Freeze")
            {
                Caption = 'Freeze öffnen';
                ApplicationArea = All;
                Image = Archive;
                ToolTip = 'Öffnet das Freeze-Archiv und zeigt mit dem ausgewählten Datensatz verknüpfte Anhänge an.';
                ShortcutKey = 'Ctrl+Alt+F';
                trigger OnAction()
                var
                    ResultMgt: Codeunit "DXP FRZ Result Mgt.";
                begin
                    ResultMgt.SubmitSearchAndShowResults(Rec.RecordId(), FrzSearchMgt.GetSearchCombinationForTable(Rec.SystemId, Rec.RecordId.TableNo()));
                end;
            }
        }
        
        addfirst(Category_Category16)
        {
            actionref("DXP Freeze_Promoted"; "DXP Freeze")
            {
            }
        }
        modify(Category_Category16)
        {
            Caption = 'DEXPRO Freeze', Locked = true;
        }
    }

    trigger OnAfterGetCurrRecord()
    begin
        CurrPage."DXP Quick Archive".Page.SetSourceRecord(Rec.RecordId());
    end;

    var
        ExtMgt: Codeunit "DXP Extension Mgt.";
        FrzSearchMgt: Codeunit "DXP Freeze Search Mgt.";
}

```

## Zusätzliche Suchabfragen einrichten

Um auch Archiveinträge anhand von nicht direkt über die System-ID/Tabellennummer verbundenen Datensätzen (via BC archiviert) anzeigen zu können, können zusätzliche Suchabfragen eingerichtet werden. Dies ist hier dokumentiert: [https://docs.squeeze.one/books/freeze-for-dynamics-365-bc-de-de/page/zusatzliche-suchabfragen](https://docs.squeeze.one/books/freeze-for-dynamics-365-bc-de-de/page/zusatzliche-suchabfragen)

## Testen Ihrer Implementierung

1. Navigieren Sie zu Ihrer erweiterten Seite
2. Überprüfen Sie, ob die Quick Freeze Infobox erscheint
3. Testen Sie die Aktion "Freeze öffnen" (Strg+Alt+F)
4. Laden Sie Testdateien per Drag-and-Drop hoch
5. Überprüfen Sie, ob archivierte Datensätze in der Infobox erscheinen

Dieses Integrationsmuster gewährleistet konsistente Freeze-Funktionalität über alle Business Central-Seiten hinweg und behält dabei die von der DEXPRO Freeze Extension etablierte Benutzererfahrung bei.

# Anhang Download

Diese Dokumentation bietet umfassende Anleitung für externe AL-Entwickler zum Abrufen und Herunterladen einzelner Anhänge aus DXP Freeze für verknüpfte Business Central Datensätze.

## Überblick

Die DXP Freeze Result Management Codeunit bietet Methoden zum direkten Zugriff auf einzelne Anhänge archivierter Datensätze:

1. **`GetAttachments`** - Lädt Metadaten aller Anhänge eines Datensatzes
2. **`GetSpecificRecordAttachment`** - Lädt den Inhalt eines spezifischen Anhangs als Base64
3. Kombinierte Verwendung ermöglicht gezielte Filterung und Download einzelner Dateien

Diese Methoden ermöglichen präzise Kontrolle über welche Anhänge abgerufen werden und wie sie verarbeitet werden, ohne ZIP-Archive zu erstellen.

---

## Methode 1: GetAttachments

### Zweck

Ruft Metadaten aller Anhänge eines archivierten Datensatzes ab. Diese Methode füllt einen temporären `DXP FRZ Attachment Result` Record mit Informationen über verfügbare Anhänge ohne den eigentlichen Dateiinhalt herunterzuladen.

### Verfügbare Überladungen

#### 1. Standard-Verwendung

```al
procedure GetAttachments(RecordHeaderID: Guid; RecordHeaderFileLink: Text[2048]; var TempFrzAttachmentResult: Record "DXP FRZ Attachment Result" temporary)

```

#### 2. Mit Kontrolle über Datenlöschung

```al
procedure GetAttachments(RecordHeaderID: Guid; RecordHeaderFileLink: Text[2048]; var TempFrzAttachmentResult: Record "DXP FRZ Attachment Result" temporary; ClearAttachmentTable: Boolean)

```

### Parameter

<table id="bkmrk-parameter-typ-beschr"><thead><tr><th>Parameter</th><th>Typ</th><th>Beschreibung</th></tr></thead><tbody><tr><td>`RecordHeaderID`</td><td>Guid</td><td>Eindeutige ID des Freeze-Datensatzes</td></tr><tr><td>`RecordHeaderFileLink`</td><td>Text\[2048\]</td><td>API-Link zum Datensatz (aus `DXP FRZ Record Result Header`."File Link")</td></tr><tr><td>`TempFrzAttachmentResult`</td><td>Record (temporary, var)</td><td>Temporärer Record, der mit Anhang-Metadaten gefüllt wird</td></tr><tr><td>`ClearAttachmentTable`</td><td>Boolean</td><td>`true` = Record vor Befüllung löschen; `false` = zu bestehenden Einträgen hinzufügen</td></tr></tbody></table>

### Rückgabewert

Keine - Die Methode füllt den übergebenen temporären Record mit Anhang-Metadaten.

### Anhang-Metadaten-Felder

Jeder Eintrag in `TempFrzAttachmentResult` enthält:

<table id="bkmrk-feld-beschreibung-id"><thead><tr><th>Feld</th><th>Beschreibung</th></tr></thead><tbody><tr><td>`ID`</td><td>Eindeutige Anhang-ID (Guid)</td></tr><tr><td>`Record Header Id`</td><td>Verknüpfung zum übergeordneten Datensatz</td></tr><tr><td>`Filename`</td><td>Dateiname mit Erweiterung</td></tr><tr><td>`File Extension`</td><td>Dateierweiterung (automatisch aus Filename extrahiert)</td></tr><tr><td>`Filesize`</td><td>Größe in Bytes</td></tr><tr><td>`Author`</td><td>Ersteller des Anhangs</td></tr><tr><td>`Archived at`</td><td>Archivierungszeitpunkt</td></tr><tr><td>`File Link`</td><td>API-Link zum Datensatz</td></tr></tbody></table>

### Verwendungsbeispiel

```al
procedure ListAttachmentsForSalesInvoice(SalesInvoiceNo: Code[20])
var
    SalesInvoiceHeader: Record "Sales Invoice Header";
    TempFrzResultQueryHeader: Record "DXP FRZ Query Result Header" temporary;
    TempFrzResultRecordHeader: Record "DXP FRZ Record Result Header" temporary;
    TempFrzResultRecordField: Record "DXP FRZ Result Record-Field" temporary;
    TempFrzAttachmentResult: Record "DXP FRZ Attachment Result" temporary;
    FrzSearchMgt: Codeunit "DXP Freeze Search Mgt.";
    FrzResultMgt: Codeunit "DXP FRZ Result Mgt.";
    FrzApiMgt: Codeunit "DXP Freeze API Mgt.";
    QueryResult: JsonObject;
    SearchQuery: Text;
begin
    if not SalesInvoiceHeader.Get(SalesInvoiceNo) then
        Error('Rechnung %1 nicht gefunden.', SalesInvoiceNo);

    // Suchanfrage für diesen Datensatz erstellen
    SearchQuery := FrzSearchMgt.GetSearchCombinationForTable(
        SalesInvoiceHeader.SystemId, 
        Database::"Sales Invoice Header"
    );

    // Freeze durchsuchen
    QueryResult := FrzApiMgt.SubmitSearch(SearchQuery);

    // Ergebnisse verarbeiten - dies füllt den Record Header
    FrzResultMgt.CreateResult(
        QueryResult,
        TempFrzResultQueryHeader,
        TempFrzResultRecordHeader,
        TempFrzResultRecordField,
        TempFrzAttachmentResult,
        SearchQuery
    );

    // Prüfen ob Datensatz gefunden wurde
    if TempFrzResultRecordHeader.FindFirst() then begin
        // Anhänge für diesen Record Header abrufen
        FrzResultMgt.GetAttachments(
            TempFrzResultRecordHeader.ID, 
            TempFrzResultRecordHeader."File Link", 
            TempFrzAttachmentResult
        );

        // Anhänge durchlaufen
        if TempFrzAttachmentResult.FindSet() then
            repeat
                //Verarbeitung der Anhänge
            until TempFrzAttachmentResult.Next() = 0;
    end else
        Message('Keine archivierten Anhänge für Rechnung %1 gefunden.', SalesInvoiceNo);
end;

```

---

## Methode 2: GetSpecificRecordAttachment (Freeze API Mgt.)

### Zweck

Lädt den tatsächlichen Inhalt eines spezifischen Anhangs als Base64-kodierten String herunter. Diese Methode wird typischerweise nach `GetAttachments` verwendet, um ausgewählte Anhänge herunterzuladen.

### Verfügbare Überladungen

#### 1. Mit automatischem Browser-Download

```al
procedure GetSpecificRecordAttachment(FileLink: Text; AttachmentID: Guid; pFileName: Text): Text

```

#### 2. Mit Kontrolle über Browser-Download

```al
procedure GetSpecificRecordAttachment(FileLink: Text; AttachmentID: Guid; pFileName: Text; DownloadFromStream: Boolean): Text

```

### Parameter

<table id="bkmrk-parameter-typ-beschr-0"><thead><tr><th>Parameter</th><th>Typ</th><th>Beschreibung</th></tr></thead><tbody><tr><td>`FileLink`</td><td>Text</td><td>API-Link zum Datensatz (aus `TempFrzAttachmentResult`."File Link")</td></tr><tr><td>`AttachmentID`</td><td>Guid</td><td>Eindeutige Anhang-ID (aus `TempFrzAttachmentResult`.ID)</td></tr><tr><td>`pFileName`</td><td>Text</td><td>Dateiname für Download (aus `TempFrzAttachmentResult`.Filename)</td></tr><tr><td>`DownloadFromStream`</td><td>Boolean</td><td>`true` = Sofortiger Browser-Download; `false` = Nur Base64 zurückgeben</td></tr></tbody></table>

### Rückgabewert

- `Text`: Base64-kodierter Inhalt der Datei
- Leerer String wenn Anhang nicht gefunden oder Fehler auftritt

### Verwendungsbeispiele

#### Beispiel 1: Einzelnen Anhang sofort herunterladen

```al
procedure DownloadFirstPDFAttachment(SalesInvoiceNo: Code[20])
var
    SalesInvoiceHeader: Record "Sales Invoice Header";
    TempFrzResultRecordHeader: Record "DXP FRZ Record Result Header" temporary;
    TempFrzResultRecordField: Record "DXP FRZ Result Record-Field" temporary;
    TempFrzResultQueryHeader: Record "DXP FRZ Query Result Header" temporary;
    TempFrzAttachmentResult: Record "DXP FRZ Attachment Result" temporary;
    FrzSearchMgt: Codeunit "DXP Freeze Search Mgt.";
    FrzResultMgt: Codeunit "DXP FRZ Result Mgt.";
    FrzApiMgt: Codeunit "DXP Freeze API Mgt.";
    QueryResult: JsonObject;
    SearchQuery: Text;
begin
    if not SalesInvoiceHeader.Get(SalesInvoiceNo) then
        Error('Rechnung %1 nicht gefunden.', SalesInvoiceNo);

    // Suchanfrage erstellen
    SearchQuery := FrzSearchMgt.GetSearchCombinationForTable(
        SalesInvoiceHeader.SystemId, 
        Database::"Sales Invoice Header"
    );

    // Freeze durchsuchen
    QueryResult := FrzApiMgt.SubmitSearch(SearchQuery);

    // Ergebnisse verarbeiten
    FrzResultMgt.CreateResult(
        QueryResult,
        TempFrzResultQueryHeader,
        TempFrzResultRecordHeader,
        TempFrzResultRecordField,
        TempFrzAttachmentResult,
        SearchQuery
    );

    // Nach PDF-Anhängen filtern
    TempFrzAttachmentResult.SetRange("File Extension", 'pdf');
    
    if TempFrzAttachmentResult.FindFirst() then begin
        // Direkter Download im Browser
        FrzApiMgt.GetSpecificRecordAttachment(
            TempFrzAttachmentResult."File Link",
            TempFrzAttachmentResult.ID,
            TempFrzAttachmentResult.Filename
        );
        
        Message('PDF-Anhang %1 wird heruntergeladen.', TempFrzAttachmentResult.Filename);
    end else
        Message('Keine PDF-Anhänge für Rechnung %1 gefunden.', SalesInvoiceNo);
end;

```

#### Beispiel 2: Base64-Inhalt für weitere Verarbeitung

```al
procedure GetAttachmentAsBase64(RecordRef: RecordRef; var AttachmentBase64: Text; var AttachmentFilename: Text): Boolean
var
    TempFrzResultRecordHeader: Record "DXP FRZ Record Result Header" temporary;
    TempFrzResultRecordField: Record "DXP FRZ Result Record-Field" temporary;
    TempFrzResultQueryHeader: Record "DXP FRZ Query Result Header" temporary;
    TempFrzAttachmentResult: Record "DXP FRZ Attachment Result" temporary;
    FrzSearchMgt: Codeunit "DXP Freeze Search Mgt.";
    FrzResultMgt: Codeunit "DXP FRZ Result Mgt.";
    FrzApiMgt: Codeunit "DXP Freeze API Mgt.";
    SystemIdFieldRef: FieldRef;
    QueryResult: JsonObject;
    SystemId: Guid;
    SearchQuery: Text;
begin
    // SystemId aus RecordRef extrahieren
    SystemIdFieldRef := RecordRef.Field(RecordRef.SystemIdNo());
    SystemId := SystemIdFieldRef.Value();

    // Suchanfrage erstellen
    SearchQuery := FrzSearchMgt.GetSearchCombinationForTable(SystemId, RecordRef.Number());

    // Freeze durchsuchen
    QueryResult := FrzApiMgt.SubmitSearch(SearchQuery);

    // Ergebnisse verarbeiten
    FrzResultMgt.CreateResult(
        QueryResult,
        TempFrzResultQueryHeader,
        TempFrzResultRecordHeader,
        TempFrzResultRecordField,
        TempFrzAttachmentResult,
        SearchQuery
    );

    if TempFrzAttachmentResult.FindFirst() then begin
        // Base64-Inhalt abrufen OHNE Browser-Download
        AttachmentBase64 := FrzApiMgt.GetSpecificRecordAttachment(
            TempFrzAttachmentResult."File Link",
            TempFrzAttachmentResult.ID,
            TempFrzAttachmentResult.Filename,
            false  // Kein automatischer Download
        );

        AttachmentFilename := TempFrzAttachmentResult.Filename;
        exit(AttachmentBase64 <> '');
    end;

    exit(false);
end;

```

---

## Spezialszenario: Ältesten PDF-Anhang herunterladen

Dieses Beispiel zeigt, wie Sie den ältesten (zuerst archivierten) PDF-Anhang eines Datensatzes finden und herunterladen.

### Vollständiges Beispiel

```al
procedure DownloadOldestPDFAttachment(RecordVariant: Variant): Boolean
var
    TempFrzResultRecordHeader: Record "DXP FRZ Record Result Header" temporary;
    TempFrzResultRecordField: Record "DXP FRZ Result Record-Field" temporary;
    TempFrzResultQueryHeader: Record "DXP FRZ Query Result Header" temporary;
    TempFrzAttachmentResult: Record "DXP FRZ Attachment Result" temporary;
    FrzSearchMgt: Codeunit "DXP Freeze Search Mgt.";
    FrzResultMgt: Codeunit "DXP FRZ Result Mgt.";
    FrzApiMgt: Codeunit "DXP Freeze API Mgt.";
    RecordRef: RecordRef;
    SystemIdFieldRef: FieldRef;
    QueryResult: JsonObject;
    SystemId: Guid;
    SearchQuery: Text;
    OldestArchiveDate: DateTime;
    AttachmentBase64: Text;
begin
    // RecordRef aus Variant erstellen
    RecordRef.GetTable(RecordVariant);

    // SystemId extrahieren
    SystemIdFieldRef := RecordRef.Field(RecordRef.SystemIdNo());
    SystemId := SystemIdFieldRef.Value();

    // Suchanfrage für diesen Datensatz erstellen
    SearchQuery := FrzSearchMgt.GetSearchCombinationForTable(SystemId, RecordRef.Number());

    // Freeze durchsuchen
    QueryResult := FrzApiMgt.SubmitSearch(SearchQuery);

    // Ergebnisse mit Anhängen abrufen
    FrzResultMgt.CreateResult(
        QueryResult,
        TempFrzResultQueryHeader,
        TempFrzResultRecordHeader,
        TempFrzResultRecordField,
        TempFrzAttachmentResult,
        SearchQuery
    );

    // Nach PDF-Anhängen filtern
    TempFrzAttachmentResult.SetRange("File Extension", 'pdf');

    if TempFrzAttachmentResult.IsEmpty() then begin
        Message('Keine PDF-Anhänge für diesen Datensatz gefunden.');
        exit(false);
    end;

    // Nach Archivierungsdatum aufsteigend sortieren (älteste zuerst)
    TempFrzAttachmentResult.SetCurrentKey("Archived at");
    TempFrzAttachmentResult.Ascending(true);

    // Ersten (ältesten) Anhang abrufen
    if TempFrzAttachmentResult.FindFirst() then begin
        OldestArchiveDate := TempFrzAttachmentResult."Archived at";

        Message('Ältester PDF-Anhang gefunden:\Datei: %1\Größe: %2 KB\Archiviert am: %3\Autor: %4',
            TempFrzAttachmentResult.Filename,
            Round(TempFrzAttachmentResult.Filesize / 1024, 1),
            OldestArchiveDate,
            TempFrzAttachmentResult.Author);

        // Anhang herunterladen
        AttachmentBase64 := FrzApiMgt.GetSpecificRecordAttachment(
            TempFrzAttachmentResult."File Link",
            TempFrzAttachmentResult.ID,
            TempFrzAttachmentResult.Filename,
            true  // Sofortiger Browser-Download
        );

        exit(AttachmentBase64 <> '');
    end;

    exit(false);
end;

```

### Verwendung des Beispiels

```al
// Von einer Verkaufsrechnung aus
var
    SalesInvoiceHeader: Record "Sales Invoice Header";
begin
    SalesInvoiceHeader.Get('SI-001');
    DownloadOldestPDFAttachment(SalesInvoiceHeader);
end;

// Von einer gebuchten Einkaufsrechnung aus
var
    PurchInvHeader: Record "Purch. Inv. Header";
begin
    PurchInvHeader.Get('PI-001');
    DownloadOldestPDFAttachment(PurchInvHeader);
end;

```

---

## Zusammenfassung

### Typischer Workflow

1. **Datensatz identifizieren**: SystemId und TableNo des BC-Datensatzes ermitteln
2. **Suchanfrage erstellen**: `FrzSearchMgt.GetSearchCombinationForTable()`
3. **Freeze durchsuchen**: `FrzApiMgt.SubmitSearch()`
4. **Ergebnisse verarbeiten**: `FrzResultMgt.CreateResult()`
5. **Anhänge filtern**: Filter auf `TempFrzAttachmentResult` setzen
6. **Anhänge herunterladen**: `FrzApiMgt.GetSpecificRecordAttachment()`