diff --git a/DocumentOperator.API/Endpoints/v1/DocumentEndpoints.cs b/DocumentOperator.API/Endpoints/v1/DocumentEndpoints.cs index ac6f22c..48b1f97 100644 --- a/DocumentOperator.API/Endpoints/v1/DocumentEndpoints.cs +++ b/DocumentOperator.API/Endpoints/v1/DocumentEndpoints.cs @@ -33,7 +33,19 @@ public static class DocumentEndpoints group.MapPost("/extract-swiss-qr-code", ExtractSwissQrCode) .WithName("ExtractSwissQrCode") .WithSummary("Extracts Swiss QR Code from the last page of a PDF document") - .WithDescription("Extracts and parses a Swiss QR Code (Swiss QR Bill Standard 2.0) from the last page of a PDF. Returns references (passed through) and all QR code fields.") + .WithDescription(@"Extracts and parses a Swiss QR Code (Swiss QR Bill Standard 2.0) from the last page of a PDF document. + +**Requirements:** +- PDF must contain a valid Swiss QR Code on the last page +- QR Code must conform to Swiss QR Bill Standard 2.0 +- References array is required (can be empty) + +**Returns:** +- All QR code fields (IBAN, amount, creditor, debtor, reference, etc.) +- References array (passed through from request) + +**Use Case:** +Extract payment information from Swiss QR invoices for automated processing.") .Produces(StatusCodes.Status200OK) .Produces(StatusCodes.Status400BadRequest) .Produces(StatusCodes.Status404NotFound) diff --git a/DocumentOperator.API/PHASENPLAN.md b/DocumentOperator.API/PHASENPLAN.md index 8212c3d..d8dda70 100644 --- a/DocumentOperator.API/PHASENPLAN.md +++ b/DocumentOperator.API/PHASENPLAN.md @@ -1,6 +1,6 @@ # ?? DocumentOperator - Phasenplan (Feature-Driven Development) -> **Stand:** 17.01.2025 | **Aktuell:** Feature 2 - ExtractSwissQrCode ? IN PROGRESS | **Projektdauer:** 6 Wochen +> **Stand:** 17.01.2025 | **Aktuell:** Feature 3 - ExtractAttachments ? NEXT | **Projektdauer:** 6 Wochen --- @@ -9,8 +9,8 @@ | Woche | Features / Concerns | Status | Fortschritt | |-------|---------------------|--------|-------------| | **W1** | Feature 1: ValidatePDF | ? Abgeschlossen | 100% (Foundation + Application + API + Swagger fertig) | -| **W1-W2** | Feature 2: ExtractSwissQrCode | ? In Progress | 0% (Gerade gestartet!) | -| **W2** | Feature 3: ExtractAttachments | ? Geplant | 0% | +| **W1-W2** | Feature 2: ExtractSwissQrCode | ? Abgeschlossen | 100% (Domain + Infrastructure + Application + API + Swagger fertig) | +| **W2** | Feature 3: ExtractAttachments | ? Nächstes Feature | 0% | | **W2** | Feature 4: ApplyStamp | ? Geplant | 0% | | **W3** | Feature 5: EmbedCertificate | ? Geplant | 0% | | **W3** | Feature 6: ConcatenatePDFs (Async) | ? Geplant | 0% | @@ -170,36 +170,46 @@ --- -### WOCHE 1-2 - Feature 2: ExtractSwissQrCode | ? In Progress - 0% +### WOCHE 1-2 - Feature 2: ExtractSwissQrCode | ? ABGESCHLOSSEN - 100% **Dauer:** ~1-2 Tage -**Status:** ? In Progress (gerade gestartet!) +**Status:** ? Abgeschlossen **Endpoint:** POST /api/v1/documents/extract-swiss-qr-code -**Was wird gebaut:** +**Was wurde gebaut:** - Client sendet Referenzen (Array) + PDF (Base64) - API extrahiert Swiss QR Code von **letzter Seite** - API gibt Referenzen + alle QR Code Felder zurück (Swiss QR Bill Standard 2.0) **Technologie:** - **DevExpress PDF Document API** (PDF-Zugriff, letzte Seite) +- **ZXing.Net.Bindings.Windows.Compatibility** (QR Code Detection) - **Codecrete.SwissQRBill.Generator** (Swiss QR Code Parsing - Standard 2.0) +- **System.Drawing.Common** (Bitmap Support) **Steps:** -- ?? Step 2.1: Domain Layer (SwissQrCodeData Value Object) -- ?? Step 2.2: Infrastructure Layer (IQrCodeProcessor + DevExpressSwissQrCodeProcessor + Codecrete Integration) -- ?? Step 2.3: Application Layer (ExtractSwissQrCodeQuery + Handler + Validator) -- ?? Step 2.4: API Layer (Endpoint + Integration Tests) -- ?? Step 2.5: Swagger Dokumentation +- ? Step 2.1: Domain Layer (SwissQrCodeData Value Object) - ABGESCHLOSSEN +- ? Step 2.2: Infrastructure Layer (ISwissQrCodeProcessor + DevExpressSwissQrCodeProcessor + Library Integration) - ABGESCHLOSSEN +- ? Step 2.3: Application Layer (ExtractSwissQrCodeQuery + Handler + Validator + DTOs) - ABGESCHLOSSEN +- ? Step 2.4: API Layer (Endpoint + Exception Mapping + Integration Tests) - ABGESCHLOSSEN +- ? Step 2.5: Swagger Dokumentation (XML Comments + Examples) - ABGESCHLOSSEN **Akzeptanzkriterien:** - ? QR Code wird von letzter Seite extrahiert - ? Alle Swiss QR Bill Felder werden geparst (Standard 2.0) - ? Referenzen werden durchgeschliffen (Echo) -- ? Fehler wenn kein QR Code gefunden +- ? Fehler wenn kein QR Code gefunden (404 Not Found) - ? Swagger-testbar -- ? Tests grün +- ? Tests grün (19/19) + +**Ergebnis:** +- ? POST /api/v1/documents/extract-swiss-qr-code im Swagger testbar +- ? Nested Response Structure (References + SwissQrCodeData) +- ? SwissQrCodeNotFoundException wird zu 404 gemappt +- ? Unit Tests + Integration Tests grün (19/19) +- ? Clean Architecture eingehalten +- ? Swagger-Dokumentation vollständig --- @@ -487,21 +497,19 @@ ### Nächstes Feature -**Feature 2: ExtractSwissQrCode** - **IN PROGRESS** -1. ?? Step 2.1: Domain Layer (SwissQrCodeData Value Object) -2. ?? Step 2.2: Infrastructure Layer (IQrCodeProcessor + DevExpressSwissQrCodeProcessor + Codecrete Integration) -3. ?? Step 2.3: Application Layer (ExtractSwissQrCodeQuery + Handler + Validator + DTOs) -4. ?? Step 2.4: API Layer (Endpoint + Integration Tests) -5. ?? Step 2.5: Swagger Dokumentation +**Feature 3: ExtractAttachments** - **NEXT** +1. ?? Step 3.1: Domain Layer (Attachment Value Object) +2. ?? Step 3.2: Infrastructure Layer (IAttachmentProcessor + DevExpressAttachmentProcessor) +3. ?? Step 3.3: Application Layer (ExtractAttachmentsQuery + Handler + Validator + DTOs) +4. ?? Step 3.4: API Layer (Endpoint + Integration Tests) +5. ?? Step 3.5: Swagger Dokumentation -**Erwarteter Zeitaufwand:** ~1-2 Tage +**Erwarteter Zeitaufwand:** ~1 Tag **Akzeptanzkriterien:** -- ? POST /api/v1/documents/extract-swiss-qr-code im Swagger testbar -- ? Swiss QR Code wird von letzter Seite extrahiert -- ? Alle Swiss QR Bill Felder geparst (Standard 2.0) -- ? Referenzen durchgeschliffen -- ? Fehler wenn kein QR Code gefunden +- ? POST /api/v1/documents/extract-attachments im Swagger testbar +- ? Alle Attachments werden extrahiert +- ? Attachment-Metadaten werden zurückgegeben - ? Alle Tests grün - ? Clean Architecture eingehalten @@ -523,7 +531,12 @@ | 17.01.2025 | **Fix: Attachment Detection (Multiple Attachments)** | ? **KORRIGIERT** - ValidatePDF erkennt jetzt auch PDFs mit mehreren Attachments korrekt (globale Suche statt 1000-Zeichen-Limit) - 13/13 Tests grün | | 17.01.2025 | **Fix: Attachment Count (6 Attachments)** | ? **KORRIGIERT** - AttachmentCount wird jetzt korrekt gezählt (objectCount statt objectCount/2). PDFs mit 6 Attachments werden korrekt erkannt - 13/13 Tests grün | | 17.01.2025 | **PHASENPLAN** | ?? **Feature-Reihenfolge geändert** - Neues Feature 2: ExtractSwissQrCode (Swiss QR Bill Standard 2.0) eingefügt. Alte Features 2-5 werden zu Features 3-6. | -| 17.01.2025 | **Feature 2 - ExtractSwissQrCode** | ?? **GESTARTET** - Swiss QR Code Extraktion von letzter PDF-Seite (DevExpress + Codecrete.SwissQRBill.Generator) | +| 17.01.2025 | **Feature 2 - Step 2.1** | ? **ABGESCHLOSSEN** - Domain Layer (SwissQrCodeData, AddressData, SwissQrCodeNotFoundException) | +| 17.01.2025 | **Feature 2 - Step 2.2** | ? **ABGESCHLOSSEN** - Infrastructure Layer (ISwissQrCodeProcessor, DevExpressSwissQrCodeProcessor, ZXing + Codecrete Integration) | +| 17.01.2025 | **Feature 2 - Step 2.3** | ? **ABGESCHLOSSEN** - Application Layer (ExtractSwissQrCodeQuery, Handler, Validator, Request/Response DTOs, Unit Tests) | +| 17.01.2025 | **Feature 2 - Step 2.4** | ? **ABGESCHLOSSEN** - API Layer (Endpoint, Exception Mapping, Integration Tests - 19/19 Tests grün) | +| 17.01.2025 | **Feature 2 - Step 2.5** | ? **ABGESCHLOSSEN** - Swagger Dokumentation (XML Comments, Examples, Endpoint Description) | +| 17.01.2025 | **Feature 2** | ? **KOMPLETT ABGESCHLOSSEN** - ExtractSwissQrCode Feature testbar im Swagger UI! (19/19 Tests grün) | --- diff --git a/DocumentOperator.API/ROADMAP.md b/DocumentOperator.API/ROADMAP.md index 2cffde6..d8360ec 100644 --- a/DocumentOperator.API/ROADMAP.md +++ b/DocumentOperator.API/ROADMAP.md @@ -45,7 +45,7 @@ | Feature | Type | Status | Swagger Testbar? | |---------|------|--------|------------------| | **1. ValidatePDF** | Synchron | ? Abgeschlossen | ? | -| **2. ExtractSwissQrCode** | Synchron | ? In Progress | ? | +| **2. ExtractSwissQrCode** | Synchron | ? Abgeschlossen | ? | | **3. ExtractAttachments** | Synchron | ? Pending | ? | | **4. ApplyStamp** | Synchron | ? Pending | ? | | **5. EmbedCertificate** | Synchron | ? Pending | ? | @@ -279,11 +279,11 @@ Response: { "pageCount": 5, "fileSizeBytes": 1024, "pdfVersion": "1.4", "hasAtta - ? Swagger-Dokumentation vollständig **Nächstes Feature:** -? **Feature 2: ExtractSwissQrCode** +? **Feature 3: ExtractAttachments** --- -## ?? FEATURE 2: ExtractSwissQrCode (Synchron) - **IN PROGRESS** +## ?? FEATURE 2: ExtractSwissQrCode (Synchron) - **ABGESCHLOSSEN** **Was macht dieses Feature?** - Client sendet PDF als Base64 + Referenzen (Array von Strings) @@ -342,11 +342,11 @@ Response: - **Codecrete.SwissQRBill.Generator** (Swiss QR Code Parsing - Standard 2.0) **Steps:** -- ?? Step 2.1: Domain Layer (SwissQrCodeData Value Object) -- ?? Step 2.2: Infrastructure Layer (IQrCodeProcessor + DevExpressSwissQrCodeProcessor) -- ?? Step 2.3: Application Layer (ExtractSwissQrCodeQuery + Handler + Validator) -- ?? Step 2.4: API Layer (Endpoint + Integration Tests) -- ?? Step 2.5: Swagger Dokumentation +- ? Step 2.1: Domain Layer (SwissQrCodeData Value Object) +- ? Step 2.2: Infrastructure Layer (IQrCodeProcessor + DevExpressSwissQrCodeProcessor) +- ? Step 2.3: Application Layer (ExtractSwissQrCodeQuery + Handler + Validator) +- ? Step 2.4: API Layer (Endpoint + Integration Tests) +- ? Step 2.5: Swagger Dokumentation **Akzeptanzkriterien:** - ? QR Code wird von letzter Seite extrahiert @@ -354,6 +354,21 @@ Response: - ? Referenzen werden durchgeschliffen (Echo) - ? Fehler wenn kein QR Code gefunden - ? Swagger-testbar +- ? Tests grün (19/19) + +### ? Feature 2 ABGESCHLOSSEN! + +**Was haben wir erreicht?** +- ? ExtractSwissQrCode Feature komplett implementiert (Domain ? Infrastructure ? Application ? API ? Tests ? Swagger) +- ? Endpoint ist im Swagger UI testbar: `POST /api/v1/documents/extract-swiss-qr-code` +- ? Unit Tests + Integration Tests grün (19/19) +- ? 3 Libraries integriert: DevExpress PDF, ZXing.Net, Codecrete.SwissQRBill.Generator +- ? Vollständige Swiss QR Bill Standard 2.0 Unterstützung +- ? Clean Architecture eingehalten +- ? Swagger-Dokumentation vollständig + +**Nächstes Feature:** +? **Feature 3: ExtractAttachments** --- @@ -1071,7 +1086,12 @@ DocumentOperator.Tests/ | 17.01.2025 | **Fix: Attachment Detection (Multiple Attachments)** | ? **KORRIGIERT** - ValidatePDF erkennt jetzt auch PDFs mit mehreren Attachments korrekt (globale Suche statt 1000-Zeichen-Limit) - 13/13 Tests grün | | 17.01.2025 | **Fix: Attachment Count (6 Attachments)** | ? **KORRIGIERT** - AttachmentCount wird jetzt korrekt gezählt (objectCount statt objectCount/2). PDFs mit 6 Attachments werden korrekt erkannt - 13/13 Tests grün | | 17.01.2025 | **ROADMAP** | ?? **Feature-Reihenfolge geändert** - Neues Feature 2: ExtractSwissQrCode (Swiss QR Bill Standard 2.0) eingefügt. Alte Features 2-5 werden zu Features 3-6. | -| 17.01.2025 | **Feature 2 - ExtractSwissQrCode** | ?? **GESTARTET** - Swiss QR Code Extraktion von letzter PDF-Seite (DevExpress + Codecrete.SwissQRBill.Generator) | +| 17.01.2025 | **Feature 2 - Step 2.1** | ? **ABGESCHLOSSEN** - Domain Layer (SwissQrCodeData, AddressData Value Objects, SwissQrCodeNotFoundException) | +| 17.01.2025 | **Feature 2 - Step 2.2** | ? **ABGESCHLOSSEN** - Infrastructure Layer (ISwissQrCodeProcessor, DevExpressSwissQrCodeProcessor, Libraries: Codecrete.SwissQRBill.Generator, ZXing.Net, System.Drawing.Common) | +| 17.01.2025 | **Feature 2 - Step 2.3** | ? **ABGESCHLOSSEN** - Application Layer (Query, Handler, Validator, DTOs, Unit Tests - 2/2 grün) | +| 17.01.2025 | **Feature 2 - Step 2.4** | ? **ABGESCHLOSSEN** - API Layer (Endpoint /extract-swiss-qr-code, Exception Handling, Integration Tests - 4/4 grün) | +| 17.01.2025 | **Feature 2 - Step 2.5** | ? **ABGESCHLOSSEN** - Swagger Dokumentation (XML Comments, Request/Response Beispiele, Endpoint-Beschreibung) | +| 17.01.2025 | **Feature 2** | ? **KOMPLETT ABGESCHLOSSEN** - ExtractSwissQrCode Feature testbar im Swagger UI! (19/19 Tests grün) | --- diff --git a/DocumentOperator.Application/Common/DTOs/ExtractSwissQrCodeRequest.cs b/DocumentOperator.Application/Common/DTOs/ExtractSwissQrCodeRequest.cs index f84b639..efb4622 100644 --- a/DocumentOperator.Application/Common/DTOs/ExtractSwissQrCodeRequest.cs +++ b/DocumentOperator.Application/Common/DTOs/ExtractSwissQrCodeRequest.cs @@ -6,6 +6,12 @@ namespace DocumentOperator.Application.Common.DTOs; /// /// Array of reference strings to pass through in the response /// PDF document encoded as Base64 string +/// +/// { +/// "references": ["REF-001", "REF-002"], +/// "base64Pdf": "JVBERi0xLjQK..." +/// } +/// public record ExtractSwissQrCodeRequest( IReadOnlyList References, string Base64Pdf diff --git a/DocumentOperator.Application/Common/DTOs/ExtractSwissQrCodeResponse.cs b/DocumentOperator.Application/Common/DTOs/ExtractSwissQrCodeResponse.cs index 2ea3ad4..ebf1b1a 100644 --- a/DocumentOperator.Application/Common/DTOs/ExtractSwissQrCodeResponse.cs +++ b/DocumentOperator.Application/Common/DTOs/ExtractSwissQrCodeResponse.cs @@ -5,42 +5,112 @@ namespace DocumentOperator.Application.Common.DTOs; /// /// Reference strings passed through from the request /// Parsed Swiss QR Code data from the last page of the PDF +/// +/// { +/// "references": ["REF-001", "REF-002"], +/// "qrCodeData": { +/// "qrType": "SPC", +/// "version": "0200", +/// "codingType": "1", +/// "iban": "CH4431999123000889012", +/// "creditor": { +/// "addressType": "S", +/// "name": "Robert Schneider AG", +/// "street": "Rue du Lac", +/// "buildingNumber": "1268", +/// "postalCode": "2501", +/// "city": "Biel", +/// "country": "CH" +/// }, +/// "amount": 1949.75, +/// "currency": "CHF", +/// "referenceType": "QRR", +/// "reference": "210000000003139471430009017" +/// } +/// } +/// public record ExtractSwissQrCodeResponse( IReadOnlyList References, SwissQrCodeDataDto QrCodeData ); /// -/// Swiss QR Code data according to Swiss QR Bill Standard 2.0 +/// Swiss QR Code data according to Swiss QR Bill Standard 2.0. +/// Contains all fields defined in the Swiss Payment Standards. /// public record SwissQrCodeDataDto( + /// QR type - always "SPC" for Swiss Payment Code string QrType, + + /// Version of the Swiss QR Code standard (e.g., "0200" for version 2.0) string Version, + + /// Character set code (always "1" for UTF-8) string CodingType, + + /// IBAN of the creditor (payee) string Iban, + + /// Creditor (payee) information AddressDataDto Creditor, + + /// Ultimate creditor information (optional) AddressDataDto? UltimateCreditor, + + /// Payment amount (null if not specified) decimal? Amount, + + /// Currency code (CHF or EUR) string Currency, + + /// Ultimate debtor (payer) information (optional) AddressDataDto? UltimateDebtor, + + /// Reference type: "QRR" (QR Reference), "SCOR" (Creditor Reference ISO 11649), or "NON" (No Reference) string ReferenceType, + + /// Payment reference (format depends on ReferenceType) string? Reference, + + /// Unstructured message (max 140 characters) string? UnstructuredMessage, + + /// Bill information (structured data for automated processing) string? BillInformation, + + /// Alternative procedure parameters (up to 2 entries) IReadOnlyList? AlternativeProcedureParameters ); /// -/// Address data (creditor or debtor) in Swiss QR Code +/// Address data in Swiss QR Code (creditor or debtor). +/// Can be either structured (S) or combined (K) format. /// public record AddressDataDto( + /// Address type: "S" for structured, "K" for combined string AddressType, + + /// Name of person or company string Name, + + /// Street name (structured address only) string? Street, + + /// Building number (structured address only) string? BuildingNumber, + + /// Address line 1 (combined address only) string? AddressLine1, + + /// Address line 2 (combined address only) string? AddressLine2, + + /// Postal code string PostalCode, + + /// City/town name string City, + + /// Two-letter country code (ISO 3166-1 alpha-2) string Country );