mdAL is a Domain Specific Language (DSL) that enables a Model-Driven approach to extension module development for the ERP System Microsoft Dynamics 365 Business Central (BC). mdAL stands for model-driven AL.
!> Please note that the current mdAL releases are pre-releases. If you encounter errors or have suggestions please open an issue in the corresponding repositories.
While supporting a standard set of processes contained in modules such as finance, purchasing and sales, Microsoft Dynamics 365 BC is often times customized due to specific requirements. Such customizations are not limited to e. g. small configuration changes adapting the user interface to the business needs but, instead, can entail programming of completely new solutions integrating processes into the system not considered by the ERP vendor. It is common for ERP introduction projects that customizing takes a large portion of the budget. Thus, increasing programming efficiency in this area seems beneficial.
When developing an AL extension that adds a whole new solution to Microsoft Dynamics 365 BC there are many reoccurring patterns. For example:
- Every solution contains tables of different categories (e. g. Master, Supplemental, (Posted) Document Header, (Posted) Document Line, Setup).
- The tables in the different table categories have to be designed in a specific way (e. g. Master tables must implement the No. Series logic, all Document Header (Line) fields are carried over to the Posted Document Header (Line)).
- For the tables in each table category specific pages with a standard set of actions have to be provided (e. g. Card Page and List Page for Master tables).
- There is a standard design for posting routines (e. g. Post, Post (Yes/No), Jnl. Check Line, Jnl. Post Line).
- There is a standard set of customizations required to integrate the solution into the BC standard (e. g. Navigate, Source Code Setup, Comment Line Table Name).
Because of these patterns, there is a high portion of code that can be considered as "boilerplate code" because it is either completely identical in every solution or can be derived after investigating the data model. mdAL aims at automatically generating this "boilerplate code" from a mdAL model file.
mdAL is a descriptive language that defines the needed core entities and views of an AL solution. In addition, the data flow between entities can be specified. mdAL provides AL code generation for:
- Tables
- Setup
- Master
- Supplemental
- (Posted) Document Header and Line
- Document Comment Line
- Journal Line
- Ledger Entry
- Register
- Enums
- From specified table fields
- Pages
- Master
- Card and List Page
- Supplemental
- List Page
- (Posted) Document
- Document and List Page
- Ledger Entry
- List Page
- Master
- Codeunits
- Post
- Post (Yes/No)
- Jnl. Check Line
- Jnl. Post Line
- Reg.-Show Ledger
Moreover, these customizations to standard objects are generated:
- Event subscribers implementing the Navigate functionality for the specified Posted Document Header
Source Code Setuptable extensionComment Linetable extensionComment Line Table Nameenum extension
Specific code that cannot be generated from a mdAL model file can be integrated by subscribing to the various event publishers available in the generated AL code. Hence, you can use mdAL to automatically generate a base AL extension and create an additional AL extension that depends on the base extension and adds your specific code. This way you do not have to change generated code in order to do customizations. Take a look at the demo projects mdal-lang/mdal-demo and mdal-lang/mdal-demo-extenion to see how this could be done.
!> Please note that mdAL currently is targeting Business Central 16 and later.
Try mdAL with a demo project specifying a seminar management solution. You can install the mdAL VS Code Extension for local development or try mdAL inside the Gitpod online IDE:
In the mdal-lang/mdal-demo-extenion repository you can find an example on how to further customize the base app defined by the demo project (e. g. in order to complement the posting routines). The customizations are implemented as an additional AL extension that uses the base app as dependency.
Using the solution keyword a new solution can be defined. You have to specify the name of the solution and the prefix used for all AL objects. Inside of this block the tables (Master, Supplemental, Document Header (Line), Ledger Entry) are defined. Supplemental tables are optional.
Example:
solution "Seminar Management" {
Prefix = "SEM";
// Master
// Supplemental
// Document
// Ledger Entry
}
Depending on the table type different types of fields can be used. Custom Fields and Template Fields can be used in all table types. Include Fields can only be used in Document Header, Document Line and Ledger Entry tables.
A Custom Field defines a regular table field known from AL. The only difference is that you do not have to specify a field number. In mdAL field numbers are generated automatically based on the order of the field definitions. So the field name and type are required to define a field. You can use all of the standard AL types (Boolean, Integer, BigInteger, Decimal, Code, Text, Date, Time, DateTime, Guid, Blob, Enum, Option, Media, MediaSet, DateFormula, Duration, RecordId, TableFilter). The only property available in Custom Fields is the TableRelation property that can be used to reference AL tables contained in the symbol references. For Enum and Option fields you can define the members directly in the type definition. Enum objects are then automatically generated from the field definition.
Example:
field("Gen. Prod. Posting Group"; Code[20]) {
// Optionally you can define a table relation to AL tables
TableRelation = "Gen. Product Posting Group";
}
field("Resource No."; Code[20]) {
// where and const are also supported
TableRelation = "Resource" where("Type" = const("Person"));
}
// Option/Enum members are specified directly in the type definition
field("Internal/External"; Option[" ", "Internal", "External"])
field("Source Type"; Enum[" ", "Seminar Registration"])
A Template Field can be used to automatically generate a set of fields and/or standard fields that need additional AL code (e. g. for validation). A field name and the template type have to be specified.
Example:
template("Contact Information"; ContactInfo)
template("Dimensions"; Dimensions)
The following templates types are available in mdAL:
TemplateName: Generates the fieldsName,Name 2andSearch Name.TemplateDescription: Generates the fieldsDescription,Description 2andSearch Description.TemplateDimensions: Generates the fieldsGlobal Dimension Code 1andGlobal Dimension Code 2(for Master and Supplemental tables) orShortcut Dimension Code 1andShortcut Dimension Code 2fields (otherwise).TemplateAddress: Generates the fieldsAddress,Address 2,City,Country/Region Code,Post CodeandCounty.TemplateContactInfo: Generates the fieldsContact Person,Phone No.,Telex No.,Fax No.,Telex Answer Back,E-MailandHome Page.TemplateSalesperson: Generates the fieldSalesperson Code.TemplateContact: Generates a field with the name of the template field with relation to theContacttable.
A Include Field can be used to specify the data flow between tables. Using this language element you only need to define the type of a field once and can include this field in other tables. A field name and a reference to the original field ("Table Name"."Field Name") have to be specified. If the optional property Validate is set, field assignments are validated (e. g. assignments implemented after a Master record is entered in a Document Header table).
Example:
// Custom Fields in Master table "Seminar"
field("Duration Days"; Decimal)
field("Seminar Price"; Decimal)
// Include Fields in Document Header table "Seminar Registration"
include("Duration Days"; "Seminar"."Duration Days")
include("Seminar Price"; "Seminar"."Seminar Price") {
Validate = true;
}
A Page Field is used to include a table field on a page. You only have to reference the name of the table field.
Example:
field("Duration Days")
A Page Field Group contains multiple Page Fields to be grouped on Card Pages or Document Pages. You have to specify the group name.
Example:
group("General") {
// Page Field(s)
}
To define a Master table you first have to specify its name and short name. Using the keywords fields the table fields are specified. The keywords cardPage and listPage start a Card Page and List Page definition.
Example:
master "Seminar" {
ShortName = "Sem.";
fields {
// Table Field(s)
}
cardPage {
// Page Field Group(s)
}
listPage {
// Page Field(s)
}
}
To define a Supplemental table you first have to specify its name and short name. Using the keywords fields the table fields are specified. The keyword and listPage starts a List Page definition.
Example:
supplemental "Instructor" {
ShortName = "Inst.";
fields {
// Table Field(s)
}
listPage {
// Page Field(s)
}
}
To define a document you first have to specify its name and short name. Then the Document Header and Document Line tables are define inside of this block.
Example:
document "Seminar Registration" {
ShortName = "Sem. Reg.";
// Header
// Line
}
To define a Document Header table you first have to specify its name and short name. Furthermore, the status captions for the Document Header have to be provided. Using the keywords fields the table fields are specified. The keywords documentPage and listPage start a Document Page and List Page definition.
Example:
header "Seminar Registration Header" {
ShortName = "Sem. Reg. Header";
StatusCaptions = ["Planning", "Registration", "Closed", "Canceled"];
fields {
// Table Field(s)
}
documentPage {
// Page Field Group(s)
}
listPage {
// Page Field(s)
}
}
To define a Document Line table you first have to specify its name and short name. Using the keywords fields the table fields are specified. The keyword listPartPage starts a List Part Page definition.
Example:
line "Seminar Registration Line" {
ShortName = "Sem. Reg. Line";
fields {
// Table Field(s)
}
listPartPage {
// Page Field(s)
}
}
To define a Ledger Entry table you first have to specify its name and short name. Using the keywords fields the table fields are specified. The keyword listPage starts a List Page definition.
Example:
ledgerEntry "Seminar Ledger Entry" {
ShortName = "Sem. Ledger Entry";
fields {
// Table Field(s)
}
listPage {
// Page Field(s)
}
}
From the VS Code Marketplace you can download the mdAL VS Code extension which adds language support for mdAL. To use the extension Java JRE 8 or newer is required (please restart after installing Java JRE). The extension provides the following features:
- Language Support
- Syntax highlighting for
mdALfiles.
- Syntax highlighting for
- Commands (available on
*.mdalfiles)- Load symbol references: Loads the symbol references found in the
.alpackagesfolder. - Generate AL Code: Generates AL Code from the currently opened
mdALfile into thesrc-genfolder. - Clean: Deletes the
src-genfolder.
- Load symbol references: Loads the symbol references found in the
- Menu items (available on
*.mdalfiles)- Load symbol references
- Generate AL Code
- Code Actions
- Content Assist
- Documentation hovers
- Snippets
If you want to use the mdAL generator as a standalone component (e. g. inside of a CI/CD pipeline) you can either download the binaries form the mdal-lang/mdal releases or use the docker image mdal/cli. The CLI tool takes the provided mdAL model file as an input and generates AL code into the src-gen folder.
Unix:
docker run --rm -v $(pwd):/project mdal/cli model.mdalWindows
docker run --rm -v "$(pwd):C:/project" mdal/cli model.mdalIf you want to use the mdAL generator inside of GitHub Actions workflows you can use the mdAL Action.
Example:
uses: mdal-lang/mdal-action@v1
with:
model-file: 'src/model.mdal'