Erik's ToolBox Documentation
Index

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.

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 the runTrigger argument 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.

  • Clear keeps the data. Calling Clear(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().

  • SetCurrentKey sorts in memory — it can even sort by a non-key field, and Ascending(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 var temporary 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 temporary is not a reserved word in ToolBox: it is recognized only in the Record <Table> temporary position, so you can still use temporary as 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:

  1. Always check the return value of FindSet() with an if statement. If no records

match, you should not enter the loop.

  1. The repeat-until loop processes each record. Next() returns 0 when there are no

more records, which terminates the loop.

  1. If you intend to modify records inside the loop, pass true to FindSet(true).
  2. 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.