Accounting reconciliation with Copilot

Few months ago, Microsoft announced “Bank account reconciliation with Copilot” an exciting new feature for Business Central.

Bookkeepers in SMB organizations need to import bank statements and reconcile transactions with their bank ledger entries, making sure all transactions are accounted for. While Business Central already reduces effort through rule-based transaction matching, the residual work to process the unmatched transactions every week remains cumbersome and quickly accrues to lost workdays. Copilot reduces bookkeeping effort by matching more transactions and suggesting G/L accounts to post the remaining transactions.

So I remembered and old problem that is annoying for each accountant and that waste a lot of time only to reach… zero! 😅 I tried to change approach and use Copilot to gain better results.

The problem

Imagine that you are an accountant in a retail company with several shops. Each shop receives different amount of money (cash) every day and sometimes goes to the bank for deposit.

Usually these entries are posted in a transitory account with many positive (debit) entries for collection and many negative (credit) entries for deposit.

The problem is that collection and deposit are alternating and the running balance of the account never goes to zero. The zero is the magic value for accountants and it means that all cash has been deposited!

Imagine hundreds of days and hundreds of shops plus errors and unexpected events due the manual activity to handle cash in a store.

But not only cash is the problem: the accountants have similar problems with electronic payments (here in Italy “POS”) or with taxes or with accruals.

Minimum solution

The minimum acceptable solution for this problem is to manage with batches also these kind of G/L Accounts, similarly to Customers or Vendors.

I’ve always added a new “Detailed G/L Entry” table and a “Open” flag in the G/L Entries. The accountant can now play 😅 to match entries and close them.

“We need a Copilot!”

When the number of entries is huge, manual matching is very time consuming. So, after the “minimum solution”, customers always wanted something of more automated!

Math combinatorics is not applicable due the high number of necessary attempts. The software should calculate all possibile combination of entries until zero is reached and the number of these combination is… the sum of all possible binomial coefficients of the entries. 🤯

2 entries is easy… 1 attempt!
3 entries: 4 attempts
4 entries: 10 attemps
100 entries: N = 100, for K = 2 to 100 … when K is 4 the attempts overcomes 4 millions, imagine to reach 100

It’s how to find a complex password with a brute force attack 😁 and real examples are made of hundreds of entries.

I alway solved (or tried to solve) this problem with a randomic algorithm but, in the Copilot era, I asked for help to Artificial Intelligence.

ChatGPT playground

Hmm… one plus one minus one is not zero 😁 try to lower the temperature

Hmm… nothing to do. Maybe I mistaken the SYSTEM prompt. Try with something easier

No, GPT is working fine… try again in verbose mode

Wow! AI tells me to use Python and Itertools to solve this problem, but I went to AI to hoping in something special 😭

Business Central Bank Reconciliation with Copilot

I didn’t give up and I was sure that in Business Central a similar feature is available. So I analyzed the “Bank Account Reconciliation With AI” source code and the key procedure “GenerateMatchProposals” do a lot of “traditional” code and algorithms before invoke the AI:

At that point I was confused (not really, it’s a joke) because in the collective imagination AI is a powerful tool with infinite capabilities and you can talk with it in a natural way. I expected something “hey AI, here are my bank entries, please reconcile them for me, in the meantime I will take a coffee” 😁

PromptDialog page

I want to add at any cost Copilot to my Business Central customizations and I want that my customers use AI…

What is behind PromptDialog?

Easy! A page of type “PromptDialog” that must be not extensibile:

page 70001 "AIT Reconciliation"
{
    PageType = PromptDialog;
    Extensible = false;
    ApplicationArea = All;
    UsageCategory = Lists;
    ...

Plus a prompt area that store user request in a string variable:

    ...
    layout
    {
        area(Prompt)
        {
            field(PromptTxt; PromptTxt)
            {
                ApplicationArea = All;
                MultiLine = true;
                ShowCaption = false;
            }
        }
        ...

Plus a “Generate” button that… does what you want!

    actions
    {
        area(SystemActions)
        {
            systemaction(Generate)
            {
                trigger OnAction()
                begin
                    // your AI code here
                end;
            }
        }
    }

So you can use embedded (and free) Copilot features of Business Central or write your own. If you have an Azure account you can activate OpenAI services (not directly but after a module submission) and you get an “Endpoint” and an “API key”:

Next you have to go on “Azure OpenAI Studio” and create your personal distribution:

Now you can use embedded OpenAI codeunits in Business Central:

                var
                    OpenAi: Codeunit "Azure OpenAi";
                    AiDeployment: Codeunit "AOAI Deployments";
                    AiParams: Codeunit "AOAI Chat Completion Params";
                    AiMessages: Codeunit "AOAI Chat Messages";
                    AiResponse: Codeunit "AOAI Operation Response";
                    CopilotCapability: Codeunit "Copilot Capability";
                    ApiKey: SecretText;
                begin
                    if not CopilotCapability.IsCapabilityRegistered(Enum::"Copilot Capability"::"MYSPECIFICCAPABILITY") then
                        CopilotCapability.RegisterCapability(Enum::"Copilot Capability"::"MYSPECIFICCAPABILITY", '');

                    ApiKey := Format('MYSUPERSECRETAPIKEY');
                    OpenAi.SetAuthorization(Enum::"AOAI Model Type"::"Chat Completions", 'https://MYENDPOINT.openai.azure.com/', 'MYDEPLOYNAME', ApiKey);
                    OpenAi.SetCopilotCapability(Enum::"Copilot Capability"::"MYSPECIFICCAPABILITY");
                    AiParams.SetMaxTokens(4096);
                    AiParams.SetTemperature(0);
                    AiMessages.AddSystemMessage(SystemTxt);
                    AiMessages.AddUserMessage(PromptTxt);
                    OpenAi.GenerateChatCompletion(AiMessages, AiParams, AiResponse);
                    ResultTxt := AiResponse.GetResult();
                end;

But “Generate” action can call also “traditional” code, so you can mix algorithms and AI as you want. Interesting: PromptDialog works also in On Premises environments!

The “secret” is to arrange “System Message” and “User Message” to obtain a well formed (JSON) answer from AI and then elaborate it with traditional code.

My old algorithm

Let’s go back to reconciliation: “Brute force” 😁 is too slow so I always preferred random algorithm. Seems incredible, but works!

        repeat
            if Selected.Count() = Amounts.Count() then begin
                Clear(Selected);
                Amt := 0;
                Tries += 1;
            end;

            Index := Random(EntryNos.Count());
            if not Selected.Contains(Index) then begin
                Selected.Add(Index);
                Amt += Amounts.Get(Index);
            end;

            if (CurrentDateTime - DT) > 60000 then
                Error('Timeout: no matches found');
        until Amt = 0;

Conclusion

PromptDialog page, Copilot key on keyboards and so on… are incredible opportunities to enhance the software and update old features with an improved human-machine interface.

As you have seen, better results are obtained mixing algorithms and the powerful language model of GPT and we are only at the beginning of this era.

Embedding AI in traditional softwares such the ERP, can finally give the right value to process automation (the famous “struca-butun” 😅 a key which pressed do all the job for you!).