Hier tonen we hoe je met ChatGPT kunt communiceren om openstaande leveranciersposten te controleren. Dit vereist de installatie van de plugin MercatorAI.OpenAI en werkt alleen met een Core-versie van Mercator. (De code is echter ook compileerbaar in een klassieke versie). Deze code somt de openstaande leveranciersposten op en stuurt ze door, waarbij de context van de vraag wordt verduidelijkt.
De code is als volgt opgebouwd:
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("Kan de OpenAI-chat niet initialiseren: " + 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 = @"Duid eventuele mogelijke anomalieën aan, zoals:
- niet-afgepunte bewegingen die wel afgepunt zouden kunnen worden (identieke of gelijkaardige bedragen, saldo van meerdere bewegingen is nul of bijna nul, ...),
- betalingen zonder bijbehorende factuur,
- facturen zonder betaling, met uitzondering van diegene die nog niet vervallen zijn,
- andere anomalieën die je opmerkt.
De betalingen staan in de dagboeken ING en VBDA. ODV is een dagboek voor correctieve diverse boekingen.".UnIndent(8);
string systemMessage = "Je bent een expert-boekhoudassistent. Je antwoordt UITSLUITEND in Markdown. Hier is een lijst van openstaande leveranciersboekhoudkundige posten op heden in JSON-formaat.\n" + Api.JsonConvertSerializeObject(bookings);
MercatorAI.FactoryOpenAI.ChatOptions chatOptions = new MercatorAI.FactoryOpenAI.ChatOptions
{
Temperature = 0.2f, // lichte variatie toestaan
TopP = 1.0f
};
MercatorUi.Forms.Other.ChatForm.SimpleOpenAiChat("gpt-4o", "Analyseer openstaande leveranciersposten", 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; }
}
}
}
Als de hoeveelheid gegevens die naar ChatGPT gestuurd wordt te groot is, volstaat het om de leveranciers afzonderlijk te verwerken, door ze op te splitsen in blokken. Bijvoorbeeld:
where (id_fou between 'A' and 'E')
De code kan eenvoudig worden aangeroepen via een knop op het lint met deze eenvoudige C#-code:
// <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();
}
}
}
Het resultaat dat door ChatGPT wordt teruggestuurd, is een HTML-pagina die er ongeveer zo uitziet:
Bij het bekijken van de aangeleverde gegevens in JSON-formaat, identificeer ik enkele mogelijke anomalieën in de leveranciersboekhouding.
1. Niet-afgepuntte bewegingen:
- EXYZRED
- Factuur: ACH, nummer 20250348, credit 4419.84 EUR, datum 2025-06-20.
- Betaling: ING, nummer 20250119, debet 4419.84 EUR, datum 2025-06-13.
- In evenwicht maar niet afgepunt.
2. Betalingen zonder factuur:
AG
- ING, nummer 20250134, betaling 1000.0 EUR, datum 2025-07-03.
- Geen overeenkomstig bedrag gevonden in ACH.
LXABC
- ING, nummer 20250109, betaling 191.88 EUR, datum 2025-06-02.
- Geen overeenkomst gevonden.
3. Facturen zonder betaling (behalve diegene die nog niet vervallen zijn) :
BELGACOM
- ACH, nummer 20250324, credit 306.47 EUR, vervaldatum 2025-06-23.
- Nog niet betaald, maar een gelijkaardige betaling aan Proximus bestaat.
HUGO
- ACH, nummer 20250311, credit 576.6 EUR, vervaldatum 2025-06-05.
- Nog steeds niet betaald.
4. Niet-gelijkende bedragen:
PARET
- ACH, nummer 20250384, credit 32890.0 EUR, betaling aan andere leverancier (PIRET) voor 29022.5 EUR.
- Mogelijk een verschil om te onderzoeken (fout bedrag of verkeerde leverancier).
PROXIMUS / BELGACOM
- Betaling: ING, nummer 20250126, bedrag 306.47 EUR.
- Hoewel dit bedrag aan PROXIMUS werd betaald, kan het overeenkomen met een factuur van BELGACOM.
5. Gedeeltelijk betaalde facturen of dubbels:
- CHARGESITE
- Twee afzonderlijke debetboekingen (15.0 EUR en 8.68 EUR) op hetzelfde nummer (20250007).
- Moet gecontroleerd worden op een mogelijke dubbelboeking.
Deze anomalieën moeten in detail worden onderzocht om hun geldigheid te bevestigen. Zorg ervoor dat de afpunting correct gebeurt voor overeenkomende transacties en dat de betalingen aan de juiste nummers zijn gekoppeld.
Deze functionaliteit kan ook uitgevoerd worden via de consolemodus van Mercator. Zo kan de controle buiten de werkuren plaatsvinden en via e-mail verzonden worden naar de persoon die deze boekhoudgegevens moet nakijken. Merk op dat de code hier geen gebruik meer maakt van Async-methodes.
De code is als volgt:
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("Kan de OpenAI-chat niet initialiseren: " + error, isError: true);
return;
}
string prompt = @"Hier is een lijst van openstaande leveranciersposten op dit moment in JSON-formaat.
Duid eventuele mogelijke anomalieën aan, zoals:
- niet-afgepunte bewegingen die wel afgepunt zouden kunnen worden (identieke of gelijkaardige bedragen, saldo van meerdere bewegingen is nul of bijna nul, ...),
- betalingen zonder bijbehorende factuur,
- facturen zonder betaling, met uitzondering van diegene die nog niet vervallen zijn,
- andere anomalieën die je opmerkt.
De betalingen staan in de dagboeken ING en VBDA. ODV is een dagboek voor correctieve diverse boekingen
JSON:".UnIndent(4) + Api.JsonConvertSerializeObject(bookingDescriptors);
string htmlResponse;
try
{
htmlResponse = openAiChat.Ask(prompt, systemMessage: "Je bent een expert in boekhoudkundige assistentie.");
}
catch (Exception ex)
{
Globals.MercatorTasksToMain.Log(ex.Message, isError: true);
return;
}
IneoSmtp.Smtp mail = Globals.MercatorTasksToMain.GetNewStmp();
mail.Subject = "Analyseer openstaande leveranciersposten";
mail.Message = htmlResponse;
mail.ForceHtml = true;
if (!mail.SendMail())
Globals.MercatorTasksToMain.Log("Fout bij verzenden van e-mail: " + mail.Error, isError: true);
else
Globals.MercatorTasksToMain.Log("E-mail correct verzonden naar " + 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; }
}
}
}
De informatie wordt verzonden naar een derde partij (OpenAI). Er mogen dus geen vertrouwelijke gegevens worden doorgestuurd.
De antwoorden die door deze code worden gegenereerd, zijn automatisch gegenereerd met behulp van een model voor kunstmatige intelligentie (AI). Hoewel het ontworpen is om nuttige en relevante antwoorden te geven, kunnen deze antwoorden onvolledig, onjuist of gedeeltelijk fout zijn.
De gebruiker blijft als enige verantwoordelijk voor de evaluatie en het gebruik van deze antwoorden. Mercator of de leverancier van de AI-dienst kan in geen geval aansprakelijk worden gesteld voor de gevolgen van het gebruik van deze antwoorden.
Het gebruik van deze dienst moet compatibel zijn met de GDPR-voorschriften die binnen het bedrijf van toepassing zijn.