Record Methods
Introduction
Records are the primary way you interact with Business Central data in ToolBox. Every record variable represents a row (or set of rows) in a database table. This page documents all the record methods available in ToolBox for navigating, filtering, counting, and manipulating data.
Each method includes its syntax, description, and a practical example.
Declaring Record Variables
Before using any record method, you must declare a record variable that references a specific table.
var
Customer: Record Customer;
SalesHeader: Record "Sales Header";
Item: Record Item;
begin
// You can now use these variables to interact with their tables
end;
Fields
Fields are accessed with dot notation and quoted when the name contains spaces or punctuation: Customer."No.", Customer."Balance (LCY)". Names are case-insensitive. The system fields SystemId, SystemCreatedAt, SystemCreatedBy, SystemModifiedAt, and SystemModifiedBy are available on every table.
Fields of these types can be read and assigned: Integer, BigInteger, Decimal, Text, Code, Boolean, Date, Time, DateTime, Duration, Option (including enum fields), Guid, and DateFormula. Blob fields are not readable as values but open streams directly (Rec.BlobField.CreateOutStream(Out), CreateInStream(In), HasValue, Length; see Streams). Media, MediaSet, RecordId, and TableFilter fields cannot be accessed.
Option and enum values are written Rec.Field::Member, quoting the member when needed:
SalesHeader.SetRange("Document Type", SalesHeader."Document Type"::Order);
if SalesHeader.Status = SalesHeader.Status::"Pending Approval" then
Message('Status caption: %1', Format(SalesHeader.Status));
Assigning a text longer than the field length raises an overflow error; use CopyStr(Value, 1, MaxStrLen(Customer.Name)) to fit the value.
Navigation Methods
FindSet
record.FindSet([forUpdate: Boolean]): Boolean
Finds a set of records matching the current filters. Returns true if at least one record is found. Use this method when you intend to iterate through multiple records.
| Parameter | Type | Description |
|---|---|---|
| forUpdate | Boolean | Optional. Set to true if you plan to modify records |
var
Customer: Record Customer;
begin
Customer.SetRange("Country/Region Code", 'US');
if Customer.FindSet() then
repeat
Message('Customer: %1', Customer.Name);
until Customer.Next() = 0;
end;
FindFirst
record.FindFirst(): Boolean
Finds the first record matching the current filters. Returns true if a record is found. Use this method when you only need the first matching record.
var
Customer: Record Customer;
begin
Customer.SetRange("Country/Region Code", 'GB');
if Customer.FindFirst() then
Message('First UK customer: %1', Customer.Name)
else
Message('No UK customers found');
end;
FindLast
record.FindLast(): Boolean
Finds the last record matching the current filters. Returns true if a record is found.
var
SalesHeader: Record "Sales Header";
begin
SalesHeader.SetRange("Document Type", SalesHeader."Document Type"::Invoice);
if SalesHeader.FindLast() then
Message('Latest invoice: %1', SalesHeader."No.");
end;
Find
record.Find(which: Text): Boolean
Finds a record based on the specified search direction. Returns true if a record is found.
| Parameter | Type | Description |
|---|---|---|
| which | Text | Search direction: '-' first, '+' last, '=' exact match on the current key values, '<' / '>' before or after the current key values, '<=' / '>=' at or before / after |
var
Customer: Record Customer;
begin
Customer."No." := '30000';
if Customer.Find('=') then
Message('Found exact match: %1', Customer.Name)
else if Customer.Find('>') then
Message('Found next: %1 - %2', Customer."No.", Customer.Name);
end;
Next
record.Next([steps: Integer]): Integer
Moves to the next record in the current set. Returns the number of records actually moved (0 means no more records).
| Parameter | Type | Description |
|---|---|---|
| steps | Integer | Optional. Number of records to skip (default 1) |
var
Item: Record Item;
begin
if Item.FindFirst() then begin
Message('First item: %1', Item.Description);
if Item.Next() <> 0 then
Message('Second item: %1', Item.Description);
if Item.Next(5) <> 0 then
Message('Skipped ahead to: %1', Item.Description);
end;
end;
Get
record.Get(primaryKeyValue1[, primaryKeyValue2, ...]): Boolean
Retrieves a record by its primary key values, ignoring any filters on the variable. Used in an expression (if Customer.Get(...) then) it returns true if the record is found. Used as a statement (Customer.Get('10000');) a missing record raises an error, as in standard AL.
var
Customer: Record Customer;
SalesLine: Record "Sales Line";
begin
// Simple primary key (single field)
if Customer.Get('10000') then
Message('Customer: %1', Customer.Name);
// Compound primary key (multiple fields)
if SalesLine.Get(SalesLine."Document Type"::Order, 'SO-001000', 10000) then
Message('Line: %1', SalesLine.Description);
end;
GetBySystemId
record.GetBySystemId(systemId: Guid): Boolean
Retrieves a record by its SystemId. Like Get, it returns Boolean in an expression and raises an error when used as a statement and the record does not exist.
Counting Methods
Count
record.Count(): Integer
Returns the total number of records matching the current filters.
var
Customer: Record Customer;
begin
Customer.SetRange("Country/Region Code", 'US');
Message('US customers: %1', Customer.Count());
end;
CountApprox
record.CountApprox(): Integer
Returns an approximate count of records. This is faster than Count() for large tables because it does not require an exact scan.
var
GLEntry: Record "G/L Entry";
begin
Message('Approximate G/L entries: %1', GLEntry.CountApprox());
end;
IsEmpty
record.IsEmpty(): Boolean
Returns true if no records match the current filters. This is more efficient than checking Count() = 0.
var
Customer: Record Customer;
begin
Customer.SetRange(Blocked, Customer.Blocked::All);
if Customer.IsEmpty() then
Message('No fully blocked customers found')
else
Message('There are blocked customers');
end;
Data Manipulation Methods
Init
record.Init()
Initializes a new record with default values. You typically call this before Insert().
var
Contact: Record Contact;
begin
Contact.Init();
Contact."No." := 'CT-NEW-001';
Contact.Name := 'New Contact';
Contact.Insert(true);
Message('Contact created: %1', Contact."No.");
end;
Insert
record.Insert([runTrigger: Boolean]): Boolean
Inserts the current record into the database. In an expression it returns true if the insert succeeds; as a statement a failure (for example a duplicate key) raises an error. The same rule applies to Modify and Delete.
Writes to database tables require write access on the ToolBox license, and the first write to a table in a session asks the user for permission unless it was granted before. Temporary records are exempt.
| Parameter | Type | Description |
|---|---|---|
| runTrigger | Boolean | Optional. Run table triggers on insert |
var
Customer: Record Customer;
begin
Customer.Init();
Customer."No." := '';
Customer.Name := 'New Customer';
Customer.Insert(true);
Message('Created customer: %1', Customer."No.");
end;
Modify
record.Modify([runTrigger: Boolean]): Boolean
Saves changes made to the current record. Returns true if the modification succeeds.
| Parameter | Type | Description |
|---|---|---|
| runTrigger | Boolean | Optional. Run table triggers on modify |
var
Customer: Record Customer;
begin
if Customer.Get('10000') then begin
Customer.Name := 'Updated Customer Name';
Customer.Modify(true);
Message('Customer updated');
end;
end;
Delete
record.Delete([runTrigger: Boolean]): Boolean
Deletes the current record from the database. Returns true if the deletion succeeds.
| Parameter | Type | Description |
|---|---|---|
| runTrigger | Boolean | Optional. Run table triggers on delete |
var
TempRecord: Record "Your Temp Table";
begin
if TempRecord.Get('OLD-001') then begin
TempRecord.Delete(false);
Message('Record deleted');
end;
end;
DeleteAll
record.DeleteAll([runTrigger: Boolean])
Deletes all records matching the current filters. Use with caution.
| Parameter | Type | Description |
|---|---|---|
| runTrigger | Boolean | Optional. Run table triggers for each deleted record |
var
LogEntry: Record "Activity Log";
begin
LogEntry.SetFilter("Activity Date", '<%1', CalcDate('<-1Y>', Today()));
if Confirm('Delete %1 old log entries?', true, LogEntry.Count()) then begin
LogEntry.DeleteAll(false);
Message('Old log entries deleted');
end;
end;
ModifyAll
record.ModifyAll(field: Field, value: Any[, useValidate: Boolean])
Modifies a specific field in all records matching the current filters.
| Parameter | Type | Description |
|---|---|---|
| field | Field | The field to modify |
| value | Any | The new value to set |
| useValidate | Boolean | Optional. Run the field's validation trigger for each modified record |
Differs from standard AL: The interpreter's third argument is
useValidate(run the field validation trigger), not therunTriggerargument you may know from platform AL.
var
Customer: Record Customer;
begin
Customer.SetRange("Salesperson Code", 'OLD-SP');
Customer.ModifyAll("Salesperson Code", 'NEW-SP', false);
Message('Salesperson reassigned for all matching customers');
end;
Filtering Methods
Reset
record.Reset()
Clears all filters, sorting, and key selections on the record variable, returning it to its default state.
var
Customer: Record Customer;
begin
Customer.SetRange("Country/Region Code", 'US');
Message('US customers: %1', Customer.Count());
Customer.Reset();
Message('All customers: %1', Customer.Count());
end;
SetRange
record.SetRange(field: Field[, fromValue: Any[, toValue: Any]])
Sets a simple filter on a field. When called with one value, it filters for an exact match. When called with two values, it filters for a range. When called with no values, it clears the filter on that field.
| Parameter | Type | Description |
|---|---|---|
| field | Field | The field to filter |
| fromValue | Any | Optional. Start of range or exact value |
| toValue | Any | Optional. End of range |
var
Customer: Record Customer;
begin
// Exact match
Customer.SetRange("Country/Region Code", 'US');
// Range filter
Customer.SetRange("No.", '10000', '30000');
// Clear filter on field
Customer.SetRange("Country/Region Code");
end;
SetFilter
record.SetFilter(field: Field, filter: Text[, arg1: Any, ...])
Sets a text-based filter on a field. This gives you more flexibility than SetRange, including wildcards and expressions.
| Parameter | Type | Description |
|---|---|---|
| field | Field | The field to filter |
| filter | Text | The filter expression (supports %1, %2, etc.) |
| arg1..N | Any | Optional. Up to seven values to substitute into the filter |
For a case-insensitive wildcard search build the filter text with +; do not combine @ with a quoted literal inside the filter string ('@''abc''' matches nothing):
Customer.SetFilter(Name, '@*' + SearchText + '*');
var
Customer: Record Customer;
begin
// Wildcard filter
Customer.SetFilter(Name, '*Sport*');
// Expression filter
Customer.SetFilter("Balance (LCY)", '>%1', 10000);
// Multiple conditions
Customer.SetFilter("No.", '%1|%2', '10000', '20000');
if Customer.FindSet() then
repeat
Message('%1: %2', Customer."No.", Customer.Name);
until Customer.Next() = 0;
end;
GetFilter / GetFilters
record.GetFilter(field: Field): Text
record.GetFilters(): Text
GetFilter returns the current filter text applied to a field; GetFilters returns all active filters as display text.
GetRangeMin / GetRangeMax
record.GetRangeMin(field: Field): Any
record.GetRangeMax(field: Field): Any
Return the lower or upper bound of the range filter currently set on a field, typed like the field. They raise an error when the field has no range filter.
SetRecFilter
record.SetRecFilter()
Filters the variable to exactly the current record by setting a range on every primary key field.
var
Customer: Record Customer;
FilterText: Text;
begin
Customer.SetFilter("Balance (LCY)", '>10000');
FilterText := Customer.GetFilter("Balance (LCY)");
Message('Current filter: %1', FilterText);
end;
FilterGroup
record.FilterGroup(groupNumber: Integer)
Switches between filter groups. Filter groups allow you to apply multiple layers of filters independently.
| Parameter | Type | Description |
|---|---|---|
| groupNumber | Integer | The filter group number to switch to |
var
Customer: Record Customer;
begin
// Set a filter in the default group
Customer.FilterGroup(0);
Customer.SetRange("Country/Region Code", 'US');
// Set an additional filter in group 2
Customer.FilterGroup(2);
Customer.SetFilter("Balance (LCY)", '>1000');
// Switch back to default group
Customer.FilterGroup(0);
// Both filters are active
Message('Count: %1', Customer.Count());
end;
Marking Methods
Marks let you pick out an arbitrary set of records that no filter expression can describe, and then work with just that set. Marks belong to the record variable and are not stored in the database. They are not available on a record routed to another environment with ChangeEnvironment.
Mark
[isMarked: Boolean :=] record.Mark([setMark: Boolean])
Marks or unmarks the current record. Called without an argument, it returns whether the current record is marked.
| Parameter | Type | Description |
|---|---|---|
| setMark | Boolean | Optional. true marks the current record, false unmarks it |
MarkedOnly
[isMarkedOnly: Boolean :=] record.MarkedOnly([markedOnly: Boolean])
With true, restricts the record variable to the marked records, so FindSet, Next, Count, and the other navigation methods see only those. false removes the restriction. Called without an argument, it returns whether the restriction is active.
| Parameter | Type | Description |
|---|---|---|
| markedOnly | Boolean | Optional. true shows only marked records, false shows all |
ClearMarks
record.ClearMarks()
Removes all marks from the record variable.
var
Customer: Record Customer;
begin
// Mark customers that match a rule no single filter can express
if Customer.FindSet() then
repeat
Customer.CalcFields("Balance (LCY)");
if (Customer."Balance (LCY)" > Customer."Credit Limit (LCY)") and
(Customer."Credit Limit (LCY)" <> 0)
then
Customer.Mark(true);
until Customer.Next() = 0;
// Work with only the marked customers
Customer.MarkedOnly(true);
Message('%1 customers are over their credit limit', Customer.Count());
if Customer.FindSet() then
repeat
Message('%1 %2', Customer."No.", Customer.Name);
until Customer.Next() = 0;
// Back to the full set
Customer.MarkedOnly(false);
Customer.ClearMarks();
end;
Sorting Methods
SetCurrentKey
record.SetCurrentKey(field1: Field[, field2: Field, ...])
Sets the sort order (the current key) for subsequent record navigation. Any filters already applied are kept — SetCurrentKey changes only the order in which records are returned, not which records match.
| Parameter | Type | Description |
|---|---|---|
| field1..N | Field | The fields that define the sort order |
var
Item: Record Item;
begin
// Sort items by unit price instead of by primary key
Item.SetCurrentKey("Unit Price");
if Item.FindSet() then
repeat
Message('%1: %2', Item.Description, Item."Unit Price");
until Item.Next() = 0;
end;
Ascending
record.Ascending(): Boolean
record.Ascending(ascending: Boolean)
Gets or sets the sort direction for the current key. Call it with no argument to read the current direction, or with a Boolean to set it — false sorts in descending order.
var
Item: Record Item;
begin
Item.SetCurrentKey("Unit Price");
Item.Ascending(false); // highest price first
if Item.FindFirst() then
Message('Most expensive item: %1 (%2)', Item.Description, Item."Unit Price");
Message('Currently ascending: %1', Item.Ascending()); // false
end;
Validation Methods
Validate
record.Validate(field: Field, value: Any)
Sets a field value and runs the field's validation trigger. This ensures that any business logic tied to the field is executed.
var
SalesLine: Record "Sales Line";
begin
SalesLine.SetRange("Document Type", SalesLine."Document Type"::Order);
SalesLine.SetRange("Document No.", 'SO-001000');
if SalesLine.FindFirst() then begin
SalesLine.Validate(Quantity, 10);
SalesLine.Validate("Unit Price", 25.00);
SalesLine.Modify(true);
Message('Sales line updated: Amount = %1', SalesLine."Line Amount");
end;
end;
TestField
record.TestField(field: Field[, value: Any])
Asserts that a field has a non-empty value. When called with a second parameter, it asserts the field equals that specific value. If the assertion fails, an error is raised.
FieldError
record.FieldError(field: Field[, text: Text])
Raises an error that names the field and table: "Name must not be value in Customer", or "Name text in Customer" when a text is given.
var
Customer: Record Customer;
begin
if Customer.Get('10000') then begin
// Assert that Name is not empty
Customer.TestField(Name);
// Assert that Country equals 'US'
Customer.TestField("Country/Region Code", 'US');
Message('All validations passed');
end;
end;
Calculation Methods
CalcFields
record.CalcFields(field1: Field[, field2: Field, ...])
Calculates FlowField values. FlowFields are not stored in the database; they are computed on demand. You must call CalcFields before reading their values.
var
Customer: Record Customer;
begin
if Customer.Get('10000') then begin
Customer.CalcFields("Balance (LCY)", "Net Change (LCY)");
Message('Balance: %1', Customer."Balance (LCY)");
Message('Net Change: %1', Customer."Net Change (LCY)");
end;
end;
CalcSums
record.CalcSums(field1: Field[, field2: Field, ...])
Calculates the sum of SumIndexFields for all records matching the current filters.
var
GLEntry: Record "G/L Entry";
begin
GLEntry.SetRange("G/L Account No.", '6110');
GLEntry.SetRange("Posting Date", DMY2Date(1, 1, 2025), DMY2Date(31, 12, 2025));
GLEntry.CalcSums(Amount);
Message('Total for account 6110: %1', GLEntry.Amount);
end;
Copying Between Records
TransferFields
record.TransferFields(fromRecord: Record[, initPrimaryKeyFields: Boolean])
Copies the values of all fields that have the same number and type from another record variable (which may belong to a different table), like standard AL.
Copy
record.Copy(fromRecord: Record)
Copies the field values and filters from another record variable of the same table.
Assignment
Target := Source; between two record variables of the same table copies the current record (fields and filters), as in standard AL.
Metadata Methods
FieldCaption / TableCaption
record.FieldCaption(field: Field): Text
record.TableCaption(): Text
Return the caption of a field or of the table.
RecordId
record.RecordId(): Text
Returns the record identifier as formatted text (for example Customer: 10000). There is no RecordId type in ToolBox.
Other Methods
LockTable
record.LockTable([wait: Boolean[, versionCheck: Boolean]])
Locks the table to prevent other sessions from modifying records. Use this before performing operations that require data consistency.
var
NoSeries: Record "No. Series Line";
begin
NoSeries.LockTable();
if NoSeries.FindFirst() then begin
NoSeries."Last No. Used" := IncStr(NoSeries."Last No. Used");
NoSeries.Modify();
Message('Next number: %1', NoSeries."Last No. Used");
end;
end;
ChangeCompany
record.ChangeCompany([companyName: Text])
Switches the record variable to operate against a different company. All subsequent operations on this variable will use the specified company's data. Without an argument the variable returns to the current company.
var
Customer: Record Customer;
CustomerOther: Record Customer;
begin
// Read from current company
if Customer.Get('10000') then
Message('Current company customer: %1', Customer.Name);
// Read from another company
CustomerOther.ChangeCompany('CRONUS International Ltd.');
if CustomerOther.Get('10000') then
Message('Other company customer: %1', CustomerOther.Name);
end;
ChangeEnvironment
record.ChangeEnvironment([environmentName: Text[, companyName: Text]])
Switches the record variable to operate against a different Business Central environment. This is a ToolBox-specific method that allows you to read data from other environments (e.g., sandbox or production) within the same tenant. It requires the Remote ToolBox connection (Application ID, Secret, and Tenant ID) to be configured in ToolBox Setup and the ToolBox to be installed in the other environment. Without arguments the variable is routed back to the local environment.
A remote record is read-only and supports this subset of methods: Reset, SetRange, SetFilter, GetFilter, GetFilters, SetCurrentKey, Ascending, and CalcFields (applied locally), and FindSet, Find, FindFirst, FindLast, Next, Get, and Count (executed remotely). Any other method raises ... is not supported on a remote record.
var
Customer: Record Customer;
begin
Customer.ChangeEnvironment('Sandbox');
Customer.SetRange("Country/Region Code", 'US');
if Customer.FindSet() then
repeat
Message('Sandbox customer: %1', Customer.Name);
until Customer.Next() = 0;
end;
IsTemporary
record.IsTemporary(): Boolean
Returns true when the record variable was declared as temporary, and false for an ordinary database record. See Temporary Records below.
var
Temp: Record Customer temporary;
Live: Record Customer;
begin
Message('Temp is temporary: %1', Temp.IsTemporary()); // TRUE
Message('Live is temporary: %1', Live.IsTemporary()); // FALSE
end;
Temporary Records
A temporary record holds an in-memory dataset that is completely isolated from the database. You declare one by adding the temporary keyword after the table name:
var
Temp: Record "Sales Line" temporary;
Rows you Insert, Modify, or Delete on a temporary record live only in memory for the duration of the script — they never touch the underlying table, and other sessions never see them. This makes temporary records ideal for building intermediate result sets, sorting or filtering data in memory, and passing collections of records between procedures without writing to the database.
Working with a Temporary Record
All the usual record methods operate on the in-memory dataset — Init, Insert, Modify, Delete, DeleteAll, Get, Find/FindSet/Next, Count, IsEmpty, SetRange/SetFilter/ Reset, SetCurrentKey, and Ascending.
var
Temp: Record Customer temporary;
Live: Record Customer;
begin
// Copy a filtered set of live customers into an in-memory table
Live.SetRange("Country/Region Code", 'US');
if Live.FindSet() then
repeat
Temp := Live;
Temp.Insert();
until Live.Next() = 0;
Message('Buffered %1 US customers in memory', Temp.Count());
// Sort and filter the in-memory set without touching the database
Temp.SetCurrentKey(Name);
Temp.SetRange(Blocked, Temp.Blocked::" ");
if Temp.FindSet() then
repeat
Message('%1 - %2', Temp."No.", Temp.Name);
until Temp.Next() = 0;
end;
Behavior Notes
- No database writes, no permission prompt. Because a temporary record never writes to the
database, inserts and modifications do not trigger the write-permission confirmation that applies to live tables.
Clearkeeps the data. CallingClear(Temp)on a temporary record keeps both the
temporary state and the existing rows (this matches standard AL). To empty a temporary dataset, use Temp.DeleteAll().
SetCurrentKeysorts in memory — it can even sort by a non-key field, andAscending(false)
reverses the order, all without a database index.
- Procedure locals are independent. A temporary record declared inside a procedure has its own
dataset, separate from any global temporary record.
- Parameters follow standard AL rules. A
vartemporary parameter shares the caller's
in-memory dataset. Passing an ordinary database record to a var temporary parameter is rejected at runtime (the record ... must be temporary), matching standard AL. A temporary record may, however, be passed through a plain var record parameter.
Differs from standard AL: Nothing in the semantics — ToolBox mirrors standard AL temporary records. Note only that
temporaryis not a reserved word in ToolBox: it is recognized only in theRecord <Table> temporaryposition, so you can still usetemporaryas a variable name elsewhere.
Unsupported Record Methods
The following standard AL record members are not available in ToolBox and raise an error if used: Rename, SetLoadFields, SetAutoCalcFields, SetView, GetView, GetPosition, SetPosition, FieldNo, CurrentKey, AddLink, CopyFilters, CopyLinks, SetPermissionFilter, ReadIsolation, SetSecurityFilter, Consistent, Field, KeyIndex, SetTable, GetTable. RecordRef and FieldRef variables cannot be declared; use Record variables instead.
The Classic FindSet/Next Pattern
The most common pattern for iterating through records in Business Central and ToolBox is the FindSet/repeat/Next combination. You should be familiar with this pattern as it is used extensively.
var
Customer: Record Customer;
begin
Customer.SetRange("Country/Region Code", 'US');
if Customer.FindSet() then
repeat
Message('Customer: %1', Customer.Name);
until Customer.Next() = 0;
end;
Key points about this pattern:
- Always check the return value of
FindSet()with anifstatement. If no records
match, you should not enter the loop.
- The
repeat-untilloop processes each record.Next()returns 0 when there are no
more records, which terminates the loop.
- If you intend to modify records inside the loop, pass
truetoFindSet(true). - Call
Reset()before setting new filters if the record variable was previously filtered.
Complete Example: Processing Records with Modifications
var
Customer: Record Customer;
UpdateCount: Integer;
begin
Customer.SetRange("Country/Region Code", 'US');
Customer.SetRange("Salesperson Code", '');
if Customer.FindSet(true) then
repeat
Customer.Validate("Salesperson Code", 'DEFAULT');
Customer.Modify(true);
UpdateCount += 1;
until Customer.Next() = 0;
Message('Updated %1 customers', UpdateCount);
end;
Complete Example: Master-Detail Iteration
var
SalesHeader: Record "Sales Header";
SalesLine: Record "Sales Line";
OrderTotal: Decimal;
begin
SalesHeader.SetRange("Document Type", SalesHeader."Document Type"::Order);
SalesHeader.SetRange(Status, SalesHeader.Status::Released);
if SalesHeader.FindSet() then
repeat
OrderTotal := 0;
SalesLine.SetRange("Document Type", SalesHeader."Document Type");
SalesLine.SetRange("Document No.", SalesHeader."No.");
if SalesLine.FindSet() then
repeat
OrderTotal += SalesLine."Line Amount";
until SalesLine.Next() = 0;
Message('Order %1: Total = %2', SalesHeader."No.", OrderTotal);
until SalesHeader.Next() = 0;
end;
Summary
Record methods provide a complete set of operations for working with Business Central data in ToolBox. From navigating and filtering to inserting, modifying, and deleting records, these methods follow the same patterns you would use in standard AL development. The key difference is that ToolBox interprets these operations at runtime, giving you the flexibility to write and execute scripts without deploying an extension.