Codeunit Support
ToolBox scripts can declare codeunit variables and call the member functions of a set of supported standard Business Central codeunits. This brings a large body of platform functionality — math, REST, email, document posting, address formatting, and more — into your scripts with familiar Variable.Method(...) syntax.
Declaring and Calling a Codeunit
Declare a codeunit variable in a var block with Codeunit <Subtype>, then call its members with dot notation. Subtype names that contain spaces or punctuation must be quoted.
var
MyMath: Codeunit Math;
Client: Codeunit "Rest Client";
begin
Message('Square root of 2 = %1', MyMath.Sqrt(2));
Message('Pi = %1', MyMath.Pi());
end;
The editor completes codeunit subtype names after you type the codeunit keyword, and completes member functions after a codeunit variable and a dot. See Editor.
Extensible: Codeunit support is open. Other apps register additional subtypes through the interpreter's
Codeunit_Type_Hgdenum and theInterpreterCodeunit_Hgdinterface, and those subtypes then appear in completion and are callable from scripts exactly like the built-in ones below.
Only the subtypes listed on this page can be declared as codeunit variables. Any other codeunit, standard or custom, can still be started by number with the Codeunit.Run(Id[, Rec]) statement, which runs its OnRun trigger and returns nothing. Codeunit variables are reference types: passing one to a procedure shares the same instance. A procedure cannot return a codeunit.
Supported Codeunits
| Subtype | Purpose |
|---|---|
Math |
Full math library (works in Decimal) |
Type Helper |
Date formatting, time splitting, bitwise, hex |
Rest Client |
REST/HTTP calls |
Temp Blob |
In-memory blob and stream creation |
Base64 Convert |
Base64 encode/decode |
Regex |
Regular expression match / replace / split |
Data Compression |
Zip and gzip |
Environment Information |
Environment / tenant facts |
No. Series |
Number series |
Language |
Language and culture lookups |
Confirm Management |
Non-interactive confirmations |
Sales-Post / Purch.-Post |
Post sales / purchase documents |
Release Sales Document / Release Purchase Document |
Release / reopen documents |
Gen. Jnl.-Post Batch |
Post a general journal batch |
Archive Management |
Archive and restore documents |
CheckManagement |
Void checks |
SegManagement |
Log interactions and segments |
Email / Email Message |
Compose and send email |
Format Address |
Format postal addresses |
The sections below list each codeunit's members. Parameters are shown with their types; overloads are noted where they exist.
Math
The Math codeunit is a full math library. All operations work in Decimal — inputs and return values are decimals.
| Member | Returns | Notes |
|---|---|---|
Pi() |
Decimal | |
E() |
Decimal | |
Abs(Value) |
Decimal | |
Sqrt(Value) |
Decimal | |
Pow(X, Y) |
Decimal | X raised to Y |
Exp(Value) |
Decimal | e raised to Value |
Log(Value) |
Decimal | Natural logarithm |
Log10(Value) |
Decimal | |
Sin / Cos / Tan |
Decimal | Angle in radians |
Sinh / Cosh / Tanh |
Decimal | Hyperbolic |
Asin / Acos / Atan |
Decimal | Inverse; result in radians |
Floor(Value) |
Decimal | |
Ceiling(Value) |
Decimal | |
Truncate(Value) |
Decimal | Integer part |
Sign(Value) |
Decimal | -1, 0, or 1 |
BigMul(A, B) |
Decimal | |
IeeeRemainder(X, Y) |
Decimal |
var
M: Codeunit Math;
begin
Message('sin(pi/2) = %1', M.Sin(M.Pi() / 2)); // 1
Message('2^10 = %1', M.Pow(2, 10)); // 1024
Message('ceil(4.2) = %1', M.Ceiling(4.2)); // 5
end;
Type Helper
| Member | Returns | Notes |
|---|---|---|
GetLocalizedMonthToInt(Month) |
Integer | Localized month name to 1..12 |
CompareDateTime(A, B) |
Integer | -1, 0, or 1 |
FormatDate(Date, Format: Integer) |
Text | |
FormatDate(Date, Format: Text, Culture: Text) |
Text | |
FormatDateWithCurrentCulture(Date) |
Text | |
GetHmsFromTime(var Hour, var Minute, var Second, Time) |
Split a time | |
AddHoursToDateTime(DateTime, Hours) |
DateTime | |
BitwiseAnd(A, B) / BitwiseOr(A, B) / BitwiseXor(A, B) |
Integer | |
IntToHex(Value) |
Text |
var
TH: Codeunit "Type Helper";
H: Integer;
M: Integer;
S: Integer;
begin
TH.GetHmsFromTime(H, M, S, 143005T);
Message('%1h %2m %3s', H, M, S); // 14h 30m 5s
Message('255 in hex = %1', TH.IntToHex(255)); // FF
end;
Rest Client
Makes REST/HTTP calls. Configure the client, then call a verb.
| Member | Returns | Notes |
|---|---|---|
Initialize() |
Reset the client | |
SetBaseAddress(Url) |
||
GetBaseAddress() |
Text | |
SetTimeout(Timeout: Duration) |
||
GetTimeout() |
Duration | |
SetAuthorizationHeader(Value) |
||
SetDefaultRequestHeader(Name, Value) |
||
SetUserAgentHeader(Value) |
||
AddCertificate(Certificate) |
Base64-encoded certificate | |
Get(Path) |
Text | GET; returns the response body |
Delete(Path) |
Text | DELETE; returns the response body |
GetAsJson(Path) |
JsonToken | GET for JSON |
PostAsJson(Path, Body: JsonObject) |
JsonToken | POST |
PutAsJson(Path, Body: JsonObject) |
JsonToken | PUT |
PatchAsJson(Path, Body: JsonObject) |
JsonToken | PATCH |
Differs from standard AL: In ToolBox,
GetandDeletereturn the response body as Text directly (not anHttpResponseMessage), and the*AsJsonmethods return aJsonToken. This makes the common cases one line each.
var
Client: Codeunit "Rest Client";
Body: JsonObject;
Response: JsonToken;
begin
Client.SetBaseAddress('https://api.example.com');
Client.SetDefaultRequestHeader('Accept', 'application/json');
// Simple GET returning text
Message(Client.Get('/status'));
// POST JSON, read JSON back
Body.Add('name', 'Widget');
Response := Client.PostAsJson('/products', Body);
Message('Created id: %1', Response.AsObject().GetInteger('id'));
end;
Temp Blob
An in-memory blob used mainly to create streams. See Streams.
| Member | Returns | Notes |
|---|---|---|
CreateInStream(var InStream) |
||
CreateInStream(var InStream, Encoding: Text) |
Encoding: utf8 (default) / utf16 / msdos / windows, or TextEncoding::UTF8 etc. |
|
CreateOutStream(var OutStream) |
||
CreateOutStream(var OutStream, Encoding: Text) |
Same encoding options | |
HasValue() |
Boolean | |
Length() |
Integer | Size in bytes |
FromRecordRef(Record, FieldNo) |
Load the blob from a record field (FromRecord is accepted as an alias) |
|
ToRecordRef(Record, FieldNo) |
Store the blob into a record field |
Blob fields cannot be read or assigned as values through Rec.Field, but they open streams directly: Rec.BlobField.CreateOutStream(Out) / CreateInStream(In) / HasValue / Length (see Streams). FromRecordRef/ToRecordRef remain the by-field-number alternative.
Base64 Convert
| Member | Returns | Notes |
|---|---|---|
ToBase64(Value: Text) |
Text | Encode text |
ToBase64(Value: Text, InsertLineBreaks: Boolean) |
Text | |
ToBase64(InStream) |
Text | Encode a stream |
ToBase64(InStream, InsertLineBreaks: Boolean) |
Text | |
FromBase64(Base64: Text) |
Text | Decode to text |
FromBase64(Base64: Text, OutStream) |
Text | Decode into a stream |
var
B64: Codeunit "Base64 Convert";
Encoded: Text;
begin
Encoded := B64.ToBase64('Hello ToolBox');
Message('Encoded: %1', Encoded);
Message('Decoded: %1', B64.FromBase64(Encoded));
end;
Regex
| Member | Returns | Notes |
|---|---|---|
IsMatch(Input, Pattern) |
Boolean | |
Replace(Input, Pattern, Replacement) |
Text | Replace every match |
Split(Input, Pattern) |
List of [Text] | Split on every match |
var
Rx: Codeunit Regex;
Parts: List of [Text];
Part: Text;
begin
if Rx.IsMatch('ABC-123', '^[A-Z]+-\d+$') then
Message('Valid code');
Parts := Rx.Split('a1b2c3', '\d');
foreach Part in Parts do
Message('[%1]', Part);
end;
Data Compression
Create or open zip archives, and compress/decompress gzip streams. See Streams for a full worked example.
| Member | Returns | Notes |
|---|---|---|
CreateZipArchive() |
Start a new archive | |
OpenZipArchive(InStream) |
Open an existing archive | |
OpenZipArchive(InStream, OpenForUpdate: Boolean) |
||
CloseZipArchive() |
||
AddEntry(InStream, PathInArchive: Text) |
||
GetEntryList() |
List of [Text] | Entry names |
ExtractEntry(EntryName, OutStream) |
Integer | Returns the entry length |
SaveZipArchive(OutStream) |
Write the archive to a stream | |
IsGzip(InStream) |
Boolean | |
GzipCompress(InStream, OutStream) |
||
GzipDecompress(InStream, OutStream) |
Environment Information
Read-only facts about the current environment.
| Member | Returns | Member | Returns |
|---|---|---|---|
GetEnvironmentName() |
Text | IsSaaS() |
Boolean |
IsSandbox() |
Boolean | IsOnPrem() |
Boolean |
IsProduction() |
Boolean | IsSaaSInfrastructure() |
Boolean |
GetApplicationFamily() |
Text | CanStartSession() |
Boolean |
var
Env: Codeunit "Environment Information";
begin
if Env.IsSandbox() then
Message('Running in sandbox: %1', Env.GetEnvironmentName())
else
Message('Running in production');
end;
No. Series
| Member | Returns | Notes |
|---|---|---|
GetNextNo(NoSeriesCode) |
Code | Get and consume the next number |
GetNextNo(NoSeriesCode, UsageDate) |
Code | |
GetNextNo(NoSeriesCode, UsageDate, HideErrorsAndWarnings) |
Code | |
PeekNextNo(NoSeriesCode) |
Code | Preview without consuming |
PeekNextNo(NoSeriesCode, UsageDate) |
Code |
Language
Language and culture lookups.
| Member | Returns | Member | Returns |
|---|---|---|---|
GetUserLanguageCode() |
Code | GetDefaultApplicationLanguageId() |
Integer |
GetLanguageId(LanguageCode) |
Integer | ValidateApplicationLanguageId(Id) |
|
GetLanguageIdOrDefault(LanguageCode) |
Integer | ValidateWindowsLanguageId(Id) |
|
GetFormatRegionOrDefault(FormatRegion) |
Text | GetParentLanguageId(Id) |
Integer |
GetLanguageCode(LanguageId) |
Code | GetTwoLetterIsoLanguageName(Id) |
Text |
GetWindowsLanguageName(LanguageCode) |
Text | GetLanguageIdFromCultureName(Name) |
Integer |
GetWindowsLanguageName(LanguageId) |
Text | GetCultureName(LanguageId) |
Text |
GetDefaultApplicationLanguageId() |
Integer | GetCurrentCultureName() |
Text |
Confirm Management
Get a confirmation answer that works even when no dialog can be shown (for example, in a Job Queue session).
| Member | Returns | Notes |
|---|---|---|
GetResponse(ConfirmQuestion) |
Boolean | false when no dialog is possible |
GetResponse(ConfirmQuestion, DefaultButton) |
Boolean | |
GetResponseOrDefault(ConfirmQuestion) |
Boolean | Default when no dialog is possible |
GetResponseOrDefault(ConfirmQuestion, DefaultButton) |
Boolean |
Document Posting and Release
These codeunits take Record arguments — declare a record variable, load the document, and pass it.
Sales-Post / Purch.-Post
| Codeunit | Member | Returns | Notes |
|---|---|---|---|
Sales-Post |
SetSuppressCommit(Suppress) |
||
Sales-Post |
Run(SalesHeader: Record) |
Boolean | Post a sales document |
Purch.-Post |
SetSuppressCommit(Suppress) |
||
Purch.-Post |
Run(PurchaseHeader: Record) |
Boolean | Post a purchase document |
var
SalesHeader: Record "Sales Header";
Poster: Codeunit "Sales-Post";
begin
SalesHeader.SetRange("Document Type", SalesHeader."Document Type"::Order);
if SalesHeader.FindFirst() then begin
Poster.SetSuppressCommit(false);
if Poster.Run(SalesHeader) then
Message('Posted order %1', SalesHeader."No.");
end;
end;
Release Sales Document / Release Purchase Document
| Member | Returns | Notes |
|---|---|---|
Run(Header: Record) |
Boolean | Release with full checks |
PerformManualRelease(Header) |
||
PerformManualReopen(Header) |
||
Reopen(Header) |
Gen. Jnl.-Post Batch
| Member | Returns | Notes |
|---|---|---|
Run(GenJournalLine: Record) |
Boolean | Post the journal batch the line belongs to |
Archive Management
| Member | Notes |
|---|---|
ArchiveSalesDocument(SalesHeader) |
Archive with confirmation |
ArchSalesDocumentNoConfirm(SalesHeader) |
Archive without confirmation |
AutoArchiveSalesDocument(SalesHeader) |
Archive according to setup |
StoreSalesDocument(SalesHeader, InteractionExist) |
|
ArchivePurchDocument(PurchaseHeader) |
Archive with confirmation |
ArchPurchDocumentNoConfirm(PurchaseHeader) |
Archive without confirmation |
AutoArchivePurchDocument(PurchaseHeader) |
Archive according to setup |
StorePurchDocument(PurchaseHeader, InteractionExist) |
|
RestoreSalesDocument(SalesHeaderArchive) |
Restore from an archive |
CheckManagement
| Member | Notes |
|---|---|
VoidCheck(GenJournalLine) |
Void the check on a payment journal line |
FinancialVoidCheck(CheckLedgerEntry) |
Financially void a posted check |
SegManagement
| Member | Returns | Notes |
|---|---|---|
LogDocument(DocumentType, DocumentNo, DocNoOccurrence, VersionNo, AccountTableNo, AccountNo, SalespersonCode, CampaignNo, Description, OpportunityNo) |
Integer | Log a document interaction; returns the entry no. |
FindInteractionTemplateCode(DocumentType) |
Code | Template for a document type |
LogSegment(SegmentHeader, Deliver, Followup) |
Log a segment and its lines |
Email and Email Message
Compose a message (optionally with attachments) and send it.
Email Message
| Member | Notes |
|---|---|
Create(To, Subject, Body) |
Recipients separated by semicolons |
Create(To, Subject, Body, HtmlFormatted) |
|
AddAttachment(Name, ContentType, Base64: Text) |
|
AddAttachment(Name, ContentType, InStream) |
|
GetSubject() |
Returns Text |
GetBody() |
Returns Text |
| Member | Returns | Notes |
|---|---|---|
Send(Message: Codeunit "Email Message") |
Boolean | Send a composed message |
Send(To, Subject, Body) |
Boolean | |
Send(To, Subject, Body, HtmlFormatted) |
Boolean |
var
Msg: Codeunit "Email Message";
Mail: Codeunit Email;
begin
Msg.Create('user@example.com', 'ToolBox report', '<h1>Done</h1>', true);
Msg.AddAttachment('report.txt', 'text/plain', 'UmVwb3J0'); // base64 content
if Mail.Send(Msg) then
Message('Email sent');
end;
Format Address
Formats postal addresses. Each method has two calling shapes:
- Native array form — the first argument is a
var AddrArray: array[8] of Textthat the
method fills, exactly like real AL. It has no meaningful return value.
- Text/record-only form — omit the array; the method returns the compressed address lines
as a List of [Text].
| Member | Text-only returns | Notes |
|---|---|---|
FormatAddr(Name, Name2, Contact, Addr, Addr2, City, PostCode, County, CountryCode) |
List of [Text] | Free-field address |
UseCounty(CountryCode) |
Boolean | Whether county is shown for a country |
Company(CompanyInformation) |
List of [Text] | |
Customer(Customer) |
List of [Text] | |
Vendor(Vendor) |
List of [Text] | |
BankAcc(BankAccount) |
List of [Text] | |
Location(Location) |
List of [Text] | |
ContactAddr(Contact) |
List of [Text] | |
Employee(Employee) |
List of [Text] | |
SalesHeaderSellTo / SalesHeaderBillTo / SalesHeaderShipTo (SalesHeader) |
List of [Text] | Sales order addresses |
PurchHeaderBuyFrom / PurchHeaderPayTo / PurchHeaderShipTo (PurchaseHeader) |
List of [Text] | Purchase order addresses |
SalesInvSellTo / SalesInvBillTo / SalesInvShipTo (SalesInvoiceHeader) |
List of [Text] | Posted sales invoice addresses |
PurchInvBuyFrom / PurchInvPayTo / PurchInvShipTo (PurchInvHeader) |
List of [Text] | Posted purchase invoice addresses |
The
*ShipTooverloads onSalesHeader/SalesInvoiceHeadertake an extraCustAddr: array[8] of Textin their native array form (ship-to differs from the customer address) and returnBoolean.
var
Formatter: Codeunit "Format Address";
Customer: Record Customer;
Lines: List of [Text];
Line: Text;
AddrArray: array[8] of Text;
I: Integer;
begin
Customer.Get('10000');
// Text-only form -> List of [Text]
Lines := Formatter.Customer(Customer);
foreach Line in Lines do
Message('%1', Line);
// Native array form -> fills AddrArray
Formatter.Customer(AddrArray, Customer);
for I := 1 to ArrayLen(AddrArray) do
if AddrArray[I] <> '' then
Message('Line %1: %2', I, AddrArray[I]);
end;