Advanced Portal Designer Documentation
Index

Script Action

Script actions allow you to write AL scripts directly within APD, without creating separate codeunit objects. This provides a quick way to implement custom logic.

Overview

A Script action:

  • Executes AL script code directly
  • No separate codeunit required
  • Compiled and stored within APD
  • Full access to AL language features

When to Use Script Actions

Use Script actions for:

  • Quick prototyping
  • Simple data operations
  • Testing logic before creating codeunits
  • One-off custom operations
  • Learning and experimentation

Creating a Script Action

Step-by-Step

  1. Open the Advanced Portal Designer menu page (search for it in Tell Me) and click Actions
  2. Click New
  3. Enter the Web Action code
  4. Set Type to "Script"
  5. Click the "Edit Script" link
  6. Write your AL script
  7. Save and compile

The Script Editor

Opening the Editor

  1. In the action card, find the Script section
  2. Click the "Edit Script" link
  3. The script editor opens

Editor Features

Feature Description
AL syntax highlighting Color-coded AL keywords
Intellisense Auto-complete for AL commands
Error checking Compile-time validation
Line numbers Easy reference

Script Structure

Basic Script

// Variables go at the top
var
    Customer: Record Customer;
    OutputText: Text;

// Main code block
begin
    Customer.FindFirst();
    OutputText := Customer.Name;
    output(OutputText);
end;

Key Elements

  1. Variable declarations (optional): Declare any records, text, etc.
  2. Begin/End block: Contains your main logic
  3. Output function: Send content to the browser

Script Functions Reference

Script actions have access to the following additional AL commands beyond standard AL:

Function Description
output(<text>) Send text/HTML content to the response
response(<html/redirect>,<ContentType>,<RedirectLink>) Set the response type - use to return specific content types or redirect the user
parm(<parameter>) Get a parameter value from the query string or POST data
action(<name>,[Record Variable]) Navigate to another action, optionally passing a record variable
currentuserid() Get the current APD user ID
setlanguage(<language no>) Set the language for the response
updatesession(<Type>,<Number>,'',<Security Profiles Filter>) Update the current session. Type: 1=Customer, 2=Vendor, 3=Contact, 4=Salesperson
stripestatus(<Stripe Session>,<SettingToGet>) Get the status of a Stripe payment session

output()

Use output() to send content to the response:

begin
    output('Hello, World!');
    output('<h1>Welcome</h1>');
    output('<p>Current time: ' + format(CurrentDateTime) + '</p>');
end;

response()

Use response() to control the response type. This can set the content type for JSON/XML responses, or redirect the user to another page:

begin
    // Return a JSON response
    response('html', 'application/json', '');
    output('{"status": "ok"}');
end;
begin
    // Redirect the user to another action
    response('redirect', '', 'https://example.com/thankyou');
end;

parm()

Use parm() to retrieve query string or POST parameters:

var
    CustomerNo: Code[20];
begin
    CustomerNo := parm('custno');
    output('Customer: ' + CustomerNo);
end;

action()

Use action() to navigate to another APD action, optionally passing a record:

var
    Customer: Record Customer;
begin
    Customer.Get('10000');
    action('CUSTOMER-DETAIL', Customer);
end;

currentuserid()

Returns the current APD user ID:

begin
    output('Current user: ' + currentuserid());
end;

updatesession()

Updates the current user session. Use this for login flows or changing the user context:

begin
    // Log in as customer 10000
    updatesession(1, '10000', '', '');
end;

Type values:

  • 1 = Customer
  • 2 = Vendor
  • 3 = Contact
  • 4 = Salesperson

stripestatus()

Get the status of a Stripe payment session:

var
    Status: Text;
begin
    Status := stripestatus(parm('session_id'), 'status');
    output('Payment status: ' + Status);
end;

Building HTML

var
    Html: TextBuilder;
begin
    Html.AppendLine('<html>');
    Html.AppendLine('<body>');
    Html.AppendLine('<h1>Customer List</h1>');
    Html.AppendLine('</body>');
    Html.AppendLine('</html>');
    output(Html.ToText());
end;

Accessing Data

Reading Records

var
    Customer: Record Customer;
begin
    if Customer.FindSet() then
        repeat
            output('<p>' + Customer.Name + '</p>');
        until Customer.Next() = 0;
end;

Filtering Records

var
    SalesHeader: Record "Sales Header";
begin
    SalesHeader.SetRange("Document Type", SalesHeader."Document Type"::Order);
    SalesHeader.SetRange("Sell-to Customer No.", '10000');
    
    if SalesHeader.FindSet() then
        repeat
            output('<p>Order: ' + SalesHeader."No." + '</p>');
        until SalesHeader.Next() = 0;
end;

Accessing Request Parameters

Using Session

The session record is available as Rec:

begin
    if Rec."User Authenticated" then
        output('Welcome, ' + Rec.UserID)
    else
        output('Please log in');
end;

Getting URL Parameters

Use parm() to access query string or POST parameters:

var
    CustomerNo: Code[20];
begin
    // Access ?custno=10000 parameter
    CustomerNo := parm('custno');
    output('Customer: ' + CustomerNo);
end;

Generating JSON

For API-style responses:

var
    Customer: Record Customer;
    JsonArray: JsonArray;
    JsonObj: JsonObject;
begin
    if Customer.FindSet() then
        repeat
            Clear(JsonObj);
            JsonObj.Add('no', Customer."No.");
            JsonObj.Add('name', Customer.Name);
            JsonArray.Add(JsonObj);
        until Customer.Next() = 0;
    
    output(format(JsonArray));
end;

Error Handling

Using Error

begin
    if not ValidateInput() then
        Error('Invalid input provided');
        
    // Continue with valid input
end;

Try Functions

[TryFunction]
procedure TryProcessData()
begin
    // Code that might fail
end;

begin
    if not TryProcessData() then
        output('Error: ' + GetLastErrorText());
end;

Script Examples

Example 1: Customer Dashboard

var
    Customer: Record Customer;
    SalesHeader: Record "Sales Header";
    OpenOrders: Integer;
begin
    Customer.Get(Rec."Navision number");
    
    SalesHeader.SetRange("Document Type", SalesHeader."Document Type"::Order);
    SalesHeader.SetRange("Sell-to Customer No.", Customer."No.");
    OpenOrders := SalesHeader.Count;
    
    output('<html><body>');
    output('<h1>Welcome, ' + Customer.Name + '</h1>');
    output('<p>You have ' + format(OpenOrders) + ' open orders.</p>');
    output('</body></html>');
end;

Example 2: JSON API Response

var
    Item: Record Item;
    JsonArray: JsonArray;
    JsonItem: JsonObject;
begin
    Item.SetRange(Blocked, false);
    
    if Item.FindSet() then
        repeat
            Clear(JsonItem);
            JsonItem.Add('itemNo', Item."No.");
            JsonItem.Add('description', Item.Description);
            JsonItem.Add('unitPrice', Item."Unit Price");
            JsonItem.Add('inventory', Item.Inventory);
            JsonArray.Add(JsonItem);
        until Item.Next() = 0;
    
    output(format(JsonArray));
end;

Example 3: Form Handler

var
    Customer: Record Customer;
    Name: Text;
    Email: Text;
begin
    Name := parm('name');
    Email := parm('email');
    
    if (Name = '') or (Email = '') then begin
        output('{"error": "Name and email are required"}');
        exit;
    end;
    
    Customer.Init();
    Customer.Name := Name;
    Customer."E-Mail" := Email;
    Customer.Insert(true);
    
    output('{"success": true, "customerNo": "' + Customer."No." + '"}');
end;

Compilation

How Compilation Works

  1. Script is parsed as AL code
  2. Syntax is validated
  3. Table/field references are checked
  4. Compiled form is stored

Compilation Errors

Error Type Example Solution
Syntax error Missing semicolon Fix AL syntax
Unknown identifier Misspelled variable Check variable names
Type mismatch Text assigned to Integer Use correct types

Viewing Errors

Compilation errors appear in the editor with:

  • Line number
  • Error description
  • Quick access to problematic code

Limitations

Script actions have some limitations compared to codeunits:

Feature Codeunit Script
Separate procedures Yes Limited
Events/Triggers Yes No
Unit testing Yes No
Source control Yes (AL file) In BC only
Debugging Full debugger Limited

Migrating to Codeunit

When your script grows complex:

  1. Create a new codeunit in VS Code
  2. Copy your script logic
  3. Add proper procedure structure
  4. Add error handling
  5. Deploy and test
  6. Change action type to Codeunit

Best Practices

Keep It Simple

  • Scripts are best for simple operations
  • Complex logic belongs in codeunits
  • Consider maintainability

Security

  • Always validate input
  • Check authentication where needed
  • Don't expose sensitive operations

Performance

  • Avoid unnecessary loops
  • Use appropriate filters
  • Consider caching for repeated calls