# 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()`