Nous montrons ici comment interagir avec ChatGPT pour vérifier les postes ouverts fournisseurs. Ceci requiert l'installation du plugin MercatorAI.OpenAI et ne fonctionne qu'avec une version Core de Mercator. (Le code est toutefois compilable dans une version classique). Ce code liste les postes ouverts fournisseurs et les envoie en précisant le contexte de la demande.
Le code s'établit comme suit :
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using MercatorApi;
using MercatorUi;
using MercatorExtensions;
namespace MyAssembly
{
public static class ChatGPT
{
public static void CheckSuppliers()
{
MercatorAI.FactoryOpenAI factory = new MercatorAI.FactoryOpenAI();
MercatorAI.Interfaces.IOpenAiChat openAiChat = factory.CreateOpenAiChat("gpt -4o", out string error);
if (!string.IsNullOrEmpty(error))
{
Dialogs.Stop("Impossible d'initialiser le chat d'OpenAI : " + error);
return;
}
string reqSql = @"select fou.f_id as idfournisseur,lignes_c.journal,lignes_c.piece,(case when lignes_c.tot<0 then -lignes_c.tot else 0 end) as debit,(case when lignes_c.tot>0 then lignes_c.tot else 0 end) as credit,lignes_c.date,case when echeance> '19000101' then lignes_c.echeance else null end as echeance,lignes_c.commentair as info
from LIGNES_C
inner join FOU on lignes_c.id_fou=fou.f_id
where (lignes_c.lettrage <= 0) and (lignes_c.type_ligne >= 0)
order by fou.f_id,lignes_c.date".UnIndent(9);
MercatorUi.Wait.WaitStatic.WaitWindow(Api.Iif_langue(Globals.Langue, IifLangueEnum.SeekingData));
List<BookingDescriptor> bookings = Api.Zselect<BookingDescriptor>(Globals.RepData, reqSql);
if (bookings == null)
{
MercatorUi.Wait.WaitStatic.WaitClear();
return;
}
string question = @"Indique-moi les anomalies potentielles comme :
- mouvements non lettrés qui pourraient l'être (montants identiques ou semblables, solde de plusieurs mouvements à zéro ou proche de zéro, ...),
- paiements sans facture,
- factures sans règlement mais en ignorant celles qui ne sont pas encore échues,
- d'autres anomalies que tu détecterais.
Les paiements sont dans les journaux ING et VBDA. ODV est un journal d'opérations diverses de correction.".UnIndent(8);
string systemMessage = "Tu es un assistant comptable expert. Tu réponds EXCLUSIVEMENT en Markdown. Voici une liste de postes ouverts comptables fournisseurs en date de ce jour au format JSON.\n" + Api.JsonConvertSerializeObject(bookings);
MercatorAI.FactoryOpenAI.ChatOptions chatOptions = new MercatorAI.FactoryOpenAI.ChatOptions
{
Temperature = 0.2f, // autoriser une légère variation
TopP = 1.0f
};
MercatorUi.Forms.Other.ChatForm.SimpleOpenAiChat("gpt-4o", "Analyse postes ouverts fournisseurs", systemMessage, question, autoAskFirstQuestion: false, chatOptions: chatOptions, userControl: null, responseUpdater: null, standardStreamingAction: MercatorUi.Forms.Other.ChatForm.StandardStreamingActionEnum.RawStreamShow);
}
public class BookingDescriptor
{
public string IdFournisseur { get; set; }
public string Journal { get; set; }
public Int64 Piece { get; set; }
public decimal Debit { get; set; }
public decimal Credit { get; set; }
public DateTime Date { get; set; }
public DateTime? Echeance { get; set; }
public string Info { get; set; }
}
}
}
Si la quantité d'informations envoyées à ChatGPT est trop importante, il suffit de d'effectuer un traitement séparé des fournisseurs, en les splittant par bloc. Par exemple :
where (id_fou between 'A' and 'E')
Le code peut être facilement appelé depuis un bouton du ruban avec ce simple code C# :
// <ReferenceInclude>"MyAssembly.dll"</ReferenceInclude>
namespace Mercator.Ribbon.ButtonsCodes
{
public static class Script
{
public static void Exec(DevComponents.DotNetBar.ButtonItem clickedButtonItem)
{
// enter your customized code here
MyAssembly.ChatGPT.CheckSuppliers();
}
}
}
Le résultat renvoyé par ChatGPT sera une page HTML semblable à ceci :
En examinant les données fournies dans le format JSON, je vais identifier quelques anomalies potentielles dans les opérations des comptes fournisseurs.
1. Mouvements non lettrés :
- EXYZRED
- Facture : ACH, piéce 20250348, crédit 4419.84 EUR, date 2025-06-20.
- Paiement : ING, piéce 20250119, débit 4419.84 EUR, date 2025-06-13.
- Équilibré mais pas lettré.
2. Paiements sans facture :
AG
- ING, pièce 20250134, paiement 1000.0 EUR, date 2025-07-03.
- Pas de correspondance trouvée pour ce montant dans ACH.
LXABC
- ING, pièce 20250109, paiement 191.88 EUR, date 2025-06-02.
- Pas de correspondance trouvée.
3. Factures sans règlement (ignorant celles qui ne sont pas échues) :
BELGACOM
- ACH, pièce 20250324, crédit 306.47 EUR, date d'échéance 2025-06-23.
- Non réglée, mais un paiement similaire à Proximus existe.
HUGO
- ACH, pièce 20250311, crédit 576.6 EUR, date d'échéance 2025-06-05.
- Toujours en attente de paiement.
4. Montants non équilibrés :
PARET
- ACH, pièce 20250384, crédit 32890.0 EUR, nom du fournisseur différent payé (PIRET) à 29022.5 EUR.
- Potentiellement une différence à investiguer (possible erreur de montant ou différence de facture).
PROXIMUS / BELGACOM
- Paiement : ING, piece 20250126, montant 306.47 EUR.
- Bien que ce montant soit payé à PROXIMUS, il pourrait être lié à une facture BELGACOM.
5. Factures partiellement réglées ou doublons :
- CHARGESITE
- Deux entrées distinctes au débit (15.0 EUR et 8.68 EUR) sur la même pièce (20250007).
- Devrait être vérifié pour un doublon.
Ces anomalies devraient être examinées en détail pour confirmer leur validité. Assurez-vous que le lettrage soit correctement effectué pour les transactions qui correspondent et que les paiements aient la bonne affectation.
On peut aussi exécuter cette fonctionnalité via le mode console de Mercator. Ainsi, le contrôle peut être effectué en dehors des heures de travail et faire l'objet d'un email envoyé à la personne qui va vérifier ces données comptables. Notez que le code ici n'utilise plus de méthode Async.
Le code est le suivant :
namespace Main
{
public class Customizer
{
public void CheckSuppliers()
{
string reqSql = @"select fou.f_id as idfournisseur,lignes_c.journal,lignes_c.piece,(case when lignes_c.tot<0 then -lignes_c.tot else 0 end) as debit,(case when lignes_c.tot>0 then lignes_c.tot else 0 end) as credit,lignes_c.date,case when echeance> '19000101' then lignes_c.echeance else null end as echeance,lignes_c.commentair as info
from LIGNES_C
inner join FOU on lignes_c.id_fou=fou.f_id
where (lignes_c.lettrage <= 0) and (lignes_c.type_ligne >= 0)
order by fou.f_id,lignes_c.date".UnIndent(8);
List<BookingDescriptor> bookingDescriptors = Api.Zselect<BookingDescriptor>(Globals.RepData, reqSql);
if (bookingDescriptors == null)
{
Globals.MercatorTasksToMain.Log("Select : " + Api.LastError, isError: true);
return;
}
MercatorAI.FactoryOpenAI factory = new MercatorAI.FactoryOpenAI();
MercatorAI.Interfaces.IOpenAiChat openAiChat = factory.CreateOpenAiChat("gpt-5", out string error);
if (!string.IsNullOrEmpty(error))
{
Globals.MercatorTasksToMain.Log("Impossible d'initialiser le chat d'OpenAI : " + error, isError: true);
return;
}
string prompt = @"Voici une liste de postes ouverts comptables fournisseurs en date de ce jour au format JSON.
Indique-moi les anomalies potentielles comme :
- mouvements non lettrés qui pourraient l'être (montants identiques ou semblables, solde de plusieurs mouvements à zéro ou proche de zéro, ...),
- paiements sans facture,
- factures sans règlement mais en ignorant celles qui ne sont pas encore échues,
- d'autres anomalies que tu détecterais.
Les paiements sont dans les journaux ING et VBDA. ODV est un journal d'opérations diverses de correction.
JSON :".UnIndent(4) + Api.JsonConvertSerializeObject(bookingDescriptors);
string htmlResponse;
try
{
htmlResponse = openAiChat.Ask(prompt, systemMessage: "Tu es un assistant comptable expert.");
}
catch (Exception ex)
{
Globals.MercatorTasksToMain.Log(ex.Message, isError: true);
return;
}
IneoSmtp.Smtp mail = Globals.MercatorTasksToMain.GetNewStmp();
mail.Subject = "Analyse postes ouverts fournisseurs";
mail.Message = htmlResponse;
mail.ForceHtml = true;
if (!mail.SendMail())
Globals.MercatorTasksToMain.Log("Erreur envoi mail : " + mail.Error, isError: true);
else
Globals.MercatorTasksToMain.Log("Mail correctement envoyé à " + mail.Recipient);
}
public class BookingDescriptor
{
public string IdFournisseur { get; set; }
public string Journal { get; set; }
public Int64 Piece { get; set; }
public decimal Debit { get; set; }
public decimal Credit { get; set; }
public DateTime Date { get; set; }
public DateTime? Echeance { get; set; }
public string Info { get; set; }
}
}
}
Les informations sont envoyées à un tiers (OpenAI). Il ne faut donc en aucun cas transmettre des données revêtant un caractère confidentiel.
Les réponses données par ce code sont générées automatiquement à l’aide d’un modèle d’intelligence artificielle (IA). Bien que conçu pour fournir des réponses utiles et pertinentes, ces réponses peuvent être incomplètes, inexactes, partiellement ou totalement.
L’utilisateur reste seul responsable de l'évaluation et de l’utilisation de ces réponses. En aucun cas Mercator ou le fournisseur du service IA ne saurait être tenu responsable des conséquences qui découleraient de l'utilisation de ces réponses.
L'usage de ce service doit être rendu compatible avec les normes relatives au RGPD qui sont en place dans l'entreprise.