Break out of isolation with .ChangeCompany

In this video, I demonstrate how to access data outside the current company and how to run objects in another company.

https://youtu.be/C36Gp8PxiYo

This is another video in my Beginning AL series with essential information for developers who are new to AL.


In this video, Erik demonstrates how to break out of the company isolation model in Business Central using the .ChangeCompany function. He walks through practical examples of reading data from other companies, looping through all companies in a database, and discusses the important limitation that .ChangeCompany is shallow — it only applies to the specific record variable, not to any downstream code triggered from it. Finally, he shows how the Task Scheduler can be used to execute full business logic in another company’s context.

Understanding Company Isolation in Business Central

In Business Central, the concept of a company is a self-contained, isolated unit. When you’re working in a company, all your data operations — reading customers, posting transactions, and so on — are confined to that company. You never have to worry about accidentally pulling data from another company; the isolation is built in.

But what if you need data from another company? For example, Erik’s real-world scenario involved showing information about a customer from another company directly on the customer card. To break out of that isolation, you need something special: the .ChangeCompany function.

Basic Usage of .ChangeCompany

The simplest use of .ChangeCompany is to call it on a record variable before performing any data operations. This redirects all subsequent reads and writes on that variable to the specified company.

For example, if you have two companies — “CRONUS Canada” and “My Company” — you can read the first customer from “My Company” while your session is running in “CRONUS Canada”:

var
    Customer: Record Customer;
begin
    Customer.ChangeCompany('My Company');
    Customer.FindFirst();
    Message('%1', Customer.Name);
end;

This will display the first customer from “My Company” even though you’re currently working in “CRONUS Canada”.

Using Two Record Variables

You can also use two separate record variables — one pointing at the current company and one at another — to compare data side by side:

var
    Customer: Record Customer;
    Customer2: Record Customer;
begin
    Customer.ChangeCompany('My Company');
    Customer.FindFirst();

    Customer2.FindFirst();

    Message('%1 versus %2', Customer.Name, Customer2.Name);
end;

Here, Customer reads from “My Company” while Customer2 reads from the current company. Each record variable operates independently.

Looping Through All Companies

A common pattern is to loop through all companies in the database and perform operations in each one. Erik’s final code example does exactly this — it filters out the current company and then iterates through the remaining ones:

pageextension 50139 CustomerListExt extends "Customer List"
{
    trigger OnOpenPage()
    var
        Company: Record Company;
        Customer: Record Customer;
        Customer2: Record Customer;
    begin
        Company.setfilter(Name, '<>%1', CompanyName());
        if Company.FindSet() then
            repeat
                Customer.ChangeCompany(Company.Name);
                Customer.FindFirst();
                Message('%1', Customer.Name);
            until Company.Next() = 0;
    end;
}

There are a few important details here:

  • The Company table has both a Name field and a Display Name field. You must always use the Name field (the primary key) with .ChangeCompany. The Display Name can be changed to anything and is unreliable for this purpose.
  • The built-in function CompanyName() returns the name of the company your session is currently running in, making it easy to filter it out.
  • The filter '<>%1' ensures you only look at other companies, which is the typical use case when you want to aggregate or compare data across companies.

The Shallow Scope Problem

One critical thing to understand about .ChangeCompany is that it is not scope-based. It only affects the specific record variable you call it on. Any downstream code that gets triggered — such as validation logic, OnInsert/OnModify triggers, or other record variables created inside called functions — will not automatically inherit the company change.

For example, if you do:

Customer.ChangeCompany('My Company');
Customer.Validate("Customer Posting Group", 'DOMESTIC');

The validation code for “Customer Posting Group” may internally use other record variables (like the Customer Posting Group table). Those variables will operate in your current company, not in “My Company.” This mismatch can cause subtle and hard-to-debug errors.

The rule of thumb: .ChangeCompany works well for reading data from other companies and for simple direct writes. But if you need to trigger complex business logic, you need a different approach.

Using the Task Scheduler for Cross-Company Execution

For scenarios where you need to execute full business logic in another company — such as posting documents — the recommended approach is to use the Task Scheduler. A commonly overlooked feature of the Task Scheduler is that you can supply a company name as a parameter, which causes the scheduled codeunit to execute in a completely new session scoped to that company.

This is exactly what the base application does in one place: the IC Inbox Outbox Subscribers codeunit. As part of Intercompany (IC) functionality, when a transaction is inserted into a receiving company’s inbox, the code:

  1. Uses .ChangeCompany to read the IC Partner record from the receiving company.
  2. Checks whether that IC Partner has “Auto Accept Transactions” enabled.
  3. If so, fires off the Task Scheduler to run a codeunit in the receiving company’s context, passing the necessary parameters.

Because the Task Scheduler creates a new session in the target company, you get full scope — all record variables, triggers, validation logic, and codeunits will operate within that company. You can run reports, post documents, and do anything that doesn’t require a UI.

This pattern has become the recommended approach for customers with many companies who need cross-company processing:

  1. Use .ChangeCompany to set up or read the necessary data.
  2. Create a codeunit that performs the business logic (posting, processing, etc.).
  3. Use the Task Scheduler to execute that codeunit in the target company.

Summary

To break out of the company isolation in Business Central, you have two options:

  • .ChangeCompany on a record variable — Lets you read and write data in another company. However, it is shallow: only the specific record variable is affected. Any triggered code, validation logic, or other record variables within called functions will still run in the current company context. This makes it ideal for reading data or performing simple direct writes.
  • Task Scheduler with a company parameter — Lets you execute a codeunit in a fully scoped session within another company. This gives you deep execution context, enabling you to post documents, run reports, and execute complex business logic. This is the pattern used in the base app’s Intercompany functionality.

Always remember to use the Name field (not Display Name) from the Company table, and use the built-in CompanyName() function to reference your current company.