refactor: rename namespace to FiscalFlow and upgrade to .NET 10

- 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
This commit is contained in:
Invoice Master
2026-02-04 23:18:59 +01:00
parent 05ea67144f
commit ad6ce08e3e
214 changed files with 29692 additions and 1820 deletions

View File

@@ -0,0 +1,154 @@
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; }
}