Multi-companies solution designing in Business Central

Some of you might have struggled against Business Central companies architecture.

For exemple you can need to build a page showing aggregation of table across all companies, or create a process that will run on Company A and then post something in Company B.

The C/AL and AL code allow us to use “CHANGECOMPANY” on record but the problem is still on all existing feature that are build arround the idea “to be executed on current company”.

Here are the different solution you can consider

Page showing Multi companies data

Simple example, you need a page showing bank account of all companies.

Easiest way is to do some code on a new page to load data for each company in memory (this is not recommanded on big tables like ledgers)

-First step is to add a field “Company Name” on the table, the field will remain empty in SQL table (only used in RAM)

-Then, create a new page, with property SourceTableTemporary = true and ModifyAllowed = false as the data will stay only in local memory

-Put following few line of code when opening the page in order to load the data of all companies :


Rec.DeleteAll(); // record is temporary in ram only

  if BankAccount.findfirst() then 
      Rec."Company Name" := Companies.Name;
    until BankAccount.Next() = 0;
Until Companies.Next() = 0;

Pro/Con :
+ Easy to install
+ No performance impact on existing table
– Loading time on page opening
– Data not available out of the page

Second option is to store everything in a complete new table and fill it automaticaly with original table events.

This require to create a new table with DataPerCompany = false and same fields as original + Company Name.

Then subscribe on Insert, Modify, delete and rename event to update this table from every companies.

Pro/Con :
+ No loading time on page opening
+ Data available out of Nav (eg Jet Report, …)
– Performance impact on existing table, double storage
– Have to fill the table on first installation
– Change on original table structure have to be propagate on the new table

Execute a function remotely using Web Service

Let’s say you are in Company A, and during a process want to execute a function in Company B.
Then if the code in Company B trigger an error or not, handle it back in Company A

It’s possible to publish any codeunit in Business Central/Nav as Webservice, then call it with some code to request a fonction for execution on another any company (codeunit function must have the property local = no)

Note : Microsoft announced at Direction 2021 to plan the removing of SOAP in upcoming years. This might be replaced by REST call instead, I don’t have code yet for that.

I made a codeunit to manage this call easly as I know it can be touchy to found informations about SOAP requets enveloppe for Nav.
Import following codeunit “Navcraft SOAP Helper”  SOAP (.txt and .al object)

Publish the codeunit you want to use from another company (page “Web Services”, create a new line with Object Type = Codeunit, Object ID, define a Service Name and tick “Published”)

SOAP Url will be generated on Web Service page, afterwhat you can use following code :

URL := 'https://localhost:7047/BC190/WS/%1/MyCodeunitWS'; // copy from Web service page "SOAP URL" and replace company with %1
URL := Strsubstno(URL, 'Cronus'); // execute the code on 'Cronus' company

// (URL, Function, TimeOut MS)
SOAPHelper.PrepareRequestCurrentUser(URL, 'UpdateCustomer', 10000);
// (Argument, Value)
SOAPHelper.AddParameter('customerNo', 'C-0001');
if SOAPHelper.SendRequest(ErrDescr) then
  result := SOAPHelper.GetResultText()

-PrepareRequestCurrentUser : Start a new request with current logged user and basic authentication. The user WS Key is generated on the fly then removed if it was not set before.

-PrepareRequest : Start a new request using specific user, passord and authentication method (Basic or NTLM). Is you want to use windows authentication, please enable NTLM property on the service configuration.

-AddParameter : Add a pair of argument name (as it is define in codeunit function) and the value as text. Datatype different then text will be parsed internaly by the service.

-SendRequest : Execute the web service call and wait until the execution end. Return if it success or not

If any error occur, the description is saved in text variable passed to SendRequest.

Please Clear the codeunit if you want to do several call in a row.

Pro/Con :
+ The WS call is executed immediatly and the result can be handled
+ Possibility to handle error details and rollback local transaction if the remote call fail
– The web service can be tricky to setup, you can have struggle with authentication, URL and HttpS
– SOAP will be removed in upcoming years

Execute a codeunit remotely using Job Queue

It is also possible to create a job queue in another company to be runned instantly.

This can only be used to execute OnRun() codeunit fonction.

Remote JobQueue (.txt and .al object)

It can be used as following :

IF RemoteCdu.RunRemoteJobQueue('Cronus', CODEUNIT::"Custom Codeunit", TRUE, ErrDescription) THEN
  ERROR('Run Codeunit remotely failed : \\' + ErrDescription);

The WaitCompletion option will look the job queue to finish and catch any error into Job Queue Log Entry.

Pro/Con :
+ Nothing to setup (just check if task scheduler is enabled and concurent tasks limitations)
+ Can be used for parallels tasks (multi-threading)
– Unable to rollback if error occur, the WaitCompletion option use COMMIT to be able to catch error
– Can only execute OnRun() function

Execute a codeunit remotely using StartSession

The standard function STARTSESSION(VAR SessionID, CodeunitID [, CompanyName] [,Record]) allow us to run a codeunit in another company with a new session. Have a look at following link :–sessions-

Pro/con :
+ Nothing to setup, easy to use, standard
+ Can be used for parallels tasks (multi-threading)
– Not able to wait for completion
– Unable to handle error neither rollback (only manual consultation in windows event viewer)
– Can only execute OnRun() function

Leave a Reply

Your email address will not be published. Required fields are marked *