- Rename InvoiceMaster.* to FiscalFlow.* namespace - Upgrade from .NET 8 to .NET 10 - Update all NuGet packages to latest versions - Update C# language version to 14.0
155 lines
5.1 KiB
C#
155 lines
5.1 KiB
C#
using FiscalFlow.Core.Interfaces;
|
|
using Microsoft.Extensions.Configuration;
|
|
using Microsoft.Extensions.Logging;
|
|
using System.Net.Http.Headers;
|
|
using System.Net.Http.Json;
|
|
using System.Text.Json;
|
|
|
|
namespace FiscalFlow.Infrastructure.Services;
|
|
|
|
public class OcrService : IOcrService
|
|
{
|
|
private readonly HttpClient _httpClient;
|
|
private readonly ILogger<OcrService> _logger;
|
|
private readonly string _apiUrl;
|
|
private readonly string? _apiKey;
|
|
|
|
public OcrService(IHttpClientFactory httpClientFactory, IConfiguration configuration, ILogger<OcrService> logger)
|
|
{
|
|
_httpClient = httpClientFactory.CreateClient();
|
|
_logger = logger;
|
|
_apiUrl = configuration["Ocr:ApiUrl"] ?? "http://localhost:8000/api/v1";
|
|
_apiKey = configuration["Ocr:ApiKey"];
|
|
}
|
|
|
|
public async Task<OcrResult> ExtractAsync(Stream fileStream, string fileName, CancellationToken cancellationToken = default)
|
|
{
|
|
try
|
|
{
|
|
var content = new MultipartFormDataContent();
|
|
var streamContent = new StreamContent(fileStream);
|
|
streamContent.Headers.ContentType = new MediaTypeHeaderValue("application/pdf");
|
|
content.Add(streamContent, "file", fileName);
|
|
|
|
var request = new HttpRequestMessage(HttpMethod.Post, $"{_apiUrl}/infer")
|
|
{
|
|
Content = content
|
|
};
|
|
|
|
if (!string.IsNullOrEmpty(_apiKey))
|
|
{
|
|
request.Headers.Add("X-API-Key", _apiKey);
|
|
}
|
|
|
|
var response = await _httpClient.SendAsync(request, cancellationToken);
|
|
var responseContent = await response.Content.ReadAsStringAsync(cancellationToken);
|
|
|
|
if (!response.IsSuccessStatusCode)
|
|
{
|
|
_logger.LogError("OCR API error: {StatusCode} - {Content}", response.StatusCode, responseContent);
|
|
return new OcrResult
|
|
{
|
|
Success = false,
|
|
ErrorMessage = $"OCR API returned {response.StatusCode}"
|
|
};
|
|
}
|
|
|
|
var result = JsonSerializer.Deserialize<OcrApiResponse>(responseContent, new JsonSerializerOptions
|
|
{
|
|
PropertyNameCaseInsensitive = true
|
|
});
|
|
|
|
if (result == null)
|
|
{
|
|
return new OcrResult
|
|
{
|
|
Success = false,
|
|
ErrorMessage = "Invalid OCR response"
|
|
};
|
|
}
|
|
|
|
return new OcrResult
|
|
{
|
|
Success = result.Success,
|
|
Data = MapToInvoiceData(result.Fields),
|
|
Confidence = result.Confidence,
|
|
ErrorMessage = result.Error
|
|
};
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
_logger.LogError(ex, "Error calling OCR API");
|
|
return new OcrResult
|
|
{
|
|
Success = false,
|
|
ErrorMessage = ex.Message
|
|
};
|
|
}
|
|
}
|
|
|
|
private static InvoiceData MapToInvoiceData(Dictionary<string, OcrField>? fields)
|
|
{
|
|
if (fields == null)
|
|
{
|
|
return new InvoiceData();
|
|
}
|
|
|
|
return new InvoiceData
|
|
{
|
|
SupplierName = GetFieldValue(fields, "supplier_name"),
|
|
SupplierOrgNumber = GetFieldValue(fields, "supplier_org_number"),
|
|
InvoiceNumber = GetFieldValue(fields, "invoice_number"),
|
|
InvoiceDate = ParseDate(GetFieldValue(fields, "invoice_date")),
|
|
DueDate = ParseDate(GetFieldValue(fields, "due_date")),
|
|
AmountTotal = ParseDecimal(GetFieldValue(fields, "amount_total")),
|
|
AmountVat = ParseDecimal(GetFieldValue(fields, "amount_vat")),
|
|
VatRate = ParseInt(GetFieldValue(fields, "vat_rate")),
|
|
OcrNumber = GetFieldValue(fields, "ocr_number"),
|
|
Bankgiro = GetFieldValue(fields, "bankgiro"),
|
|
Plusgiro = GetFieldValue(fields, "plusgiro"),
|
|
Currency = GetFieldValue(fields, "currency") ?? "SEK"
|
|
};
|
|
}
|
|
|
|
private static string? GetFieldValue(Dictionary<string, OcrField> fields, string key)
|
|
{
|
|
return fields.TryGetValue(key, out var field) ? field.Value : null;
|
|
}
|
|
|
|
private static DateTime? ParseDate(string? value)
|
|
{
|
|
if (string.IsNullOrEmpty(value)) return null;
|
|
if (DateTime.TryParse(value, out var date)) return date;
|
|
return null;
|
|
}
|
|
|
|
private static decimal? ParseDecimal(string? value)
|
|
{
|
|
if (string.IsNullOrEmpty(value)) return null;
|
|
value = value.Replace(",", "").Replace(" ", "");
|
|
if (decimal.TryParse(value, out var result)) return result;
|
|
return null;
|
|
}
|
|
|
|
private static int? ParseInt(string? value)
|
|
{
|
|
if (string.IsNullOrEmpty(value)) return null;
|
|
if (int.TryParse(value, out var result)) return result;
|
|
return null;
|
|
}
|
|
}
|
|
|
|
public class OcrApiResponse
|
|
{
|
|
public bool Success { get; set; }
|
|
public string? Error { get; set; }
|
|
public Dictionary<string, OcrField>? Fields { get; set; }
|
|
public decimal Confidence { get; set; }
|
|
}
|
|
|
|
public class OcrField
|
|
{
|
|
public string? Value { get; set; }
|
|
public decimal Confidence { get; set; }
|
|
}
|