Add Swiss QR Code extraction feature

Introduced DTOs for request and response to handle Swiss QR Code extraction from PDF documents. Implemented `ExtractSwissQrCodeHandler` to process the extraction using `ISwissQrCodeProcessor`. Added validation for the query with `ExtractSwissQrCodeValidator`. Developed unit tests to ensure correct behavior for successful and failure scenarios.
This commit is contained in:
OlgunR
2026-06-26 08:46:42 +02:00
parent 47cba50d0f
commit 586fd4a207
6 changed files with 233 additions and 0 deletions

View File

@@ -0,0 +1,12 @@
namespace DocumentOperator.Application.Common.DTOs;
/// <summary>
/// Request to extract Swiss QR Code from a PDF document.
/// The QR code must be located on the last page of the document.
/// </summary>
/// <param name="References">Array of reference strings to pass through in the response</param>
/// <param name="Base64Pdf">PDF document encoded as Base64 string</param>
public record ExtractSwissQrCodeRequest(
IReadOnlyList<string> References,
string Base64Pdf
);

View File

@@ -0,0 +1,46 @@
namespace DocumentOperator.Application.Common.DTOs;
/// <summary>
/// Response containing extracted Swiss QR Code data and passed-through references.
/// </summary>
/// <param name="References">Reference strings passed through from the request</param>
/// <param name="QrCodeData">Parsed Swiss QR Code data from the last page of the PDF</param>
public record ExtractSwissQrCodeResponse(
IReadOnlyList<string> References,
SwissQrCodeDataDto QrCodeData
);
/// <summary>
/// Swiss QR Code data according to Swiss QR Bill Standard 2.0
/// </summary>
public record SwissQrCodeDataDto(
string QrType,
string Version,
string CodingType,
string Iban,
AddressDataDto Creditor,
AddressDataDto? UltimateCreditor,
decimal? Amount,
string Currency,
AddressDataDto? UltimateDebtor,
string ReferenceType,
string? Reference,
string? UnstructuredMessage,
string? BillInformation,
IReadOnlyList<string>? AlternativeProcedureParameters
);
/// <summary>
/// Address data (creditor or debtor) in Swiss QR Code
/// </summary>
public record AddressDataDto(
string AddressType,
string Name,
string? Street,
string? BuildingNumber,
string? AddressLine1,
string? AddressLine2,
string PostalCode,
string City,
string Country
);

View File

@@ -0,0 +1,33 @@
using DocumentOperator.Application.Common.Interfaces;
using MediatR;
namespace DocumentOperator.Application.Features.Documents.ExtractSwissQrCode;
/// <summary>
/// Handles extraction of Swiss QR Code from PDF documents.
/// Uses ISwissQrCodeProcessor to extract QR code from the last page and parse it.
/// </summary>
public sealed class ExtractSwissQrCodeHandler : IRequestHandler<ExtractSwissQrCodeQuery, ExtractSwissQrCodeResult>
{
private readonly ISwissQrCodeProcessor _qrCodeProcessor;
public ExtractSwissQrCodeHandler(ISwissQrCodeProcessor qrCodeProcessor)
{
_qrCodeProcessor = qrCodeProcessor;
}
public async Task<ExtractSwissQrCodeResult> Handle(ExtractSwissQrCodeQuery request, CancellationToken cancellationToken)
{
// Convert Base64 string to byte array
byte[] pdfBytes = request.PdfContent.ToByteArray();
// Extract and parse Swiss QR Code from last page
var qrCodeData = await _qrCodeProcessor.ExtractSwissQrCodeAsync(pdfBytes, cancellationToken);
// Return references (passed through) + QR code data
return new ExtractSwissQrCodeResult(
References: request.References,
QrCodeData: qrCodeData
);
}
}

View File

@@ -0,0 +1,26 @@
using DocumentOperator.Domain.Models.ValueObjects;
using DocumentOperator.Domain.ValueObjects;
using MediatR;
namespace DocumentOperator.Application.Features.Documents.ExtractSwissQrCode;
/// <summary>
/// Query to extract Swiss QR Code data from a PDF document.
/// Returns references (passed through) and parsed QR code data.
/// </summary>
/// <param name="References">Array of reference strings to pass through in the response</param>
/// <param name="PdfContent">PDF document content as Base64 string</param>
public record ExtractSwissQrCodeQuery(
IReadOnlyList<string> References,
Base64String PdfContent
) : IRequest<ExtractSwissQrCodeResult>;
/// <summary>
/// Result containing passed-through references and extracted Swiss QR Code data
/// </summary>
/// <param name="References">Reference strings passed through from request</param>
/// <param name="QrCodeData">Parsed Swiss QR Code data from the last page of the PDF</param>
public record ExtractSwissQrCodeResult(
IReadOnlyList<string> References,
SwissQrCodeData QrCodeData
);

View File

@@ -0,0 +1,21 @@
using FluentValidation;
namespace DocumentOperator.Application.Features.Documents.ExtractSwissQrCode;
/// <summary>
/// Validates ExtractSwissQrCodeQuery before handler execution.
/// Ensures references array and PDF content are provided.
/// </summary>
public sealed class ExtractSwissQrCodeValidator : AbstractValidator<ExtractSwissQrCodeQuery>
{
public ExtractSwissQrCodeValidator()
{
RuleFor(x => x.References)
.NotNull()
.WithMessage("References array is required.");
RuleFor(x => x.PdfContent)
.NotNull()
.WithMessage("PDF content is required.");
}
}