BC AL Journey #6
“Creativity is thinking up new things. Innovation is doing new things.” – Theodore Levitt
So far in our BC AL Journey we have setup our development environment and extended a few records. For a lot of customizations, this is a solid start. There is a lot more we can, and will, do with just table and page extensions, but we are going to expand a little further and talk about adding new tables and pages to Business Central.
We are adding the concept of new tables and pages now, then we will be talking about concepts that apply to both new and extended tables and pages.
In some ways, a new table and page are easier to create than an extension, as you don’t have to work the existing structures. The challenging part is that you have to create the entire table and page, and there are a few more things that need to be configured.
Grab a cup of coffee. This will be the longest lesson in the Journey Series. My goal is to end each step with something that is complete and functional. This step has a lot of components, take your time, you’ve got this.
Let’s get right into a use case, and implement a solution:
Use Case
User would like to define different warranty details. The user would like an interface to manage all available warranty details, including a list of all warranties and a card view to edit.
Implementation
- Create a new table that includes the fields:
- ID
- Name
- Description
- Create a List page to manage the records
- Create a card page to create and edit the records
Test Plan
- Open the Warranty list
- Add a new Warranty
- Edit an existing Warranty
First, we are going to design the table to hold the Warranty data. Logically all our customizations start with the data tier.
The first file is added to the 0 – Table folder, ARDWarranty.Table.al
namespace AardvarkLabs;
table 50000 ARD_Warranty
{
Caption = 'Warranty';
DataClassification = CustomerContent;
DataCaptionFields = "ARD_No.", ARD_Name;
fields
{
field(1; "ARD_No."; Integer)
{
Caption = 'No.';
ToolTip = 'Warranty record is number.';
AutoIncrement = true;
}
field(2; ARD_Name; Text[100])
{
Caption = 'Name';
ToolTip = 'Warranty offering name.';
}
field(3; ARD_Description; Text[255])
{
Caption = 'Description';
ToolTip = 'Warranty offering description.';
}
}
keys
{
key(PK; "ARD_No.")
{
Clustered = true;
}
}
fieldgroups
{
fieldgroup(DropDown; "ARD_No.", ARD_Name)
{
}
fieldgroup(Brick; "ARD_No.", ARD_Name)
{
}
}
}
Reading the code here you should see several familiar bits, the fields are defined the same way as we did in the Table Extensions. We get to start the field IDs at 1 because we own the table, but the object ID for the table is in our number range defined in the App.JSON.
Starting at the top of the code, there is the type of “table”, and ID, and a Name. Next, we can define a caption and data classification for the table, which is necessary for it to show up properly in searches and audits.
The last bit in the header of the table definition are the Data Caption fields. These are the fields that appear in places like card headers. You can see it here in the completed header, but in reality, you create the fields first then come back and add this last.
Like all databases we need a field identified as the Primary Key, and that field must be unique. In this example, we are going to use the ARD_No. field as the Primary Key. For that to work we have a few considerations to make.
First off, the Primary Key must be unique. There are several ways we can force the value to be unique, but the easiest is to create it as an integer as set it to auto increment with each record.
field(1; "ARD_No."; Integer)
{
Caption = 'No.';
ToolTip = 'Warranty record is number.';
AutoIncrement = true;
}
Note the AutoIncrement = true statement. This is all it takes to set this field to increment. There are other methods that utilize the number series technology we find on customers, items, sales and purchase documents. For this step, this works great.
The next component is to identify the ARD_No. field as a Primary Key, which Business Central AL shortens to PK. The following code segment handles that.
keys
{
key(PK; "ARD_No.")
{
Clustered = true;
}
}
The Primary Key should be Clustered by default. This is needed for efficient indexing. Depending on your record and how you intend to search for things additional keys can be added for indexing. We will learn about that more when we talk about system performance.
The last thing in our table definition is the Field Groups. We have two that we create with a table, the DropDown and the Brick. The DropDown sets the fields that appear in a record selection Drop Down List. The Brick field group sets what appears on other system generated boxes and displays.
fieldgroups
{
fieldgroup(DropDown; "ARD_No.", ARD_Name)
{
}
fieldgroup(Brick; "ARD_No.", ARD_Name)
{
}
}
With the table sorted out, we can now create the Pages to manage the data in that table. When given the option I like to start with the Card page.
I added a file to the 2 – Page folder ARDWarrantyCard.Page.al
namespace AardvarkLabs;
page 50000 ARD_WarrantyCard
{
ApplicationArea = All;
Caption = 'Warranty Card';
PageType = Card;
SourceTable = ARD_Warranty;
UsageCategory = Documents;
layout
{
area(content)
{
group(General)
{
Caption = 'General';
field(ARD_Name; Rec.ARD_Name)
{
InstructionalText = 'Warranty Name';
}
field(ARD_Description; Rec.ARD_Description)
{
InstructionalText = 'Warranty details description.';
}
}
}
}
}
Like all our Business Central objects, we start with a type, in this case Page, an ID from our App.JSON and a Name. There are many values we can set at to define the page, but I’ve included a minimal set for this example. ApplicationArea and Caption should be familiar by now. We provide a Page Type, which is “Card” and a Source Table which is our new ARD_Warranty table. Lastly is the Usage Category, which sets the department column for when this page is listed in a search.
We can now create and scope a Layout section with { }. A card is broken up into several areas, we are going to use the “content” area for this example. A card will need at least one Group to add the fields. The name is arbitrary, but it is customary to have a “General” group.
From here, we can add the fields just like we did for the Page Extensions we did earlier. For this example, I added a new feature released in Business Central 24.0 the Instructional Text. This is text that appears in light grey to prompt the user to enter data.
The other page we need for this set of requirements is a List Page.
I added a file to the 2 – Page folder ARDWarrantyList.Page.al
namespace AardvarkLabs;
page 50001 ARD_WarrantyList
{
ApplicationArea = All;
Caption = 'Warranty List';
PageType = List;
SourceTable = ARD_Warranty;
UsageCategory = Documents;
CardPageId = ARD_WarrantyCard;
layout
{
area(content)
{
repeater(General)
{
field("ARD_No."; Rec."ARD_No.")
{
}
field(ARD_Name; Rec.ARD_Name)
{
}
field(ARD_Description; Rec.ARD_Description)
{
}
}
}
}
}
This is very similar to the Card page with some important differences. Note that the Page Type is now “List”, there is also a CardPageId that is our ARD_WarrantyCard that we just created. When you click Create or Edit, the CardPageId is the card page that Business Central will display.
The layout also has an important difference, the Group has been replaced by a Repeater. Lists represent repeating data across a data set, and the repeater control is that component.
The last thing we need to create is a Permissions file. When we add new elements to Business Central, we need to provide a way to secure access to the new resources.
In the 17 – Permission Set folder create a file named ARDPermissions.PermissionSet.al. Here is the content of the file.
namespace AardvarkLabs;
permissionset 50000 ARD_Permissions
{
Assignable = true;
Caption = 'AardvarkLabs Permissions', MaxLength = 30;
Permissions = table ARD_Warranty=X,
tabledata ARD_Warranty=RMID,
page ARD_WarrantyCard=X,
page ARD_WarrantyList=X;
}
Things to notice here is that it lists out the permissions for Table and Page elements with an X, meaning that you have access to those objects. The table data value specifies what access you have Read, Modify, Insert, and Delete. RIMD are the initials for each access type, and you simply put the letters in for the permissions you want to provide. In this case we want full access and use the letter RIMD.
This is very similar to the old CRUD for Create, Retrieve, Update, and Delete. I guess people didn’t like the acronym CRUD.
This is a great spot to test out what we have created so far.
We haven’t added the cards to any location in the Role Center yet, we will need to find them with the Magnifying Glass. Searching for “warranty” should find them.

Click on the “Warranty List” to bring the list up. We left the list page editable, so you can enter data directly into the list page.

Clicking “New”, “Edit”, or “View” will open the record in the Card we created. Clicking on the No. value will also open the record in the Card.

Note the Instruction Text we added to the page.

Note the Header is the ARD_No. and ARD_Name fields. This is from the Data Caption settings in the table header.
Naturally as developers we are Super Users in our development environments. If you want to grant access to the new tables we have created to a non-super user, you will need to update their permissions.

Here you can see our permission set ready to be assigned to a user or security group.
There we go, we have created something new and never before seen in Business Central. Congratulations!
Don’t go putting this code too far away, in Journey #7 we are going to combine work we did in Journey #5 and #6. Being completely honest, I was going to do this in a single post, but it is too long already. I’ve run out of coffee.
Source Code: https://github.com/AardvarkMan/BC-Journey





Leave a comment