|
- using Services.ReActAgent;
- using System.Text;
- using System.Text.RegularExpressions;
- using ToolsServices;
-
- namespace Services
- {
- public static class FactureService
- {
- #region Variables
- private static readonly string NomFichierParametres = FichiersInternesService.ParamsFactures;
- #endregion
-
- #region Méthodes publiques
- public static async Task<(bool, string)> Traitement(string inputPath, string outputPath, bool isApiExterne)
- {
- StringBuilder sb = new();
- LoggerService.LogInfo($"FactureService.Traitement");
-
- if (!Directory.Exists(inputPath))
- {
- LoggerService.LogWarning($"Le dossier {inputPath} n'existe pas.");
- return (false, $"Le dossier {inputPath} n'existe pas.");
- }
-
- if (!Directory.Exists(outputPath))
- {
- LoggerService.LogWarning($"Le dossier {outputPath} n'existe pas.");
- return (false, $"Le dossier {outputPath} n'existe pas.");
- }
-
- var fichiers = Directory
- .EnumerateFiles(inputPath, "*.*", SearchOption.AllDirectories)
- .Where(f => f.EndsWith(".pdf", StringComparison.OrdinalIgnoreCase))
- .ToArray();
-
- if (fichiers.Length == 0)
- {
- LoggerService.LogWarning($"Aucun fichier trouvé dans le dossier spécifié : {inputPath}");
- return (false, $"Aucun fichier trouvé dans le dossier spécifié : {inputPath}");
- }
-
- var reActRagAgent = new ReActAgent.ReActAgent();
- var isOk = true;
- foreach (var fichier in fichiers)
- {
-
- try
- {
- #region Analyse de la facture
- var facturePDF = FilesService.ExtractText(fichier);
- //facturePDF = CleanPdfText(facturePDF);
- //facturePDF = SegmentInvoice(facturePDF);
-
- var prompt = PromptService.GetPrompt(PromptService.ePrompt.FactureService_AnalyserFacture_Extract, facturePDF);
-
- LoggerService.LogInfo($"Traitement de facture PDF to XML : {fichier}");
- var (reponse,m) = await reActRagAgent.AppelerLLMAsync(ModelsUseCases.TypeUseCase.Facture_To_Xml, true, prompt, "Traitement de facture PDF to XML", "", isApiExterne);
-
- if (!XmlService.IsXML(reponse))
- {
- sb.AppendLine($"Le résultat n'est pas au format XML pour le fichier : {fichier}");
- LoggerService.LogWarning($"Le résultat n'est pas au format XML pour le fichier : {fichier}");
-
- string fileNameXML = Path.Combine(outputPath, Path.GetFileNameWithoutExtension(fichier) + ".xml");
- TxtService.CreateTextFile(fileNameXML, reponse);
- }
- else
- {
- #region Sauvegarde du résultat
- LoggerService.LogDebug($"Le résultat est au format XML pour le fichier : {fichier}");
- string fileNameXML = Path.Combine(outputPath, Path.GetFileNameWithoutExtension(fichier) + ".xml");
- TxtService.CreateTextFile(fileNameXML, reponse);
- #endregion
- }
- #endregion
- }
-
- catch (Exception ex)
- {
- isOk = false;
- LoggerService.LogError($"Erreur lors de l'extraction du texte de {fichier} : {ex.Message}");
- }
- }
-
- LoggerService.LogInfo($"Traitement des factures PDF to XML terminée");
-
- return (isOk, sb.ToString());
- }
-
- public static (string, string) LoadParametres()
- {
- LoggerService.LogInfo("FactureService.LoadParametres");
- //ParametresOllamaService SelectedItem = new();
-
- try
- {
- string FicheMission = "";
- string PathCV = "";
- if (File.Exists(NomFichierParametres))
- {
-
- string[] lignes = File.ReadAllLines(NomFichierParametres);
-
- if (lignes.Length > 0)
- FicheMission = lignes[0];
-
- if (lignes.Length > 1)
- PathCV = lignes[1];
-
- }
- return (FicheMission, PathCV);
-
- }
- catch
- {
- LoggerService.LogError($"Erreur lors du chargement des paramètres depuis {NomFichierParametres}");
- return ("", "");
- }
- }
-
- public static bool SaveParametres(string inputPath, string outputPath)
- {
- LoggerService.LogInfo("FactureService.SaveParametres");
- try
- {
- StringBuilder sb = new();
- sb.AppendLine(inputPath);
- sb.AppendLine(outputPath);
-
- File.WriteAllText(NomFichierParametres, sb.ToString());
-
- return true;
- }
- catch
- {
- LoggerService.LogError($"Erreur lors de la sauvegarde des paramètres dans {NomFichierParametres}");
- return false;
- }
-
- }
- #endregion
-
- #region Méthodes privées
- private static string CleanPdfText(string rawText)
- {
- var text = rawText;
-
- // Normaliser les fins de ligne
- text = text.Replace("\r", "\n");
-
- // Supprimer les caractères de contrôle (sauf \n)
- text = new string(text.Where(c => !char.IsControl(c) || c == '\n').ToArray());
-
- // Supprimer les en-têtes et pieds de page typiques (ex: "Page 1/3", "Facture ACME", etc.)
- text = Regex.Replace(text, @"Page\s+\d+(/\d+)?", "", RegexOptions.IgnoreCase);
- text = Regex.Replace(text, @"Facture\s+n[°º]\s*\d+", "", RegexOptions.IgnoreCase);
-
- // Uniformiser les espaces : tabulations → espace, et espaces multiples → 1 seul
- text = Regex.Replace(text, @"[ \t]+", " ");
-
- // Supprimer les lignes vides excessives
- text = Regex.Replace(text, @"\n{2,}", "\n");
-
- // Supprimer les traits de séparation (souvent des "-----" ou "=====")
- text = Regex.Replace(text, @"[-=]{3,}", "");
-
- // Corriger les montants : ex "1 234,56 €" → "1234.56 EUR"
- text = Regex.Replace(text, @"(\d{1,3}(?:[ \u00A0]\d{3})*,\d{2}) ?€",
- m => m.Groups[1].Value.Replace(" ", "").Replace("\u00A0", "").Replace(",", ".") + " EUR");
-
- // Corriger les dates : ex "01-02-2025" → "2025-02-01"
- text = Regex.Replace(text, @"\b(\d{2})[-/](\d{2})[-/](\d{4})\b", "$3-$2-$1");
-
- // Fusionner les lignes coupées artificiellement (ex: "TOTAL\n123,45" → "TOTAL 123,45")
- text = Regex.Replace(text, @"([A-Za-z])\n(\d)", "$1 $2");
-
- // Supprimer les espaces en trop au début et fin
- text = text.Trim();
-
- return text;
- }
-
- private static string SegmentInvoice(string text)
- {
- var sb = new StringBuilder();
-
- // Bloc fournisseur
- var fournisseurMatch = Regex.Match(text, @"(?i)(?:fournisseur|vendor|seller).{0,50}\n(.+?)(\n|$)");
- if (fournisseurMatch.Success)
- {
- sb.AppendLine("=== FOURNISSEUR ===");
- sb.AppendLine(fournisseurMatch.Groups[1].Value.Trim());
- }
-
- // Bloc client
- var clientMatch = Regex.Match(text, @"(?i)(?:client|acheteur|buyer|bill to|destinataire).{0,50}\n(.+?)(\n|$)");
- if (clientMatch.Success)
- {
- sb.AppendLine("\n=== CLIENT ===");
- sb.AppendLine(clientMatch.Groups[1].Value.Trim());
- }
-
- // Numéro de facture
- var numeroMatch = Regex.Match(text, @"(?i)facture[^\d]*(\d+)");
- if (numeroMatch.Success)
- {
- sb.AppendLine("\n=== NUMERO FACTURE ===");
- sb.AppendLine(numeroMatch.Groups[1].Value.Trim());
- }
-
- // Date de facture
- var dateMatch = Regex.Match(text, @"(?i)(?:date|issued|emission)[^\d]*(\d{4}-\d{2}-\d{2})");
- if (dateMatch.Success)
- {
- sb.AppendLine("\n=== DATE FACTURE ===");
- sb.AppendLine(dateMatch.Groups[1].Value.Trim());
- }
-
- // Bloc lignes de facture (simplifié : cherche un tableau prix/quantité)
- var lignesMatch = Regex.Match(text, @"(?is)(description.*?total)", RegexOptions.IgnoreCase);
- if (lignesMatch.Success)
- {
- sb.AppendLine("\n=== LIGNES FACTURE ===");
- sb.AppendLine(lignesMatch.Groups[0].Value.Trim());
- }
-
- // Total général
- var totalMatch = Regex.Match(text, @"(?i)(total (?:ttc|general|amount)).{0,10}([\d.,]+ ?eur)");
- if (totalMatch.Success)
- {
- sb.AppendLine("\n=== TOTAL GENERAL ===");
- sb.AppendLine(totalMatch.Groups[2].Value.Trim());
- }
-
- // Retour du texte segmenté
- return sb.Length > 0 ? sb.ToString() : text;
- }
- #endregion
-
- }
-
-
-
- }
|