diff --git a/.devcontainer/Dockerfile b/.devcontainer/Dockerfile
new file mode 100644
index 00000000..b5d6dfb3
--- /dev/null
+++ b/.devcontainer/Dockerfile
@@ -0,0 +1,27 @@
+# syntax=docker/dockerfile:1
+FROM mcr.microsoft.com/devcontainers/base:ubuntu-22.04
+
+# VARIANT can be either 'hugo' for the standard version or 'hugo_extended' for the extended version.
+ARG VARIANT=hugo_extended
+# VERSION can be either 'latest' or a specific version number
+ARG VERSION=latest
+
+# Download Hugo
+RUN case ${VERSION} in \
+ latest) \
+ export VERSION=$(curl -s https://api.github.com/repos/gohugoio/hugo/releases/latest | grep "tag_name" | awk '{print substr($2, 3, length($2)-4)}') ;;\
+ esac && \
+ echo ${VERSION} && \
+ case $(uname -m) in \
+ aarch64) \
+ export ARCH=ARM64 ;; \
+ *) \
+ export ARCH=64bit ;; \
+ esac && \
+ echo ${ARCH} && \
+ wget -O ${VERSION}.tar.gz https://github.com/gohugoio/hugo/releases/download/v${VERSION}/${VARIANT}_${VERSION}_Linux-${ARCH}.tar.gz && \
+ tar xf ${VERSION}.tar.gz && \
+ mv hugo /usr/bin/hugo
+
+# Hugo dev server port
+EXPOSE 1313
diff --git a/.devcontainer/devcontainer.json b/.devcontainer/devcontainer.json
new file mode 100644
index 00000000..12d6163f
--- /dev/null
+++ b/.devcontainer/devcontainer.json
@@ -0,0 +1,35 @@
+// For format details, see https://aka.ms/devcontainer.json. For config options, see the README at:
+// https://github.com/microsoft/vscode-dev-containers/tree/v0.217.4/containers/hugo
+{
+ "name": "Hugo",
+ "build": {
+ "dockerfile": "Dockerfile"
+ },
+ "customizations": {
+ "vscode": {
+ // Set *default* container specific settings.json values on container create.
+ "settings": {
+ "html.format.templating": true
+ },
+ // Add the IDs of extensions you want installed when the container is created.
+ "extensions": [
+ "bungcip.better-toml",
+ "davidanson.vscode-markdownlint",
+ "GitHub.vscode-pull-request-github"
+ ]
+ }
+ },
+ // Use 'forwardPorts' to make a list of ports inside the container available locally.
+ "forwardPorts": [
+ 1313
+ ],
+ "remoteUser": "vscode",
+ // Use 'postCreateCommand' to run commands after the container is created.
+ // "postCreateCommand": ""
+ "features": {
+ "ghcr.io/devcontainers/features/git:1": {},
+ "ghcr.io/devcontainers/features/go:1": {},
+ "ghcr.io/devcontainers/features/hugo:1": {},
+ "ghcr.io/devcontainers/features/node:1": {}
+ }
+}
diff --git a/.gitattributes b/.gitattributes
new file mode 100644
index 00000000..1fbdaf78
--- /dev/null
+++ b/.gitattributes
@@ -0,0 +1,8 @@
+* text=auto eol=lf
+*.{cmd,[cC][mM][dD]} text eol=crlf
+*.{bat,[bB][aA][tT]} text eol=crlf
+*.gif binary
+*.jpeg binary
+*.png binary
+*.gz binary
+*.jar binary
\ No newline at end of file
diff --git a/.github/workflows/hugo.yml b/.github/workflows/hugo.yml
new file mode 100644
index 00000000..8ce2ef8c
--- /dev/null
+++ b/.github/workflows/hugo.yml
@@ -0,0 +1,73 @@
+# Sample workflow for building and deploying a Hugo site to GitHub Pages
+name: Deploy Hugo site to Pages
+
+on:
+ # Runs on pushes targeting the default branch
+ push:
+ branches: ["main"]
+
+ # Allows you to run this workflow manually from the Actions tab
+ workflow_dispatch:
+
+# Sets permissions of the GITHUB_TOKEN to allow deployment to GitHub Pages
+permissions:
+ contents: read
+ pages: write
+ id-token: write
+
+# Allow only one concurrent deployment, skipping runs queued between the run in-progress and latest queued.
+# However, do NOT cancel in-progress runs as we want to allow these production deployments to complete.
+concurrency:
+ group: "pages"
+ cancel-in-progress: false
+
+# Default to bash
+defaults:
+ run:
+ shell: bash
+
+jobs:
+ # Build job
+ build:
+ runs-on: ubuntu-latest
+ env:
+ HUGO_VERSION: 0.133.1
+ steps:
+ - name: Install Hugo CLI
+ run: |
+ wget -O ${{ runner.temp }}/hugo.deb https://github.com/gohugoio/hugo/releases/download/v${HUGO_VERSION}/hugo_extended_${HUGO_VERSION}_linux-amd64.deb \
+ && sudo dpkg -i ${{ runner.temp }}/hugo.deb
+ - name: Install Dart Sass
+ run: sudo snap install dart-sass
+ - name: Checkout
+ uses: actions/checkout@v3
+ - name: Setup Pages
+ id: pages
+ uses: actions/configure-pages@v3
+ - name: Install Node.js dependencies
+ run: "[[ -f package-lock.json || -f npm-shrinkwrap.json ]] && npm ci || true"
+ - name: Build with Hugo
+ env:
+ # For maximum backward compatibility with Hugo modules
+ HUGO_ENVIRONMENT: production
+ HUGO_ENV: production
+ run: |
+ hugo \
+ --minify \
+ --baseURL "${{ steps.pages.outputs.base_url }}/"
+ - name: Upload artifact
+ uses: actions/upload-pages-artifact@v3
+ with:
+ path: ./public
+
+ # Deployment job
+ deploy:
+ environment:
+ name: github-pages
+ url: ${{ steps.deployment.outputs.page_url }}
+ runs-on: ubuntu-latest
+ needs: build
+ steps:
+ - name: Deploy to GitHub Pages
+ id: deployment
+ uses: actions/deploy-pages@v2
diff --git a/.github/workflows/regen-gh-pages.yml b/.github/workflows/regen-gh-pages.yml
deleted file mode 100644
index 36ff7fa8..00000000
--- a/.github/workflows/regen-gh-pages.yml
+++ /dev/null
@@ -1,49 +0,0 @@
-name: hugo CI
-
-on:
- push:
- branches:
- - main # Set a branch name to trigger deployment
- pull_request:
-
-jobs:
- deploy:
- runs-on: ubuntu-20.04
- concurrency:
- group: ${{ github.workflow }}-${{ github.ref }}
- steps:
- - uses: actions/checkout@v2
- with:
- submodules: recursive # Fetch Hugo themes (true OR recursive)
- fetch-depth: 0 # Fetch all history for .GitInfo and .Lastmod
-
- - name: Setup Hugo
- uses: peaceiris/actions-hugo@v2
- with:
- hugo-version: 'latest'
- extended: true # Use extended Hugo
-
- - name: Setup Node
- uses: actions/setup-node@v2
- with:
- node-version: '14'
-
- - name: Cache dependencies
- uses: actions/cache@v1
- with:
- path: ~/.npm
- key: ${{ runner.os }}-node-${{ hashFiles('**/package-lock.json') }}
- restore-keys: |
- ${{ runner.os }}-node-
- - name: Prepare postcss-cli
- run: npm ci
-
- - name: Build
- run: hugo --minify
-
- - name: Deploy
- uses: peaceiris/actions-gh-pages@v3
- if: ${{ github.ref == 'refs/heads/main' }}
- with:
- github_token: ${{ secrets.GITHUB_TOKEN }}
- publish_dir: ./public
diff --git a/.gitmodules b/.gitmodules
deleted file mode 100644
index b76244d3..00000000
--- a/.gitmodules
+++ /dev/null
@@ -1,3 +0,0 @@
-[submodule "themes/docsy"]
- path = themes/docsy
- url = https://github.com/google/docsy.git
diff --git a/.vscode/tasks.json b/.vscode/tasks.json
new file mode 100644
index 00000000..d4117c41
--- /dev/null
+++ b/.vscode/tasks.json
@@ -0,0 +1,11 @@
+{
+ "version": "2.0.0",
+ "tasks": [
+ {
+ "label": "Start local Hugo server",
+ "type": "shell",
+ "command": "hugo serve",
+ "problemMatcher": []
+ }
+ ]
+}
\ No newline at end of file
diff --git a/CITATION.cff b/CITATION.cff
new file mode 100644
index 00000000..51c15c7e
--- /dev/null
+++ b/CITATION.cff
@@ -0,0 +1,23 @@
+cff-version: 1.2.0
+title: ALGuidelines.dev
+abstract: |
+ "Microsoft ALGuidelines - A Community driven project.
+
+ Best Practices and Design Patterns for the AL Language."
+keywords:
+ - AL
+ - "Design Patterns"
+ - "Best Practices"
+authors:
+ - given-names: Eric
+ family-names: Wauters
+ - given-names: Arend-Jan
+ family-names: Kauffmann
+ - given-names: Henrik
+ family-names: Helgesen
+ orcid: https://orcid.org/0000-0002-3281-6712
+ - given-names: Jeremy
+ family-names: Vyska
+license: MIT
+url: "https://alguidelines.dev"
+repository-code: "https://github.com/microsoft/alguidelines"
\ No newline at end of file
diff --git a/README.md b/README.md
index 3e15c226..e41ff2b1 100644
--- a/README.md
+++ b/README.md
@@ -40,14 +40,7 @@ contact [opencode@microsoft.com](mailto:opencode@microsoft.com) with any additio
### Setup
-1. Clone and setup
-```sh
-# Clone all submodules
-git submodule update --init --recursive --depth 1
-# Install NPM dependencies
-npm install
-```
-2. Run Hugo server
+**Run Hugo server**
```
$ hugo server
Web Server is available at http://localhost:1313/ (bind address 127.0.0.1)
diff --git a/assets/icons/logo.png b/assets/icons/logo.png
deleted file mode 100644
index 92fced3e..00000000
Binary files a/assets/icons/logo.png and /dev/null differ
diff --git a/assets/scss/_styles_project.scss b/assets/scss/_styles_project.scss
index 78520674..e165c6a4 100644
--- a/assets/scss/_styles_project.scss
+++ b/assets/scss/_styles_project.scss
@@ -1,2 +1,7 @@
.td-page-meta--child { display: none !important; }
-.td-page-meta--project-issue { display: none !important; }
\ No newline at end of file
+.td-page-meta--project-issue { display: none !important; }
+
+.td-content pre code {
+ font-family: Consolas, "Courier New", monospace;
+ size: 115%;
+}
\ No newline at end of file
diff --git a/assets/scss/_variables_project.scss b/assets/scss/_variables_project.scss
index de6c519b..bb13042b 100644
--- a/assets/scss/_variables_project.scss
+++ b/assets/scss/_variables_project.scss
@@ -534,6 +534,10 @@ input[type="search"]::placeholder {
display: none;
}
+.show-mobile-inline {
+ display: none;
+}
+
@media (max-width: 1299px) {
.hero {
background: url("/images/hero-bg.webp") no-repeat;
@@ -556,6 +560,10 @@ input[type="search"]::placeholder {
display: block;
}
+ .show-mobile-inline {
+ display: inline;
+ }
+
.hero {
background: url("/images/hero-bg.webp") no-repeat;
background-size: cover;
diff --git a/content/Discussions/_index.md b/content/Discussions/_index.md
deleted file mode 100644
index 6fdaeb1d..00000000
--- a/content/Discussions/_index.md
+++ /dev/null
@@ -1,21 +0,0 @@
-+++
-chapter = true
-pre = ""
-title = "Discussions"
-weight = 200
-+++
-
-# [Discussions](https://github.com/microsoft/alguidelines/discussions)
-
-We enabled the "Discussions" forum on the github-page of our repository for you to discuss anything "Design Pattern" or "Best Practices" related.
-
-You can find the discussions here: https://github.com/microsoft/alguidelines/discussions.
-
-Feel free to browse through them, find certain topics and/or participate in the discussions!
-
-## Create your own discussion
-
-You are obviously also free to start a new discussion. You can simply do that by using the "New Discussion" button on the top right.
-
-Or use this link: https://github.com/microsoft/alguidelines/discussions/new?category=bc-patterns
-
diff --git a/content/_index.md b/content/_index.md
index 0117a774..776ca694 100644
--- a/content/_index.md
+++ b/content/_index.md
@@ -123,16 +123,16 @@ images: ["images/og-image-fission.png"]
- PATTERN
+ NEW
- Event Bridge
+ Vibe Coding for AL
- In the world of interfaces, it is important to preserve (certain) events over multiple implementation of the interface.
+ AI-optimized coding rules and guidelines designed to enhance the AL developer experience in modern AI-powered IDEs like VS Code and Cursor.
- The intent of this pattern is to provide a unified API to a single or a collection of potentially complex subsystems.
+ In the world of interfaces, it is important to preserve (certain) events over multiple implementation of the interface.
diff --git a/content/docs/BestPractices/CustomTelemetry/index.md b/content/docs/BestPractices/CustomTelemetry/index.md
new file mode 100644
index 00000000..c2f1eb20
--- /dev/null
+++ b/content/docs/BestPractices/CustomTelemetry/index.md
@@ -0,0 +1,96 @@
+---
+title: "Custom Telemetry"
+tags: ["AL","Telemetry"]
+categories: ["Best Practice"]
+---
+
+_Created by Microsoft, Described by Arend-Jan Kauffmann_
+
+## Description
+With AL it is possible to emit custom telemetry signals to Azure Application Insights. There are a number of considerations that you should keep in mind when designing custom telemetry signals.
+
+- Think about it as an API
+- Naming conventions and telemetry schema
+- Objects emitting telemetry signals
+- Candidate data for telemetry
+- How customers use telemetry
+- Privacy
+
+## Think about it as an API
+
+Customers will build analytics and monitoring solutions on top of their telemetry data.
+
+Therefore, signal must be treated as any other API
+
+- documented
+- versioned
+- discoverable
+- non-breaking
+
+## Naming conventions and telemetry schema
+
+To make it easy for the consumer of telemetry to work with the data, please
+
+- use **PascalCasing**. This makes all fields in Application Insights look the same (signal logged through the AL LogMessage method will have "al" prefixed to dimension names.
+- **Do not use special characters or spaces** for fields/custom dimension keys. This makes the KQL queries so much easier to write
+- for custom dimensions, consider using prefixes that helps the telemetry consumer understand where the dimension is coming from (e.g. HttpStatusCode, SqlStatement, ...)
+
+Consider always having a **"message"** field that expresses in human readable form what the telemetry event is about.
+If you do, let message names follow the Object ActionInPastTense pattern
+Some examples
+
+- Web Service Called:
+- Email attempt failed
+- Authorization to environment succeeded
+
+```al
+local procedure ProcessHttpResponse(var Request: HttpRequestMessage; var Response: HttpResponseMessage)
+var
+ CustomTelemetryDimensions: Dictionary of [Text,Text];
+begin
+ if Response.HttpStatusCode <> 200 then begin
+ CustomTelemetryDimensions.Add('Url', Request.GetRequestUri);
+ CustomTelemetryDimensions.Add('HttpStatusCode', Format(Response.HttpStatusCode));
+ CustomTelemetryDimensions.Add('ReasonPhrase', Response.ReasonPhrase);
+ Session.LogMessage(
+ 'MyExt0001',
+ 'Web service call failed',
+ Verbosity::Error,
+ DataClassification::SystemMetadata,
+ TelemetryScope::ExtensionPublisher,
+ CustomTelemetryDimensions);
+ end;
+end;
+```
+
+## Objects emitting telemetry signals
+
+Telemetry data includes information about the object that emitted the telemetry signal. It's recommended to call Session.LogMessage() **from within the object** that causes a situation that you want to have telemetry for. That will make it easier to analyze where exactly in the code an issue occurred.
+
+Of course it is possible to have a single object as a central place to emit telemetry signals. The telemetry data includes a callstack, so eventually it would be possible to trace back to the exact place where an issue occurred. But that requires a more complicated query, so it would be better to emit telemetry signals right from place in the code where an issue occurred.
+
+## Candidate data for telemetry
+
+Telemetry must be **actionable** for the customer. Do not emit signals that they cannot act on (knowing about CPU performance counters on the database is useless if the partner cannot scale the database).
+
+Also, note that customers pay for data ingestion. So be mindful to not flood their telemetry resources. Consider to use TelemetryScope::ExtensionPublisher by default and only use TelemetryScope::All in case the customer can also act on the data.
+
+If you do not know where to start, consider using telemetry for deflection. In Dynamics 365 Business Central, they started with signal about authorization (successful/failed) to deflect support cases that was due to disabled users/wrong licenses.
+
+## How customers use telemetry
+
+The following are known scenarios for customer telemetry
+
+- The tenant admin (typically an IT-pro) wants to troubleshoot a performance problem and they need more details than what is provided in the analytics reports in the admin center.
+- The customer wants to analyze (and correct) errors happening in the environment (typically an IT-pro)
+- The customer wants to analyze usage of features (typically an analytics user, maybe with BI experience)
+
+Customers typically start in the Application Insights portal and then move on to use more advanced tools for analytics (KQL, Power BI, Excel, ...). Once they have seen the light, they will likely start alerting on telemetry using Azure Monitor Alerts or setting up Power Automate flows.
+
+Business Central have developed a telemetry maturity model (based on the Gartner BI maturity model) for how organizations can evolve to use telemetry proactively in their business processes.
+
+## Privacy
+
+Telemetry must be **privacy compliant**.
+
+For privacy reasons, events that have a DataClassification other than SystemMetadata aren't sent to Application Insight resources set up on the tenant. During development of your extension, it's good practice to have a privacy review of the use of LOGMESSAGE calls to ensure that customer data isn't mistakenly leaked into Application Insights resources.
\ No newline at end of file
diff --git a/content/docs/BestPractices/DeleteAll/index.md b/content/docs/BestPractices/DeleteAll/index.md
index 802d1e2d..3ed623a5 100644
--- a/content/docs/BestPractices/DeleteAll/index.md
+++ b/content/docs/BestPractices/DeleteAll/index.md
@@ -1,10 +1,10 @@
---
title: "DeleteAll"
-tags: ["Performance"]
+tags: ["AL","Performance"]
categories: ["Best Practice"]
---
-<_Created by waldo, Described by waldo_\>
+_Created by waldo, Described by waldo_
## Description
@@ -25,11 +25,3 @@ Therefore it's good practice to always check if the table is empty when performi
if not EmptyTableWLD.IsEmpty() then
EmptyTableWLD.DeleteAll(true);
```
-
-## Discussions
-
-You can discuss the guideline [here](https://github.com/microsoft/alguidelines/discussions/107)
-
-You can find discussions on all "Best Practices" [here](https://github.com/microsoft/alguidelines/discussions/categories/bc-best-practices).
-
-If you don't find the discussion of this guideline, please feel free to create a new one with the same title as this article.
\ No newline at end of file
diff --git a/content/docs/BestPractices/SetLoadFields/Index.md b/content/docs/BestPractices/SetLoadFields/Index.md
new file mode 100644
index 00000000..e3aae546
--- /dev/null
+++ b/content/docs/BestPractices/SetLoadFields/Index.md
@@ -0,0 +1,69 @@
+---
+title: "SetLoadFields"
+tags: ["AL","Readability"]
+categories: ["Best Practice"]
+---
+
+See the documentation on learn.microsoft.com for more information about [SetLoadFields](https://learn.microsoft.com/en-us/dynamics365/business-central/dev-itpro/developer/methods-auto/record/record-setloadfields-method).
+
+For the performance of your code it is important that you use SetLoadFields as much as possible.
+
+If you want to retrieve a record from the database to check if the record is available always use SetLoadFields on the primary key fields of the table so only those fields will be retrieved from the database.
+
+## Bad code
+
+```AL
+if not Item.Get(ItemNo) then
+ exit();
+```
+
+## Good code
+
+```AL
+Item.SetLoadFields("No.");
+if not Item.Get(ItemNo) then
+ exit();
+```
+
+
+Place the SetLoadFields in the code before the line of the Get (or find). (there is no need to record filter fields in the SetLoadFields because these will be retrieved automatically).
+## Bad code
+
+```AL
+Item.SetLoadFields("Item Category Code");
+Item.SetRange("Third Party Item Exists", false);
+Item.FindFirst();
+```
+
+## Good code
+
+```AL
+Item.SetRange("Third Party Item Exists", false);
+Item.SetLoadFields("Item Category Code");
+Item.FindFirst();
+```
+
+Place the SetLoadFields in the code before the case statement
+## Bad code
+
+```AL
+Item.SetLoadFields("Item Category Code");
+ItemCategoryCode := FindItemCategoryCode;
+
+case true of
+ Item.Get(ItemNo):
+ SetItemCategoryCode(Item, ItemCategoryCode);
+end;
+```
+
+## Good code
+
+```AL
+ItemCategoryCode := FindItemCategoryCode;
+Item.SetLoadFields("Item Category Code");
+
+case true of
+ Item.Get(ItemNo):
+ SetItemCategoryCode(Item, ItemCategoryCode);
+end;
+```
diff --git a/content/docs/BestPractices/SubscriberCodeunits/index.md b/content/docs/BestPractices/SubscriberCodeunits/index.md
index 2aadfa54..9f4e61ea 100644
--- a/content/docs/BestPractices/SubscriberCodeunits/index.md
+++ b/content/docs/BestPractices/SubscriberCodeunits/index.md
@@ -1,6 +1,6 @@
---
title: "Subscriber Codeunits"
-tags: ["Performance"]
+tags: ["AL","Performance"]
categories: ["Best Practice"]
---
@@ -18,15 +18,18 @@ In general, subscribers have to be put in codeunits. There are a few performanc
Let's discuss all points
## Keep the codeunit as small as possible
+
Every time a subscriber gets called, a new instance of the codeunit is being loaded in memory, which takes memory and processing power. The smaller the codeunit, the less memory, and the faster it is.
-Therefore, it's suggested to split the subscribers by functionality and avoid putting business logic in the actual codeunit. Tip: put all business logic in an "[Method Codeunit](https://alguidelines.dev/bcpatterns/generic-method-pattern/)".
+Therefore, it's suggested to split the subscribers by functionality and avoid putting business logic in the actual codeunit. Tip: put all business logic in an "[Method Codeunit](https://alguidelines.dev/docs/patterns/generic-method-pattern/)".
Examples:
+
- if you app does things on Sales and Purchase, create a Sales-subs codeunit, and a Purchase-subs.
- if you have multiple functionalities in your app (let's call'm modules), create a subs-codeunit per module, and only add the subscribers in there that are necessary for that module.
### Bad code
+
```AL
codeunit 2037325 "Setup Subs"
{
@@ -73,6 +76,7 @@ codeunit 2037325 "Setup Subs"
end;
}
```
+
### Good code
Split into 2 codeunits, and move the business logic out.
@@ -112,6 +116,7 @@ codeunit 2037324 "RHE Setup Subs"
To avoid the extra "loading of the content" while a subscriber is being executed, use Single Instance codeunit for subscribers. Do take into account, of course, that it would share the state across the entire session.
### Bad code
+
```AL
codeunit 2037324 "RHE Setup Subs"
{
@@ -124,7 +129,9 @@ codeunit 2037324 "RHE Setup Subs"
end;
}
```
+
### Good code
+
```AL
codeunit 2037324 "RHE Setup Subs"
{
@@ -145,6 +152,7 @@ codeunit 2037324 "RHE Setup Subs"
If possible, only execute the subscriber when really necessary by using Manual Binding.
### Bad code
+
```AL
//subscriber - code should actually only run when Color=Red.
[EventSubscriber(ObjectType::Table, Database::"Just Some Table WLD", 'OnAfterValidateEvent', 'Message 2', false, false)]
@@ -162,7 +170,9 @@ If possible, only execute the subscriber when really necessary by using Manual B
JustSomeTable.Validate("Message 2", format(Random(1000)));
until JustSomeTable.Next() < 1;
```
+
### Good code
+
```AL
if JustSomeTable.FindSet() then
repeat
@@ -177,21 +187,15 @@ If possible, only execute the subscriber when really necessary by using Manual B
```
## Avoid OnInsert/OnModify/OnDelete
+
The reason for this is, that it breaks the batch-calls:
+
- Any "OnInsert" subscriber breaks the bulk inserts, simply because it needs to perform an operation after every record that was inserted
- Any "OnModify" subscriber slows down the "ModifyAll", simply because it needs to perform an operation after every record that was modified. I fact: 1 SQL call is turned into a loop of SQL calls.
- Any "OnDelete" subscriber slows down the "DeleteAll", simply because it needs to perform an operation after every record that was deleted. I fact: 1 SQL call is turned into a loop of SQL calls.
Avoid subscribers to these events.
-## [Discussions](https://github.com/microsoft/alguidelines/discussions/92)
-
-You can discuss this guidelines [here](https://github.com/microsoft/alguidelines/discussions/92).
-
-You can find discussions on all "Best Practices" [here](https://github.com/microsoft/alguidelines/discussions/categories/bc-best-practices).
-
-If you don't find the discussion of this guideline, please feel free to create a new one with the same title as this article.
-
## References
-The [Generic Method Pattern](https://alguidelines.dev/bcpatterns/generic-method-pattern/)
\ No newline at end of file
+The [Generic Method Pattern](https://alguidelines.dev/docs/patterns/generic-method-pattern/)
diff --git a/content/docs/BestPractices/_index.md b/content/docs/BestPractices/_index.md
index 174f7d91..9684d0df 100644
--- a/content/docs/BestPractices/_index.md
+++ b/content/docs/BestPractices/_index.md
@@ -5,9 +5,8 @@ description: >
AL Code Best Practices
---
-# Business Central Best Practices
-
This section will be cover things that aren't as simple as Design Patterns, but will help make sure your development is:
+
- high-performance
- complies with good designs
- has high maintainability
@@ -18,4 +17,8 @@ Generally, all readability rules are Microsoft style choices only. You can use t
## Performance
-Some guidelines are simply better for performance considerations rather than readability or anything else. In this section, let's look into some..
\ No newline at end of file
+Some guidelines are simply better for performance considerations rather than readability or anything else. In this section, let's look into some..
+
+## Discussion
+
+All discussion related to Best Practice are to be found on the Github Repo's Discussion pages, found [here](https://github.com/microsoft/alguidelines/discussions/categories/bc-best-practices)
diff --git a/content/docs/BestPractices/api-page/index.md b/content/docs/BestPractices/api-page/index.md
new file mode 100644
index 00000000..b1dc4488
--- /dev/null
+++ b/content/docs/BestPractices/api-page/index.md
@@ -0,0 +1,187 @@
+---
+title: "API Page / Query"
+tags: ["AL","API"]
+categories: ["Best Practice"]
+---
+
+_Created by Arend-Jan Kauffmann, Described by Arend-Jan Kauffmann_
+
+## Description
+
+API pages are different from UI pages. They require different properties and don't behave the same. Because API pages are used for integration with external applications, they should be treated as contracts. To achieve this, the following topics are important.
+
+- Separate API app
+- Page properties
+- Versioning
+- Field properties
+- Default fields
+
+## Separate API app
+
+It's a good practice to develop API pages in a separate app instead of combining them in a solution. By doing so, it provides better maintainability and is a good way of separation of concerns.
+
+## Page properties
+An API page must define a minimum set of properties. Some of these properties will be part of the URL of the API endpoint. It is recommended to define the properties in the same order as they appear in the URL.
+
+The properties that must be defined are:
+
+- PageType = API / QueryType = API
+- APIPublisher
+- APIGroup
+- APIVersion
+- EntitySetName
+- EntityName
+- DelayedInsert (only Page)
+- ODataKeyFields
+
+### APIPublisher
+The name of the API publisher is usually the company creating the API. It is the first custom part in the URL for a given endpoint. The value is case insensitive.
+
+Example:
+
+```al
+APIPublisher = 'contoso';
+```
+
+### APIGroup
+Sets the group of the API endpoint that page or query is exposed in. In the URL the APIGroup comes after the APIPublisher. It can be used to distinguish different API apps or groups of APIs from each other. The value is case insensitive.
+
+Example:
+
+```al
+APIGroup = 'app1';
+```
+
+### APIVersion
+Sets the version(s) of the API endpoint the page or query is exposed in. This property is not mandatory. If it is not specified, then APIs will be exposed as version 'beta'.
+
+The APIVersion can be set to 'beta' or have the format 'vx.y'.
+Example:
+
+```al
+APIVersion = 'beta';
+```
+
+or
+
+```al
+APIVersion = 'v1.0';
+```
+#### _Multiple API versions_
+You should __never__ break existing versions. Any breaking change requires to create a new version.
+
+It is possible to expose an API in multiple versions:
+```al
+APIVersion = 'beta', 'v1.0';
+```
+This allows to publish a new version of an API app without copying all individual objects and updating the version numbers. Only those API objects that are changed in a new version need to be copied. The other objects only need an addition to the APIVersion property to become available in the new version endpoint.
+
+### EntitySetName
+The EntitySetName is the plural entity name. Think of it as the name of the collection of entities. It is recommended to use camelCasing for this property. The value is case sensitive!
+
+Example:
+
+```al
+EntitySetName = 'itemCategories';
+```
+
+### EntityName
+The EntityName sets the singular entity name for the API page or query. This name is not used in the URL. Instead, the EntityName is used in the metadata information. It is recommended to use camelCasing for this property.
+
+Example:
+
+```al
+EntityName = 'itemCategory';
+```
+
+### DelayedInsert
+This property is required on an editable API page. It does not apply to an API query object. If ```Editable = false``` is set on the API page, then DelayedInsert is not required.
+All APIs pages apply the behavior to first specify all field values and then insert the record at once.
+
+Example:
+
+```al
+DelayedInsert = true;
+```
+
+### Full example
+Together, the page properties look like:
+```al
+PageType = API;
+APIPublisher = 'contoso';
+APIGroup = 'app1';
+APIVersion = 'v1.0';
+EntitySetName = 'itemCategories';
+EntityName = 'itemCategory';
+DelayedInsert = true;
+```
+
+The full url will look like: ```https://{url}/api/contoso/app1/v1.0/companies({id})/itemCategories```
+
+### ODataKeyFields
+The ```EntitySetName``` property in the URL can be extended with an identifier to indicate a single record.
+
+Example:
+```
+.../itemCategories(768b6173-9b19-40ea-8e5d-ce181ec0d645)
+```
+
+The property ```ODataKeyFields``` defines which field(s) will be used for the identifier value. It is highly recommended to always use the SystemId field for this property. The SystemId field is immutable and will never change for a record.
+
+The field that is defined in this property should be part of the API page.
+
+## Field properties
+The base structure of an API page is similar to a UI list page:
+
+```al
+layout
+{
+ area(Content)
+ {
+ repeater(records)
+ {
+ ...
+ }
+ }
+}
+```
+
+When specifying the fields there are some considerations to keep in mind.
+
+```
+field(displayName; Rec.Name) { }
+```
+
+There are no mandatory properties. The property ```ApplicationArea``` does not play a role in API pages, so it can be skipped. The property ```Caption``` is also optional and should only be used in case the external application requires captions and the caption should be different from the standard caption as defined in the table.
+
+The name of the field, in the example above ```displayName```, should be defined in camelCasing. It may not contain spaces, dots, or other special characters.
+
+It is common use to give certain fields a more describing name. Some examples are:
+
+* id for field SystemId
+* number for field "No."
+* displayName for field Name
+
+## Mandatory fields
+These fields should always be part of the API Page:
+
+* SystemId
+ * This field should be exposed with the name ```id```
+* SystemModifiedAt
+ * This field should be exposed with the name ```lastModifiedDateTime```. If you choose a different name, then the webhook functionality will not work properly.
+
+Example:
+
+```al
+layout
+{
+ area(Content)
+ {
+ repeater(records)
+ {
+ field(id; Rec.SystemId) { }
+ field(lastModifiedDateTime; Rec.SystemModifiedAt) { }
+ }
+ }
+}
+```
diff --git a/content/docs/BestPractices/begin-as-an-afterword/index.md b/content/docs/BestPractices/begin-as-an-afterword/index.md
index 190bae4f..5b6bac47 100644
--- a/content/docs/BestPractices/begin-as-an-afterword/index.md
+++ b/content/docs/BestPractices/begin-as-an-afterword/index.md
@@ -1,6 +1,6 @@
---
title: "begin as an afterword"
-tags: ["Readability"]
+tags: ["AL","Readability"]
categories: ["Best Practice"]
---
@@ -8,14 +8,14 @@ _Created by Microsoft, Described by waldo_
## Description
-When `begin` follows `then`, `else`, `do`, it should be on the same line, preceded by one space character.
+When `begin` follows `then`, `else`, `do`, it should be on the same line, preceded by one space character.
## Bad code
```al
if ICPartnerRefType = ICPartnerRefType::"Common Item No." then
begin
- ...
+ ...
end;
```
@@ -23,12 +23,6 @@ When `begin` follows `then`, `else`, `do`, it should be on the same line, preced
```al
if ICPartnerRefType = ICPartnerRefType::"Common Item No." then begin
- ...
+ ...
end;
```
-
-## [Discussions](https://github.com/microsoft/alguidelines/discussions/categories/bc-best-practices?discussions_q=begin+as+an+After+Word+category%3A%22BC+Best+Practices%22)
-
-You can find discussions on all "Best Practices" [here](https://github.com/microsoft/alguidelines/discussions/categories/bc-best-practices).
-
-If you don't find the discussion of this guideline, please feel free to create a new one with the same title as this article.
\ No newline at end of file
diff --git a/content/docs/BestPractices/begin-end/index.md b/content/docs/BestPractices/begin-end/index.md
index 22433d65..d35dea99 100644
--- a/content/docs/BestPractices/begin-end/index.md
+++ b/content/docs/BestPractices/begin-end/index.md
@@ -1,6 +1,6 @@
---
title: "Begin-End - Compound Only"
-tags: ["Readability"]
+tags: ["AL","Readability"]
categories: ["Best Practice"]
---
@@ -8,26 +8,30 @@ _Created by Microsoft, Described by waldo_
Only use begin..end to enclose [compound statements](https://docs.microsoft.com/en-us/cpp/c-language/compound-statement-c?view=msvc-170#:~:text=A%20compound%20statement%20%28also%20called%20a%20%22block%22%29%20typically,appear%20at%20the%20head%20of%20a%20compound%20statement.).
-## Bad code
+## Example 1
+
+### Bad code
```AL
if FindSet() then begin
repeat
- ...
+ ...
until next() = 0;
end;
```
-## Good code
+### Good code
```AL
if FindSet() then
repeat
- ...
+ ...
until next() = 0;
```
-## Bad code
+## Example 2
+
+### Bad code
```AL
if IsAssemblyOutputLine then begin
@@ -35,7 +39,7 @@ if IsAssemblyOutputLine then begin
end;
```
-## Good code
+### Good code
```AL
if IsAssemblyOutputLine then
@@ -53,8 +57,9 @@ end else
(not X)
```
-## [Discussions](https://github.com/microsoft/alguidelines/discussions/categories/bc-best-practices?discussions_q=begin+end+compound+only+category%3A%22BC+Best+Practices%22)
+## Tips
-You can find discussions on all "Best Practices" [here](https://github.com/microsoft/alguidelines/discussions/categories/bc-best-practices).
+The [AZ AL Dev Tools/AL Code Outline](https://marketplace.visualstudio.com/items?itemName=andrzejzwierzchowski.al-code-outline) extension adds two new commands to Visual Studio Code to remove begin..end around single statements.
-If you don't find the discussion of this guideline, please feel free to create a new one with the same title as this article.
+- `Remove Begin..End around Single Statements from the Active Editor` : removes begin..end around single statement from the current editor
+- `Remove Begin..End around Single Statements from the Active Project` : removes begin..end around single statement from the current project
diff --git a/content/docs/BestPractices/binary-operator-line-start/index.md b/content/docs/BestPractices/binary-operator-line-start/index.md
index 7408ef63..2f7ecae8 100644
--- a/content/docs/BestPractices/binary-operator-line-start/index.md
+++ b/content/docs/BestPractices/binary-operator-line-start/index.md
@@ -1,6 +1,6 @@
---
title: "Binary Operator to Start Line"
-tags: ["Readability"]
+tags: ["AL","Readability"]
categories: ["Best Practice"]
---
@@ -8,26 +8,20 @@ _Created by Microsoft, Described by waldo_
## Description
-Do not start a line with a binary operator.
+Do not start a line with a binary operator.
## Bad code
```AL
"Quantity to Ship" :=
-Quantity
-- "Quantity Shipped"
+ Quantity
+ - "Quantity Shipped"
```
## Good code
```AL
"Quantity to Ship" :=
-Quantity -
-"Quantity Shipped"
+ Quantity -
+ "Quantity Shipped"
```
-
-## [Discussions](https://github.com/microsoft/alguidelines/discussions/categories/bc-best-practices?discussions_q=binary+operator+to+start+line+category%3A%22BC+Best+Practices%22)
-
-You can find discussions on all "Best Practices" [here](https://github.com/microsoft/alguidelines/discussions/categories/bc-best-practices).
-
-If you don't find the discussion of this guideline, please feel free to create a new one with the same title as this article.
diff --git a/content/docs/BestPractices/blank-lines/index.md b/content/docs/BestPractices/blank-lines/index.md
new file mode 100644
index 00000000..7f1b8efe
--- /dev/null
+++ b/content/docs/BestPractices/blank-lines/index.md
@@ -0,0 +1,115 @@
+---
+title: "When not to use Blank Lines"
+tags: ["AL","Readability"]
+categories: ["Best Practice"]
+---
+
+## Description
+
+Do not use blank lines:
+
+- at the beginning or end of any functions (after `begin` and before `end`)
+- inside multiline expression
+- after blank lines
+
+## Example 1
+
+### Bad code
+
+```al
+procedure MATRIX_OnDrillDown(MATRIX_ColumnOrdinal: Integer)
+begin
+
+ SetupDrillDownCol(MATRIX_ColumnOrdinal);
+ DrillDown(false, ValueType);
+
+end;
+```
+
+### Good code
+
+```al
+procedure MATRIX_OnDrillDown(MATRIX_ColumnOrdinal: Integer)
+begin
+ SetupDrillDownCol(MATRIX_ColumnOrdinal);
+ DrillDown(false, ValueType);
+end;
+```
+
+## Example 2
+
+### Bad code
+
+```al
+if NameIsValid and
+
+ Name2IsValid
+then
+```
+
+### Good code
+
+```al
+if NameIsValid and
+ Name2IsValid
+then
+```
+
+## Example 3
+
+### Bad code
+
+```al
+var
+ GLSetup: Record "General Ledger Setup";
+ GLSetupRead: Boolean;
+
+
+local procedure GetGLSetup()
+begin
+ if not GLSetupRead then
+ GLSetup.Get();
+
+
+ GLSetupRead := true;
+
+
+ OnAfterGetGLSetup(GLSetup);
+end;
+
+
+[IntegrationEvent(false, false)]
+local procedure OnAfterGetGLSetup(var GLSetup: Record "General Ledger Setup")
+begin
+end;
+```
+
+### Good code
+
+```al
+var
+ GLSetup: Record "General Ledger Setup";
+ GLSetupRead: Boolean;
+
+local procedure GetGLSetup()
+begin
+ if not GLSetupRead then
+ GLSetup.Get();
+
+ GLSetupRead := true;
+
+ OnAfterGetGLSetup(GLSetup);
+end;
+
+[IntegrationEvent(false, false)]
+local procedure OnAfterGetGLSetup(var GLSetup: Record "General Ledger Setup")
+begin
+end;
+```
+
+## Tips
+
+The [AZ AL Dev Tools/AL Code Outline](https://marketplace.visualstudio.com/items?itemName=andrzejzwierzchowski.al-code-outline) extension adds two new commands to Visual Studio Code to remove empty duplicate lines.
+
+- `Remove Empty Lines from the Active Editor` : removes empty duplicate lines from the current editor
+- `Remove Empty Lines from the Active Project` : removes empty duplicate lines from the current project
diff --git a/content/docs/BestPractices/case-actions/index.md b/content/docs/BestPractices/case-actions/index.md
index fb041368..5062e53e 100644
--- a/content/docs/BestPractices/case-actions/index.md
+++ b/content/docs/BestPractices/case-actions/index.md
@@ -1,6 +1,6 @@
---
title: "CASE Action on next line"
-tags: ["Readability"]
+tags: ["AL","Readability"]
categories: ["Best Practice"]
---
@@ -8,7 +8,7 @@ _Created by Microsoft, Described by waldo_
## Description
-A CASE action should start on a line after the possibility.
+A CASE action should start on a line after the possibility.
## Bad code
@@ -29,9 +29,3 @@ A CASE action should start on a line after the possibility.
Letter2 := '11';
end;
```
-
-## [Discussions](https://github.com/microsoft/alguidelines/discussions/categories/bc-best-practices?discussions_q=case+action+on+next+line+category%3A%22BC+Best+Practices%22)
-
-You can find discussions on all "Best Practices" [here](https://github.com/microsoft/alguidelines/discussions/categories/bc-best-practices).
-
-If you don't find the discussion of this guideline, please feel free to create a new one with the same title as this article.
diff --git a/content/docs/BestPractices/comments-spacing/index.md b/content/docs/BestPractices/comments-spacing/index.md
index e8e2c07f..615f9e69 100644
--- a/content/docs/BestPractices/comments-spacing/index.md
+++ b/content/docs/BestPractices/comments-spacing/index.md
@@ -1,12 +1,13 @@
---
title: "Comment Spacing"
-tags: ["Readability"]
+tags: ["AL","Readability"]
categories: ["Best Practice"]
---
_Created by Microsoft, Described by waldo_
## Description
+
Always start comments with // followed by one space character.
## Bad code
@@ -14,16 +15,9 @@ Always start comments with // followed by one space character.
```al
RowNo += 1000; //Move way below the budget
```
-
-
+
## Good code
```al
RowNo += 1000; // Move way below the budget
```
-
-## [Discussions](https://github.com/microsoft/alguidelines/discussions/categories/bc-best-practices?discussions_q=comment+spacing+category%3A%22BC+Best+Practices%22)
-
-You can find discussions on all "Best Practices" [here](https://github.com/microsoft/alguidelines/discussions/categories/bc-best-practices).
-
-If you don't find the discussion of this guideline, please feel free to create a new one with the same title as this article.
\ No newline at end of file
diff --git a/content/docs/BestPractices/end-else-pair/index.md b/content/docs/BestPractices/end-else-pair/index.md
index 9c339d36..29313176 100644
--- a/content/docs/BestPractices/end-else-pair/index.md
+++ b/content/docs/BestPractices/end-else-pair/index.md
@@ -1,6 +1,6 @@
---
title: "end else pair"
-tags: ["Readability"]
+tags: ["AL","Readability"]
categories: ["Best Practice"]
---
@@ -15,11 +15,11 @@ The `end else` pair should always appear on the same line.
```al
if OppEntry.Find('-') then
if SalesCycleStage.Find('-') then begin
- ...
+ ...
end
else
begin
- ...
+ ...
end;
```
@@ -28,14 +28,8 @@ The `end else` pair should always appear on the same line.
```al
if OppEntry.Find('-') then
if SalesCycleStage.Find('-') then begin
- ...
+ ...
end else begin
- ...
+ ...
end;
```
-
-## [Discussions](https://github.com/microsoft/alguidelines/discussions/categories/bc-best-practices?discussions_q=end+else/pair+category%3A%22BC+Best+Practices%22)
-
-You can find discussions on all "Best Practices" [here](https://github.com/microsoft/alguidelines/discussions/categories/bc-best-practices).
-
-If you don't find the discussion of this guideline, please feel free to create a new one with the same title as this article.
diff --git a/content/docs/BestPractices/if-not-find-then-exit/index.md b/content/docs/BestPractices/if-not-find-then-exit/index.md
new file mode 100644
index 00000000..6c097864
--- /dev/null
+++ b/content/docs/BestPractices/if-not-find-then-exit/index.md
@@ -0,0 +1,109 @@
+---
+title: "if not then exit"
+tags: ["AL"]
+categories: ["Best Practice"]
+---
+
+_Created by sirhc101, Described by sirhc101_
+
+## Description
+
+In general when we are working with tables we want to make sure, the filtered dataset includes records and does not result in a runtime error, so we use `if` to handle the result of `Find()`, `FindSet()`, `Get()`, etc.
+This automatically causes on indent in source code and often the source code does not just contain one but two or more tables involved which leads to multi-level indentation.
+
+Basically this is a result of bad coding structure but maybe sometimes necessary. On the other hand this causes multiple `end;` usages and leads to the usage of colorization and other helpers to see which `begin` belongs to which `end;`.
+
+Instead of using `if (Record.FindSet()) then` to fetch records from a database it's good practice to use `if (not Record.FindSet()) then` following by an `exit();` to not further process the source code and make it clear for other developers where they can stop reading in certain cases.
+
+Furthermore, this more or less automatically leads to smaller and better structured procedures and reduces the complexity of the source code.
+
+## Bad code
+
+```al
+ SalesHeader.Reset();
+ SalesHeader.SetRange("Document Type", SalesHeader."Document Type"::Order);
+ SalesHeader.SetRange(Status, SalesHeader.Status::Open);
+ if (SalesHeader.FindSet(false)) then begin
+ repeat
+ SalesLine.Reset();
+ SalesLine.SetRange("Document Type", SalesHeader."Document Type"::Order);
+ SalesLine.SetRange("Document No.", SalesHeader."No.");
+ if (SalesLine.FindSet(true)) then begin
+ repeat
+ DoSomething();
+ until SalesLine.Next() = 0;
+ end;
+ until SalesHeader.Next() = 0;
+
+ DoSomethingElse();
+ end;
+```
+
+or
+
+```al
+ SalesLine.Reset();
+ SalesLine.SetRange("Document Type", SalesHeader."Document Type"::Order);
+ if (SalesLine.FindSet(true)) then begin
+ repeat
+ case SalesLine."Type" of
+ SalesLine."Type"::Item:
+ DoSomethingItem();
+ SalesLine."Type"::Resource:
+ DoSomethingResource();
+ end;
+ until SalesLine.Next() = 0;
+ end;
+```
+
+## Good code
+
+```al
+ procedure DoSomethingSalesOrder()
+ var
+ SalesHeader: Record "Sales Header";
+ begin
+ SalesHeader.SetRange("Document Type", SalesHeader."Document Type"::Order);
+ SalesHeader.SetRange(Status, SalesHeader.Status::Open);
+ if (not SalesHeader.FindSet(false)) then
+ exit;
+
+ repeat
+ DoSomethingSalesLine(SalesHeader);
+ until SalesHeader.Next() = 0;
+
+ DoSomethingElse();
+ end;
+
+ procedure DoSomethingSalesLine(var SalesHeader: Record "Sales Header")
+ var
+ SalesLine: Record "Sales Line";
+ begin
+ SalesLine.Reset();
+ SalesLine.SetRange("Document Type", SalesHeader."Document Type"::Order);
+ SalesLine.SetRange("Document No.", SalesHeader."No.");
+ if (not SalesLine.FindSet(true)) then
+ exit;
+
+ repeat
+ DoSomething();
+ until SalesLine.Next() = 0;
+ end;
+```
+
+or
+
+```al
+ SalesLine.SetRange("Document Type", SalesHeader."Document Type"::Order);
+ if (not SalesLine.FindSet(true)) then
+ exit;
+
+ repeat
+ case SalesLine."Type" of
+ SalesLine."Type"::Item:
+ DoSomethingItem();
+ SalesLine."Type"::Resource:
+ DoSomethingResource();
+ end;
+ until SalesLine.Next() = 0;
+```
diff --git a/content/docs/BestPractices/istemporary-table-safeguard/index.md b/content/docs/BestPractices/istemporary-table-safeguard/index.md
new file mode 100644
index 00000000..7331b34c
--- /dev/null
+++ b/content/docs/BestPractices/istemporary-table-safeguard/index.md
@@ -0,0 +1,60 @@
+---
+title: "IsTemporary record safeguard"
+tags: ["AL"]
+categories: ["Best Practice"]
+---
+
+_Created by Kine, Described by Kine_
+
+## Description
+
+When you are working with temporary tables or real tables, you can have code, where you assume that Record variable is or is not temporary. Best practice is to not assume, but test it to be sure. In history,
+many developers went through painful period when they did unwanted "DeleteAll" over real table in production database, because they were only assuming something (mostly it happened only once to them).
+
+Therefore it is good practice to use [Record.IsTemporary()](https://docs.microsoft.com/en-us/dynamics365/business-central/dev-itpro/developer/methods-auto/record/record-istemporary-method) method to test this predicate, mainly when you are doing destructive action.
+
+Another case when it is good practice to use this test is when you are subscribing to triggers on table. In most cases, you do not want to run your code when the trigger is running over temporary record. And you cannot assume, that
+this specific table will not be used as temporary by someone else. Reacting to the trigger as if it was triggered by real table change could lead to corrupted data or unpredictable errors and the reason could be hard to find.
+
+## Bad code
+
+```al
+ ShouldBeTemporary.DeleteAll(true);
+```
+
+or
+
+```al
+ [EventSubscriber(ObjectType::Table, Database::"Some Table", 'OnAfterInsertEvent', '', false, false)]
+ local procedure DoSomethingOnAfterInsertSomeTable(var Rec: Record "Some Table")
+ begin
+ DoSomething(Rec);
+ end;
+```
+
+## Good code
+
+```al
+ if ShouldBeTemporary.IsTemporary() then
+ ShouldBeTemporary.DeleteAll(true);
+```
+
+or
+
+```al
+ if not ShouldBeTemporary.IsTemporary() then
+ Error(RecNotTemporaryErr);
+ ShouldBeTemporary.DeleteAll(true);
+```
+
+or
+
+```al
+ [EventSubscriber(ObjectType::Table, Database::"Some Table", 'OnAfterInsertEvent', '', false, false)]
+ local procedure DoSomethingOnAfterInsertSomeTable(var Rec: Record "Some Table")
+ begin
+ if Rec.IsTemporary() then
+ Exit;
+ DoSomething(Rec);
+ end;
+```
diff --git a/content/docs/BestPractices/keyboard-shortcuts/index.md b/content/docs/BestPractices/keyboard-shortcuts/index.md
new file mode 100644
index 00000000..a9b15863
--- /dev/null
+++ b/content/docs/BestPractices/keyboard-shortcuts/index.md
@@ -0,0 +1,39 @@
+---
+title: "Keyboard Shortcuts"
+tags: ["AL","Productivity"]
+categories: ["Best Practice"]
+---
+
+_Created by Christian Lenz, Described by Christian Lenz_
+
+## Description
+
+To increase developer productivity while coding, use keyboard shortcuts that are available in the specific context to execute actions faster.
+
+This is a selection of the community's favorites (more to come).
+
+**Windows**
+
+| What | Where | How |
+|---|---|---|
+| Delete word | Editor | CTRL + Backspace |
+
+
+**VS Code**
+
+| What | Where | How |
+|---|---|---|
+| Switch Tab | Editor | ALT + |
+| Move Line Up/Down | Editor | ALT + |
+| Copy Line Below/Above | Editor | ALT + SHIFT + |
+| Delete Line | Editor | CTRL + X (without selection) |
+| Add Selection To Next Match | Editor | CTRL + D |
+| Select All Occurences Of Find Match | Editor | CTRL + SHIFT + L |
+| Add Cursors To Line Ends | Editor | SHIFT + ALT + I (after selecting lines) |
+| Add Cursor Above/Below | Editor | CTRL + ALT + |
+| Place Multiple Cursors Manually | Editor | ALT + Click |
+| Fast Scrolling | Editor | ALT + Mouse Wheel |
+| Go To Symbol In File | Editor | CTRL + SHIFT + O |
+| Breadcrumbs - Open And Select | Editor | CTRL + SHIFT + . |
+| Go Back / Forward | Go To Definition | ALT + |
+
diff --git a/content/docs/BestPractices/keyword-pairs-indentation/index.md b/content/docs/BestPractices/keyword-pairs-indentation/index.md
index 625310f4..b25ea498 100644
--- a/content/docs/BestPractices/keyword-pairs-indentation/index.md
+++ b/content/docs/BestPractices/keyword-pairs-indentation/index.md
@@ -1,12 +1,13 @@
---
title: "Keyword Pairs - Indentation"
-tags: ["Readability"]
+tags: ["AL","Readability"]
categories: ["Best Practice"]
---
_Created by Microsoft, Described by waldo_
## Description
+
The `if..then` pair, `while..do` pair, and `for..do` pair must appear on the same line or the same level of indentation. If possible, you can align the lines it is even much more readable.
## Bad code
@@ -23,9 +24,3 @@ The `if..then` pair, `while..do` pair, and `for..do` pair must appear on the sam
(a = b)
then
```
-
-## [Discussions](https://github.com/microsoft/alguidelines/discussions/categories/bc-best-practices?discussions_q=keyword+pair+indentation+category%3A%22BC+Best+Practices%22)
-
-You can find discussions on all "Best Practices" [here](https://github.com/microsoft/alguidelines/discussions/categories/bc-best-practices).
-
-If you don't find the discussion of this guideline, please feel free to create a new one with the same title as this article.
\ No newline at end of file
diff --git a/content/docs/BestPractices/line-start-keywords/index.md b/content/docs/BestPractices/line-start-keywords/index.md
index 51cee4ee..113e587c 100644
--- a/content/docs/BestPractices/line-start-keywords/index.md
+++ b/content/docs/BestPractices/line-start-keywords/index.md
@@ -1,11 +1,12 @@
---
title: "Line Start Keywords"
-tags: ["Readability"]
+tags: ["AL","Readability"]
categories: ["Best Practice"]
---
-<_Created by Microsoft, Described by waldo_\>
+_Created by Microsoft, Described by waldo_
## Description
+
The `end`, `if`, `repeat`, `for`, `while`, `else` and `case` statement should always start a line.
## Bad code
@@ -28,10 +29,3 @@ The `end`, `if`, `repeat`, `for`, `while`, `else` and `case` statement should al
if IsSalesCycleCode then
ValidatSalesCycleCode();
```
-
-
-## [Discussions](https://github.com/microsoft/alguidelines/discussions/categories/bc-best-practices?discussions_q=line+start+keyword+category%3A%22BC+Best+Practices%22)
-
-You can find discussions on all "Best Practices" [here](https://github.com/microsoft/alguidelines/discussions/categories/bc-best-practices).
-
-If you don't find the discussion of this guideline, please feel free to create a new one with the same title as this article.
\ No newline at end of file
diff --git a/content/docs/BestPractices/lonely-repeat/index.md b/content/docs/BestPractices/lonely-repeat/index.md
index 87551a9e..d15c0945 100644
--- a/content/docs/BestPractices/lonely-repeat/index.md
+++ b/content/docs/BestPractices/lonely-repeat/index.md
@@ -1,12 +1,13 @@
---
title: "Lonely Repeat"
-tags: ["Readability"]
+tags: ["AL","Readability"]
categories: ["Best Practice"]
---
_Created by Microsoft, Described by waldo_
## Description
+
The `repeat` statement should always be alone on a line.
## Bad code
@@ -14,16 +15,10 @@ The `repeat` statement should always be alone on a line.
```al
if ReservEntry.FindSet() then repeat
```
-
+
## Good code
```al
if ReservEntry.FindSet() then
repeat
```
-
-## [Discussions](https://github.com/microsoft/alguidelines/discussions/categories/bc-best-practices?discussions_q=lonely+repeat+category%3A%22BC+Best+Practices%22)
-
-You can find discussions on all "Best Practices" [here](https://github.com/microsoft/alguidelines/discussions/categories/bc-best-practices).
-
-If you don't find the discussion of this guideline, please feel free to create a new one with the same title as this article.
\ No newline at end of file
diff --git a/content/docs/BestPractices/named-invocations/index.md b/content/docs/BestPractices/named-invocations/index.md
index 9d817731..395848e4 100644
--- a/content/docs/BestPractices/named-invocations/index.md
+++ b/content/docs/BestPractices/named-invocations/index.md
@@ -1,12 +1,13 @@
---
title: "Named Invocations"
-tags: ["Readability"]
+tags: ["AL","Readability"]
categories: ["Best Practice"]
---
_Created by Microsoft, Described by waldo_
## Description
+
When calling an object statically use the Object Name, not the Object Id.
## Bad code
@@ -21,8 +22,8 @@ When calling an object statically use the Object Name, not the Object Id.
Page.RunModal(Page::"Posted Sales Shipment Lines", SalesShptLine);
```
-## [Discussions](https://github.com/microsoft/alguidelines/discussions/categories/bc-best-practices?discussions_q=named+invocations+category%3A%22BC+Best+Practices%22)
+## Tips
-You can find discussions on all "Best Practices" [here](https://github.com/microsoft/alguidelines/discussions/categories/bc-best-practices).
+The [BusinessCentral.LinterCop](https://marketplace.visualstudio.com/items?itemName=StefanMaron.businesscentral-lintercop) extension adds a new rule to check your code for hardcoded object IDs.
-If you don't find the discussion of this guideline, please feel free to create a new one with the same title as this article.
+- [LC0012](https://github.com/StefanMaron/BusinessCentral.LinterCop/wiki/LC0012): Using hardcoded IDs in functions like Codeunit.Run() is not allowed.
diff --git a/content/docs/BestPractices/one-statement-per-line/index.md b/content/docs/BestPractices/one-statement-per-line/index.md
index a8508ff5..822c05c1 100644
--- a/content/docs/BestPractices/one-statement-per-line/index.md
+++ b/content/docs/BestPractices/one-statement-per-line/index.md
@@ -1,44 +1,41 @@
---
title: "One Statement per Line"
-tags: ["Readability"]
+tags: ["AL","Readability"]
categories: ["Best Practice"]
---
_Created by Microsoft, Described by waldo_
## Description
+
A line of code should not have more than one statement.
-## Bad code
+## Example 1
+
+### Bad code
```al
if OppEntry.Find('-') then exit;
-```
-
+```
-## Good code
+### Good code
```al
if OppEntry.Find('-') then
exit;
-```
-
-## Bad code
+```
+
+## Example 2
+
+### Bad code
```al
TotalCost += Cost; TotalAmt += Amt;
-```
-
+```
-## Good code
+### Good code
```al
TotalCost += Cost;
TotalAmt += Amt;
```
-
-## [Discussions](https://github.com/microsoft/alguidelines/discussions/categories/bc-best-practices?discussions_q=one+statement+per+line+category%3A%22BC+Best+Practices%22)
-
-You can find discussions on all "Best Practices" [here](https://github.com/microsoft/alguidelines/discussions/categories/bc-best-practices).
-
-If you don't find the discussion of this guideline, please feel free to create a new one with the same title as this article.
diff --git a/content/docs/BestPractices/separate-if-and-else/index.md b/content/docs/BestPractices/separate-if-and-else/index.md
index 0cfe515b..805ff90c 100644
--- a/content/docs/BestPractices/separate-if-and-else/index.md
+++ b/content/docs/BestPractices/separate-if-and-else/index.md
@@ -1,35 +1,29 @@
---
title: "Seperate if and else"
-tags: ["Readability"]
+tags: ["AL","Readability"]
categories: ["Best Practice"]
---
_Created by Microsoft, Described by waldo_
## Description
-`if` and `else` statements should be on separate lines.
+
+`if` and `else` statements should be on separate lines.
## Bad code
```al
- if Atom = '\>' then HasLogicalOperator := true else begin
- ...
+ if Atom = '>' then HasLogicalOperator := true else begin
+ ...
end;
```
## Good code
```al
- if Atom = '\>' then
+ if Atom = '>' then
HasLogicalOperator := true
else begin
...
end;
```
-
-
-## [Discussions](https://github.com/microsoft/alguidelines/discussions/categories/bc-best-practices?discussions_q=separate+if+and+else+category%3A%22BC+Best+Practices%22)
-
-You can find discussions on all "Best Practices" [here](https://github.com/microsoft/alguidelines/discussions/categories/bc-best-practices).
-
-If you don't find the discussion of this guideline, please feel free to create a new one with the same title as this article.
diff --git a/content/docs/BestPractices/spacing-binary-operators/index.md b/content/docs/BestPractices/spacing-binary-operators/index.md
index 48bbbbe5..e0bfaac8 100644
--- a/content/docs/BestPractices/spacing-binary-operators/index.md
+++ b/content/docs/BestPractices/spacing-binary-operators/index.md
@@ -1,46 +1,53 @@
---
title: "Spacing Binary Operators"
-tags: ["Readability"]
+tags: ["AL","Readability"]
categories: ["Best Practice"]
---
_Created by Microsoft, Described by waldo_
## Description
+
There must be exactly one space character on each side of a binary operator such as = + - AND OR =. The parameter comma operator however, should have a space after the comma.
-## Bad code
+## Example 1
+
+### Bad code
```al
- "Line Discount %" := "Line Discount Amount"/"Line Value"*100;
-```
-
-## Good code
-
-```al
- "Line Discount %" := "Line Discount Amount" / "Line Value" * 100;
-```
-
-## Bad code
-
-```al
- StartDate := CalcDate('<+'+Format(Days+i)+'D\>',StartDate);
-```
-
-## Good code
-
-```al
- StartDate := CalcDate('<+' + Format(Days + i) + 'D\>', StartDate);
-```
-
-## Bad code
-
-```al
- StartDate:=0D; // Initialize
+ "Line Discount %" := "Line Discount Amount"/"Line Value"*100;
```
-
-## Good code
+
+### Good code
+
+```al
+ "Line Discount %" := "Line Discount Amount" / "Line Value" * 100;
+```
+
+## Example 2
+
+### Bad code
+
+```al
+ StartDate := CalcDate('<+'+Format(Days+i)+'D\>',StartDate);
+```
+
+### Good code
+
+```al
+ StartDate := CalcDate('<+' + Format(Days + i) + 'D\>', StartDate);
+```
+
+## Example 3
+
+### Bad code
+
+```al
+ StartDate:=0D; // Initialize
+```
+
+### Good code
```al
StartDate := 0D; // Initialize
-```
\ No newline at end of file
+```
diff --git a/content/docs/BestPractices/suggested-abbreviations/index.md b/content/docs/BestPractices/suggested-abbreviations/index.md
index 7ff5136d..722f1561 100644
--- a/content/docs/BestPractices/suggested-abbreviations/index.md
+++ b/content/docs/BestPractices/suggested-abbreviations/index.md
@@ -1,6 +1,6 @@
---
title: "Suggested Abbreviations"
-tags: ["Readability"]
+tags: ["AL","Readability"]
categories: ["Best Practice"]
---
@@ -359,9 +359,3 @@ If there is no other choice, then use the suggestions below.
| % | Pct |
| 3-tier | Three-Tier |
| Outlook Synch | Osynch |
-
-## [Discussions](https://github.com/microsoft/alguidelines/discussions/categories/bc-best-practices?discussions_q=abbreviations+category%3A%22BC+Best+Practices%22)
-
-You can find discussions on all "Best Practices" [here](https://github.com/microsoft/alguidelines/discussions/categories/bc-best-practices).
-
-If you don't find the discussion of this guideline, please feel free to create a new one with the same title as this article.
\ No newline at end of file
diff --git a/content/docs/BestPractices/unnecessary-else/index.md b/content/docs/BestPractices/unnecessary-else/index.md
index a90c97a0..4dd6d1bd 100644
--- a/content/docs/BestPractices/unnecessary-else/index.md
+++ b/content/docs/BestPractices/unnecessary-else/index.md
@@ -1,12 +1,13 @@
---
title: "Unnecessary else"
-tags: ["Readability"]
+tags: ["AL","Readability"]
categories: ["Best Practice"]
---
_Created by Microsoft, Described by waldo_
## Description
+
`else` should not be used when the last action in the `then` part is an `exit`, `break`, `skip`, `quit`, `error`.
## Bad code
@@ -22,6 +23,7 @@ _Created by Microsoft, Described by waldo_
```
## Good code
+
```al
procedure SomeProcedure()
begin
@@ -30,10 +32,3 @@ _Created by Microsoft, Described by waldo_
Error(BinCodeChangeNotAllowedErr, ...);
end;
```
-
-
-## [Discussions](https://github.com/microsoft/alguidelines/discussions/categories/bc-best-practices?discussions_q=unnecessary+else+category%3A%22BC+Best+Practices%22)
-
-You can find discussions on all "Best Practices" [here](https://github.com/microsoft/alguidelines/discussions/categories/bc-best-practices).
-
-If you don't find the discussion of this guideline, please feel free to create a new one with the same title as this article.
diff --git a/content/docs/BestPractices/unnecessary-truefalse/index.md b/content/docs/BestPractices/unnecessary-truefalse/index.md
index 8e020968..57d6864b 100644
--- a/content/docs/BestPractices/unnecessary-truefalse/index.md
+++ b/content/docs/BestPractices/unnecessary-truefalse/index.md
@@ -1,6 +1,6 @@
---
title: "Unnecessary true/false"
-tags: ["Readability"]
+tags: ["AL","Readability"]
categories: ["Best Practice"]
---
@@ -9,33 +9,30 @@ _Created by Microsoft, Described by waldo_
## Description
Do not use `true` or `false` keywords unnecessarily if the expression is already an logical expression.
-## Bad code
+## Example 1
+
+### Bad code
```al
if IsPositive() = true then
-```
-
-## Good code
+```
+
+### Good code
```al
if IsPositive() then
-```
-
+```
-## Bad code
+## Example 2
+
+### Bad code
```al
if Complete <> true then
-```
-
-## Good code
+```
+
+### Good code
```al
if not Complete then
```
-
-## [Discussions](https://github.com/microsoft/alguidelines/discussions/categories/bc-best-practices?discussions_q=unnecessary+true+false+category%3A%22BC+Best+Practices%22)
-
-You can find discussions on all "Best Practices" [here](https://github.com/microsoft/alguidelines/discussions/categories/bc-best-practices).
-
-If you don't find the discussion of this guideline, please feel free to create a new one with the same title as this article.
\ No newline at end of file
diff --git a/content/docs/BestPractices/variable-naming/index.md b/content/docs/BestPractices/variable-naming/index.md
index e722b779..242d1d15 100644
--- a/content/docs/BestPractices/variable-naming/index.md
+++ b/content/docs/BestPractices/variable-naming/index.md
@@ -1,12 +1,13 @@
---
title: "Variable Naming"
-tags: ["Readability"]
+tags: ["AL","Readability"]
categories: ["Best Practice"]
---
_Created by Microsoft, Described by waldo_
## Description
+
Variables that refer to a AL object must contain the objects name, abbreviated where necessary.
A variable must begin with a capital letter.
@@ -15,34 +16,48 @@ Blanks, periods, and other characters (such as parentheses) that would make quot
If a variable is a compound of two or more words or abbreviations, each word or abbreviation should begin with a capital letter.
-## Bad code
+## Example 1
+
+### Bad code
```al
WIPBuffer: Record "Job WIP Buffer"
```
-## Good code
+
+### Good code
+
```al
JobWIPBuffer: Record "Job WIP Buffer"
```
-## Bad code
+
+## Example 2
+
+### Bad code
+
```al
Postline: Codeunit "Gen. Jnl.-Post Line";
```
-## Good code
+
+### Good code
+
```al
GenJnlPostLine: Codeunit "Gen. Jnl.-Post Line";
```
-## Bad code
+
+## Example 3
+
+### Bad code
+
```al
"Amount (LCY)": Decimal;
```
-## Good code
+
+### Good code
+
```al
AmountLCY: Decimal;
```
-## [Discussions](https://github.com/microsoft/alguidelines/discussions/categories/bc-best-practices?discussions_q=one+variable+naming+category%3A%22BC+Best+Practices%22)
+## Tips
-You can find discussions on all "Best Practices" [here](https://github.com/microsoft/alguidelines/discussions/categories/bc-best-practices).
-
-If you don't find the discussion of this guideline, please feel free to create a new one with the same title as this article.
+The [AL Variable Helper](https://marketplace.visualstudio.com/items?itemName=rasmus.al-var-helper) extension provides Intellisense support to assign correct variable names in AL.
diff --git a/content/docs/BestPractices/variables-declarations-order/index.md b/content/docs/BestPractices/variables-declarations-order/index.md
index 1701e044..8f7e2131 100644
--- a/content/docs/BestPractices/variables-declarations-order/index.md
+++ b/content/docs/BestPractices/variables-declarations-order/index.md
@@ -1,12 +1,13 @@
---
title: "Variables Declarations Order"
-tags: ["Readability"]
+tags: ["AL","Readability"]
categories: ["Best Practice"]
---
_Created by Microsoft, Described by waldo_
## Description
+
Variables declarations should be ordered by type. In general, object and complex variable types are listed first followed by simple variables. The order should be:
- Record
@@ -25,7 +26,6 @@ Variables declarations should be ordered by type. In general, object and complex
(Ref: [Microsoft Docs](https://docs.microsoft.com/en-us/dynamics365/business-central/dev-itpro/developer/analyzers/codecop-aa0021))
-
## Bad code
```al
@@ -40,8 +40,9 @@ Variables declarations should be ordered by type. In general, object and complex
StartingDateFilter: Text;
```
-## [Discussions](https://github.com/microsoft/alguidelines/discussions/categories/bc-best-practices?discussions_q=one+variables+declarations+order+category%3A%22BC+Best+Practices%22)
+## Tips
-You can find discussions on all "Best Practices" [here](https://github.com/microsoft/alguidelines/discussions/categories/bc-best-practices).
+The [AZ AL Dev Tools/AL Code Outline](https://marketplace.visualstudio.com/items?itemName=andrzejzwierzchowski.al-code-outline) extension adds two new commands to Visual Studio Code to sorts variables.
-If you don't find the discussion of this guideline, please feel free to create a new one with the same title as this article.
+- `Sort Variables in the Active Editor` : sorts variables in the current editor
+- `Sort Variables in the Active Project` : sorts variables in the current project
diff --git a/content/docs/Contributing/FormattingTips/index.md b/content/docs/Contributing/FormattingTips/index.md
index 4f4e525e..bf6f6a18 100644
--- a/content/docs/Contributing/FormattingTips/index.md
+++ b/content/docs/Contributing/FormattingTips/index.md
@@ -38,6 +38,66 @@ Results in:
end;
```
+## Diagrams with Mermaid
+
+When providing a documentation, diagrams come in handy. [Mermaid](https://mermaid-js.github.io/mermaid/#/) lets you create diagrams and visualizations using text and code.
+
+For example the following markdown section:
+
+````
+```mermaid
+classDiagram
+ Animal <|-- Duck
+ Animal <|-- Fish
+ Animal <|-- Zebra
+ Animal : +int age
+ Animal : +String gender
+ Animal: +isMammal()
+ Animal: +mate()
+ class Duck{
+ +String beakColor
+ +swim()
+ +quack()
+ }
+ class Fish{
+ -int sizeInFeet
+ -canEat()
+ }
+ class Zebra{
+ +bool is_wild
+ +run()
+ }
+```
+````
+
+Results in:
+
+```mermaid
+classDiagram
+ Animal <|-- Duck
+ Animal <|-- Fish
+ Animal <|-- Zebra
+ Animal : +int age
+ Animal : +String gender
+ Animal: +isMammal()
+ Animal: +mate()
+ class Duck{
+ +String beakColor
+ +swim()
+ +quack()
+ }
+ class Fish{
+ -int sizeInFeet
+ -canEat()
+ }
+ class Zebra{
+ +bool is_wild
+ +run()
+ }
+```
+
+Can't wait to get started? Use the Mermaid [Live Editor](https://mermaid.live/edit).
+
## Hugo Shortcodes
Since we're using "Hugo", we can use it's shortcode. Here is a reference: [https://gohugo.io/content-management/shortcodes/](https://gohugo.io/content-management/shortcodes/)
diff --git a/content/docs/Contributing/InstallHugo/ManuallyOnWindows11/EnvironmentVariables.png b/content/docs/Contributing/InstallHugo/ManuallyOnWindows11/EnvironmentVariables.png
new file mode 100644
index 00000000..1f76acbe
Binary files /dev/null and b/content/docs/Contributing/InstallHugo/ManuallyOnWindows11/EnvironmentVariables.png differ
diff --git a/content/docs/Contributing/InstallHugo/ManuallyOnWindows11/EnvironmentVariablesPath.png b/content/docs/Contributing/InstallHugo/ManuallyOnWindows11/EnvironmentVariablesPath.png
new file mode 100644
index 00000000..7c4bb5f6
Binary files /dev/null and b/content/docs/Contributing/InstallHugo/ManuallyOnWindows11/EnvironmentVariablesPath.png differ
diff --git a/content/docs/Contributing/InstallHugo/ManuallyOnWindows11/Extract.png b/content/docs/Contributing/InstallHugo/ManuallyOnWindows11/Extract.png
new file mode 100644
index 00000000..35cb5f98
Binary files /dev/null and b/content/docs/Contributing/InstallHugo/ManuallyOnWindows11/Extract.png differ
diff --git a/content/docs/Contributing/InstallHugo/ManuallyOnWindows11/ExtractToBin.png b/content/docs/Contributing/InstallHugo/ManuallyOnWindows11/ExtractToBin.png
new file mode 100644
index 00000000..ba466115
Binary files /dev/null and b/content/docs/Contributing/InstallHugo/ManuallyOnWindows11/ExtractToBin.png differ
diff --git a/content/docs/Contributing/InstallHugo/ManuallyOnWindows11/HugoServe.png b/content/docs/Contributing/InstallHugo/ManuallyOnWindows11/HugoServe.png
new file mode 100644
index 00000000..569b5098
Binary files /dev/null and b/content/docs/Contributing/InstallHugo/ManuallyOnWindows11/HugoServe.png differ
diff --git a/content/docs/Contributing/InstallHugo/ManuallyOnWindows11/SearchForEnv.png b/content/docs/Contributing/InstallHugo/ManuallyOnWindows11/SearchForEnv.png
new file mode 100644
index 00000000..881136cd
Binary files /dev/null and b/content/docs/Contributing/InstallHugo/ManuallyOnWindows11/SearchForEnv.png differ
diff --git a/content/docs/Contributing/InstallHugo/ManuallyOnWindows11/SelectExtended.png b/content/docs/Contributing/InstallHugo/ManuallyOnWindows11/SelectExtended.png
new file mode 100644
index 00000000..a8adec9c
Binary files /dev/null and b/content/docs/Contributing/InstallHugo/ManuallyOnWindows11/SelectExtended.png differ
diff --git a/content/docs/Contributing/InstallHugo/ManuallyOnWindows11/SystemProperties.png b/content/docs/Contributing/InstallHugo/ManuallyOnWindows11/SystemProperties.png
new file mode 100644
index 00000000..ae8adee9
Binary files /dev/null and b/content/docs/Contributing/InstallHugo/ManuallyOnWindows11/SystemProperties.png differ
diff --git a/content/docs/Contributing/InstallHugo/ManuallyOnWindows11/index.md b/content/docs/Contributing/InstallHugo/ManuallyOnWindows11/index.md
new file mode 100644
index 00000000..671484d5
--- /dev/null
+++ b/content/docs/Contributing/InstallHugo/ManuallyOnWindows11/index.md
@@ -0,0 +1,57 @@
+---
+title: "Manually on Windows 11"
+---
+
+This guide will walk You thrugh installing Hugo on a Windows 11 PC. For the official install guide, You can wisit .
+
+## Download Binaries
+
+The path of least resistance is to just download the .zip file from the official Hugo GitHub Repository [here](https://github.com/gohugoio/hugo/releases).
+
+The theme that are used, needs the extended version of Hugo, so make sure to download the **extended** version by ensuring the file name starts with `hugo_extended...`
+
+{{% alert title="info" color="info" %}}
+For the purpose of this install guide, we are assuming You will want to follow the "standard" used by Hugo for installing. We will be creating a `Hugo` folder in the root of `C:\`. That folder will have a `Bin` folder for the binaries, and a `Sites` folder for each website You are building.
+
+Once You are done, You should have a folder structure that looks like this:
+
+```text
+C:\Hugo
+├── Bin # Binaries
+└┬─ Sites # Hugo Site Repositories
+ └── alguidelines # Hugo Source for alguidelines.dev
+```
+
+{{% /alert %}}
+
+
+
+After downloading the .zip file, extract the zip-file to `c:\Hugo\Bin`
+
+
+
+
+
+## Add Hugo to Path
+
+After copying the binaries to Your PC, You will need to add Hugo binaries to the `%PATH%` system environment variables.
+
+To do that, search for `environment`
+
+
+
+once you see the `Edit the system environment variables`, open it and select `Environment Variables`
+
+
+
+Once the Environment Variables screen is open, highlight the `Path` lines and press the `Edit...` button
+
+
+
+Now press `New` and add the `C:\Hugo\Bin` to the path. Press `OK` and `OK` to save the new `Path`
+
+
+
+Once complete. You should now be able to preview the Hugo site on by opening a command promt, and open the `C:\Hugo\Sites\alguidelines` folder and execute `Hugo Serve`
+
+
\ No newline at end of file
diff --git a/content/docs/Contributing/InstallHugo/TheShortcut/alguidelines codespace.mp4 b/content/docs/Contributing/InstallHugo/TheShortcut/alguidelines codespace.mp4
new file mode 100644
index 00000000..173ea8d5
Binary files /dev/null and b/content/docs/Contributing/InstallHugo/TheShortcut/alguidelines codespace.mp4 differ
diff --git a/content/docs/Contributing/InstallHugo/TheShortcut/alguidelines walkthrough.mp4 b/content/docs/Contributing/InstallHugo/TheShortcut/alguidelines walkthrough.mp4
new file mode 100644
index 00000000..269de8a0
Binary files /dev/null and b/content/docs/Contributing/InstallHugo/TheShortcut/alguidelines walkthrough.mp4 differ
diff --git a/content/docs/Contributing/InstallHugo/TheShortcut/index.md b/content/docs/Contributing/InstallHugo/TheShortcut/index.md
new file mode 100644
index 00000000..9f46fc7f
--- /dev/null
+++ b/content/docs/Contributing/InstallHugo/TheShortcut/index.md
@@ -0,0 +1,35 @@
+---
+Title: Devcontainer with VS Code
+---
+
+## Use a local devcontainer
+
+If you don't want any local setup (apart from Docker Desktop), but still run your own Hugo instance, you can make use of the preconfigured devcontainer. If you want to learn more about the concept, visit [https://code.visualstudio.com/docs/remote/containers](https://code.visualstudio.com/docs/remote/containers). To use it, you need to take the following steps:
+
+1. Start [Docker Desktop](https://www.docker.com/products/docker-desktop) and [switch to Linux containers](https://docs.docker.com/desktop/windows/#switch-between-windows-and-linux-containers) by right-clicking on the Docker logo in the system tray and selecting "Switch to Linux containers...". If you only see "Switch to Windows containers...", then you are already switched to Linux containers. If anything goes wrong, check if you are on the latest version of Docker Desktop and have [the WSL2 integration enabled](https://docs.docker.com/desktop/windows/wsl/#install). If you have all that in place and it still doesn't work, check the extended installation documentation [here](https://code.visualstudio.com/docs/remote/containers#_installation)
+{{% alert title="Consequences of switching" color="info" %}}
+When switching to Linux, you will probably see a warning that tells you that you "will not be able to manage the Windows containers until you switch back to Windows containers". That means that the Docker Desktop management GUI can only show either the Windows containers or the Linux containers and if you switch to Linux, you consequently won't see the Windows containers until you switch back. But the Windows containers will continue to run, you won't loose data and you can keep using them e.g. for Business Central development, you just can't manage them through the Docker Desktop GUI
+{{% /alert %}}
+2. Install the [Remote development extension pack](https://marketplace.visualstudio.com/items?itemName=ms-vscode-remote.vscode-remote-extensionpack) in Visual Studio Code
+3. Run the action "Remote containers: Clone Repository in Container Volume" and select the fork you created. If you haven't done that before, go through the [docs](https://alguidelines.dev/docs/contributing/forkandpr/#step-1-fork).
+4. Wait for a bit. When you do this for the first time, it can take a couple of minutes. Next time it will be faster...
+5. After a while, you will have VS Code with the cloned repository and the terminal should show something like "Done. Press any key to close the terminal."
+6. Run the action "Tasks: Run task" and select "Start local Hugo Server" from the list. If you don't see that entry, you might have to reload your VS Code window and try again
+7. After Hugo has generated the site, you will get a notification that offers you to "Open a browser". Click on that and you will see your local instance of the AL guidelines! Again, on the first try it will be a bit slow and sluggish, but the second one should be fast.
+8. Now you can make changes and just save them. If you open the terminal, you will see a message that tells you that a change was detected and the site was rebuilt. After that, the change should automatically appear in your browser
+
+Here is a walkthrough of the full process:
+
+
+
+## Use GitHub Codespaces
+
+What is also great about this, is that you can also use [GitHub Codespaces](https://github.com/features/codespaces) with that setup. In that case, steps 1-5 become two clicks... Here is another full walkthrough:
+
+
diff --git a/content/docs/Contributing/InstallHugo/UsingPowershellOnWindows11/index.md b/content/docs/Contributing/InstallHugo/UsingPowershellOnWindows11/index.md
new file mode 100644
index 00000000..3b64af8f
--- /dev/null
+++ b/content/docs/Contributing/InstallHugo/UsingPowershellOnWindows11/index.md
@@ -0,0 +1,22 @@
+---
+Title: Powershell on Windows 11
+description: >
+ Install Hugo with a simple Powershell Script and chocolatey
+---
+
+It is possible to use a powershell script and Chocolatey to install and other dependencies. Execute the following script:
+
+```powershell
+Set-ExecutionPolicy Bypass -Scope Process -Force
+[System.Net.ServicePointManager]::SecurityProtocol = [System.Net.ServicePointManager]::SecurityProtocol -bor 3072
+Invoke-Expression ((New-Object System.Net.WebClient).DownloadString('https://community.chocolatey.org/install.ps1'))
+choco install -y nodejs
+choco install -y hugo-extended
+```
+
+Once complete, in root of of the repository execute the following commands
+
+```powershell
+git submodule update --init --recursive --depth 1
+npm install
+```
diff --git a/content/docs/Contributing/InstallHugo/_index.md b/content/docs/Contributing/InstallHugo/_index.md
new file mode 100644
index 00000000..b59eb861
--- /dev/null
+++ b/content/docs/Contributing/InstallHugo/_index.md
@@ -0,0 +1,9 @@
+---
+title: Install Hugo
+---
+
+There are multiple ways to install Hugo for you to properly preview your contributions. Please select the scenario that matches your setup.
+
+For the official install guide, you can visit
+
+{{< youtube G7umPCU-8xc >}}
diff --git a/content/docs/Contributing/Templates/BestPractice/index.md b/content/docs/Contributing/Templates/BestPractice/index.md
new file mode 100644
index 00000000..7a82fbe5
--- /dev/null
+++ b/content/docs/Contributing/Templates/BestPractice/index.md
@@ -0,0 +1,31 @@
+---
+title: "Title Here"
+tags: ["AL"]
+categories: ["Best Practice"]
+---
+
+
+
+
+_Created by Described by _
+
+## Description
+
+In depth description on what this Pattern is all about
+
+- basic components
+- how the interact
+- steps to implement
+- considerations to take
+
+## Bad code
+
+```al
+PutCodeblocksHere()
+```
+
+## Good code
+
+```al
+PutCodeblocksHere()
+```
diff --git a/content/docs/Contributing/Templates/Guidelines/index.md b/content/docs/Contributing/Templates/Guidelines/index.md
deleted file mode 100644
index da408a49..00000000
--- a/content/docs/Contributing/Templates/Guidelines/index.md
+++ /dev/null
@@ -1,36 +0,0 @@
-+++
-title = "Title of the Guideline"
-weight = 1180
-+++
-This is a guideline, some parts are optional (if there's no content, remove the whole paragraph).
-
-<_Created by (company), Described by (company)_\>
-
-## Description
-
-In depth description on what this Pattern is all about
-- basic components
-- how the interact
-- steps to implement
-- considerations to take
-
-
-## Bad code
-
-```al
-PutCodeblocksHere()
-```
-
-## Good code
-
-```al
-PutCodeblocksHere()
-```
-
-## [Discussions](https://github.com/microsoft/alguidelines/discussions/categories/bc-best-practices?discussions_q=TITLEOFYOURGUIDELINEGOESHERE+category%3A%22BC+Best+Practices%22)
-
-
-
-You can find discussions on all "Best Practices" [here](https://github.com/microsoft/alguidelines/discussions/categories/bc-best-practices).
-
-If you don't find the discussion of this guideline, please feel free to create a new one with the same title as this article.
\ No newline at end of file
diff --git a/content/docs/Contributing/Templates/Patterns/index.md b/content/docs/Contributing/Templates/Patterns/index.md
index f42c7dc1..7e39e182 100644
--- a/content/docs/Contributing/Templates/Patterns/index.md
+++ b/content/docs/Contributing/Templates/Patterns/index.md
@@ -1,10 +1,12 @@
-+++
-title = "Title of the pattern"
-weight = 1180
-+++
-This is a guideline, some parts are optional (if there's no content, remove the whole paragraph).
+---
+title: "Title Here"
+tags: ["AL"]
+categories: ["Pattern"]
+---
-<_Created by (company), Described by (company)_\>
+
+
+_Created by Described by _
## Abstract
@@ -21,6 +23,7 @@ What happens before this pattern is used? How can it go wrong? 1-5 lines.
## Description
In depth description on what this Pattern is all about
+
- basic components
- how the interact
- steps to implement
@@ -41,7 +44,3 @@ Usually, there are occasions where NOT to implement the pattern. List the disad
## List of references
Youtube-link? BaseApp? Tweet? ...
-
-## Discussions
-
-Create a discussions-page of your pattern, and add the sentence "You can discuss this pattern [here](https://github.com/microsoft/alguidelines/discussions/42)" with the right link to that discussions-page.
\ No newline at end of file
diff --git a/content/docs/Contributing/Templates/_index.md b/content/docs/Contributing/Templates/_index.md
index ca59e5a3..55b88a6e 100644
--- a/content/docs/Contributing/Templates/_index.md
+++ b/content/docs/Contributing/Templates/_index.md
@@ -1,14 +1,12 @@
-+++
-chapter = true
-pre = ""
-title = "Templates"
-weight = 100
-+++
+---
+title: "Templates"
+---
-# Templates
+We have created some template-files that you can simply copy and use. Look at them as "Patterns for describing patterns"
-We have foreseen some template-files that you can simply copy and use. Look at them as "Patterns for describing patterns" 😉.
+We currently offer the following templates:
-We have foreseen a Template:
- - for [patterns](/contributing/templates/patterns/)
- - for [guidelines](/contributing/templates/guidelines/)
\ No newline at end of file
+- for [Patterns](/contributing/templates/patterns/) ([raw](https://raw.githubusercontent.com/microsoft/alguidelines/main/content/docs/Contributing/Templates/Patterns/index.md))
+- for [Best Practice](/contributing/templates/bestpractice/) ([raw](https://raw.githubusercontent.com/microsoft/alguidelines/main/content/docs/Contributing/Templates/BestPractice/index.md))
+
+opening the "raw" link, will allow for the best copy/paste result.
diff --git a/content/docs/NAVPatterns/2-anti-patterns/_index.md b/content/docs/NAVPatterns/2-anti-patterns/_index.md
index e0c88db2..e7695291 100644
--- a/content/docs/NAVPatterns/2-anti-patterns/_index.md
+++ b/content/docs/NAVPatterns/2-anti-patterns/_index.md
@@ -2,6 +2,7 @@
chapter = true
title = "2. Anti-Patterns"
weight = 130
+tags = ["C/AL"]
+++
Some of the software development practices, had **not** stood the test of time. Despite that, some are still being used today by developers everywhere.
diff --git a/content/docs/NAVPatterns/2-anti-patterns/nav-upgrade/index.md b/content/docs/NAVPatterns/2-anti-patterns/nav-upgrade/index.md
index 2c827cdf..244abe96 100644
--- a/content/docs/NAVPatterns/2-anti-patterns/nav-upgrade/index.md
+++ b/content/docs/NAVPatterns/2-anti-patterns/nav-upgrade/index.md
@@ -1,6 +1,7 @@
+++
title = "Nav Upgrade"
weight = 840
+tags = ["C/AL"]
+++
## Anti-Patterns in NAV Upgrade
diff --git a/content/docs/NAVPatterns/2-anti-patterns/reusable-bugs/index.md b/content/docs/NAVPatterns/2-anti-patterns/reusable-bugs/index.md
index 8809e65a..09d31469 100644
--- a/content/docs/NAVPatterns/2-anti-patterns/reusable-bugs/index.md
+++ b/content/docs/NAVPatterns/2-anti-patterns/reusable-bugs/index.md
@@ -1,6 +1,7 @@
+++
title = "Reusable Bugs"
weight = 1020
+tags = ["C/AL"]
+++
_By Bogdana Botez, Andreas Moth, Eric Wauters (waldo), Elly Nkya, Nikola Kukrika_
diff --git a/content/docs/NAVPatterns/3-cal-coding-guidelines/_index.md b/content/docs/NAVPatterns/3-cal-coding-guidelines/_index.md
index e5d3b5ba..84612f77 100644
--- a/content/docs/NAVPatterns/3-cal-coding-guidelines/_index.md
+++ b/content/docs/NAVPatterns/3-cal-coding-guidelines/_index.md
@@ -2,6 +2,8 @@
chapter = true
title = "3. CAL Coding Guidelines"
weight = 150
+tags = ["C/AL"]
+categories = ["Best Practice"]
+++
We've decided to publish our current C/AL coding guidelines. They are actual, as per January 2015 when this is published (but might fall out of sync as time goes by).
diff --git a/content/docs/NAVPatterns/3-cal-coding-guidelines/design/_index.md b/content/docs/NAVPatterns/3-cal-coding-guidelines/design/_index.md
index e6278922..f77a18f4 100644
--- a/content/docs/NAVPatterns/3-cal-coding-guidelines/design/_index.md
+++ b/content/docs/NAVPatterns/3-cal-coding-guidelines/design/_index.md
@@ -1,6 +1,8 @@
+++
title = "Design"
weight = 490
+tags = ["C/AL"]
+categories = ["Best Practice"]
+++
## C/AL Coding Guidelines
diff --git a/content/docs/NAVPatterns/3-cal-coding-guidelines/design/by-reference-parameters/index.md b/content/docs/NAVPatterns/3-cal-coding-guidelines/design/by-reference-parameters/index.md
index d3689a6e..c58006b1 100644
--- a/content/docs/NAVPatterns/3-cal-coding-guidelines/design/by-reference-parameters/index.md
+++ b/content/docs/NAVPatterns/3-cal-coding-guidelines/design/by-reference-parameters/index.md
@@ -1,6 +1,8 @@
+++
title = "By Reference Parameters"
weight = 280
+tags = ["C/AL"]
+categories = ["Best Practice"]
+++
Do not declare parameters by reference if their values are not intended to be changed.
diff --git a/content/docs/NAVPatterns/3-cal-coding-guidelines/design/class-coupling/index.md b/content/docs/NAVPatterns/3-cal-coding-guidelines/design/class-coupling/index.md
index 9219ad2b..aa47a30e 100644
--- a/content/docs/NAVPatterns/3-cal-coding-guidelines/design/class-coupling/index.md
+++ b/content/docs/NAVPatterns/3-cal-coding-guidelines/design/class-coupling/index.md
@@ -1,6 +1,8 @@
+++
title = "Class Coupling"
weight = 320
+tags = ["C/AL"]
+categories = ["Best Practice"]
+++
Do not write functions that have high class coupling. This makes the code hard to maintain.
diff --git a/content/docs/NAVPatterns/3-cal-coding-guidelines/design/cyclomatic-complexity/index.md b/content/docs/NAVPatterns/3-cal-coding-guidelines/design/cyclomatic-complexity/index.md
index ffa2a71b..5a2aa8ac 100644
--- a/content/docs/NAVPatterns/3-cal-coding-guidelines/design/cyclomatic-complexity/index.md
+++ b/content/docs/NAVPatterns/3-cal-coding-guidelines/design/cyclomatic-complexity/index.md
@@ -1,6 +1,8 @@
+++
title = "Cyclomatic Complexity"
weight = 460
+tags = ["C/AL"]
+categories = ["Best Practice"]
+++
Do not write functions that have high cyclomatic complexity. This makes the code hard to maintain.
diff --git a/content/docs/NAVPatterns/3-cal-coding-guidelines/design/encapsulate-local-functionality/index.md b/content/docs/NAVPatterns/3-cal-coding-guidelines/design/encapsulate-local-functionality/index.md
index b52d9285..04d84879 100644
--- a/content/docs/NAVPatterns/3-cal-coding-guidelines/design/encapsulate-local-functionality/index.md
+++ b/content/docs/NAVPatterns/3-cal-coding-guidelines/design/encapsulate-local-functionality/index.md
@@ -1,5 +1,7 @@
+++
title = "Encapsulate Local Functionality"
weight = 530
+tags = ["C/AL"]
+categories = ["Best Practice"]
+++
Any function used local must be defined as local.
diff --git a/content/docs/NAVPatterns/3-cal-coding-guidelines/design/findset-findfirst-findlast/index.md b/content/docs/NAVPatterns/3-cal-coding-guidelines/design/findset-findfirst-findlast/index.md
index 5112e849..2dc93c17 100644
--- a/content/docs/NAVPatterns/3-cal-coding-guidelines/design/findset-findfirst-findlast/index.md
+++ b/content/docs/NAVPatterns/3-cal-coding-guidelines/design/findset-findfirst-findlast/index.md
@@ -1,6 +1,8 @@
+++
title = "FINDSET FINDFIRST FINDLAST"
weight = 600
+tags = ["C/AL"]
+categories = ["Best Practice"]
+++
FINDSET, FIND('+') or FIND('-') should only be used when NEXT is used and vice versa.
diff --git a/content/docs/NAVPatterns/3-cal-coding-guidelines/design/initialized-variables/index.md b/content/docs/NAVPatterns/3-cal-coding-guidelines/design/initialized-variables/index.md
index 15903ec7..35a05a85 100644
--- a/content/docs/NAVPatterns/3-cal-coding-guidelines/design/initialized-variables/index.md
+++ b/content/docs/NAVPatterns/3-cal-coding-guidelines/design/initialized-variables/index.md
@@ -1,6 +1,8 @@
+++
title = "Initialized Variables"
weight = 660
+tags = ["C/AL"]
+categories = ["Best Practice"]
+++
Variables should always be set to a specific value, before they are used.
diff --git a/content/docs/NAVPatterns/3-cal-coding-guidelines/design/maintainability-index/index.md b/content/docs/NAVPatterns/3-cal-coding-guidelines/design/maintainability-index/index.md
index 0d6a486e..051ac2fb 100644
--- a/content/docs/NAVPatterns/3-cal-coding-guidelines/design/maintainability-index/index.md
+++ b/content/docs/NAVPatterns/3-cal-coding-guidelines/design/maintainability-index/index.md
@@ -1,6 +1,8 @@
+++
title = "Maintainability Index"
weight = 770
+tags = ["C/AL"]
+categories = ["Best Practice"]
+++
[Maintainability Index][anchor0]: Do not write functions that have a very low maintainability index. This makes the code hard to maintain.
diff --git a/content/docs/NAVPatterns/3-cal-coding-guidelines/design/parameter-placeholders/index.md b/content/docs/NAVPatterns/3-cal-coding-guidelines/design/parameter-placeholders/index.md
index de4ee689..8811e459 100644
--- a/content/docs/NAVPatterns/3-cal-coding-guidelines/design/parameter-placeholders/index.md
+++ b/content/docs/NAVPatterns/3-cal-coding-guidelines/design/parameter-placeholders/index.md
@@ -1,6 +1,8 @@
+++
title = "Parameter Placeholders"
weight = 920
+tags = ["C/AL"]
+categories = ["Best Practice"]
+++
The number of parameters passed to a string must match the placeholders.
diff --git a/content/docs/NAVPatterns/3-cal-coding-guidelines/design/static-object-invocation/index.md b/content/docs/NAVPatterns/3-cal-coding-guidelines/design/static-object-invocation/index.md
index 54aaf03b..97e89c8a 100644
--- a/content/docs/NAVPatterns/3-cal-coding-guidelines/design/static-object-invocation/index.md
+++ b/content/docs/NAVPatterns/3-cal-coding-guidelines/design/static-object-invocation/index.md
@@ -1,6 +1,8 @@
+++
title = "Static Object Invocation"
weight = 1160
+tags = ["C/AL"]
+categories = ["Best Practice"]
+++
Call objects statically whenever possible. It reduces extra noise and removes extra variables. Downside: changing the name of the object which is called statically will need a code update.
diff --git a/content/docs/NAVPatterns/3-cal-coding-guidelines/design/unreachable-code/index.md b/content/docs/NAVPatterns/3-cal-coding-guidelines/design/unreachable-code/index.md
index 6046b986..69f713e1 100644
--- a/content/docs/NAVPatterns/3-cal-coding-guidelines/design/unreachable-code/index.md
+++ b/content/docs/NAVPatterns/3-cal-coding-guidelines/design/unreachable-code/index.md
@@ -1,6 +1,8 @@
+++
title = "Unreachable Code"
weight = 1310
+tags = ["C/AL"]
+categories = ["Best Practice"]
+++
Do not write code that will never be hit.
diff --git a/content/docs/NAVPatterns/3-cal-coding-guidelines/design/unused-initialized-variables/index.md b/content/docs/NAVPatterns/3-cal-coding-guidelines/design/unused-initialized-variables/index.md
index 898bac08..7e8f6738 100644
--- a/content/docs/NAVPatterns/3-cal-coding-guidelines/design/unused-initialized-variables/index.md
+++ b/content/docs/NAVPatterns/3-cal-coding-guidelines/design/unused-initialized-variables/index.md
@@ -1,6 +1,8 @@
+++
title = "Unused Initialized Variables"
weight = 1320
+tags = ["C/AL"]
+categories = ["Best Practice"]
+++
The value assigned to a variable must be used. Else the variable is not necessary.
diff --git a/content/docs/NAVPatterns/3-cal-coding-guidelines/design/unused-variables/index.md b/content/docs/NAVPatterns/3-cal-coding-guidelines/design/unused-variables/index.md
index c0e5e8b3..d7bd6da8 100644
--- a/content/docs/NAVPatterns/3-cal-coding-guidelines/design/unused-variables/index.md
+++ b/content/docs/NAVPatterns/3-cal-coding-guidelines/design/unused-variables/index.md
@@ -1,6 +1,8 @@
+++
title = "Unused Variables"
weight = 1330
+tags = ["C/AL"]
+categories = ["Best Practice"]
+++
Do not declare variables that are unused.
diff --git a/content/docs/NAVPatterns/3-cal-coding-guidelines/design/variable-capacity-mismatch/index.md b/content/docs/NAVPatterns/3-cal-coding-guidelines/design/variable-capacity-mismatch/index.md
index 87bbe3d6..0f3d5aab 100644
--- a/content/docs/NAVPatterns/3-cal-coding-guidelines/design/variable-capacity-mismatch/index.md
+++ b/content/docs/NAVPatterns/3-cal-coding-guidelines/design/variable-capacity-mismatch/index.md
@@ -1,6 +1,8 @@
+++
title = "Variable Capacity Mismatch"
weight = 1410
+tags = ["C/AL"]
+categories = ["Best Practice"]
+++
Do not assign a value to a variable whose capacity is smaller.
diff --git a/content/docs/NAVPatterns/3-cal-coding-guidelines/design/with-scope-name-collision/index.md b/content/docs/NAVPatterns/3-cal-coding-guidelines/design/with-scope-name-collision/index.md
index 74bbdd31..d14ab704 100644
--- a/content/docs/NAVPatterns/3-cal-coding-guidelines/design/with-scope-name-collision/index.md
+++ b/content/docs/NAVPatterns/3-cal-coding-guidelines/design/with-scope-name-collision/index.md
@@ -1,6 +1,8 @@
+++
title = "WITH Scope Name Collision"
weight = 1450
+tags = ["C/AL"]
+categories = ["Best Practice"]
+++
Do not use the WITH scope when it has a variable whose name is the same as a local variable. This can lead to wrong code assumptions.
diff --git a/content/docs/NAVPatterns/3-cal-coding-guidelines/internally-used-dot-net-types/index.md b/content/docs/NAVPatterns/3-cal-coding-guidelines/internally-used-dot-net-types/index.md
index b543f50b..cb1f1493 100644
--- a/content/docs/NAVPatterns/3-cal-coding-guidelines/internally-used-dot-net-types/index.md
+++ b/content/docs/NAVPatterns/3-cal-coding-guidelines/internally-used-dot-net-types/index.md
@@ -1,6 +1,8 @@
+++
title = "Internally used DotNet Types"
weight = 690
+tags = ["C/AL"]
+categories = ["Best Practice"]
+++
_(Dynamics NAV 2015)_
diff --git a/content/docs/NAVPatterns/3-cal-coding-guidelines/internationalization/_index.md b/content/docs/NAVPatterns/3-cal-coding-guidelines/internationalization/_index.md
index 1881ceea..6edfff5a 100644
--- a/content/docs/NAVPatterns/3-cal-coding-guidelines/internationalization/_index.md
+++ b/content/docs/NAVPatterns/3-cal-coding-guidelines/internationalization/_index.md
@@ -1,6 +1,8 @@
+++
title = "Internationalization"
weight = 700
+tags = ["C/AL"]
+categories = ["Best Practice"]
+++
## C/AL Coding Guidelines
diff --git a/content/docs/NAVPatterns/3-cal-coding-guidelines/internationalization/using-calcdate/index.md b/content/docs/NAVPatterns/3-cal-coding-guidelines/internationalization/using-calcdate/index.md
index 4387da52..cf18657b 100644
--- a/content/docs/NAVPatterns/3-cal-coding-guidelines/internationalization/using-calcdate/index.md
+++ b/content/docs/NAVPatterns/3-cal-coding-guidelines/internationalization/using-calcdate/index.md
@@ -1,6 +1,8 @@
+++
title = "Using Calcdate"
weight = 1370
+tags = ["C/AL"]
+categories = ["Best Practice"]
+++
CALCDATE should only be used with DateFormula variables. Alternatively the string should be enclosed using the <> symbols.
diff --git a/content/docs/NAVPatterns/3-cal-coding-guidelines/localizability/_index.md b/content/docs/NAVPatterns/3-cal-coding-guidelines/localizability/_index.md
index 9b16cefa..714eeeb3 100644
--- a/content/docs/NAVPatterns/3-cal-coding-guidelines/localizability/_index.md
+++ b/content/docs/NAVPatterns/3-cal-coding-guidelines/localizability/_index.md
@@ -1,6 +1,8 @@
+++
title = "Localizability"
weight = 750
+tags = ["C/AL"]
+categories = ["Best Practice"]
+++
## C/AL Coding Guidelines
diff --git a/content/docs/NAVPatterns/3-cal-coding-guidelines/localizability/captionml-for-system-tables/index.md b/content/docs/NAVPatterns/3-cal-coding-guidelines/localizability/captionml-for-system-tables/index.md
index ad50b16c..4cad77f9 100644
--- a/content/docs/NAVPatterns/3-cal-coding-guidelines/localizability/captionml-for-system-tables/index.md
+++ b/content/docs/NAVPatterns/3-cal-coding-guidelines/localizability/captionml-for-system-tables/index.md
@@ -1,6 +1,8 @@
+++
title = "CaptionML on System Pages"
weight = 300
+tags = ["C/AL"]
+categories = ["Best Practice"]
+++
CaptionML should always be specified on a page field for a system table. By default, system tables do not have captions, so if you need to use them in the UI then captions need to be added.
diff --git a/content/docs/NAVPatterns/3-cal-coding-guidelines/localizability/fieldcaption-and-tablecaption/index.md b/content/docs/NAVPatterns/3-cal-coding-guidelines/localizability/fieldcaption-and-tablecaption/index.md
index 9b05906f..ae4c8bdc 100644
--- a/content/docs/NAVPatterns/3-cal-coding-guidelines/localizability/fieldcaption-and-tablecaption/index.md
+++ b/content/docs/NAVPatterns/3-cal-coding-guidelines/localizability/fieldcaption-and-tablecaption/index.md
@@ -1,6 +1,8 @@
+++
title = "FIELDCAPTION and TABLECAPTION"
weight = 580
+tags = ["C/AL"]
+categories = ["Best Practice"]
+++
For user messages, errors etc., use FIELDCAPTION not FIELDNAME and TABLECAPTION not TABLENAME.
diff --git a/content/docs/NAVPatterns/3-cal-coding-guidelines/localizability/global-text-constants/index.md b/content/docs/NAVPatterns/3-cal-coding-guidelines/localizability/global-text-constants/index.md
index 579e57df..0f8c2f63 100644
--- a/content/docs/NAVPatterns/3-cal-coding-guidelines/localizability/global-text-constants/index.md
+++ b/content/docs/NAVPatterns/3-cal-coding-guidelines/localizability/global-text-constants/index.md
@@ -1,6 +1,8 @@
+++
title = "Global Text Constants"
weight = 610
+tags = ["C/AL"]
+categories = ["Best Practice"]
+++
Declare Text Constant as global variables.
diff --git a/content/docs/NAVPatterns/3-cal-coding-guidelines/localizability/use-text-constants/index.md b/content/docs/NAVPatterns/3-cal-coding-guidelines/localizability/use-text-constants/index.md
index a6209174..29129263 100644
--- a/content/docs/NAVPatterns/3-cal-coding-guidelines/localizability/use-text-constants/index.md
+++ b/content/docs/NAVPatterns/3-cal-coding-guidelines/localizability/use-text-constants/index.md
@@ -1,6 +1,8 @@
+++
title = "Use Text Constants"
weight = 1360
+tags = ["C/AL"]
+categories = ["Best Practice"]
+++
Pass user messages using Text Constants. It makes translation easy.
diff --git a/content/docs/NAVPatterns/3-cal-coding-guidelines/localizability/using-optioncaptionml/index.md b/content/docs/NAVPatterns/3-cal-coding-guidelines/localizability/using-optioncaptionml/index.md
index 1de0a0c8..c02403b5 100644
--- a/content/docs/NAVPatterns/3-cal-coding-guidelines/localizability/using-optioncaptionml/index.md
+++ b/content/docs/NAVPatterns/3-cal-coding-guidelines/localizability/using-optioncaptionml/index.md
@@ -1,6 +1,8 @@
+++
title = "Using OptionCaptionML"
weight = 1380
+tags = ["C/AL"]
+categories = ["Best Practice"]
+++
The OptionCaptionML should be filled in for sourceexpression using option data types.
diff --git a/content/docs/NAVPatterns/3-cal-coding-guidelines/readability/_index.md b/content/docs/NAVPatterns/3-cal-coding-guidelines/readability/_index.md
index 18360f86..0a930440 100644
--- a/content/docs/NAVPatterns/3-cal-coding-guidelines/readability/_index.md
+++ b/content/docs/NAVPatterns/3-cal-coding-guidelines/readability/_index.md
@@ -1,6 +1,8 @@
+++
title = "Readability"
weight = 980
+tags = ["C/AL","Readability"]
+categories = ["Best Practice"]
+++
## C/AL Coding Guidelines
diff --git a/content/docs/NAVPatterns/3-cal-coding-guidelines/readability/begin-as-an-afterword/index.md b/content/docs/NAVPatterns/3-cal-coding-guidelines/readability/begin-as-an-afterword/index.md
index 9e967112..498e7b86 100644
--- a/content/docs/NAVPatterns/3-cal-coding-guidelines/readability/begin-as-an-afterword/index.md
+++ b/content/docs/NAVPatterns/3-cal-coding-guidelines/readability/begin-as-an-afterword/index.md
@@ -1,6 +1,8 @@
+++
title = "Begin as an 'After Word'"
weight = 230
+tags = ["C/AL","Readability"]
+categories = ["Best Practice"]
+++
When BEGIN follows THEN, ELSE, DO, it should be on the same line, preceded by one space character.
diff --git a/content/docs/NAVPatterns/3-cal-coding-guidelines/readability/begin-end/index.md b/content/docs/NAVPatterns/3-cal-coding-guidelines/readability/begin-end/index.md
index c3351b48..a0caf6ad 100644
--- a/content/docs/NAVPatterns/3-cal-coding-guidelines/readability/begin-end/index.md
+++ b/content/docs/NAVPatterns/3-cal-coding-guidelines/readability/begin-end/index.md
@@ -1,6 +1,8 @@
+++
title = "Begin-End - Compound Only"
weight = 240
+tags = ["C/AL","Readability"]
+categories = ["Best Practice"]
+++
Only use BEGIN..END to enclose compound statements.
diff --git a/content/docs/NAVPatterns/3-cal-coding-guidelines/readability/binary-operator-line-start/index.md b/content/docs/NAVPatterns/3-cal-coding-guidelines/readability/binary-operator-line-start/index.md
index feea3be3..8ff81042 100644
--- a/content/docs/NAVPatterns/3-cal-coding-guidelines/readability/binary-operator-line-start/index.md
+++ b/content/docs/NAVPatterns/3-cal-coding-guidelines/readability/binary-operator-line-start/index.md
@@ -1,6 +1,8 @@
+++
title = "Binary Operator to Start Line"
weight = 250
+tags = ["C/AL","Readability"]
+categories = ["Best Practice"]
+++
Do not start a line with a binary operator.
diff --git a/content/docs/NAVPatterns/3-cal-coding-guidelines/readability/blank-lines/index.md b/content/docs/NAVPatterns/3-cal-coding-guidelines/readability/blank-lines/index.md
index 5ead097e..5929b91e 100644
--- a/content/docs/NAVPatterns/3-cal-coding-guidelines/readability/blank-lines/index.md
+++ b/content/docs/NAVPatterns/3-cal-coding-guidelines/readability/blank-lines/index.md
@@ -1,6 +1,8 @@
+++
title = "Blank Lines"
weight = 260
+tags = ["C/AL","Readability"]
+categories = ["Best Practice"]
+++
Do not use blank lines at the beginning or end of any functions, after BEGIN, before END, or inside multiline expressions.
diff --git a/content/docs/NAVPatterns/3-cal-coding-guidelines/readability/case-actions/index.md b/content/docs/NAVPatterns/3-cal-coding-guidelines/readability/case-actions/index.md
index dc76df2f..d4bbea8c 100644
--- a/content/docs/NAVPatterns/3-cal-coding-guidelines/readability/case-actions/index.md
+++ b/content/docs/NAVPatterns/3-cal-coding-guidelines/readability/case-actions/index.md
@@ -1,6 +1,8 @@
+++
title = "CASE Action"
weight = 310
+tags = ["C/AL","Readability"]
+categories = ["Best Practice"]
+++
A CASE action should start on a line after the possibility.
diff --git a/content/docs/NAVPatterns/3-cal-coding-guidelines/readability/colon-usage-in-case/index.md b/content/docs/NAVPatterns/3-cal-coding-guidelines/readability/colon-usage-in-case/index.md
index 137c127c..e02393fe 100644
--- a/content/docs/NAVPatterns/3-cal-coding-guidelines/readability/colon-usage-in-case/index.md
+++ b/content/docs/NAVPatterns/3-cal-coding-guidelines/readability/colon-usage-in-case/index.md
@@ -1,6 +1,8 @@
+++
title = "Colon usage in CASE"
weight = 340
+tags = ["C/AL","Readability"]
+categories = ["Best Practice"]
+++
The last possibility on a CASE statement must be immediately followed by a colon.
diff --git a/content/docs/NAVPatterns/3-cal-coding-guidelines/readability/comments-curly-brackets/index.md b/content/docs/NAVPatterns/3-cal-coding-guidelines/readability/comments-curly-brackets/index.md
index 5c4ea36c..049cbd86 100644
--- a/content/docs/NAVPatterns/3-cal-coding-guidelines/readability/comments-curly-brackets/index.md
+++ b/content/docs/NAVPatterns/3-cal-coding-guidelines/readability/comments-curly-brackets/index.md
@@ -1,6 +1,8 @@
+++
title = "Comments inside Curly Brackets"
weight = 350
+tags = ["C/AL","Readability"]
+categories = ["Best Practice"]
+++
Never use curly bracket comments. During development, the "Block comment" functionality can be used instead. However, in production code, block comments are not recommended.
diff --git a/content/docs/NAVPatterns/3-cal-coding-guidelines/readability/comments-spacing/index.md b/content/docs/NAVPatterns/3-cal-coding-guidelines/readability/comments-spacing/index.md
index 9ffc3802..7203208c 100644
--- a/content/docs/NAVPatterns/3-cal-coding-guidelines/readability/comments-spacing/index.md
+++ b/content/docs/NAVPatterns/3-cal-coding-guidelines/readability/comments-spacing/index.md
@@ -1,6 +1,8 @@
+++
title = "Comment Spacing"
weight = 360
+tags = ["C/AL","Readability"]
+categories = ["Best Practice"]
+++
Always start comments with // followed by one space character.
diff --git a/content/docs/NAVPatterns/3-cal-coding-guidelines/readability/end-else-pair/index.md b/content/docs/NAVPatterns/3-cal-coding-guidelines/readability/end-else-pair/index.md
index 520c1b95..a31fb67e 100644
--- a/content/docs/NAVPatterns/3-cal-coding-guidelines/readability/end-else-pair/index.md
+++ b/content/docs/NAVPatterns/3-cal-coding-guidelines/readability/end-else-pair/index.md
@@ -1,6 +1,8 @@
+++
title = "END ELSE Pair"
weight = 540
+tags = ["C/AL","Readability"]
+categories = ["Best Practice"]
+++
The END ELSE pair should always appear on the same line.
diff --git a/content/docs/NAVPatterns/3-cal-coding-guidelines/readability/indentation/index.md b/content/docs/NAVPatterns/3-cal-coding-guidelines/readability/indentation/index.md
index 9b15844a..614aab16 100644
--- a/content/docs/NAVPatterns/3-cal-coding-guidelines/readability/indentation/index.md
+++ b/content/docs/NAVPatterns/3-cal-coding-guidelines/readability/indentation/index.md
@@ -1,6 +1,8 @@
+++
title = "Indentation"
weight = 650
+tags = ["C/AL","Readability"]
+categories = ["Best Practice"]
+++
In general, use an indentation of two space characters. Logical expressions in the IF, WHILE, and UNTIL parts are indented at least 3, 6, and 6 spaces respectively.
diff --git a/content/docs/NAVPatterns/3-cal-coding-guidelines/readability/keyword-pairs-indentation/index.md b/content/docs/NAVPatterns/3-cal-coding-guidelines/readability/keyword-pairs-indentation/index.md
index acd13053..e109817a 100644
--- a/content/docs/NAVPatterns/3-cal-coding-guidelines/readability/keyword-pairs-indentation/index.md
+++ b/content/docs/NAVPatterns/3-cal-coding-guidelines/readability/keyword-pairs-indentation/index.md
@@ -1,6 +1,8 @@
+++
title = "Keyword Pairs - Indentation"
weight = 730
+tags = ["C/AL","Readability"]
+categories = ["Best Practice"]
+++
The IF..THEN pair, WHILE..DO pair, and FOR..DO pair must appear on the same line or the same level of indentation.
diff --git a/content/docs/NAVPatterns/3-cal-coding-guidelines/readability/line-start-keywords/index.md b/content/docs/NAVPatterns/3-cal-coding-guidelines/readability/line-start-keywords/index.md
index ec7754ec..58ce7884 100644
--- a/content/docs/NAVPatterns/3-cal-coding-guidelines/readability/line-start-keywords/index.md
+++ b/content/docs/NAVPatterns/3-cal-coding-guidelines/readability/line-start-keywords/index.md
@@ -1,6 +1,8 @@
+++
title = "Line Start Keywords"
weight = 740
+tags = ["C/AL","Readability"]
+categories = ["Best Practice"]
+++
The END, IF, REPEAT, FOR, WHILE, ELSE and CASE statement should always start a line.
diff --git a/content/docs/NAVPatterns/3-cal-coding-guidelines/readability/lonely-repeat/index.md b/content/docs/NAVPatterns/3-cal-coding-guidelines/readability/lonely-repeat/index.md
index a5e278e4..a7bae1df 100644
--- a/content/docs/NAVPatterns/3-cal-coding-guidelines/readability/lonely-repeat/index.md
+++ b/content/docs/NAVPatterns/3-cal-coding-guidelines/readability/lonely-repeat/index.md
@@ -1,6 +1,8 @@
+++
title = "Lonely Repeat"
weight = 760
+tags = ["C/AL","Readability"]
+categories = ["Best Practice"]
+++
The REPEAT statement should always be alone on a line.
diff --git a/content/docs/NAVPatterns/3-cal-coding-guidelines/readability/named-invocations/index.md b/content/docs/NAVPatterns/3-cal-coding-guidelines/readability/named-invocations/index.md
index bff692d7..64b8fff3 100644
--- a/content/docs/NAVPatterns/3-cal-coding-guidelines/readability/named-invocations/index.md
+++ b/content/docs/NAVPatterns/3-cal-coding-guidelines/readability/named-invocations/index.md
@@ -1,6 +1,8 @@
+++
title = "Named Invocations"
weight = 830
+tags = ["C/AL","Readability"]
+categories = ["Best Practice"]
+++
When calling an object statically use the name, not the number
diff --git a/content/docs/NAVPatterns/3-cal-coding-guidelines/readability/nested-withs/index.md b/content/docs/NAVPatterns/3-cal-coding-guidelines/readability/nested-withs/index.md
index b1a57b16..3dcabde2 100644
--- a/content/docs/NAVPatterns/3-cal-coding-guidelines/readability/nested-withs/index.md
+++ b/content/docs/NAVPatterns/3-cal-coding-guidelines/readability/nested-withs/index.md
@@ -1,6 +1,8 @@
+++
title = "Nested WITHs"
weight = 850
+tags = ["C/AL","Readability"]
+categories = ["Best Practice"]
+++
Do not nest WITHs that reference different types of objects.
diff --git a/content/docs/NAVPatterns/3-cal-coding-guidelines/readability/one-statement-per-line/index.md b/content/docs/NAVPatterns/3-cal-coding-guidelines/readability/one-statement-per-line/index.md
index 348ddaac..5d9e6379 100644
--- a/content/docs/NAVPatterns/3-cal-coding-guidelines/readability/one-statement-per-line/index.md
+++ b/content/docs/NAVPatterns/3-cal-coding-guidelines/readability/one-statement-per-line/index.md
@@ -1,6 +1,8 @@
+++
title = "One Statement Per Line"
weight = 910
+tags = ["C/AL","Readability"]
+categories = ["Best Practice"]
+++
A line of code should not have more than one statement.
diff --git a/content/docs/NAVPatterns/3-cal-coding-guidelines/readability/separate-if-and-else/index.md b/content/docs/NAVPatterns/3-cal-coding-guidelines/readability/separate-if-and-else/index.md
index 8b9948fa..f1c05ab4 100644
--- a/content/docs/NAVPatterns/3-cal-coding-guidelines/readability/separate-if-and-else/index.md
+++ b/content/docs/NAVPatterns/3-cal-coding-guidelines/readability/separate-if-and-else/index.md
@@ -1,6 +1,8 @@
+++
title = "Separate IF and ELSE"
weight = 1050
+tags = ["C/AL","Readability"]
+categories = ["Best Practice"]
+++
IF and ELSE statements should be on separate lines.
diff --git a/content/docs/NAVPatterns/3-cal-coding-guidelines/readability/spacing-binary-operators/index.md b/content/docs/NAVPatterns/3-cal-coding-guidelines/readability/spacing-binary-operators/index.md
index 6766688a..ec18a52d 100644
--- a/content/docs/NAVPatterns/3-cal-coding-guidelines/readability/spacing-binary-operators/index.md
+++ b/content/docs/NAVPatterns/3-cal-coding-guidelines/readability/spacing-binary-operators/index.md
@@ -1,6 +1,8 @@
+++
title = "Spacing Binary Operators"
weight = 1120
+tags = ["C/AL","Readability"]
+categories = ["Best Practice"]
+++
There must be exactly one space character on each side of a binary operator such as = + - AND OR =. The parameter comma operator however, should have no spaces.
diff --git a/content/docs/NAVPatterns/3-cal-coding-guidelines/readability/spacing-brackets-and/index.md b/content/docs/NAVPatterns/3-cal-coding-guidelines/readability/spacing-brackets-and/index.md
index 1187bcce..087b1ac8 100644
--- a/content/docs/NAVPatterns/3-cal-coding-guidelines/readability/spacing-brackets-and/index.md
+++ b/content/docs/NAVPatterns/3-cal-coding-guidelines/readability/spacing-brackets-and/index.md
@@ -1,6 +1,8 @@
+++
title = "Spacing Brackets and ::"
weight = 1130
+tags = ["C/AL","Readability"]
+categories = ["Best Practice"]
+++
There must be no spaces characters before and after [] dimension brackets symbols or :: option symbols.
diff --git a/content/docs/NAVPatterns/3-cal-coding-guidelines/readability/spacing-unary-operators/index.md b/content/docs/NAVPatterns/3-cal-coding-guidelines/readability/spacing-unary-operators/index.md
index 63277cff..970b2d16 100644
--- a/content/docs/NAVPatterns/3-cal-coding-guidelines/readability/spacing-unary-operators/index.md
+++ b/content/docs/NAVPatterns/3-cal-coding-guidelines/readability/spacing-unary-operators/index.md
@@ -1,6 +1,8 @@
+++
title = "Spacing Unary Operators"
weight = 1140
+tags = ["C/AL","Readability"]
+categories = ["Best Practice"]
+++
There must be no space between a unary operator and its argument (except for the NOT keyword).
diff --git a/content/docs/NAVPatterns/3-cal-coding-guidelines/readability/suggested-abbreviations/index.md b/content/docs/NAVPatterns/3-cal-coding-guidelines/readability/suggested-abbreviations/index.md
index df4af1ec..97b84557 100644
--- a/content/docs/NAVPatterns/3-cal-coding-guidelines/readability/suggested-abbreviations/index.md
+++ b/content/docs/NAVPatterns/3-cal-coding-guidelines/readability/suggested-abbreviations/index.md
@@ -1,6 +1,8 @@
+++
title = "Suggested Abbreviations"
weight = 1170
+tags = ["C/AL","Readability"]
+categories = ["Best Practice"]
+++
### Suggested Abbreviations
diff --git a/content/docs/NAVPatterns/3-cal-coding-guidelines/readability/temporary-variable-naming/index.md b/content/docs/NAVPatterns/3-cal-coding-guidelines/readability/temporary-variable-naming/index.md
index 53630c54..bbd36a69 100644
--- a/content/docs/NAVPatterns/3-cal-coding-guidelines/readability/temporary-variable-naming/index.md
+++ b/content/docs/NAVPatterns/3-cal-coding-guidelines/readability/temporary-variable-naming/index.md
@@ -1,6 +1,8 @@
+++
title = "Temporary Variable Naming"
weight = 1200
+tags = ["C/AL","Readability"]
+categories = ["Best Practice"]
+++
The name of a temporary variable must be prefixed with the word Temp and not otherwise.
diff --git a/content/docs/NAVPatterns/3-cal-coding-guidelines/readability/textconst-suffixes/index.md b/content/docs/NAVPatterns/3-cal-coding-guidelines/readability/textconst-suffixes/index.md
index 09f44459..7de92fbf 100644
--- a/content/docs/NAVPatterns/3-cal-coding-guidelines/readability/textconst-suffixes/index.md
+++ b/content/docs/NAVPatterns/3-cal-coding-guidelines/readability/textconst-suffixes/index.md
@@ -1,6 +1,8 @@
+++
title = "TextConst Suffixes"
weight = 1210
+tags = ["C/AL","Readability"]
+categories = ["Best Practice"]
+++
TextConst variable names should have a suffix (an approved three-letter suffix: Msg, Tok, Err, Qst, Lbl, Txt) describing usage.
diff --git a/content/docs/NAVPatterns/3-cal-coding-guidelines/readability/unary-operator-line-end/index.md b/content/docs/NAVPatterns/3-cal-coding-guidelines/readability/unary-operator-line-end/index.md
index c121de69..11ad406e 100644
--- a/content/docs/NAVPatterns/3-cal-coding-guidelines/readability/unary-operator-line-end/index.md
+++ b/content/docs/NAVPatterns/3-cal-coding-guidelines/readability/unary-operator-line-end/index.md
@@ -1,6 +1,8 @@
+++
title = "Unary Operator Line End"
weight = 1250
+tags = ["C/AL","Readability"]
+categories = ["Best Practice"]
+++
Do not end a line with unary operator.
diff --git a/content/docs/NAVPatterns/3-cal-coding-guidelines/readability/unnecessary-compound-parenthesis/index.md b/content/docs/NAVPatterns/3-cal-coding-guidelines/readability/unnecessary-compound-parenthesis/index.md
index 2e3f3f14..127418c2 100644
--- a/content/docs/NAVPatterns/3-cal-coding-guidelines/readability/unnecessary-compound-parenthesis/index.md
+++ b/content/docs/NAVPatterns/3-cal-coding-guidelines/readability/unnecessary-compound-parenthesis/index.md
@@ -1,6 +1,8 @@
+++
title = "Unnecessary Compound Parenthesis"
weight = 1260
+tags = ["C/AL","Readability"]
+categories = ["Best Practice"]
+++
Use parenthesis only to enclose compound expressions inside compound expressions.
diff --git a/content/docs/NAVPatterns/3-cal-coding-guidelines/readability/unnecessary-else/index.md b/content/docs/NAVPatterns/3-cal-coding-guidelines/readability/unnecessary-else/index.md
index 8ba88518..f3755ab2 100644
--- a/content/docs/NAVPatterns/3-cal-coding-guidelines/readability/unnecessary-else/index.md
+++ b/content/docs/NAVPatterns/3-cal-coding-guidelines/readability/unnecessary-else/index.md
@@ -1,6 +1,8 @@
+++
title = "Unnecessary ELSE"
weight = 1270
+tags = ["C/AL","Readability"]
+categories = ["Best Practice"]
+++
ELSE should not be used when the last action in the THEN part is an EXIT, BREAK, SKIP, QUIT, ERROR.
diff --git a/content/docs/NAVPatterns/3-cal-coding-guidelines/readability/unnecessary-function-parenthesis/index.md b/content/docs/NAVPatterns/3-cal-coding-guidelines/readability/unnecessary-function-parenthesis/index.md
index 44bc6402..9750286a 100644
--- a/content/docs/NAVPatterns/3-cal-coding-guidelines/readability/unnecessary-function-parenthesis/index.md
+++ b/content/docs/NAVPatterns/3-cal-coding-guidelines/readability/unnecessary-function-parenthesis/index.md
@@ -1,6 +1,8 @@
+++
title = "Unnecessary Function Parenthesis"
weight = 1280
+tags = ["C/AL","Readability"]
+categories = ["Best Practice"]
+++
Do not use parenthesis in a function call if the function does not have any parameters.
diff --git a/content/docs/NAVPatterns/3-cal-coding-guidelines/readability/unnecessary-separators/index.md b/content/docs/NAVPatterns/3-cal-coding-guidelines/readability/unnecessary-separators/index.md
index 2a653922..ecefcdf6 100644
--- a/content/docs/NAVPatterns/3-cal-coding-guidelines/readability/unnecessary-separators/index.md
+++ b/content/docs/NAVPatterns/3-cal-coding-guidelines/readability/unnecessary-separators/index.md
@@ -1,6 +1,8 @@
+++
title = "Unnecessary Separators"
weight = 1290
+tags = ["C/AL","Readability"]
+categories = ["Best Practice"]
+++
There should be no unnecessary separators.
diff --git a/content/docs/NAVPatterns/3-cal-coding-guidelines/readability/unnecessary-truefalse/index.md b/content/docs/NAVPatterns/3-cal-coding-guidelines/readability/unnecessary-truefalse/index.md
index ed6e3679..181b2067 100644
--- a/content/docs/NAVPatterns/3-cal-coding-guidelines/readability/unnecessary-truefalse/index.md
+++ b/content/docs/NAVPatterns/3-cal-coding-guidelines/readability/unnecessary-truefalse/index.md
@@ -1,6 +1,8 @@
+++
title = "Unnecessary TRUE/FALSE"
weight = 1300
+tags = ["C/AL","Readability"]
+categories = ["Best Practice"]
+++
Do not use TRUE or FALSE keywords unnecessarily if the expression is already an logical expression.
diff --git a/content/docs/NAVPatterns/3-cal-coding-guidelines/readability/variable-already-scoped/index.md b/content/docs/NAVPatterns/3-cal-coding-guidelines/readability/variable-already-scoped/index.md
index 73e682b5..f24f9358 100644
--- a/content/docs/NAVPatterns/3-cal-coding-guidelines/readability/variable-already-scoped/index.md
+++ b/content/docs/NAVPatterns/3-cal-coding-guidelines/readability/variable-already-scoped/index.md
@@ -1,6 +1,8 @@
+++
title = "Variable Already Scoped"
weight = 1400
+tags = ["C/AL","Readability"]
+categories = ["Best Practice"]
+++
Do not use scope ''.'' qualifier unnecessarily when a variable is already implicitly or explicitly scoped. It keeps the code simpler.
diff --git a/content/docs/NAVPatterns/3-cal-coding-guidelines/readability/variable-naming/index.md b/content/docs/NAVPatterns/3-cal-coding-guidelines/readability/variable-naming/index.md
index 9b2b5639..a678e0a3 100644
--- a/content/docs/NAVPatterns/3-cal-coding-guidelines/readability/variable-naming/index.md
+++ b/content/docs/NAVPatterns/3-cal-coding-guidelines/readability/variable-naming/index.md
@@ -1,6 +1,8 @@
+++
title = "Variable Naming"
weight = 1420
+tags = ["C/AL","Readability"]
+categories = ["Best Practice"]
+++
Variables that refer to a C/AL object must contain the objects name, abbreviated where necessary.
diff --git a/content/docs/NAVPatterns/3-cal-coding-guidelines/readability/variables-declarations-order/index.md b/content/docs/NAVPatterns/3-cal-coding-guidelines/readability/variables-declarations-order/index.md
index a578ca7d..d9b7cc08 100644
--- a/content/docs/NAVPatterns/3-cal-coding-guidelines/readability/variables-declarations-order/index.md
+++ b/content/docs/NAVPatterns/3-cal-coding-guidelines/readability/variables-declarations-order/index.md
@@ -1,6 +1,8 @@
+++
title = "Variables Declarations Order"
weight = 1430
+tags = ["C/AL","Readability"]
+categories = ["Best Practice"]
+++
Variables declarations should be ordered by type. In general, object and complex variable types are listed first followed by simple variables. The order should be the same as the object list in the object designer for C/AL objects. Afterwards come the complex variables like RecordRef, .NET, FieldRef etc. At the end come all the simple data types in no particular order.
diff --git a/content/docs/NAVPatterns/3-cal-coding-guidelines/ux/_index.md b/content/docs/NAVPatterns/3-cal-coding-guidelines/ux/_index.md
index b755e886..cc4c977b 100644
--- a/content/docs/NAVPatterns/3-cal-coding-guidelines/ux/_index.md
+++ b/content/docs/NAVPatterns/3-cal-coding-guidelines/ux/_index.md
@@ -1,6 +1,8 @@
+++
title = "UX"
weight = 1390
+tags = ["C/AL"]
+categories = ["Best Practice"]
+++
## C/AL Coding Guidelines
diff --git a/content/docs/NAVPatterns/3-cal-coding-guidelines/ux/actions-images/index.md b/content/docs/NAVPatterns/3-cal-coding-guidelines/ux/actions-images/index.md
index c07df2c0..863d0609 100644
--- a/content/docs/NAVPatterns/3-cal-coding-guidelines/ux/actions-images/index.md
+++ b/content/docs/NAVPatterns/3-cal-coding-guidelines/ux/actions-images/index.md
@@ -1,6 +1,8 @@
+++
title = "Actions - Images"
weight = 200
+tags = ["C/AL"]
+categories = ["Best Practice"]
+++
All actions must have an image assigned to them.
diff --git a/content/docs/NAVPatterns/3-cal-coding-guidelines/ux/confirm/index.md b/content/docs/NAVPatterns/3-cal-coding-guidelines/ux/confirm/index.md
index fb3394f9..155b747d 100644
--- a/content/docs/NAVPatterns/3-cal-coding-guidelines/ux/confirm/index.md
+++ b/content/docs/NAVPatterns/3-cal-coding-guidelines/ux/confirm/index.md
@@ -1,6 +1,8 @@
+++
title = "CONFIRM"
weight = 380
+tags = ["C/AL"]
+categories = ["Best Practice"]
+++
Always end CONFIRM with a question mark.
diff --git a/content/docs/NAVPatterns/3-cal-coding-guidelines/ux/fielderror/index.md b/content/docs/NAVPatterns/3-cal-coding-guidelines/ux/fielderror/index.md
index 1e041417..1f87ff3a 100644
--- a/content/docs/NAVPatterns/3-cal-coding-guidelines/ux/fielderror/index.md
+++ b/content/docs/NAVPatterns/3-cal-coding-guidelines/ux/fielderror/index.md
@@ -1,6 +1,8 @@
+++
title = "FIELDERROR"
weight = 590
+tags = ["C/AL"]
+categories = ["Best Practice"]
+++
Never use FIELDERROR with a period as it is automatically inserted.
diff --git a/content/docs/NAVPatterns/3-cal-coding-guidelines/ux/message-and-error/index.md b/content/docs/NAVPatterns/3-cal-coding-guidelines/ux/message-and-error/index.md
index 93ede77d..93bce63a 100644
--- a/content/docs/NAVPatterns/3-cal-coding-guidelines/ux/message-and-error/index.md
+++ b/content/docs/NAVPatterns/3-cal-coding-guidelines/ux/message-and-error/index.md
@@ -1,6 +1,8 @@
+++
title = "MESSAGE and ERROR"
weight = 790
+tags = ["C/AL"]
+categories = ["Best Practice"]
+++
Always end MESSAGE or ERROR with a period.
diff --git a/content/docs/NAVPatterns/4-get-involved/_index.md b/content/docs/NAVPatterns/4-get-involved/_index.md
index 98bf3470..43d27a4a 100644
--- a/content/docs/NAVPatterns/4-get-involved/_index.md
+++ b/content/docs/NAVPatterns/4-get-involved/_index.md
@@ -2,6 +2,7 @@
chapter = true
title = "(OLD) Get Involved"
weight = 170
+tags = ["C/AL"]
+++
**Reminder, this is an ARCHIVE of the Patterns site, this information is not current.**
diff --git a/content/docs/NAVPatterns/4-get-involved/code-of-conduct/index.md b/content/docs/NAVPatterns/4-get-involved/code-of-conduct/index.md
index 02cdce3a..76927c3e 100644
--- a/content/docs/NAVPatterns/4-get-involved/code-of-conduct/index.md
+++ b/content/docs/NAVPatterns/4-get-involved/code-of-conduct/index.md
@@ -1,6 +1,7 @@
+++
title = "Code of Conduct"
weight = 330
+tags = ["C/AL"]
+++
Find below the rules to be used when disseminating or relating to the NAV Design Patterns.
diff --git a/content/docs/NAVPatterns/4-get-involved/patterns-authors/index.md b/content/docs/NAVPatterns/4-get-involved/patterns-authors/index.md
index de316d5c..104b6b21 100644
--- a/content/docs/NAVPatterns/4-get-involved/patterns-authors/index.md
+++ b/content/docs/NAVPatterns/4-get-involved/patterns-authors/index.md
@@ -1,6 +1,7 @@
+++
title = "Patterns Authors"
weight = 930
+tags = ["C/AL"]
+++
This is the list of people that have been part of the NAV Design Patterns team. If you would like to join the project follow the instructions provided on [Be a NAV Pattern Author][anchor0] page.
diff --git a/content/docs/NAVPatterns/4-get-involved/template-for-writing-nav-design-patterns/index.md b/content/docs/NAVPatterns/4-get-involved/template-for-writing-nav-design-patterns/index.md
index 77a8b22b..a47abbd6 100644
--- a/content/docs/NAVPatterns/4-get-involved/template-for-writing-nav-design-patterns/index.md
+++ b/content/docs/NAVPatterns/4-get-involved/template-for-writing-nav-design-patterns/index.md
@@ -1,6 +1,7 @@
+++
title = "Template for writing Nav Design Patterns"
weight = 1180
+tags = ["C/AL"]
+++
This is a guideline, some parts are optional (if there's no content, remove the whole paragraph).
diff --git a/content/docs/NAVPatterns/_index.md b/content/docs/NAVPatterns/_index.md
index 5865a369..644ec7ec 100644
--- a/content/docs/NAVPatterns/_index.md
+++ b/content/docs/NAVPatterns/_index.md
@@ -1,6 +1,7 @@
+++
title = "NAV Patterns Archive"
weight = 20
+tags = ["C/AL"]
+++
## About the archive
diff --git a/content/docs/NAVPatterns/patterns/_index.md b/content/docs/NAVPatterns/patterns/_index.md
index 075fc693..527ceb23 100644
--- a/content/docs/NAVPatterns/patterns/_index.md
+++ b/content/docs/NAVPatterns/patterns/_index.md
@@ -1,8 +1,8 @@
---
-title: "Patterns"
+title: "1. Patterns"
weight: 110
-tags: ["NAV", "C/AL"]
-categories: ["Archived Pattern"]
+tags: ["C/AL"]
+categories: ["Pattern"]
description: >
Patterns described to be used with Microsoft Dynamics NAV
---
diff --git a/content/docs/NAVPatterns/patterns/activity-log/index.md b/content/docs/NAVPatterns/patterns/activity-log/index.md
index 37a327a2..f6acd62a 100644
--- a/content/docs/NAVPatterns/patterns/activity-log/index.md
+++ b/content/docs/NAVPatterns/patterns/activity-log/index.md
@@ -1,6 +1,8 @@
+++
title = "Activity Logs"
weight = 210
+tags = ["C/AL"]
+categories = ["Pattern"]
+++
_Originally by Ciprian Iordache at Microsoft Development Center Copenhagen_
diff --git a/content/docs/NAVPatterns/patterns/argument-table/index.md b/content/docs/NAVPatterns/patterns/argument-table/index.md
index 13a4c70d..b413e0c8 100644
--- a/content/docs/NAVPatterns/patterns/argument-table/index.md
+++ b/content/docs/NAVPatterns/patterns/argument-table/index.md
@@ -1,6 +1,8 @@
+++
title = "Argument Table"
weight = 220
+tags = ["C/AL"]
+categories = ["Pattern"]
+++
_Originally By Nikola Kukrika and waldo_
diff --git a/content/docs/NAVPatterns/patterns/blocked-entity/_index.md b/content/docs/NAVPatterns/patterns/blocked-entity/_index.md
index 765791a5..79b0f5ac 100644
--- a/content/docs/NAVPatterns/patterns/blocked-entity/_index.md
+++ b/content/docs/NAVPatterns/patterns/blocked-entity/_index.md
@@ -1,6 +1,8 @@
+++
title = "Blocked Entity"
weight = 270
+tags = ["C/AL"]
+categories = ["Pattern"]
+++
_Originally by Abhishek Ghosh at Microsoft Development Center Copenhagen_
diff --git a/content/docs/NAVPatterns/patterns/blocked-entity/data-driven-blocked-entity/index.md b/content/docs/NAVPatterns/patterns/blocked-entity/data-driven-blocked-entity/index.md
index 295cf07b..3e4413b3 100644
--- a/content/docs/NAVPatterns/patterns/blocked-entity/data-driven-blocked-entity/index.md
+++ b/content/docs/NAVPatterns/patterns/blocked-entity/data-driven-blocked-entity/index.md
@@ -1,6 +1,8 @@
+++
title = "Data Driven Blocked Entity"
weight = 470
+tags = ["C/AL"]
+categories = ["Pattern"]
+++
_Written by Bogdan Andrei Sturzoiu, at Microsoft Development Center Copenhagen_
diff --git a/content/docs/NAVPatterns/patterns/cached-web-service-calls/index.md b/content/docs/NAVPatterns/patterns/cached-web-service-calls/index.md
index baa4cea5..fd64729e 100644
--- a/content/docs/NAVPatterns/patterns/cached-web-service-calls/index.md
+++ b/content/docs/NAVPatterns/patterns/cached-web-service-calls/index.md
@@ -1,6 +1,8 @@
+++
title = "Cached Web Server Calls"
weight = 290
+tags = ["C/AL"]
+categories = ["Pattern"]
+++
_Originally by Mostafa Balat, Microsoft Development Center Copenhagen_
diff --git a/content/docs/NAVPatterns/patterns/conditional-cascading-update/index.md b/content/docs/NAVPatterns/patterns/conditional-cascading-update/index.md
index a690d1ff..2b29989d 100644
--- a/content/docs/NAVPatterns/patterns/conditional-cascading-update/index.md
+++ b/content/docs/NAVPatterns/patterns/conditional-cascading-update/index.md
@@ -1,6 +1,8 @@
+++
title = "Conditional Cascading Update"
weight = 370
+tags = ["C/AL"]
+categories = ["Pattern"]
+++
_Originally by Jan Hoek at IDYN_
diff --git a/content/docs/NAVPatterns/patterns/copy-document/index.md b/content/docs/NAVPatterns/patterns/copy-document/index.md
index 324b617a..98f1bd0b 100644
--- a/content/docs/NAVPatterns/patterns/copy-document/index.md
+++ b/content/docs/NAVPatterns/patterns/copy-document/index.md
@@ -1,6 +1,8 @@
+++
title = "Copy Document"
weight = 390
+tags = ["C/AL"]
+categories = ["Pattern"]
+++
_By Bogdan Sturzoiu at Microsoft Development Center Copenhagen_
diff --git a/content/docs/NAVPatterns/patterns/create-data-from-templates/index.md b/content/docs/NAVPatterns/patterns/create-data-from-templates/index.md
index 9ecad138..d51d6c92 100644
--- a/content/docs/NAVPatterns/patterns/create-data-from-templates/index.md
+++ b/content/docs/NAVPatterns/patterns/create-data-from-templates/index.md
@@ -1,6 +1,8 @@
+++
title = "Create Data from Templates"
weight = 400
+tags = ["C/AL"]
+categories = ["Pattern"]
+++
_Originally by Nikola Kukrika at Microsoft Development Center Copenhagen_
diff --git a/content/docs/NAVPatterns/patterns/create-urls-to-nav-clients/index.md b/content/docs/NAVPatterns/patterns/create-urls-to-nav-clients/index.md
index fdcbee7c..5f604ef4 100644
--- a/content/docs/NAVPatterns/patterns/create-urls-to-nav-clients/index.md
+++ b/content/docs/NAVPatterns/patterns/create-urls-to-nav-clients/index.md
@@ -1,6 +1,8 @@
+++
title = "Create URLs to NAV Clients"
weight = 410
+tags = ["C/AL"]
+categories = ["Pattern"]
+++
_By Mike Borg Cardona and Bogdana Botez at Microsoft Development Center Copenhagen_
diff --git a/content/docs/NAVPatterns/patterns/creating-custom-charts/index.md b/content/docs/NAVPatterns/patterns/creating-custom-charts/index.md
index 4bd83358..64d58fd4 100644
--- a/content/docs/NAVPatterns/patterns/creating-custom-charts/index.md
+++ b/content/docs/NAVPatterns/patterns/creating-custom-charts/index.md
@@ -1,6 +1,8 @@
+++
title = "Creating Custom Charts"
weight = 420
+tags = ["C/AL"]
+categories = ["Pattern"]
+++
_Originally by Nikola Kukrika at Microsoft Development Center Copenhagen_
diff --git a/content/docs/NAVPatterns/patterns/cross-session-events/index.md b/content/docs/NAVPatterns/patterns/cross-session-events/index.md
index 1a5fae83..b9aecd0b 100644
--- a/content/docs/NAVPatterns/patterns/cross-session-events/index.md
+++ b/content/docs/NAVPatterns/patterns/cross-session-events/index.md
@@ -1,6 +1,8 @@
+++
title = "Cross Session Events"
weight = 430
+tags = ["C/AL"]
+categories = ["Pattern"]
+++
_By Nikolai L'Estrange, from TVision Technology Ltd. in the UK_
diff --git a/content/docs/NAVPatterns/patterns/currently-active-record/index.md b/content/docs/NAVPatterns/patterns/currently-active-record/index.md
index 1f1f56a0..ad7e9e2e 100644
--- a/content/docs/NAVPatterns/patterns/currently-active-record/index.md
+++ b/content/docs/NAVPatterns/patterns/currently-active-record/index.md
@@ -1,6 +1,8 @@
+++
title = "Currently Active Record"
weight = 450
+tags = ["C/AL"]
+categories = ["Pattern"]
+++
_Authors: Henrik Langbak and Kim Ginnerup, Bording Data_
diff --git a/content/docs/NAVPatterns/patterns/data-migration-facade/index.md b/content/docs/NAVPatterns/patterns/data-migration-facade/index.md
index bd0650da..e4e5aa4e 100644
--- a/content/docs/NAVPatterns/patterns/data-migration-facade/index.md
+++ b/content/docs/NAVPatterns/patterns/data-migration-facade/index.md
@@ -1,6 +1,8 @@
+++
title = "Data Migration Façade"
weight = 480
+tags = ["C/AL"]
+categories = ["Pattern"]
+++
_By David Bastide and Soumya Dutta at Microsoft Development Center Copenhagen_
@@ -106,7 +108,7 @@ begin
end;
end;
-procedure MigrateItemUnitOfMeasure(ItemDataMigrationFacade : Codeunit "Item Data Migration Facade"; ItemJson : Text);
+procedure MigrateItemUnitOfMeasure(ItemDataMigrationFacade : Codeunit "Item Data Migration Facade"; ItemJson : Text)
var
MyUnitCodeStagingTable: Record "My Unit Code Staging Table";
DataMigrationStatusFacade: Codeunit "Data Migration Status Facade";
@@ -152,7 +154,7 @@ _Figure 3: Simplified sequence diagram of the data migration with staging tables
Below is a simplified example showing how to create an item:
```al
[EventSubscriber(ObjectType::Codeunit, Codeunit::"Item Data Migration Facade", 'OnMigrateItem', '', true, true)]
-procedure OnMigrateItem(VAR Sender : Codeunit "Item Data Migration Facade";RecordIdToMigrate : RecordId);
+procedure OnMigrateItem(VAR Sender : Codeunit "Item Data Migration Facade";RecordIdToMigrate : RecordId)
var
MyItemStagingTable : Record "My Item Staging Table";
begin
@@ -184,7 +186,7 @@ Below is another example showing how to use additional events to set fields that
```al
[EventSubscriber(ObjectType::Codeunit, Codeunit::"Item Data Migration Facade", 'OnMigrateItemUnitOfMeasure', '', true, true)]
-procedure OnMigrateItemUnitOfMeasure(VAR Sender : Codeunit "Item Data Migration Facade";RecordIdToMigrate : RecordId);
+procedure OnMigrateItemUnitOfMeasure(VAR Sender : Codeunit "Item Data Migration Facade";RecordIdToMigrate : RecordId)
var
MyItemStagingTable : Record "My Item Staging Table";
MyUnitCodeStagingTable : Record "My Unit Code Staging Table";
diff --git a/content/docs/NAVPatterns/patterns/discovery-event/index.md b/content/docs/NAVPatterns/patterns/discovery-event/index.md
index eba8381a..d8cbf195 100644
--- a/content/docs/NAVPatterns/patterns/discovery-event/index.md
+++ b/content/docs/NAVPatterns/patterns/discovery-event/index.md
@@ -1,6 +1,8 @@
+++
title = "Discovery Event"
weight = 500
+tags = ["C/AL"]
+categories = ["Pattern"]
+++
_by waldo_
diff --git a/content/docs/NAVPatterns/patterns/document/index.md b/content/docs/NAVPatterns/patterns/document/index.md
index 653ed482..66120a29 100644
--- a/content/docs/NAVPatterns/patterns/document/index.md
+++ b/content/docs/NAVPatterns/patterns/document/index.md
@@ -1,6 +1,8 @@
+++
title = "Document"
weight = 510
+tags = ["C/AL"]
+categories = ["Pattern"]
+++
_By Xavier Garonnat, knk Ingénierie (France), xgaronnat@knk.fr_
diff --git a/content/docs/NAVPatterns/patterns/easy-update-of-setup-or-supplementary-information/index.md b/content/docs/NAVPatterns/patterns/easy-update-of-setup-or-supplementary-information/index.md
index aea9f8d3..f652f12f 100644
--- a/content/docs/NAVPatterns/patterns/easy-update-of-setup-or-supplementary-information/index.md
+++ b/content/docs/NAVPatterns/patterns/easy-update-of-setup-or-supplementary-information/index.md
@@ -1,6 +1,8 @@
+++
title = "Easy Update Of Setup Or Supplementary Information"
weight = 520
+tags = ["C/AL"]
+categories = ["Pattern"]
+++
_Originally by Anders Larsen at Microsoft Development Center Copenhagen_
diff --git a/content/docs/NAVPatterns/patterns/error-message-processing/index.md b/content/docs/NAVPatterns/patterns/error-message-processing/index.md
index 2b736c8c..07d9c27c 100644
--- a/content/docs/NAVPatterns/patterns/error-message-processing/index.md
+++ b/content/docs/NAVPatterns/patterns/error-message-processing/index.md
@@ -1,6 +1,8 @@
+++
title = "Error Message Processing"
weight = 550
+tags = ["C/AL"]
+categories = ["Pattern"]
+++
_By Jesper Schulz at Microsoft Development Center Copenhagen_
@@ -82,9 +84,9 @@ BEGIN
END ELSE
TempErrorMessage.LogSimpleMessage(TempErrorMessage."Message Type"::Error,NoSATAccountDefinedErr);
- IF NOT TempErrorMessage.HasErrors(TRUE) THEN
- SaveXMLToClient(Document,Year,Month,'CT');
- TempErrorMessage.ShowErrorMessages(FALSE);
+ IF NOT TempErrorMessage.HasErrors(TRUE) THEN
+ SaveXMLToClient(Document,Year,Month,'CT');
+ TempErrorMessage.ShowErrorMessages(FALSE);
END;
```
diff --git a/content/docs/NAVPatterns/patterns/extending-the-role-center-headlines/index.md b/content/docs/NAVPatterns/patterns/extending-the-role-center-headlines/index.md
index f3006cde..3f5b4615 100644
--- a/content/docs/NAVPatterns/patterns/extending-the-role-center-headlines/index.md
+++ b/content/docs/NAVPatterns/patterns/extending-the-role-center-headlines/index.md
@@ -1,6 +1,8 @@
+++
title = "Extending the Role Center Headlines"
weight = 560
+tags = ["C/AL"]
+categories = ["Pattern"]
+++
_By David Bastide at Microsoft Development Center Copenhagen_
diff --git a/content/docs/NAVPatterns/patterns/feature-localization-for-data-structures/index.md b/content/docs/NAVPatterns/patterns/feature-localization-for-data-structures/index.md
index 14ee3b5e..cd110eb1 100644
--- a/content/docs/NAVPatterns/patterns/feature-localization-for-data-structures/index.md
+++ b/content/docs/NAVPatterns/patterns/feature-localization-for-data-structures/index.md
@@ -1,6 +1,8 @@
+++
title = "Feature Localization For Data Structures"
weight = 570
+tags = ["C/AL"]
+categories = ["Pattern"]
+++
_Originally by Bogdan Sturzoiu at Microsoft Development Center Copenhagen_
diff --git a/content/docs/NAVPatterns/patterns/hooks/index.md b/content/docs/NAVPatterns/patterns/hooks/index.md
index b23b7e88..9134887d 100644
--- a/content/docs/NAVPatterns/patterns/hooks/index.md
+++ b/content/docs/NAVPatterns/patterns/hooks/index.md
@@ -1,6 +1,8 @@
+++
title = "Hooks"
weight = 620
+tags = ["C/AL"]
+categories = ["Pattern"]
+++
_By Eric Wauters ([waldo][anchor0]), Partner-Ready-Software_
diff --git a/content/docs/NAVPatterns/patterns/implementation-of-surrogate-keys-using-autoincrement-pattern/index.md b/content/docs/NAVPatterns/patterns/implementation-of-surrogate-keys-using-autoincrement-pattern/index.md
index e8bce739..a89b6b1b 100644
--- a/content/docs/NAVPatterns/patterns/implementation-of-surrogate-keys-using-autoincrement-pattern/index.md
+++ b/content/docs/NAVPatterns/patterns/implementation-of-surrogate-keys-using-autoincrement-pattern/index.md
@@ -1,6 +1,8 @@
+++
title = "Surrogate keys using Autoincrement Pattern"
weight = 630
+tags = ["C/AL"]
+categories = ["Pattern"]
+++
_By [Soren Klemmensen][anchor0], [_Partner-Ready-Software_ ][anchor1] & [360 Visibility][anchor2]_
diff --git a/content/docs/NAVPatterns/patterns/instructions-in-the-ui/index.md b/content/docs/NAVPatterns/patterns/instructions-in-the-ui/index.md
index a1ec6258..fdbe7999 100644
--- a/content/docs/NAVPatterns/patterns/instructions-in-the-ui/index.md
+++ b/content/docs/NAVPatterns/patterns/instructions-in-the-ui/index.md
@@ -1,6 +1,8 @@
+++
title = "Instructions in the UI"
weight = 670
+tags = ["C/AL"]
+categories = ["Pattern"]
+++
_Originally by Nikola Kukrika at Microsoft Development Center Copenhagen_
diff --git a/content/docs/NAVPatterns/patterns/integration-of-addresses/index.md b/content/docs/NAVPatterns/patterns/integration-of-addresses/index.md
index d397581d..95521bba 100644
--- a/content/docs/NAVPatterns/patterns/integration-of-addresses/index.md
+++ b/content/docs/NAVPatterns/patterns/integration-of-addresses/index.md
@@ -1,6 +1,8 @@
+++
title = "Integration of Addresses"
weight = 680
+tags = ["C/AL"]
+categories = ["Pattern"]
+++
{{< youtube 60Wrx9N-gfY>}}
diff --git a/content/docs/NAVPatterns/patterns/journal-error-processing/index.md b/content/docs/NAVPatterns/patterns/journal-error-processing/index.md
index a45fa1b6..7dbcd05e 100644
--- a/content/docs/NAVPatterns/patterns/journal-error-processing/index.md
+++ b/content/docs/NAVPatterns/patterns/journal-error-processing/index.md
@@ -1,6 +1,8 @@
+++
title = "Journal Error Processing"
weight = 710
+tags = ["C/AL"]
+categories = ["Pattern"]
+++
_Originally by Bogdana Botez at Microsoft Development Center Copenhagen_
diff --git a/content/docs/NAVPatterns/patterns/journal-template-batch-line/index.md b/content/docs/NAVPatterns/patterns/journal-template-batch-line/index.md
index 36cd27de..99fe99af 100644
--- a/content/docs/NAVPatterns/patterns/journal-template-batch-line/index.md
+++ b/content/docs/NAVPatterns/patterns/journal-template-batch-line/index.md
@@ -1,6 +1,8 @@
+++
title = "Journal Template Batch Line"
weight = 720
+tags = ["C/AL"]
+categories = ["Pattern"]
+++
_Originally by Bogdana Botez at Microsoft Development Center Copenhagen_
diff --git a/content/docs/NAVPatterns/patterns/master-data/index.md b/content/docs/NAVPatterns/patterns/master-data/index.md
index 6dcc814a..f42cbbd4 100644
--- a/content/docs/NAVPatterns/patterns/master-data/index.md
+++ b/content/docs/NAVPatterns/patterns/master-data/index.md
@@ -1,6 +1,8 @@
+++
title = "Master Data"
weight = 780
+tags = ["C/AL"]
+categories = ["Pattern"]
+++
_By [Soren Klemmensen][anchor0], [_Partner-Ready-Software_ ][anchor1] & [360 Visibility][anchor2]_
diff --git a/content/docs/NAVPatterns/patterns/multi-file-download/index.md b/content/docs/NAVPatterns/patterns/multi-file-download/index.md
index 775bb447..fe7bb5f8 100644
--- a/content/docs/NAVPatterns/patterns/multi-file-download/index.md
+++ b/content/docs/NAVPatterns/patterns/multi-file-download/index.md
@@ -1,6 +1,8 @@
+++
title = "Multi-file Download"
weight = 800
+tags = ["C/AL"]
+categories = ["Pattern"]
+++
_By Martin Dam at Microsoft Development Center Copenhagen_
diff --git a/content/docs/NAVPatterns/patterns/multi-page-list/index.md b/content/docs/NAVPatterns/patterns/multi-page-list/index.md
index bc7b8f68..3fea0820 100644
--- a/content/docs/NAVPatterns/patterns/multi-page-list/index.md
+++ b/content/docs/NAVPatterns/patterns/multi-page-list/index.md
@@ -1,6 +1,8 @@
+++
title = "Multi-Page List"
weight = 810
+tags = ["C/AL"]
+categories = ["Pattern"]
+++
_By Bogdana Botez at Microsoft Development Center Copenhagen_
diff --git a/content/docs/NAVPatterns/patterns/multilanguage-application-data/index.md b/content/docs/NAVPatterns/patterns/multilanguage-application-data/index.md
index 83d06dfd..79495c6f 100644
--- a/content/docs/NAVPatterns/patterns/multilanguage-application-data/index.md
+++ b/content/docs/NAVPatterns/patterns/multilanguage-application-data/index.md
@@ -1,6 +1,8 @@
+++
title = "Multilanguage Application Data"
weight = 820
+tags = ["C/AL"]
+categories = ["Pattern"]
+++
_Originally by Bogdana Botez at Microsoft Development Center Copenhagen_
diff --git a/content/docs/NAVPatterns/patterns/net-exception-handling-in-cal/_index.md b/content/docs/NAVPatterns/patterns/net-exception-handling-in-cal/_index.md
index b96c39fc..9175a3e3 100644
--- a/content/docs/NAVPatterns/patterns/net-exception-handling-in-cal/_index.md
+++ b/content/docs/NAVPatterns/patterns/net-exception-handling-in-cal/_index.md
@@ -1,6 +1,8 @@
+++
title = "NET Exception Handling in CAL"
weight = 860
+tags = ["C/AL"]
+categories = ["Pattern"]
+++
_Originally by Mostafa Balat, Microsoft Development Center Copenhagen_
diff --git a/content/docs/NAVPatterns/patterns/net-exception-handling-in-cal/tryfunction-net-exception-handling-in-cal/index.md b/content/docs/NAVPatterns/patterns/net-exception-handling-in-cal/tryfunction-net-exception-handling-in-cal/index.md
index 80239de5..209b5c70 100644
--- a/content/docs/NAVPatterns/patterns/net-exception-handling-in-cal/tryfunction-net-exception-handling-in-cal/index.md
+++ b/content/docs/NAVPatterns/patterns/net-exception-handling-in-cal/tryfunction-net-exception-handling-in-cal/index.md
@@ -1,6 +1,8 @@
+++
title = "TryFunction NET Exception Handling in CAL"
weight = 1240
+tags = ["C/AL"]
+categories = ["Pattern"]
+++
_Originally by Mostafa Balat, Microsoft Development Center Copenhagen_
diff --git a/content/docs/NAVPatterns/patterns/no-series/index.md b/content/docs/NAVPatterns/patterns/no-series/index.md
index a841e1e1..d59f2b3a 100644
--- a/content/docs/NAVPatterns/patterns/no-series/index.md
+++ b/content/docs/NAVPatterns/patterns/no-series/index.md
@@ -1,6 +1,8 @@
+++
title = "No Series"
weight = 870
+tags = ["C/AL"]
+categories = ["Pattern"]
+++
_By Bogdana Botez, at Microsoft Development Center Copenhagen_
diff --git a/content/docs/NAVPatterns/patterns/notifications/_index.md b/content/docs/NAVPatterns/patterns/notifications/_index.md
index 270c0fc4..52b7d965 100644
--- a/content/docs/NAVPatterns/patterns/notifications/_index.md
+++ b/content/docs/NAVPatterns/patterns/notifications/_index.md
@@ -1,5 +1,7 @@
+++
title = "Notifications"
weight = 890
+tags = ["C/AL"]
+categories = ["Pattern"]
+++
A collection of patterns about notifications.
diff --git a/content/docs/NAVPatterns/patterns/notifications/in-context-notifications/index.md b/content/docs/NAVPatterns/patterns/notifications/in-context-notifications/index.md
index 9c603272..1efa1a09 100644
--- a/content/docs/NAVPatterns/patterns/notifications/in-context-notifications/index.md
+++ b/content/docs/NAVPatterns/patterns/notifications/in-context-notifications/index.md
@@ -1,6 +1,8 @@
+++
title = "In-context Notifications"
weight = 640
+tags = ["C/AL"]
+categories = ["Pattern"]
+++
_By Soumya Dutta at Microsoft Development Center Copenhagen_
diff --git a/content/docs/NAVPatterns/patterns/notifications/notification-lifecycle-management-pattern/index.md b/content/docs/NAVPatterns/patterns/notifications/notification-lifecycle-management-pattern/index.md
index 89cd570c..b8d3195d 100644
--- a/content/docs/NAVPatterns/patterns/notifications/notification-lifecycle-management-pattern/index.md
+++ b/content/docs/NAVPatterns/patterns/notifications/notification-lifecycle-management-pattern/index.md
@@ -1,6 +1,8 @@
+++
title = "Notification Lifecycle Management Pattern"
weight = 880
+tags = ["C/AL"]
+categories = ["Pattern"]
+++
_By David Bastide at Microsoft Development Center Copenhagen_
diff --git a/content/docs/NAVPatterns/patterns/observer/index.md b/content/docs/NAVPatterns/patterns/observer/index.md
index 2ebeab8e..9be9315e 100644
--- a/content/docs/NAVPatterns/patterns/observer/index.md
+++ b/content/docs/NAVPatterns/patterns/observer/index.md
@@ -1,6 +1,8 @@
+++
title = "Observer"
weight = 900
+tags = ["C/AL"]
+categories = ["Pattern"]
+++
_By Nikolai L'Estrange, from TVision Technology Ltd. in the UK_
diff --git a/content/docs/NAVPatterns/patterns/posting-routine-select-behavior/index.md b/content/docs/NAVPatterns/patterns/posting-routine-select-behavior/index.md
index 738afa38..79bc2d92 100644
--- a/content/docs/NAVPatterns/patterns/posting-routine-select-behavior/index.md
+++ b/content/docs/NAVPatterns/patterns/posting-routine-select-behavior/index.md
@@ -1,6 +1,8 @@
+++
title = "Posting Routine - Select Behavior"
weight = 940
+tags = ["C/AL"]
+categories = ["Pattern"]
+++
_By waldo_
diff --git a/content/docs/NAVPatterns/patterns/product-name/index.md b/content/docs/NAVPatterns/patterns/product-name/index.md
index d3c841b4..f5f5d49c 100644
--- a/content/docs/NAVPatterns/patterns/product-name/index.md
+++ b/content/docs/NAVPatterns/patterns/product-name/index.md
@@ -1,6 +1,8 @@
+++
title = "Product Name"
weight = 950
+tags = ["C/AL"]
+categories = ["Pattern"]
+++
#### **Abstract**
diff --git a/content/docs/NAVPatterns/patterns/queries/_index.md b/content/docs/NAVPatterns/patterns/queries/_index.md
index f823a5fd..30ab3a09 100644
--- a/content/docs/NAVPatterns/patterns/queries/_index.md
+++ b/content/docs/NAVPatterns/patterns/queries/_index.md
@@ -1,5 +1,7 @@
+++
title = "Queries"
weight = 960
+tags = ["C/AL"]
+categories = ["Pattern"]
+++
Expand to see NAV design patterns which use queries.
diff --git a/content/docs/NAVPatterns/patterns/queries/select-distinct-with-queries/index.md b/content/docs/NAVPatterns/patterns/queries/select-distinct-with-queries/index.md
index 6630bca8..b0fe885a 100644
--- a/content/docs/NAVPatterns/patterns/queries/select-distinct-with-queries/index.md
+++ b/content/docs/NAVPatterns/patterns/queries/select-distinct-with-queries/index.md
@@ -1,6 +1,8 @@
+++
title = "SELECT DISTINCT with Queries"
weight = 1040
+tags = ["C/AL"]
+categories = ["Pattern"]
+++
_By Bogdana Botez, at Microsoft Development Center Copenhagen_
diff --git a/content/docs/NAVPatterns/patterns/queries/use-queries-to-detect-duplicate-records/index.md b/content/docs/NAVPatterns/patterns/queries/use-queries-to-detect-duplicate-records/index.md
index e26e5399..b2681324 100644
--- a/content/docs/NAVPatterns/patterns/queries/use-queries-to-detect-duplicate-records/index.md
+++ b/content/docs/NAVPatterns/patterns/queries/use-queries-to-detect-duplicate-records/index.md
@@ -1,6 +1,8 @@
+++
title = "Use Queries to Detect Duplicate Records"
weight = 1340
+tags = ["C/AL"]
+categories = ["Pattern"]
+++
_Originally by Abshishek Ghosh and Bogdan Sturzoiu at Microsoft Development Center Copenhagen_
diff --git a/content/docs/NAVPatterns/patterns/queries/use-queries-to-replace-nested-loops/index.md b/content/docs/NAVPatterns/patterns/queries/use-queries-to-replace-nested-loops/index.md
index 3caea2a9..a61d2583 100644
--- a/content/docs/NAVPatterns/patterns/queries/use-queries-to-replace-nested-loops/index.md
+++ b/content/docs/NAVPatterns/patterns/queries/use-queries-to-replace-nested-loops/index.md
@@ -1,6 +1,8 @@
+++
title = "Use Queries to Replace Nested Loops"
weight = 1350
+tags = ["C/AL"]
+categories = ["Pattern"]
+++
_Originally by Bogdan Sturzoiu, Microsoft Development Center Copenhagen_
diff --git a/content/docs/NAVPatterns/patterns/read-once-initialization-and-validation/index.md b/content/docs/NAVPatterns/patterns/read-once-initialization-and-validation/index.md
index 370b210c..580a7489 100644
--- a/content/docs/NAVPatterns/patterns/read-once-initialization-and-validation/index.md
+++ b/content/docs/NAVPatterns/patterns/read-once-initialization-and-validation/index.md
@@ -1,6 +1,8 @@
+++
title = "Read-once Initialization and Validation"
weight = 970
+tags = ["C/AL"]
+categories = ["Pattern"]
+++
_By Tim Grant_
diff --git a/content/docs/NAVPatterns/patterns/released-entity/index.md b/content/docs/NAVPatterns/patterns/released-entity/index.md
index 89723cb0..f4b2eeb8 100644
--- a/content/docs/NAVPatterns/patterns/released-entity/index.md
+++ b/content/docs/NAVPatterns/patterns/released-entity/index.md
@@ -1,6 +1,8 @@
+++
title = "Released Entity"
weight = 1000
+tags = ["C/AL"]
+categories = ["Pattern"]
+++
_Authors: Henrik Langbak and Kim Ginnerup, Bording Data_
diff --git a/content/docs/NAVPatterns/patterns/report-selection/index.md b/content/docs/NAVPatterns/patterns/report-selection/index.md
index 7a16d6a8..e5b2ff95 100644
--- a/content/docs/NAVPatterns/patterns/report-selection/index.md
+++ b/content/docs/NAVPatterns/patterns/report-selection/index.md
@@ -1,6 +1,8 @@
+++
title = "Report Selection"
weight = 1010
+tags = ["C/AL"]
+categories = ["Pattern"]
+++
From the PRS workshop at NAVTechDays 2013, this pattern was written by 2 work groups
diff --git a/content/docs/NAVPatterns/patterns/security/1-sensitive-data-encapsulation/index.md b/content/docs/NAVPatterns/patterns/security/1-sensitive-data-encapsulation/index.md
index 3f0c1582..89adf3be 100644
--- a/content/docs/NAVPatterns/patterns/security/1-sensitive-data-encapsulation/index.md
+++ b/content/docs/NAVPatterns/patterns/security/1-sensitive-data-encapsulation/index.md
@@ -1,6 +1,8 @@
+++
title = "Sensitive Data Encapsulation"
weight = 120
+tags = ["C/AL"]
+categories = ["Pattern"]
+++
_By Bogdana Botez at Microsoft Development Center Copenhagen_
diff --git a/content/docs/NAVPatterns/patterns/security/2-data-encryption/index.md b/content/docs/NAVPatterns/patterns/security/2-data-encryption/index.md
index 1fb363a8..20c4d26b 100644
--- a/content/docs/NAVPatterns/patterns/security/2-data-encryption/index.md
+++ b/content/docs/NAVPatterns/patterns/security/2-data-encryption/index.md
@@ -1,6 +1,8 @@
+++
title = "Data Encryption"
weight = 140
+tags = ["C/AL"]
+categories = ["Pattern"]
+++
_By Bogdana Botez at Microsoft Development Center Copenhagen_
diff --git a/content/docs/NAVPatterns/patterns/security/3-single-point-of-access/index.md b/content/docs/NAVPatterns/patterns/security/3-single-point-of-access/index.md
index e275700c..e2c42eb8 100644
--- a/content/docs/NAVPatterns/patterns/security/3-single-point-of-access/index.md
+++ b/content/docs/NAVPatterns/patterns/security/3-single-point-of-access/index.md
@@ -1,6 +1,8 @@
+++
title = "Single Point of Access"
weight = 160
+tags = ["C/AL"]
+categories = ["Pattern"]
+++
_By Bogdana Botez at Microsoft Development Center Copenhagen_
diff --git a/content/docs/NAVPatterns/patterns/security/4-masked-text/index.md b/content/docs/NAVPatterns/patterns/security/4-masked-text/index.md
index c8593751..9f08fce5 100644
--- a/content/docs/NAVPatterns/patterns/security/4-masked-text/index.md
+++ b/content/docs/NAVPatterns/patterns/security/4-masked-text/index.md
@@ -1,6 +1,8 @@
+++
title = "Masked Text"
weight = 180
+tags = ["C/AL"]
+categories = ["Pattern"]
+++
_By Bogdana Botez at Microsoft Development Center Copenhagen_
diff --git a/content/docs/NAVPatterns/patterns/security/5-ssl-in-nav/index.md b/content/docs/NAVPatterns/patterns/security/5-ssl-in-nav/index.md
index 5a64d5c1..0c8eaf6e 100644
--- a/content/docs/NAVPatterns/patterns/security/5-ssl-in-nav/index.md
+++ b/content/docs/NAVPatterns/patterns/security/5-ssl-in-nav/index.md
@@ -1,6 +1,8 @@
+++
title = "SSL in NAV"
weight = 190
+tags = ["C/AL"]
+categories = ["Pattern"]
+++
_By Bogdana Botez at Microsoft Development Center Copenhagen_
diff --git a/content/docs/NAVPatterns/patterns/security/_index.md b/content/docs/NAVPatterns/patterns/security/_index.md
index 18985e6f..19fab25e 100644
--- a/content/docs/NAVPatterns/patterns/security/_index.md
+++ b/content/docs/NAVPatterns/patterns/security/_index.md
@@ -1,6 +1,8 @@
+++
title = "Security"
weight = 1030
+tags = ["C/AL"]
+categories = ["Pattern"]
+++
_By Bogdana Botez at Microsoft Development Center Copenhagen_
diff --git a/content/docs/NAVPatterns/patterns/setup-specificity-fallback/index.md b/content/docs/NAVPatterns/patterns/setup-specificity-fallback/index.md
index 5022e8c5..d5ca5897 100644
--- a/content/docs/NAVPatterns/patterns/setup-specificity-fallback/index.md
+++ b/content/docs/NAVPatterns/patterns/setup-specificity-fallback/index.md
@@ -1,6 +1,8 @@
+++
title = "Setup Specificity Fallback"
weight = 1060
+tags = ["C/AL"]
+categories = ["Pattern"]
+++
_By Jan Hoek at IDYN_
diff --git a/content/docs/NAVPatterns/patterns/silent-file-upload-and-download/index.md b/content/docs/NAVPatterns/patterns/silent-file-upload-and-download/index.md
index 440c4b38..d6b21254 100644
--- a/content/docs/NAVPatterns/patterns/silent-file-upload-and-download/index.md
+++ b/content/docs/NAVPatterns/patterns/silent-file-upload-and-download/index.md
@@ -1,6 +1,8 @@
+++
title = "Silent File Upload and Download"
weight = 1080
+tags = ["C/AL"]
+categories = ["Pattern"]
+++
_By Bogdana Botez, at Microsoft Development Center Copenhagen_
diff --git a/content/docs/NAVPatterns/patterns/singleton/_index.md b/content/docs/NAVPatterns/patterns/singleton/_index.md
index ae9bdf13..8c61390d 100644
--- a/content/docs/NAVPatterns/patterns/singleton/_index.md
+++ b/content/docs/NAVPatterns/patterns/singleton/_index.md
@@ -1,6 +1,8 @@
+++
title = "Singleton"
weight = 1090
+tags = ["C/AL"]
+categories = ["Pattern"]
+++
_By Bogdana Botez at Microsoft Development Center Copenhagen_
diff --git a/content/docs/NAVPatterns/patterns/singleton/singleton-codeunit/index.md b/content/docs/NAVPatterns/patterns/singleton/singleton-codeunit/index.md
index 1c7069a7..1d8a7a83 100644
--- a/content/docs/NAVPatterns/patterns/singleton/singleton-codeunit/index.md
+++ b/content/docs/NAVPatterns/patterns/singleton/singleton-codeunit/index.md
@@ -1,6 +1,8 @@
+++
title = "Singleton Codeunit"
weight = 1100
+tags = ["C/AL"]
+categories = ["Pattern"]
+++
_By Bogdana Botez at Microsoft Development Center Copenhagen_
diff --git a/content/docs/NAVPatterns/patterns/singleton/singleton-table/_index.md b/content/docs/NAVPatterns/patterns/singleton/singleton-table/_index.md
index d2cec690..31bbebb7 100644
--- a/content/docs/NAVPatterns/patterns/singleton/singleton-table/_index.md
+++ b/content/docs/NAVPatterns/patterns/singleton/singleton-table/_index.md
@@ -1,6 +1,8 @@
+++
title = "Singleton Table"
weight = 1110
+tags = ["C/AL"]
+categories = ["Pattern"]
+++
## Singleton Table
diff --git a/content/docs/NAVPatterns/patterns/singleton/singleton-table/cue-table/index.md b/content/docs/NAVPatterns/patterns/singleton/singleton-table/cue-table/index.md
index 913383a6..eff99fd9 100644
--- a/content/docs/NAVPatterns/patterns/singleton/singleton-table/cue-table/index.md
+++ b/content/docs/NAVPatterns/patterns/singleton/singleton-table/cue-table/index.md
@@ -1,6 +1,8 @@
+++
title = "Cue Table"
weight = 440
+tags = ["C/AL"]
+categories = ["Pattern"]
+++
_By Bogdana Botez at Microsoft Development Center Copenhagen_
diff --git a/content/docs/NAVPatterns/patterns/singleton/singleton-table/setup-table/index.md b/content/docs/NAVPatterns/patterns/singleton/singleton-table/setup-table/index.md
index fe9800e2..11ec5be1 100644
--- a/content/docs/NAVPatterns/patterns/singleton/singleton-table/setup-table/index.md
+++ b/content/docs/NAVPatterns/patterns/singleton/singleton-table/setup-table/index.md
@@ -1,6 +1,8 @@
+++
title = "Setup Table"
weight = 1070
+tags = ["C/AL"]
+categories = ["Pattern"]
+++
_By Abhishek Ghosh, at Microsoft Development Center Copenhagen_
diff --git a/content/docs/NAVPatterns/patterns/standard-journal/index.md b/content/docs/NAVPatterns/patterns/standard-journal/index.md
index 1ebb3b12..43f4d4fd 100644
--- a/content/docs/NAVPatterns/patterns/standard-journal/index.md
+++ b/content/docs/NAVPatterns/patterns/standard-journal/index.md
@@ -1,6 +1,8 @@
+++
title = "Standard Journal"
weight = 1150
+tags = ["C/AL"]
+categories = ["Pattern"]
+++
_By Bogdana Botez, at Microsoft Development Center Copenhagen_
diff --git a/content/docs/NAVPatterns/patterns/temporary-dataset-report/index.md b/content/docs/NAVPatterns/patterns/temporary-dataset-report/index.md
index 72a7d87d..b54a4c68 100644
--- a/content/docs/NAVPatterns/patterns/temporary-dataset-report/index.md
+++ b/content/docs/NAVPatterns/patterns/temporary-dataset-report/index.md
@@ -1,6 +1,8 @@
+++
title = "Temporary Dataset Report"
weight = 1190
+tags = ["C/AL"]
+categories = ["Pattern"]
+++
_Originally by Abhishek Ghosh, at Microsoft Development Center Copenhagen_
diff --git a/content/docs/NAVPatterns/patterns/totals-and-discounts-on-subpages-sales-and-purchases/index.md b/content/docs/NAVPatterns/patterns/totals-and-discounts-on-subpages-sales-and-purchases/index.md
index 19143556..88d0d8a4 100644
--- a/content/docs/NAVPatterns/patterns/totals-and-discounts-on-subpages-sales-and-purchases/index.md
+++ b/content/docs/NAVPatterns/patterns/totals-and-discounts-on-subpages-sales-and-purchases/index.md
@@ -1,6 +1,8 @@
+++
title = "Totals and Discounts on Subpages Sales and Purchases"
weight = 1220
+tags = ["C/AL"]
+categories = ["Pattern"]
+++
_By Nikola Kukrika at Microsoft Development Center Copenhagen_
diff --git a/content/docs/NAVPatterns/patterns/transfer-custom-fields/index.md b/content/docs/NAVPatterns/patterns/transfer-custom-fields/index.md
index 3d0d3e78..7745afe0 100644
--- a/content/docs/NAVPatterns/patterns/transfer-custom-fields/index.md
+++ b/content/docs/NAVPatterns/patterns/transfer-custom-fields/index.md
@@ -1,6 +1,8 @@
+++
title = "Transfer Custom Fields"
weight = 1230
+tags = ["C/AL"]
+categories = ["Pattern"]
+++
{{< youtube cGaBqwfGCws>}}
diff --git a/content/docs/NAVPatterns/patterns/variant-facade/index.md b/content/docs/NAVPatterns/patterns/variant-facade/index.md
index 0c377d39..cb969300 100644
--- a/content/docs/NAVPatterns/patterns/variant-facade/index.md
+++ b/content/docs/NAVPatterns/patterns/variant-facade/index.md
@@ -1,6 +1,8 @@
+++
title = "Variant Facade"
weight = 1440
+tags = ["C/AL"]
+categories = ["Pattern"]
+++
_By Nikola Kukrika, waldo and Gary Winter_
diff --git a/content/docs/NAVPatterns/related-links/index.md b/content/docs/NAVPatterns/related-links/index.md
index bda8b801..dbcd675d 100644
--- a/content/docs/NAVPatterns/related-links/index.md
+++ b/content/docs/NAVPatterns/related-links/index.md
@@ -1,6 +1,7 @@
+++
title = "Related Links"
weight = 990
+tags = ["C/AL"]
+++
Find below some related NAV Design Patterns links.
diff --git a/content/docs/_index.md b/content/docs/_index.md
index 4adca734..e48539bd 100644
--- a/content/docs/_index.md
+++ b/content/docs/_index.md
@@ -3,7 +3,7 @@ title: "AL Guidelines"
linkTitle: Docs
weight: 20
description: >
- Patterns and Best Practices for AL Develolpment
+ Patterns and Best Practices for AL Development
---
## Business Central Design Patterns & Best Practices
@@ -40,4 +40,4 @@ This project is a Microsoft Business Central Community initiative with support f
#### Contributing
To find out more about contributing, read up here:
-[Contributing](/contributing/)
\ No newline at end of file
+[Contributing](/docs/contributing/)
diff --git a/content/docs/patterns/_index.md b/content/docs/patterns/_index.md
index b7813e2b..61275700 100644
--- a/content/docs/patterns/_index.md
+++ b/content/docs/patterns/_index.md
@@ -1,13 +1,18 @@
---
-title: "Patterns"
+title: "Design Patterns"
weight: 2
-no_list: true
description: >
AL Code Design Patterns
---
# Business Central Design Patterns
-## Why are Design Patterns important?
+This section will cover patterns that solve certain design challenges in Business Central.
-Blah Blah Blah
\ No newline at end of file
+From wikipedia:
+
+_In Computer science, a Design pattern is an abstract solution to a certain problem. Design patterns are used in object oriented programming. They give a possible solution to a problem of designing software ... They also simplify the language between computer scientists. Ideally, a design pattern should be reusable many times. It is like a brick of a house, it can be used for many different problems. One can also build bridges with bricks, not only houses._
+
+## Discussion
+
+All discussion related to Best Practice are to be found on the Github Repo's Discussion pages, found [here](https://github.com/microsoft/alguidelines/discussions/categories/bc-patterns)
diff --git a/content/docs/patterns/api-delegate-operation/index.md b/content/docs/patterns/api-delegate-operation/index.md
new file mode 100644
index 00000000..5b3f2a92
--- /dev/null
+++ b/content/docs/patterns/api-delegate-operation/index.md
@@ -0,0 +1,73 @@
+---
+title: "Delegate API Operation"
+tags: ["AL","API"]
+categories: ["Pattern"]
+---
+
+_Created by Arend-Jan Kauffmann, Described by Arend-Jan Kauffmann_
+
+## Abstract
+The goal of this pattern is to delegate data operations from the API page to a codeunit. The codeunit can implement its own logic for inserting, modifying or deleting data.
+
+## Context
+API pages implement a specific pattern for insert, modify and delete operations. An API page uses delayed insert behavior, which means that all fields will be validated before the record is inserted or modified.
+This is different from the standard behavior on a UI card page, where a record is first inserted, followed by a modify when field values are updated.
+
+## Problem
+The standard behavior can result in a number of challenges:
+
+* Code behind the OnValidate triggers and its event subscribers may expect that a record already exists when a field is being validated.
+* Usage of temporary buffer tables can become really complex when fully handled from the API page.
+* Applying default data to records before they are created does not work well with delayed inserts.
+
+## Description
+To mitigate these problems, we can delegate the data operation to a codeunit while canceling the data operation inside the API page.
+
+```al
+codeunit 50000 "Item API Operations"
+{
+ internal procedure InsertItem(var Item: Record Item)
+ begin
+ end;
+
+ internal procedure ModifyItem(var Item: Record Item)
+ begin
+ end;
+
+ internal procedure DeleteItem(var Item: Record Item)
+ begin
+ end;
+}
+```
+
+It is important that the record parameter is updated with the final result. This allows the API page to return the result of the API operation to the caller.
+
+The API page implements this in the page triggers as follows:
+
+```al
+ var
+ ItemAPIOperations: Codeunit "Item API Operations";
+
+ trigger OnInsertRecord(BelowxRec: Boolean): Boolean
+ begin
+ ItemAPIOperations.InsertItem(Rec);
+ exit(false);
+ end;
+
+ trigger OnModifyRecord(): Boolean
+ begin
+ ItemAPIOperations.ModifyItem(Rec);
+ exit(false);
+ end;
+
+ trigger OnDeleteRecord(): Boolean
+ begin
+ ItemAPIOperations.DeleteItem(Rec);
+ exit(false);
+ end;
+```
+
+The triggers must return false in order to cancel the operation in the page.
+
+## Benefits
+Inside the functions in the codeunit you have full control over the steps that are performed for the specific operation.
diff --git a/content/docs/patterns/api-register-fieldset/index.md b/content/docs/patterns/api-register-fieldset/index.md
new file mode 100644
index 00000000..b091e808
--- /dev/null
+++ b/content/docs/patterns/api-register-fieldset/index.md
@@ -0,0 +1,108 @@
+---
+title: "API Register Fieldset"
+tags: ["AL","API"]
+categories: ["Pattern"]
+---
+
+_Created by Arend-Jan Kauffmann, Described by Arend-Jan Kauffmann_
+
+## Abstract
+The goal of this pattern is to register the fields that are part of the request body of an API call.
+
+## Context
+A request to an API page to insert or modify a record requires a JSON body with the fields and values. It is not required to specify all fields that are exposed by the API page. Only those fields that are specified in the JSON body will be validated with a value. A call to insert a record (http POST) will leave the unspecified fields at their default value. A call to modify a record (http PATCH) will only update the specified fields and leave the other fields to their current value.
+
+## Problem
+The standard behavior can result in a number of challenges:
+
+* It's not possible to implement mandatory fields. Especially for fields with an initial value like integers, decimals and booleans the code doesn't know if they are their initial value or were included in the API request.
+* In the OnValidate trigger of a field it's not possible to verify if the API request is an insert or modify operation. Fields can't be protected to be modified during a specific operation, e.g. do not allow to modify during an insert.
+* When implementing the Delegated API Operation pattern, for example to implement a template with default values, the template values should not overwrite the provided values in the API request while non-specified fields should get a value from the template.
+
+## Description
+To mitigate these problems, we can register the fields during the OnValidate trigger in a temporary table. During the delayed insert or modify operation the code knows which fields were part of the API request.
+
+```al
+ field(displayName; Rec.Description)
+ {
+ trigger OnValidate()
+ begin
+ RegisterFieldSet(Rec.FieldNo(Description));
+ end;
+ }
+
+ ....
+
+ var
+ TempFieldSet: Record Field temporary;
+
+ local procedure RegisterFieldSet(FieldNumber: Integer)
+ begin
+ if TempFieldSet.Get(Database::Item, FieldNumber) then
+ exit;
+
+ TempFieldSet.Init();
+ TempFieldSet.TableNo := Database::Item;
+ TempFieldSet."No." := FieldNumber;
+ TempFieldSet.Insert();
+ end;
+```
+
+Now that we have the list of fields that are part of the request, they can be checked during the insert or modify operation. Or they can be handed over to a delegated operation.
+
+Some examples to work with the list of fields:
+
+```al
+ trigger OnInsertRecord(BelowxRec: Boolean): Boolean
+ begin
+ if TempFieldSet.Get(Database::Item, Rec.FieldNo(Inventory)) then
+ Error(InventoryCannotBeChangedInAPostRequestErr);
+
+ ItemAPIOperations.InsertItem(Rec, TempFieldSet)
+ exit(false);
+ end;
+
+ trigger OnModifyRecord(): Boolean
+ begin
+ ItemAPIOperations.ModifyItem(Rec, TempFieldSet);
+ exit(false);
+ end;
+
+ trigger OnDeleteRecord(): Boolean
+ begin
+ ItemAPIOperations.DeleteItem(Rec);
+ exit(false);
+ end;
+```
+
+The example code combines this with the Delegated API Operation pattern. The codeunit for the delegated operation can use the fieldset to apply a template while keeping the original values from the request.
+
+```al
+codeunit 50000 "Item API Operations"
+{
+ internal procedure InsertItem(var Item: Record Item; var TempFieldSet: Record "Field"; ModifiedDateTime: DateTime)
+ var
+ ConfigTemplateHeader: Record "Config. Template Header";
+ ConfigTemplateManagement: Codeunit "Config. Template Management";
+ RecRef: RecordRef;
+ begin
+ if not FindConfigTemplateHeader(Item, ConfigTemplateHeader) then
+ exit;
+ RecRef.GetTable(Item);
+
+ if ConfigTemplateManagement.ApplyTemplate(RecRef, TempFieldSet, RecRef, ConfigTemplateHeader) then
+ RecRef.SetTable(Item);
+ end;
+
+ internal procedure ModifyItem(var Item: Record Item)
+ begin
+ end;
+
+ internal procedure DeleteItem(var Item: Record Item)
+ begin
+ end;
+}
+```
+
+## Benefits
+Having a list of fields that are part of the API request provides more information during to the insert or modify operation. This helps to implement specific behavior, based on which fields were specified in the API request.
diff --git a/content/docs/patterns/command-queue/index.md b/content/docs/patterns/command-queue/index.md
new file mode 100644
index 00000000..a414765f
--- /dev/null
+++ b/content/docs/patterns/command-queue/index.md
@@ -0,0 +1,220 @@
+---
+title: "Command Queue"
+tags: ["AL"]
+categories: ["Pattern"]
+---
+
+_Created by Patrick Schiefer, Described by Patrick Schiefer_
+
+## Abstract
+The goal of this pattern is to control the flow of multiple processes
+
+
+## Problem
+Sometimes its neccassary to perform multiple processes in Business Central, for example you want to post more than one order or before you post an Order you also have to post a purchase order this often leads to spaghetti code with big if else structures, which is not easy to read
+
+## Description
+The pattern is ideal for executing several independent processes in succession. Since the processes are independent, each process must take care of error handling itself.
+The command queue should not be used to control a single process. Also it is important to know that the queue is just in the memory so if the service gets restarted the queue is gone and has to be rebuilt.
+
+## The Pattern
+To structure this problem we can use the "Command Queue" pattern. The pattern consist of two main parts the queue and the command interface
+
+
+
+First the command interface, it only has one procedure to execute the command
+```al
+interface ICommand
+{
+ procedure Execute()
+}
+```
+
+
+And then the Queue which consist of two codeunits, the Queue itself and a Queue Entry
+
+```al
+codeunit 50100 "Queue"
+{
+ procedure Push(var value: Interface ICommand)
+ var
+ Entry: Codeunit QueueEntry;
+ begin
+ Entry.SetValue(value);
+ if count = 0 then begin
+ first := Entry;
+ last := Entry;
+ end
+ else begin
+ last.SetNextEntry(Entry);
+ last := Entry;
+ end;
+ count += 1;
+ end;
+
+ procedure Pop() value: Interface ICommand
+ begin
+ if count > 0 then begin
+ value := first.GetValue();
+ first := first.GetNextEntry();
+ count -= 1;
+ end
+ else
+ Error('The Queue is empty!');
+ end;
+
+ procedure GetSize(): Integer
+ begin
+ exit(count);
+ end;
+
+ var
+ first: Codeunit QueueEntry;
+ last: Codeunit QueueEntry;
+ count: Integer;
+}
+
+
+
+codeunit 50102 "QueueEntry"
+{
+ procedure SetValue(var v: Interface ICommand)
+ begin
+ value := v;
+ end;
+
+ procedure GetValue(): Interface ICommand
+ begin
+ exit(value);
+ end;
+
+ procedure GetNextEntry(): Codeunit QueueEntry
+ begin
+ exit(NextEntry);
+ end;
+
+ procedure SetNextEntry(var Entry: Codeunit QueueEntry)
+ begin
+ NextEntry := Entry;
+ end;
+
+ var
+ value: Interface ICommand;
+ NextEntry: Codeunit QueueEntry;
+}
+```
+
+As we see the queue entry stores a command, since the command is an interface we can hide each business logic behind.
+
+## Benefits
+The logical flow is very easy to adopt, it is even possible to add entries to the queue while it is processed.
+
+## Example
+
+In this short example I show you how to post multiple sales orders and display message after finishing the last post.
+
+
+We have two commands in this example, the "SalesOrderPostCommander" is used to post a sales order and the "MessageCommander" displays a message.
+```al
+codeunit 50104 "SalesOrderPostCommander" implements ICommand
+{
+ procedure SetSalesOrderNumber(value : Code[20])
+ begin
+ No := value;
+ end;
+
+ procedure Execute()
+ begin
+ // TODO Post Sales Header
+ end;
+
+
+ var
+ No : Code[20];
+}
+
+
+codeunit 50103 "MessageCommander" implements ICommand
+{
+ procedure SetText(value: Text)
+ begin
+ t := value;
+ end;
+
+ procedure Execute()
+ begin
+ Message(t);
+ end;
+
+ var
+ t: Text;
+}
+```
+
+Using this two codeunits we can now implement a patch posting
+```al
+
+codeunit 50105 PatchPostQueue
+{
+ procedure PatchPost()
+ begin
+ FilterSalesOrdersToPost();
+ if not SalesOrders.Findset(false) then
+ exit(); // Nothing to post
+
+ repeat
+ AddSalesOrderToQueue(SalesOrder."No.");
+ until SalesOrders.Next() = 0;
+
+ AddMessageToQueue('Posting Complete');
+ ExecuteQueue();
+ end;
+
+ local procedure ExecuteQueue()
+ var
+ object : interface "ICommand";
+ begin
+ repeat
+ object := queue.Pop();
+ object.Execute();
+ until queue.GetSize() = 0;
+ end;
+
+ local procedure FilterSalesOrdersToPost()
+ begin
+ // Filter Sales Orders here
+ end;
+
+ local procedure AddMessageToQueue(message : Text)
+ var
+ t: Codeunit MessageCommander;
+ object: Interface ICommand;
+ begin
+ t.SetText(message);
+ object := t;
+ queue.Push(object);
+ end;
+
+ local procedure AddSalesOrderToQueue(No : Text)
+ var
+ SaleOrderCommander: Codeunit SalesOrderPostCommander;
+ object: Interface ICommand;
+ begin
+ SaleOrderCommander.SetSalesOrderNumber(No);
+ object := SaleOrderCommander;
+ queue.Push(object);
+ end;
+
+
+ var
+ SalesOrders : Record "Sales Header";
+ queue: Codeunit Queue;
+}
+
+```
+
+
+
+## References
+[Detailed Explanation of the pattern](https://patrickschiefer.wordpress.com/2022/02/24/part-2-how-to-implement-a-command-queue-in-pure-al/)
+
diff --git a/content/docs/patterns/command-queue/queue.png b/content/docs/patterns/command-queue/queue.png
new file mode 100644
index 00000000..a7411ca6
Binary files /dev/null and b/content/docs/patterns/command-queue/queue.png differ
diff --git a/content/docs/patterns/error-handling/index.md b/content/docs/patterns/error-handling/index.md
new file mode 100644
index 00000000..ecd3635a
--- /dev/null
+++ b/content/docs/patterns/error-handling/index.md
@@ -0,0 +1,24 @@
++++
+title = "Error Handling"
+tags = ["AL"]
+categories = ["Pattern"]
++++
+
+_Created by Microsoft, Described by Luuk Busschers (Dysel)_
+
+## Abstract
+
+The "Error Handling" system is used extensively to provide information to users about missing information in the system or other issues because of which the started process cannot be completed through Microsoft Dynamics 365 Business Central.
+
+## Description
+Because of there is already a lot written about how to use the error handling the best in several scenario's in this page you will find a link to documentation on learn.microsoft.com and a link to a video about this subject on youtube. These links can be found in the list of references.
+
+The Microsoft Learn part is about collecting errors which means that the process you did start will not be interupted when one error is given, it will collect the errors in the process an you are able to show the user afterwards which errors where given in the process.
+
+The youtube video shows more about errors presented in such way that the user will be informed about how to solve the error.
+
+## List of references
+
+For error handling, there is more information available on:
+- [Microsoft Learn: Error collections](https://learn.microsoft.com/en-us/dynamics365/business-central/dev-itpro/developer/devenv-error-collection/)
+- [Youtube: Microsoft Presents: User friendly error handling in AL](https://www.youtube.com/watch?v=D8233xMjVog&list=PLI1l3dMI8xlDM9onioMWUyMSCiFs_mMWw&index=27)
\ No newline at end of file
diff --git a/content/docs/patterns/event-bridge-pattern/index.md b/content/docs/patterns/event-bridge-pattern/index.md
index ca46e5b2..e05deadc 100644
--- a/content/docs/patterns/event-bridge-pattern/index.md
+++ b/content/docs/patterns/event-bridge-pattern/index.md
@@ -1,6 +1,6 @@
---
title: "Event Bridge"
-tags: ["Interface"]
+tags: ["AL","Interface","Extendability"]
categories: ["Pattern"]
---
@@ -20,8 +20,8 @@ Like in this example, we have an interface, to implement different ways for gett
```AL
interface "IScale"
{
- procedure GetWeight(): Decimal;
- procedure Tare();
+ procedure GetWeight(): Decimal
+ procedure Tare()
}
```
@@ -35,13 +35,13 @@ So, if we would implement it like this, it isn't really extensible, as a differe
```AL
codeunit 50407 "Scale Wrong" implements IScale
{
- procedure GetWeight() Result: Decimal;
+ procedure GetWeight() Result: Decimal
begin
//TODO: Implement Bar GetWeight
OnAfterGetWeight(Result);
end;
- procedure Tare();
+ procedure Tare()
begin
//TODO: Implement Bar Tare
OnAfterTare();
@@ -86,13 +86,13 @@ codeunit 50405 "Scale Bar" implements IScale
var
IScaleTriggers: Codeunit "IScale Triggers";
- procedure GetWeight() Result: Decimal;
+ procedure GetWeight() Result: Decimal
begin
//TODO: Implement Bar GetWeight
IScaleTriggers.OnAfterGetWeight(Result);
end;
- procedure Tare();
+ procedure Tare()
begin
//TODO: Implement Bar Tare
IScaleTriggers.OnAfterTare();
@@ -102,14 +102,10 @@ codeunit 50405 "Scale Bar" implements IScale
## Benefits
-This new codeunit, with public events, makes the events accessible from all places, including new apps that are dependent from this app, and wants to extend the
+This new codeunit, with public events, makes the events accessible from all places, including new apps that are dependent from this app.
The naming convention (both starting with "IScale") also makes it very easy to find that corresponding events for the interface.
## When not to use
Obviously, the events should be carefully considered: only the events that make sense to "share" over all implementations, need this approach.
-
-## Discussions
-
-You can discuss this pattern [here](https://github.com/microsoft/alguidelines/discussions/66)
\ No newline at end of file
diff --git a/content/docs/patterns/facade-pattern/index.md b/content/docs/patterns/facade-pattern/index.md
index ef138707..965a4adb 100644
--- a/content/docs/patterns/facade-pattern/index.md
+++ b/content/docs/patterns/facade-pattern/index.md
@@ -1,6 +1,6 @@
---
title: "Façade"
-tags: [""]
+tags: ["AL","Decoupling","Readability","Testability","Extendability"]
categories: ["Pattern"]
---
@@ -9,6 +9,7 @@ _Created by Erich Gamma, Richard Helm, Ralph Johnson, John Vlissides (Gang of Fo
## Abstract
The intent of this pattern is to provide a unified API to a single or a collection of potentially complex subsystems. If you apply this pattern as a general pattern, you will ensure improved:
+
- Decoupling
- Encapsulation
- Readability
@@ -22,6 +23,7 @@ Whenever you want to write an isolated piece of business logic, from now on refe
## Problem
The facade pattern addresses two main problems:
+
- Over time as systems grow, they tend to become complex and harder to comprehend. By adding a facade on top of the subsystem, that complexity is hidden, and a clear API is defined.
- Any object or method which is publicly accessible, may not receive breaking changes in future releases without announced deprecation. This complicates maintainability of the system. By adding a facade, you ensure that the subsystem is inaccessible to the outside systems, enabling you to change the implementation details of the subsystem at will.
@@ -59,9 +61,12 @@ This is arguably one of easiest patterns to understand and implement. Loosely sp
To achieve this, we are using [access modifiers](https://docs.microsoft.com/bs-cyrl-ba/dynamics365/business-central/dev-itpro/developer/devenv-using-access-modifiers). Let's try to take a look at an example, taken from the system application: [the Image module](https://github.com/microsoft/ALAppExtensions/tree/main/Modules/System/Image). I'm using this very simplified example for illustration purposes. Notice, that even the full subsystem at time of writing isn't complex - it merely has a single codeunit containing the implementation details. However, as it is expected that the complexity will increase over time or that the implementation details can change, the subsystem is already equipped with a facade from the beginning.
-*The Facade*
+_The Facade_
```AL
+///
+/// Codeunit to extract image information.
+///
codeunit 3971 Image
{
Access = Public;
@@ -90,6 +95,7 @@ codeunit 3971 Image
```
The facade codeunit above has some characteristics:
+
- Access is explicitly set to Public, to underline that this is a facade.
- All methods are public.
- All methods are documented.
@@ -102,7 +108,7 @@ Anyone who wants to access the subsystem, will only have to relate to this one p
Test of the subsystem can be limited to testing the facade - it is strictly speaking the only thing that needs verification, that it functions as designed. It is the contract of the subsystem.
-*The Subsystem*
+_The Subsystem_
```AL
codeunit 3970 "Image Impl."
@@ -135,6 +141,7 @@ codeunit 3970 "Image Impl."
There are no rules for the subsystem, except that access needs to be **internal**. How you implement, how much you document, how you test, is entirely up to you and not the business of the outside caller. Of course, you should apply all of the best practices and patterns anyway, as you and possibly other developers will have to understand, extend and maintain the subsystem too. But from the view of this pattern, the complexity of the subsystem is irrelevant - just as long as it's not accessible.
## Usage
+
The facade pattern is one of the most prominent patterns in the [system application](https://github.com/microsoft/ALAppExtensions/tree/main/Modules/System). You will find plenty of examples here.
## Benefits
@@ -142,18 +149,23 @@ The facade pattern is one of the most prominent patterns in the [system applicat
The benefits of this rather simple pattern should be abundantly clear by now. But let's go over them once more, structured by the advantages this patterns brings:
### Decoupling
+
As the entire subsystem is inaccessible to outside systems, no dependencies can be taken. Hence this patterns strongly promotes the decoupling of objects.
### Encapsulation
+
The entire purpose of this very pattern is to encapsulate complexity; you hide away the implementation details behind an easy to understand facade.
### Readability
+
If done right, the developer doesn't need to be able to understand the details of the subsystem. Everything relevant to using the subsystem is described in the facade.
### Testability
+
Ensuring the correct behaviour of the subsystem can be done by testing the facade. The facade defines the contract of your subsystem - what does it expose and how should it behave. That contract should be covered with adequate tests, which will ensure that it is upheld, even if you decide to change the implementation of the subsystem.
### Maintainability
+
The one thing you may not change freely, is the facade and the test of the facade. It can be extended, but you should not break any existing APIs. But that leaves the entire subsystem to be completely rewritten, if you desire to do so. As no external dependencies can exist, there is no risk of introducing any syntactical breaking changes to the outside world. And as the tests of the public facade remain, there is no risk of introducing semantical breaking changes either - the contract is upheld, as long as your tests pass.
## When not to use
@@ -173,6 +185,3 @@ This is one of the most commonly used and discussed, initially described here:
It is also a key pattern in the design of our system application modules, which is described here:
[Module Architecture](https://docs.microsoft.com/en-us/dynamics365/business-central/dev-itpro/developer/devenv-blueprint)
-
-## Discussions
-You can discuss this pattern [here](https://github.com/microsoft/alguidelines/discussions/42)
\ No newline at end of file
diff --git a/content/docs/patterns/generic-method-pattern/index.md b/content/docs/patterns/generic-method-pattern/index.md
index 13083941..a16e2b64 100644
--- a/content/docs/patterns/generic-method-pattern/index.md
+++ b/content/docs/patterns/generic-method-pattern/index.md
@@ -1,6 +1,6 @@
---
title: "Generic Method"
-tags: [""]
+tags: ["AL","Decoupling","Readability","Testability","Extendability"]
categories: ["Pattern"]
---
@@ -9,6 +9,7 @@ _Created by Gary Winter (Cloud Ready Software), Described by waldo (iFacto Busin
## Abstract
The goal of this pattern is to facilitate a lot of things in one single awesome way of writing code. If you apply this pattern as a general pattern, you'll implement:
+
- Extensibility
- Decoupling
- Readability
@@ -25,11 +26,11 @@ Usually, when you ask people where to place code, they all have their own opinio
## Description
-What if we have some kind of "standard way" to always write our code. The *Generic Method Pattern* is kind of like what it says: a generic way to implement a method.
+What if we have some kind of "standard way" to always write our code. The _Generic Method Pattern_ is kind of like what it says: a generic way to implement a method.
### What is a method?
-Well, a method is *a significant piece of business logic* - maybe best explained by some examples:
+Well, a method is _a significant piece of business logic_ - maybe best explained by some examples:
- Posting a document
- Any button on a page that executes business logic
@@ -41,7 +42,7 @@ In a way, except "data validation", most of the things we write in our daily lif
### The Pattern
**One method, one codeunit**
-The idea is to put the code in one *encapsulated* codeunit with the purpose to have all the code in that one codeunit function for that one method. This way, the codeunit will stay relatively small and readable.
+The idea is to put the code in one _encapsulated_ codeunit with the purpose to have all the code in that one codeunit function for that one method. This way, the codeunit will stay relatively small and readable.
Let me start by showing an example, so you can refer to this complete example during the rest of the article:
@@ -52,14 +53,15 @@ codeunit 53100 "WLD BlockCustomer Meth"
var
IsHandled: Boolean;
begin
- if not ConfirmBlockCustomer(HideDialog) then exit;
+ if not ConfirmBlockCustomer(HideDialog) then
+ exit;
OnBeforeBlockCustomer(Cust, IsHandled);
DoBlockCustomer(Cust, IsHandled);
OnAfterBlockCustomer(Cust);
AcknowledgeBlockCustomer(HideDialog)
end;
- local procedure DoBlockCustomer(var Cust: Record Customer; IsHandled: Boolean);
+ local procedure DoBlockCustomer(var Cust: Record Customer; IsHandled: Boolean)
begin
if IsHandled then
exit;
@@ -76,7 +78,8 @@ codeunit 53100 "WLD BlockCustomer Meth"
begin
DefaultAnswer := true;
- if HideDialog then exit(DefaultAnswer);
+ if HideDialog then
+ exit(DefaultAnswer);
exit(ConfirmManagement.GetResponseOrDefault(ConfirmQst, DefaultAnswer));
end;
@@ -84,34 +87,37 @@ codeunit 53100 "WLD BlockCustomer Meth"
var
AcknowledgeMsg: Label 'You successfully executed "BlockCustomer"';
begin
- if not GuiAllowed or HideDialog then exit;
+ if not GuiAllowed or HideDialog then
+ exit;
Message(AcknowledgeMsg);
end;
[IntegrationEvent(false, false)]
- local procedure OnBeforeBlockCustomer(var Cust: Record Customer; var IsHandled: Boolean);
+ local procedure OnBeforeBlockCustomer(var Cust: Record Customer; var IsHandled: Boolean)
begin
end;
[IntegrationEvent(false, false)]
- local procedure OnAfterBlockCustomer(var Cust: Record Customer);
+ local procedure OnAfterBlockCustomer(var Cust: Record Customer)
begin
end;
}
```
Within that codeunit, the pattern is always the same:
+
- One public (internal) procedure
- The rest is always local
So, from outside the codeunit, there is only one clear entrypoint: that one (public) internal function with its parameters.
The **pattern** within the codeunit exists of a few layers:
+
- The UI layer
- The Event layer
- The method layer
-*The UI layer*
+_The UI layer_
The UI layer takes care of the UI, obviously. What is important in this case, is that you always make sure that there is a "HideDialog" parameter that the business logic can use to still decide whether to use the dialog or not.
These are the UI Layer parts, where you see the public function gets the HideDialog, and passes it to the UI-related procedures, where the business logic for showing the UI takes place. Also, the default answer of the confirmation is handled there as well (what if the business logic calls this method with HideDialog to "true").
@@ -123,7 +129,8 @@ codeunit 53100 "WLD BlockCustomer Meth"
var
IsHandled: Boolean;
begin
- if not ConfirmBlockCustomer(HideDialog) then exit;
+ if not ConfirmBlockCustomer(HideDialog) then
+ exit;
...
AcknowledgeBlockCustomer(HideDialog)
end;
@@ -137,7 +144,8 @@ codeunit 53100 "WLD BlockCustomer Meth"
begin
DefaultAnswer := true;
- if HideDialog then exit(DefaultAnswer);
+ if HideDialog then
+ exit(DefaultAnswer);
exit(ConfirmManagement.GetResponseOrDefault(ConfirmQst, DefaultAnswer));
end;
@@ -145,14 +153,15 @@ codeunit 53100 "WLD BlockCustomer Meth"
var
AcknowledgeMsg: Label 'You successfully executed "BlockCustomer"';
begin
- if not GuiAllowed or HideDialog then exit;
+ if not GuiAllowed or HideDialog then
+ exit;
Message(AcknowledgeMsg);
end;
...
}
```
-*The Event layer*
+_The Event layer_
This layer is going to add flexibility to any app that has a dependency on this app. By default, the pattern always foresees an `OnBefore` and an `OnAfter` event.
This is the relevant code for the event layer:
@@ -172,18 +181,18 @@ codeunit 53100 "WLD BlockCustomer Meth"
end;
...
[IntegrationEvent(false, false)]
- local procedure OnBeforeBlockCustomer(var Cust: Record Customer; var IsHandled: Boolean);
+ local procedure OnBeforeBlockCustomer(var Cust: Record Customer; var IsHandled: Boolean)
begin
end;
[IntegrationEvent(false, false)]
- local procedure OnAfterBlockCustomer(var Cust: Record Customer);
+ local procedure OnAfterBlockCustomer(var Cust: Record Customer)
begin
end;
}
```
-*The method layer*
+_The method layer_
The last layer is obviously where the business logic will be written.
The relevant part is:
@@ -191,7 +200,7 @@ The relevant part is:
```AL
codeunit 53100 "WLD BlockCustomer Meth"
{
- internal procedure BlockCustomer(var Cust: Record Customer; HideDialog: Boolean);
+ internal procedure BlockCustomer(var Cust: Record Customer; HideDialog: Boolean)
var
IsHandled: Boolean;
begin
@@ -200,7 +209,7 @@ codeunit 53100 "WLD BlockCustomer Meth"
...
end;
- local procedure DoBlockCustomer(var Cust: Record Customer; IsHandled: Boolean);
+ local procedure DoBlockCustomer(var Cust: Record Customer; IsHandled: Boolean)
begin
if IsHandled then
exit;
@@ -211,7 +220,9 @@ codeunit 53100 "WLD BlockCustomer Meth"
...
}
```
+
Usually indicated with a "do"-function, the business logic takes place in that procedure. Obviously, when you have a decent amount of code, it's recommended that you make it readable by applying all the Best Practices in terms of readability in the codeunit. Though, a few pointers here:
+
- keep the [cyclomatic complexity](https://en.wikipedia.org/wiki/Cyclomatic_complexity) low
- one line (function call) after an IF-clause
- one line (function call) after a repeat
@@ -243,11 +254,12 @@ tableextension 53100 "Customer Ext BASE" extends Customer
This practice improves readability. In fact, by doing this, you just extended the suggestions-list in VSCode (IntelliSense) indicating a new method that your class can do. This is very convenient for the developer that might need your new method.
-*Note - it could very well be that there simply isn't any table that can act as a class for our method. In that case, you could use a codeunit as well.*
+_Note - it could very well be that there simply isn't any table that can act as a class for our method. In that case, you could use a codeunit as well._
**Naming Conventions**
You might have noticed that the naming of our method is quite strict:
+
- codeunit name: `WLD BlockCustomer Meth`
- internal proc: `BlockCustomer`
- do-procedure: `DoBlockCustomer`
@@ -255,6 +267,7 @@ You might have noticed that the naming of our method is quite strict:
It is important to align these namings. It indicates that the codeunit only does one thing (remember: encapsulation), and it improves searchability from outside the codeunit (for example when you're searching symbols or something).
## Usage
+
Currently, there is no usage of this pattern in the BaseApp.
The pattern has a main advantage in an ISV product, just because of the decoupling and extensibility. Although, I have seen many occasions where parts of the pattern was useful on PTE's as well. You simply never know if ever at the customer site, there is going to be another partner that needs to create its own PTE, and has to depend on yours. So I'd say, this pattern is everywhere applicable, no matter the type of the app.
@@ -264,9 +277,11 @@ The pattern has a main advantage in an ISV product, just because of the decoupli
As I said, it will facilitate a lot of advantages. Let's explain a bit more in depth:
### Extensibility
-Thanks to the *event layer*, by applying this pattern for all methods, we will automatically have the bare minimum of events that we need to hook into a method: the `OnBefore-` and the `OnAfter`. Of course it would make sense to even add more events to the method when appropriate (eg, when you're inserting a record in a table, it might be interesting to also raise an event just before you call the insert).
+
+Thanks to the _event layer_, by applying this pattern for all methods, we will automatically have the bare minimum of events that we need to hook into a method: the `OnBefore-` and the `OnAfter`. Of course it would make sense to even add more events to the method when appropriate (eg, when you're inserting a record in a table, it might be interesting to also raise an event just before you call the insert).
### Decoupling
+
Thanks to these same events, and the fact the pattern foresees a handler as well, we are able to "decouple" our method as well. What do I mean with that? Well, we can simply subscribe to the `OnBefore`event, and set `IsHandled` to `true`. This means it will never execute the do-procedure, which means, the original procedure/method/business logic is "decoupled".
We can use this obviously for implementing our own method (a new way to accomplish this method), or to disable the method by simply subscribing to it, and only providing the `IsHandled := true` in our subscriber. However, there are many more usages where we can use this for.
@@ -277,34 +292,41 @@ if you would apply this pattern to your product, at the customer, you'll be able
This gives a lot of flexibility.
### Readability
+
When we talk about readability, we actually talk about the part where we expose our method on the class. The rule is: never call the codeunit, but only from one place: from its "class" - or in BC terms: its table (or codeunit).
In terms of readability, that means that intellisense comes into play. In stead of:
+
```AL
Codeunit.Run(Codeunit::"Sales-Post", SalesHeader);
```
+
you simply get
+
```AL
SalesHeader.Post();
```
+
THAT is readable. The previous is not! That is just something we got used to.
### Testability
+
There are two things in terms of testability where this pattern helps a lot.
-*Unit testing*
+_Unit testing_
You can interpret "unit testing" very broadly. But just imagine: when you're building your software entirely out of "methods" - which means: when you'd build your software entirely with this "Generic Method Pattern". Now, the list of methods, are all the units that you need to test: if you test all your methods, you kind of like test the majority of your software, right?
So you could simply set up rules in your company like: EVERY method needs a test-codeunit. And even more: since every method only has one global function - it's pretty easy to know the context, and all the flavors to test your method.
The pattern describes the tests that needs to be written.
-*Disabling methods*
+_Disabling methods_
Coming back to the "decoupling" part - in tests, you actually might need it more than you realize. Just imagine: you want to test method 1, but method 2 comes in the way by interfering with configurations that you need to do, or UI that is popping up, while it could be completely pointless.
Solution: simply - within your test-codeunit - subscribe (with a manual subscriber) to method 2, set `IsHandled` to `false` - done!
### Encapsulation
-Don't underestimate the power of the encapsulation part of this pattern. One of the first questions that people ask themselves when reading into this pattern is: "*isn't it going to consume all my codeunit-id's*" or "*so many codeunits, that can't be readable, right?*".
+
+Don't underestimate the power of the encapsulation part of this pattern. One of the first questions that people ask themselves when reading into this pattern is: "_isn't it going to consume all my codeunit-id's_" or "_so many codeunits, that can't be readable, right?_".
The fact that the functionality of one method is encapsulated in one codeunit is very powerful. You'll avoid [Boat anchors](https://sourcemaking.com/antipatterns/boat-anchor) simply because because, thanks to the encapsulation, there is a limited amount of code in the codeunit, of course.
And because of that, it so much more maintainable, upgradable, readable, .. . Only advantages.
@@ -316,13 +338,14 @@ So all I can say is: use your common sense.
One example: set the bar at "validation code": any code that is solely there to facilitate data integrity doesn't belong in method codeunits.
-Another tip might be: don't let the amount of codelines trick you in deciding to *not* use this pattern: when it's a method, it's a method. When it makes sense to be able to extend, decouple, .. then this pattern can help.
+Another tip might be: don't let the amount of codelines trick you in deciding to _not_ use this pattern: when it's a method, it's a method. When it makes sense to be able to extend, decouple, .. then this pattern can help.
## Snippets
[waldo's CRS AL Language Extension](https://marketplace.visualstudio.com/items?itemName=waldo.crs-al-language-extension) contains snippets that help you in setting up the boiler plate code in a matter of seconds.
The snippets are:
+
- `tcodeunitMethodWithoutUIwaldo`
- `tcodeunitMethodWithUIwaldo`
@@ -331,7 +354,3 @@ The snippets are:
There have been a number of occasions where people have been sharing this pattern. Here is one:
{{< youtube id="CWpaD9RUa6U" yt_start="1516" >}}
-
-
-## Discussions
-You can discuss this pattern [here](https://github.com/microsoft/alguidelines/discussions/41)
\ No newline at end of file
diff --git a/content/docs/patterns/no-series/index.md b/content/docs/patterns/no-series/index.md
new file mode 100644
index 00000000..a28c48ec
--- /dev/null
+++ b/content/docs/patterns/no-series/index.md
@@ -0,0 +1,286 @@
++++
+title = "No. Series"
+tags = ["AL"]
+categories = ["Pattern"]
++++
+
+_Created by Microsoft, Described by Jeremy Vyska (Spare Brained Ideas)_
+
+## Abstract
+
+The "Number Series" system is used extensively to provide numbers to master records, documents, and other transactions through Microsoft Dynamics 365 Business Central.
+
+## Description
+
+At the heart of things, the Number Series engine allows users to define structure for a sequential numeric or alphanumeric string (collectively referred to as a 'number series'), then assign that structure to different parts of the system.
+
+Typically, one creates a single number series for each _type_ of data entity. For example, Customers or Sales Orders each could have a series defined so that all new Customers or Sales Orders get a new number automatically.
+
+The Number Series system serves a few ancillary roles:
+
+- maintains the usage information to know when the last number was generated and on which date
+- allows for date driven structures, so that different periods may have different structures
+- allows control of if manual entries are or are not permitted
+- allow for incrementing in different steps (+1 each time or +1000 each time)
+- warn users as a series is running out of numbers
+- control if any gaps in a series are permitted (as some regional laws do not allow skipping)
+
+This is many roles, features, and controls for generation of a single field so the implementation of this can seem difficult at first.
+
+{{% alert title="Note" color="info" %}}
+One additional (and somewhat optional) feature in the Number Series engine allows multiple sequences per type, called **Relationships**. For example, different numbers for Items that are finished goods versus raw materials. This requires additional hooks on the Page.
+{{% /alert %}}
+
+## Usage in Data Entities
+
+To understand an example use in the Base App, the Customer data entity is a good choice.
+
+Implementation to connect the Customer **`No.`** field to the Number Series engine is done at the table level. The Customer table contains:
+
+A field to contain the number (typically the primary key), which will be of type **`Code`**, length of **20**:
+
+```AL
+field(1; "No."; Code[20])
+{
+ Caption = 'No.';
+
+ trigger OnValidate()
+ begin
+ [...]
+ end;
+}
+```
+
+A field to contain the unique ID of the Number Series, typically called "No. Series"
+
+```AL
+field(107; "No. Series"; Code[20])
+{
+ Caption = 'No. Series';
+ Editable = false;
+ TableRelation = "No. Series";
+}
+```
+{{% alert title="Note" color="warning" %}}
+The **`TableRelation`** is important, and the **`Editable`** being false is advised.
+{{% /alert %}}
+
+And on the **`OnInsert`** trigger, code populates the **`No. Series`** and **`No.`** field.
+
+```AL
+trigger OnInsert()
+var
+ IsHandled: Boolean;
+begin
+ IsHandled := false;
+ OnBeforeInsert(Rec, IsHandled);
+ if IsHandled then
+ exit;
+
+ if "No." = '' then begin
+ SalesSetup.Get();
+ SalesSetup.TestField("Customer Nos.");
+ NoSeriesMgt.InitSeries(SalesSetup."Customer Nos.", xRec."No. Series", 0D, "No.", "No. Series");
+ end;
+ [...]
+
+ OnAfterOnInsert(Rec, xRec);
+ end;
+```
+
+In the case of Customer, this is a Data Entity within the Sales module of the system. The Sales module has a **Sales Setup** table where the user can specify a **No. Series** to use for Customers by default.
+
+`SalesSetup.Get();` fetches the sole setup table record.
+
+`SalesSetup.TestField("Customer Nos.");` is the basic validation that the **Sales Setup** table has a non-empty **Customer Nos.** field. If the setup field isn't populated, when the user attempts to create a new Customer, they will receive an error message.
+
+`NoSeriesMgt.InitSeries(SalesSetup."Customer Nos.", xRec."No. Series", 0D, "No.", "No. Series");` is more parameters to a function than most expect.
+
+The function call takes the following parameters:
+
+```AL
+procedure InitSeries(
+ DefaultNoSeriesCode: Code[20];
+ OldNoSeriesCode: Code[20];
+ NewDate: Date;
+ var NewNo: Code[20];
+ var NewNoSeriesCode: Code[20])
+```
+
+The **DefaultNoSeriesCode** parameter is typically from a setup table. In the Customer example, this comes from the **Sales Setup** **Customer Nos.** setting.
+
+The **OldNoSeriesCode** is used to verify when changing from one No Series to another that they are related.
+
+The **NewDate** parameter is used to drive numbering based on Dates. This is typically used on Documents. For master entities, like Customer, an empty date `0D` can be passed in.
+
+{{% alert title="Note" color="info" %}}
+Many parts of the NoSeriesManagement codeunit predate method overloading, so if the system was created today, some parameters like NewDate would likely be optional.
+{{% /alert %}}
+
+The **NewNo** is a `var` parameter, and is how the new value comes back from the engine. This also serves two other purposes:
+ - if passed in blank, the Number Series used must be configured to have **Default Nos.** enabled
+ - if passed in with a value, the Number Series used must be configured to have **Manual Nos** enabled.
+
+The **NewNoSeriesCode** is more often used to switch between related number series, but is a required parameter, and is also passed back from the engine, so it is also a `var`.
+
+Additionally, it is a good idea to have `OnValidate` functionality on the **`No.`** field. The complete code for the Customer **`No.`** field:
+
+```AL
+field(1; "No."; Code[20])
+{
+ Caption = 'No.';
+
+ trigger OnValidate()
+ begin
+ if "No." <> xRec."No." then begin
+ SalesSetup.Get();
+ NoSeriesMgt.TestManual(SalesSetup."Customer Nos.");
+ "No. Series" := '';
+ end;
+ if "Invoice Disc. Code" = '' then
+ "Invoice Disc. Code" := "No.";
+ end;
+}
+```
+
+If the user has changed the **`No.`** field (`"No." <> xRec."No."`), then:
+- the Number Series is checked if manually setting a new value is allowed via the `TestManual` function
+- The `No. Series` is cleared on the record, as it has no longer been given a value from that Series.
+
+
+Since the Customer data entity supports the **No. Series Relationship** functionality, there are additional components. On the table, there is a function called `AssistEdit`:
+
+```AL
+procedure AssistEdit(OldCust: Record Customer): Boolean
+var
+ Cust: Record Customer;
+begin
+ with Cust do begin
+ Cust := Rec;
+ SalesSetup.Get();
+ SalesSetup.TestField("Customer Nos.");
+ if NoSeriesMgt.SelectSeries(SalesSetup."Customer Nos.", OldCust."No. Series", "No. Series") then begin
+ NoSeriesMgt.SetSeries("No.");
+ Rec := Cust;
+ OnAssistEditOnBeforeExit(Cust);
+ exit(true);
+ end;
+ end;
+end;
+```
+
+{{% alert title="Note" color="warning" %}}
+The use of **`WITH`** is deprecated. While this code block represents the current state of the Base App, the use of **`WTIH`** should not be copied.
+{{% /alert %}}
+
+Similar to the **`OnInsert`** trigger, some setup fields are checked.
+
+Then, the `SelectSeries` function is called. This will present a List to the user of available and relevant **No. Series** that are connected to the `SalesSetup."Customer Nos."` by a Number Series Relationship.
+
+From the **`Customer Page`** (a Card type page), the **No.** field has an **`AssistEdit`** trigger:
+
+```AL
+trigger OnAssistEdit()
+begin
+ if AssistEdit(xRec) then
+ CurrPage.Update();
+end;
+```
+
+## Usage in Journals
+
+Journals utilize a **`Document No.`** as a non-primary key field and use a different strategy for use of the Number Series engine. For each Journal Batch, a different **`No. Series`** can be set.
+
+For example, on the **`General Journal`** Page, in the **`OnNewRecord`**, the **`SetUpNewLine`** function on the **`Gen. Journal Line`** Table is called:
+
+```AL
+procedure SetUpNewLine(LastGenJnlLine: Record "Gen. Journal Line"; Balance: Decimal; BottomLine: Boolean)
+var
+ IsHandled: Boolean;
+begin
+ IsHandled := false;
+ OnBeforeSetUpNewLine(GenJnlTemplate, GenJnlBatch, GenJnlLine, LastGenJnlLine, GLSetupRead, Balance, BottomLine, IsHandled);
+ if IsHandled then
+ exit;
+
+ GenJnlTemplate.Get("Journal Template Name");
+ GenJnlBatch.Get("Journal Template Name", "Journal Batch Name");
+ GenJnlLine.SetRange("Journal Template Name", "Journal Template Name");
+ GenJnlLine.SetRange("Journal Batch Name", "Journal Batch Name");
+ if GenJnlLine.FindFirst then begin
+ "Posting Date" := LastGenJnlLine."Posting Date";
+ "Document Date" := LastGenJnlLine."Posting Date";
+ "Document No." := LastGenJnlLine."Document No.";
+ OnSetUpNewLineOnBeforeIncrDocNo(GenJnlLine, LastGenJnlLine, Balance, BottomLine);
+ if BottomLine and
+ (Balance - LastGenJnlLine."Balance (LCY)" = 0) and
+ not LastGenJnlLine.EmptyLine
+ then
+ IncrementDocumentNo(GenJnlBatch, "Document No.");
+ end else begin
+ "Posting Date" := WorkDate;
+ "Document Date" := WorkDate;
+ if GenJnlBatch."No. Series" <> '' then begin
+ Clear(NoSeriesMgt);
+ "Document No." := NoSeriesMgt.TryGetNextNo(GenJnlBatch."No. Series", "Posting Date");
+ end;
+ end;
+ [...]
+```
+
+If the Batch is empty, and if the **`Gen. Journal Batch`** has a **`No. Series`** set, the **`Document No.`** is set from the number series via the **`NoSeriesManagement`** codeunit's **`TryGetNextNo`** function. This takes two parameters:
+- Which **`No. Series`** to get the next number from
+- Which date to fetch for
+
+This function does *not* update the **`Last No. Used`** and **`Last Date Used`** fields on the number series. Those will be updated during the Posting process.
+
+
+If the Batch is not empty *and* the sum of the existing lines totals to zero (in balance), the General Journal assumes the user wants to start a new set of lines under a new **`Document No.`**. The table level procedure **`IncrementDocumentNo`** function is called:
+
+```AL
+procedure IncrementDocumentNo(GenJnlBatch: Record "Gen. Journal Batch"; var LastDocNumber: Code[20])
+var
+ NoSeriesLine: Record "No. Series Line";
+begin
+ if GenJnlBatch."No. Series" <> '' then begin
+ NoSeriesMgt.SetNoSeriesLineFilter(NoSeriesLine, GenJnlBatch."No. Series", "Posting Date");
+ if NoSeriesLine."Increment-by No." > 1 then
+ NoSeriesMgt.IncrementNoText(LastDocNumber, NoSeriesLine."Increment-by No.")
+ else
+ LastDocNumber := IncStr(LastDocNumber);
+ end else
+ LastDocNumber := IncStr(LastDocNumber);
+end;
+```
+
+If the batch's **`No. Series`** is set, it is checked if the **`Increment-By No.`** setting is anything besides `1`. If so, use the special **`IncrementNoText`** function.
+
+If neither of those cases is true, then the line's **`Document No.`** is updated with the language function **`IncStr`**.
+
+
+## Objects to Inspect
+
+Business Central objects in the Base App to review to find out more:
+
+| Object Type | Object ID | Object Name |
+|-------------|-----------|--------------------------|
+| Table | 308 | No. Series |
+| Table | 309 | No. Series Line |
+| Table | 310 | No. Series Relationship |
+| Page | 456 | No. Series |
+| Page | 457 | No. Series Lines |
+| Page | 458 | No. Series Relationships |
+| Page | 571 | No. Series List |
+| Codeunit | 396 | NoSeriesManagement |
+
+## When not to use
+
+Typically, this pattern is used for unique Data Entities. It is not recommended for use in parts of the system where entries are created permanently (such as an **`Entry No.`** for ledgers) or highly mutable / working line data (such as **`Line No.`** for journals or document lines).
+
+## List of references
+
+For usage of number series, there is more information available on:
+- [Microsoft Docs: Create Number Series](https://docs.microsoft.com/en-us/dynamics365/business-central/ui-create-number-series)
+- [Microsoft Learn: Set up number series and trail codes](https://docs.microsoft.com/en-us/learn/modules/number-series-trail-codes-dynamics-365-business-central/)
+
+For more programming details, there is more information on [Microsoft Docs: Number Sequences in Business Central](https://docs.microsoft.com/en-us/dynamics365/business-central/dev-itpro/developer/devenv-number-sequences).
\ No newline at end of file
diff --git a/content/docs/patterns/template-method-pattern/index.md b/content/docs/patterns/template-method-pattern/index.md
new file mode 100644
index 00000000..95570ecd
--- /dev/null
+++ b/content/docs/patterns/template-method-pattern/index.md
@@ -0,0 +1,153 @@
+---
+title: "Template Method Pattern"
+tags: ["AL", "Interface", "Readability"]
+categories: ["Pattern"]
+---
+
+_Created by Patrick Schiefer, Described by Patrick Schiefer_
+
+## Abstract
+The goal of this pattern is to simplify the solution of similar problems and make your code more readable.
+
+## Problem
+In nearly every app you sometimes have to solve similar problems for different cases. Mostly not the same developer will solve every case. This results in different solutions.
+
+## Description
+The pattern is used when you have problems which are independent but require the same logical flow. Examples which occur very often are: _Posting Documents_, _Printing Reports_ or _Exporting Data_.
+
+## Bad Code Example
+```al
+codeunit 50010 ExportSalesLines
+{
+ procedure ExportData(SalesHeader: Record "Sales Header", SalesLine : Record "Sales Line")
+ begin
+ if not SalesHeader.CheckData() then
+ exit;
+ repeat
+ case SalesHeader.ExportType of
+ Enum::ExportType::A:
+ GenerateLineTypeA(SalesLine);
+ Enum::ExportType::B:
+ GenerateLineTypeB(SalesLine);
+ end;
+ until SalesLine.Next() = 0;
+
+ case SalesHeader.ExportType of
+ Enum::ExportType::A:
+ WriteToFile();
+ Enum::ExportType::B:
+ SendToWebService();
+ end;
+ end;
+
+ //TODO Implementation of procedures used in example
+}
+```
+As you can see in this example the readability gets worse with every new case.
+
+## The Pattern
+To implement the Pattern you need at least 3 objects:
+- A template codeunit
+- An Interface which provides the needed procedures
+- A codeunit which Implements the interrface
+
+In my example I show how to implement a data export with templating.
+
+We start with the template
+```al
+codeunit 50000 ExportTemplate
+{
+ procedure ExportData(Export: Interface IDataExport)
+ begin
+ if not Export.CheckData() then
+ exit;
+ if Export.GetLinesToExport() then
+ repeat
+ Export.ExportLine();
+ until not Export.NextLine();
+ Export.Finish();
+ end;
+}
+```
+As you can see the template just calls procedures via an interface and just defines the flow of the export without really implementing it.
+
+As the second part we need an interface for the export functions
+```al
+interface IDataExport
+{
+ procedure CheckData(): Boolean
+ procedure GetLinesToExport(): Boolean
+ procedure ExportLine()
+ procedure NextLine(): Boolean
+ procedure Finish()
+}
+```
+
+And now we need an implementation. For my example I wrote a export Codeunit for Sales Headers
+```al
+codeunit 50001 SalesHeaderExport implements IDataExport
+{
+ procedure SetSalesHeader(DocType: Enum "Sales Document Type"; No: Code[10])
+ begin
+ SalesHeader.Get(DocType, No);
+ end;
+
+ procedure CheckData(): Boolean
+ begin
+ SalesHeader.TestField(Status, Enum::"Sales Document Status"::Released);
+ end;
+
+ procedure GetLinesToExport(): Boolean
+ begin
+ SalesLines.SetRange("Document Type", SalesHeader."Document Type");
+ SalesLines.SetRange("Document No.", SalesHeader."No.");
+ exit(SalesLines.FindSet());
+ end;
+
+ procedure ExportLine()
+ begin
+ //Generate Exportdata here
+ end;
+
+ procedure NextLine(Steps : integer): Boolean
+ begin
+ exit(SalesLines.Next(Steps) <> 0);
+ end;
+
+ procedure Finish()
+ begin
+ // Send or Save data here
+ end;
+
+ var
+ SalesHeader: Record "Sales Header";
+ SalesLines: Record "Sales Line";
+}
+```
+
+Now lets have a look how to use the pattern
+```al
+codeunit 50002 ExportOrders
+{
+ procedure ExportOrder(DocType: Enum "Sales Document Type"; No: Code[10])
+ var
+ Export: Codeunit ExportTemplate;
+ ExportImpl: Codeunit SalesHeaderExport;
+ ExportInt: Interface IDataExport;
+ begin
+ ExportImpl.SetSalesHeader(DocType, No);
+ ExportInt := exportImpl;
+ Export.ExportData(exportInt);
+ end;
+}
+```
+
+## Benefits
+Your code gains readability and it is very easy to add new cases for the template. You don't always have to think about the whole logic. You just have to implement the details.
+
+## When not to use
+The Pattern should not be used for problems which differ too much. For example, if you have two data exports in your app, one is exporting header and lines and the second one only export header. In this case I would suggest to not use the pattern or to make two templates out of it.
+
+## References
+[Detailed Explanation of the pattern](https://patrickschiefer.com/2022/04/08/template-method-pattern/)
+
diff --git a/content/docs/vibe-coding/README.md b/content/docs/vibe-coding/README.md
new file mode 100644
index 00000000..48ce0477
--- /dev/null
+++ b/content/docs/vibe-coding/README.md
@@ -0,0 +1,102 @@
+# Vibe Coding Rules - Contribution Guide
+
+This directory contains AI-optimized coding rules for AL development. Each rule set is organized into markdown files that can be easily consumed by AI coding assistants.
+
+## Directory Structure
+
+```
+vibe-coding/
+├── _index.md # Main landing page
+├── README.md # This file - contribution guide
+├── al-guidelines-rules.md # Complete rules file with references to all categories
+├── al-code-style.md # Code style, formatting, and documentation rules
+├── al-naming-conventions.md # File naming, object naming, and variable naming
+├── al-performance.md # Performance optimization and query guidelines
+├── al-error-handling.md # Error handling patterns and troubleshooting
+├── al-events.md # Event-driven development and extensibility
+├── al-testing.md # AL-Go workspace structure and testing guidelines
+```
+
+## How to Add New Rules
+
+### 1. Choose the Right Category
+Select the appropriate category for your rules, or create a new category if needed.
+
+### 2. Follow the Standard Format
+Each rule file should follow this structure:
+
+```markdown
+---
+title: "AL [Category Name] Rules"
+description: >
+ Brief description of the rule category for AL development
+globs: ["*.al", "*.json"] # File types this rule applies to
+alwaysApply: true|false # Whether to always apply these rules
+---
+
+# AL [Category Name] Rules
+
+Brief introduction to the category and its importance in AL development.
+
+## Rule 1: [Descriptive Title]
+
+### Intent
+What this rule aims to achieve, including implementation details and guidance for AI assistants.
+
+### Examples
+
+```al
+// Good example
+[code example]
+```
+
+You can also add a bad example to show what to avoid, but this is optional.
+```al
+// Bad example (avoid)
+[code example]
+```
+
+## Rule 2: [Next Rule]
+[Continue with same format...]
+```
+
+### 3. Update the Index
+After adding new rule files:
+- Update `al-guidelines-rules.md` to include `@your-new-file.md` reference
+- Update `_index.md` to include links to new categories
+- Ensure all cross-references are updated
+
+### 4. Test Your Rules
+Before submitting, test your rules with AI assistants to ensure they:
+- Are clearly understood by AI agents
+- Produce the expected code patterns
+- Don't conflict with existing rules
+
+## Contribution Workflow
+
+1. **Fork** the alguidelines repository
+2. **Create** your rule files in the appropriate category
+3. **Test** the rules with your preferred AI assistant
+4. **Submit** a pull request with:
+ - Clear description of the rules added
+ - Examples of how the rules improve code quality
+ - Any testing results with AI assistants (optional)
+
+## Best Practices for Rule Writing
+
+### Make Rules AI-Friendly
+- Use clear, unambiguous language specific to AL development
+- Provide specific AL code examples with proper syntax
+- Include both positive and negative examples, when applicable
+- Structure content consistently
+
+## Questions?
+
+If you have questions about contributing rules, please:
+- Open a discussion in the GitHub repository
+- Join the Business Central community Discord
+- Contact the initiative maintainers
+
+---
+
+*This README is part of the Vibe Coding for AL initiative - enhancing AL development through AI-optimized guidelines.*
\ No newline at end of file
diff --git a/content/docs/vibe-coding/_index.md b/content/docs/vibe-coding/_index.md
new file mode 100644
index 00000000..b002939d
--- /dev/null
+++ b/content/docs/vibe-coding/_index.md
@@ -0,0 +1,100 @@
+---
+title: "Vibe Coding for AL"
+tags: ["AL", "Vibe Coding"]
+categories: ["Vibe Coding"]
+description: >
+ AI-optimized coding rules and guidelines for AL development
+---
+
+_Created by the Business Central Community, Enhanced for AI-powered AL Development_
+
+# Vibe Coding for AL
+
+Welcome to the **Vibe Coding for AL** initiative! This section contains AI-optimized coding rules and guidelines specifically designed to enhance the AL developer experience in modern AI-powered IDEs like VS Code and Cursor.
+
+## What is Vibe Coding?
+
+Vibe Coding represents a new approach to coding guidelines that are specifically formatted and structured to work seamlessly with AI coding assistants. These rules are designed to:
+
+- **Enhance AI Understanding**: Provide clear, structured guidelines that AI agents can easily parse and apply
+- **Improve Code Quality**: Maintain high standards while leveraging AI assistance
+- **Standardize Practices**: Create consistent coding patterns across the AL development community
+- **Boost Productivity**: Help developers write better code faster with AI assistance
+
+## Key Features
+
+### 📋 **Structured Rule Format**
+All rules are provided in markdown format with clear structure that AI agents can easily understand and apply during development.
+
+### 🔄 **Community-Driven**
+Built and maintained by the AL community, including MVPs and the Microsoft product team.
+
+### 🛠️ **IDE Integration**
+Future AL extension support will allow generating local rules files directly in VS Code and Cursor.
+
+### 🤖 **AI-Ready**
+Designed as a foundation for AL-focused AI tools and Model Context Protocols (MCPs).
+
+## How to Use
+
+1. **Browse the Rules**: Explore the various rule categories below
+2. **Copy for Your Project**: Use these rules as templates for your own coding standards
+3. **Contribute**: Submit your own rule variations via pull requests
+4. **Stay Updated**: Watch for AL extension integration coming soon
+
+## Rule Categories
+
+The Vibe Coding rules are organized into logical categories to make them easy to find and implement:
+
+- **[Complete AL Guidelines Rules](al-guidelines-rules/)** - Comprehensive rules file with references to other rules
+- **[AL Code Style & Formatting](al-code-style/)** - Indentation, folder organization, and code documentation
+- **[AL Naming Conventions](al-naming-conventions/)** - File naming, object naming, and variable naming patterns
+- **[AL Performance Optimization](al-performance/)** - Query optimization, temporary tables, and performance analysis
+- **[AL Error Handling & Troubleshooting](al-error-handling/)** - Try/catch patterns, debugging, and telemetry integration
+- **[AL Event-Driven Development](al-events/)** - Event subscribers, integration events, and extensibility patterns
+- **[AL Testing & Project Structure](al-testing/)** - AL-Go workspace structure, test generation, and project organization
+
+## Getting Started
+
+To get started with Vibe Coding for AL:
+
+1. Review the rule categories that apply to your development needs
+2. Adapt the rules to your specific project requirements
+3. Configure your AI assistant to use these guidelines
+4. Share your experiences and contribute improvements back to the community
+
+## Future Roadmap
+
+### Phase 1: Foundation ✅
+- Host rules in AL Guidelines repository
+- Community contribution process
+- Initial rule sets from key contributors
+
+### Phase 2: Integration 🔄
+- AL extension support for local rules generation
+- Enhanced AI agent compatibility
+- MCP server integration
+
+### Phase 3: Expansion 🚀
+- Convert legacy C/AL patterns where applicable
+- Generate new AL-specific patterns
+- Establish as central trust source for AL AI agents
+
+## Contributing
+
+This initiative thrives on community contributions! Here's how you can help:
+
+- **Submit Rule Sets**: Share your proven coding rules via pull requests
+- **Improve Existing Rules**: Suggest enhancements to current guidelines
+- **Test & Validate**: Try the rules in your projects and provide feedback
+- **Share Examples**: Contribute real-world examples of rule applications
+
+## Community & Support
+
+- **GitHub Repository**: [Microsoft AL Guidelines](https://github.com/microsoft/alguidelines)
+- **Discussions**: Join conversations about Vibe Coding rules
+- **Issues**: Report problems or suggest new features
+
+---
+
+*The Vibe Coding for AL initiative is a collaborative effort between the Business Central community and Microsoft, aimed at revolutionizing how we write AL code in the age of AI.*
\ No newline at end of file
diff --git a/content/docs/vibe-coding/al-code-style.md b/content/docs/vibe-coding/al-code-style.md
new file mode 100644
index 00000000..a8f429ef
--- /dev/null
+++ b/content/docs/vibe-coding/al-code-style.md
@@ -0,0 +1,167 @@
+---
+title: "AL Code Style & Formatting Rules"
+description: >
+ AL Code structure, formatting, and folder organization guidelines for AL development
+globs: ["*.al"]
+alwaysApply: true
+---
+
+# AL Code Style & Formatting Rules
+
+These rules ensure consistent code structure and organization across AL projects, making code more maintainable and AI-assistant friendly.
+
+## Style guidelines for AL code
+ - Always use PascalCase for variable and function names.
+ - Use PascalCase for object names (e.g., tables, pages, reports).
+ - Maintain a consistent indentation style (2 spaces preferred).
+
+## Commonly used methods and patterns
+ - Temporary tables for performance optimization
+ - Use of events for extensibility
+
+## Rule 1: Consistent Indentation and Formatting
+
+### Intent
+Maintain consistent code formatting to improve readability and enable better AI understanding of code structure. Use indentation with two spaces consistently throughout your project and maintain consistent formatting within functions and procedures.
+
+### Examples
+
+```al
+// Good example
+procedure CalculateDiscount(Amount: Decimal; DiscountPct: Decimal): Decimal
+begin
+ if DiscountPct > 0 then
+ exit(Amount * DiscountPct / 100);
+
+ exit(0);
+end;
+```
+
+## Rule 2: Feature-Based Folder Organization
+
+### Intent
+Organize code by business features rather than object types to improve maintainability and logical grouping. Use feature-based organization with `src/feature/subfeature/` structure and place shared components in `Common` or `Shared` folders.
+
+### Examples
+
+```
+// Good example - Feature-based organization
+src/
+├── NoSeries/
+│ ├── NoSeries.Table.al
+│ ├── NoSeries.Page.al
+│ └── NoSeriesSetup.Codeunit.al
+├── Sales/
+│ ├── Invoice/
+│ │ ├── SalesInvoice.Page.al
+│ │ └── SalesInvoicePosting.Codeunit.al
+│ └── Order/
+│ └── SalesOrder.Page.al
+└── Common/
+ ├── Helpers/
+ │ └── DateHelper.Codeunit.al
+ └── Interfaces/
+ └── IPostable.Interface.al
+```
+
+```
+// Bad example (avoid object-type segregation)
+src/
+├── Tables/
+│ ├── NoSeries.Table.al
+│ └── SalesHeader.Table.al
+├── Pages/
+│ ├── NoSeries.Page.al
+│ └── SalesInvoice.Page.al
+└── Codeunits/
+ ├── NoSeriesSetup.Codeunit.al
+ └── SalesInvoicePosting.Codeunit.al
+```
+
+## Rule 3: Code Documentation and Comments
+
+### Intent
+Provide clear documentation for global functions using XML documentation comments. Code should be self-documenting through clear naming, but global functions in codeunits require proper documentation for API clarity.
+
+### Examples
+
+```al
+// Good example - XML documentation for global functions
+codeunit 50100 "Base64 Convert"
+{
+ ///
+ /// Converts the value of the input string to its equivalent string representation that is encoded with base-64 digits.
+ ///
+ /// The string to convert.
+ /// The string representation, in base-64, of the input string.
+ procedure ToBase64(String: Text): Text
+ begin
+ exit(Base64ConvertImpl.ToBase64(String));
+ end;
+
+ ///
+ /// Validates discount percentage against business rules.
+ ///
+ /// The discount percentage to validate.
+ procedure ValidateDiscountPercentage(DiscountPct: Decimal)
+ begin
+ if DiscountPct > 50 then
+ Error('Discount cannot exceed 50% due to company policy');
+
+ if DiscountPct < 0 then
+ Error('Discount percentage cannot be negative');
+ end;
+}
+```
+
+```al
+// Bad example (avoid inline comments for obvious operations)
+procedure ValidateDiscountPercentage(DiscountPct: Decimal)
+begin
+ // Check if discount is greater than 50
+ if DiscountPct > 50 then
+ Error('Discount cannot exceed 50%');
+
+ // Check if discount is less than 0
+ if DiscountPct < 0 then
+ Error('Discount percentage cannot be negative');
+end;
+```
+
+## Rule 4: Modular and Reusable Code Structure
+
+### Intent
+Keep code modular and reusable to enhance maintainability and reduce duplication. Write small, focused procedures that do one thing well and use interfaces and patterns where appropriate.
+
+### Examples
+
+```al
+// Good example - Modular approach
+procedure PostDocument(var DocumentHeader: Record "Sales Header")
+begin
+ ValidateDocument(DocumentHeader);
+ CalculateTotals(DocumentHeader);
+ CreateLedgerEntries(DocumentHeader);
+ UpdateStatus(DocumentHeader);
+end;
+
+local procedure ValidateDocument(var DocumentHeader: Record "Sales Header")
+begin
+ if DocumentHeader."No." = '' then
+ Error('Document number cannot be empty');
+end;
+
+local procedure CalculateTotals(var DocumentHeader: Record "Sales Header")
+begin
+ DocumentHeader.CalcFields(Amount);
+end;
+```
+
+```al
+// Bad example (avoid monolithic procedures)
+procedure PostDocument(var DocumentHeader: Record "Sales Header")
+begin
+ // All validation, calculation, and posting logic in one procedure
+ // ... 200+ lines of mixed concerns
+end;
+```
\ No newline at end of file
diff --git a/content/docs/vibe-coding/al-error-handling.md b/content/docs/vibe-coding/al-error-handling.md
new file mode 100644
index 00000000..dcc84411
--- /dev/null
+++ b/content/docs/vibe-coding/al-error-handling.md
@@ -0,0 +1,167 @@
+---
+title: "AL Error Handling & Troubleshooting Rules"
+description: >
+ AL Error handling patterns, debugging techniques, and troubleshooting guidelines for AL development
+globs: ["*.al"]
+alwaysApply: false
+---
+
+# AL Error Handling & Troubleshooting Rules
+
+Robust error handling and effective troubleshooting practices are essential for maintaining reliable Business Central applications.
+
+## Rule 1: Use TryFunctions for Error Handling
+
+### Intent
+Implement proper error handling using TryFunctions to manage exceptions gracefully and provide meaningful user feedback. Use TryFunctions for error handling in scenarios where rollback is required, implement proper exception handling for external service calls, provide meaningful error messages to users, and log errors appropriately for debugging purposes. When generating code that might fail (external calls, data operations, calculations), implement appropriate TryFunction error handling and provide clear error messages.
+
+### Examples
+
+```al
+// Good example - TryFunction with proper error handling and error labels
+procedure ProcessPayment(Amount: Decimal): Boolean
+var
+ PaymentService: Codeunit "Payment Service";
+ ErrorText: Text;
+ PaymentProcessingFailedLbl: Label 'Payment processing failed: %1', Comment = '%1 = Error message';
+ PaymentProcessingFailedTelemetryLbl: Label 'Payment processing failed', Locked = true;
+begin
+ if not TryProcessPaymentInternal(Amount) then begin
+ ErrorText := GetLastErrorText();
+ LogError(PaymentProcessingFailedTelemetryLbl, ErrorText);
+ Message(PaymentProcessingFailedLbl, ErrorText);
+ exit(false);
+ end;
+
+ exit(true);
+end;
+
+[TryFunction]
+local procedure TryProcessPaymentInternal(Amount: Decimal)
+var
+ PaymentService: Codeunit "Payment Service";
+begin
+ PaymentService.ProcessPayment(Amount);
+end;
+```
+
+```al
+// Bad example (avoid hardcoded error messages and unhandled errors)
+procedure ProcessPayment(Amount: Decimal)
+var
+ PaymentService: Codeunit "Payment Service";
+begin
+ // No error handling - will cause unhandled exceptions
+ // Also avoid hardcoded messages like this:
+ // Message('Payment could not be processed');
+ PaymentService.ProcessPayment(Amount);
+end;
+```
+
+## Rule 2: Use Error Labels for All Messages
+
+### Intent
+All error messages, warnings, and user messages must use label variables instead of hardcoded text. This ensures proper localization support and maintainability. Define labels with appropriate comments for translators and use Locked = true for technical messages that should not be translated.
+
+### Examples
+
+```al
+// Good example - Using error labels
+procedure ValidateBusinessLogic(SalesHeader: Record "Sales Header")
+var
+ Customer: Record Customer;
+ CustomerNotFoundErr: Label 'Customer %1 does not exist for sales document %2.', Comment = '%1 = Customer No., %2 = Sales Header No.';
+ CustomerBlockedErr: Label 'Customer %1 is blocked (%2). Cannot process sales document %3.', Comment = '%1 = Customer No., %2 = Blocked reason, %3 = Sales Header No.';
+ EmptyHeaderNoErr: Label 'Sales header number cannot be empty.';
+begin
+ if SalesHeader."No." = '' then
+ Error(EmptyHeaderNoErr);
+
+ if not Customer.Get(SalesHeader."Sell-to Customer No.") then
+ Error(CustomerNotFoundErr, SalesHeader."Sell-to Customer No.", SalesHeader."No.");
+
+ if Customer.Blocked <> Customer.Blocked::" " then
+ Error(CustomerBlockedErr, Customer."No.", Customer.Blocked, SalesHeader."No.");
+end;
+```
+
+```al
+// Bad example (avoid hardcoded error messages)
+procedure ValidateBusinessLogic(SalesHeader: Record "Sales Header")
+var
+ Customer: Record Customer;
+begin
+ if not Customer.Get(SalesHeader."Sell-to Customer No.") then
+ Error('Customer not found'); // Hardcoded - avoid this
+
+ if Customer.Blocked <> Customer.Blocked::" " then
+ Error('Customer blocked'); // Hardcoded - avoid this
+end;
+```
+
+## Rule 3: Code Compilation and Correctness Priority
+
+### Intent
+Generated AL code should prioritize correctness over immediate compilation. Code can fail to compile if AI suggests base functions or events that don't exist, or if variables in event subscriptions are incorrect. When this happens, leave space for manual fixes rather than changing the intended behavior. If you're confident the logic should work as suggested but there are naming or parameter issues, leave it for user correction rather than altering the business logic.
+
+### Examples
+
+```al
+// Good example - Correct logic even if function names need verification
+procedure HandleCustomerModification(var Customer: Record Customer)
+var
+ CustomerValidation: Codeunit "Customer Validation"; // May need verification
+begin
+ // Correct business logic - even if codeunit name needs adjustment
+ if not CustomerValidation.ValidateCustomerData(Customer) then
+ Error(ValidationFailedErr);
+
+ Customer.Modify(true);
+end;
+```
+
+```al
+// Good example - Event subscription with correct intent
+[EventSubscriber(ObjectType::Table, Database::Customer, OnAfterModifyEvent, '', false, false)]
+local procedure OnAfterCustomerModify(var Rec: Record Customer; var xRec: Record Customer; RunTrigger: Boolean)
+var
+ CustomerChangeLog: Codeunit "Customer Change Log"; // Function may need verification
+begin
+ // Correct logic - even if codeunit or method names need adjustment
+ CustomerChangeLog.LogCustomerChange(Rec, xRec);
+end;
+```
+
+## Rule 4: Custom Telemetry Implementation
+
+### Intent
+Add custom telemetry for tracking business-critical operations, but only when explicitly requested by the user. Use Session.LogMessage for custom telemetry with appropriate verbosity levels and data classification. Include relevant custom dimensions for context and use proper telemetry scope for extension publishers.
+
+### Examples
+
+```al
+// Good example - Custom telemetry (only when user explicitly requests it)
+procedure PostSalesDocument(var SalesHeader: Record "Sales Header")
+var
+ TelemetryCustomDimensions: Dictionary of [Text, Text];
+ SalesDocPostedMsg: Label 'Sales document posted successfully', Locked = true;
+ SalesDocPostFailedMsg: Label 'Sales document posting failed', Locked = true;
+begin
+ // Add context for telemetry
+ TelemetryCustomDimensions.Add('DocumentType', Format(SalesHeader."Document Type"));
+ TelemetryCustomDimensions.Add('CustomerNo', SalesHeader."Sell-to Customer No.");
+
+ if TryPostSalesDocument(SalesHeader) then begin
+ // Log successful operation
+ Session.LogMessage('SAL001', SalesDocPostedMsg,
+ Verbosity::Normal, DataClassification::SystemMetadata,
+ TelemetryScope::ExtensionPublisher, TelemetryCustomDimensions);
+ end else begin
+ // Log failed operation with error details
+ TelemetryCustomDimensions.Add('ErrorText', GetLastErrorText());
+ Session.LogMessage('SAL002', SalesDocPostFailedMsg,
+ Verbosity::Error, DataClassification::SystemMetadata,
+ TelemetryScope::ExtensionPublisher, TelemetryCustomDimensions);
+ end;
+end;
+```
\ No newline at end of file
diff --git a/content/docs/vibe-coding/al-events.md b/content/docs/vibe-coding/al-events.md
new file mode 100644
index 00000000..8eab527c
--- /dev/null
+++ b/content/docs/vibe-coding/al-events.md
@@ -0,0 +1,120 @@
+---
+title: "Event-Driven Development Rules"
+description: >
+ Guidelines for implementing event-driven patterns and extensibility in AL development
+globs: ["*.al"]
+alwaysApply: false
+---
+
+# Event-Driven Development Rules
+
+Event-driven development is fundamental to creating extensible and maintainable Business Central applications that follow the platform's architecture principles.
+
+## Rule 1: Use Events for Extensibility
+
+### Intent
+Implement proper event patterns to enable extensibility without modifying base application code. Subscribe to relevant Business Central events (OnBeforeInsert, OnAfterModify, etc.), create integration events in your code for future extensibility, use extension objects or events for all changes to standard application objects. When implementing business logic, prioritize event subscribers and suggest appropriate event subscription patterns and integration event creation.
+
+### Examples
+
+```al
+// Good example - Event subscriber implementation with Handler suffix
+codeunit 50100 "Sales Document Events Handler"
+{
+ [EventSubscriber(ObjectType::Table, Database::"Sales Header", OnBeforeInsert, '', false, false)]
+ local procedure OnBeforeInsertSalesHeader(var SalesHeader: Record "Sales Header"; RunTrigger: Boolean)
+ begin
+ // Custom validation logic
+ ValidateCustomFields(SalesHeader);
+ end;
+}
+```
+
+## Rule 2: Add Integration Events for Extensibility
+
+### Intent
+Use integration events to provide better extensibility points and clearer API contracts for other developers. Create integration events at logical business process points, document integration event parameters and expected behavior, provide meaningful event names that describe the business context, and implement handled patterns to allow subscribers to control execution flow. When designing extensible code, suggest integration events at appropriate business logic points with clear documentation and meaningful names.
+
+### Examples
+
+```al
+// Good example - Integration events with handled pattern
+codeunit 50101 "Customer Management"
+{
+ procedure CreateCustomer(var Customer: Record Customer): Boolean
+ var
+ IsHandled: Boolean;
+ begin
+ OnBeforeCreateCustomer(Customer, IsHandled);
+ if IsHandled then
+ exit(true);
+
+ if not Customer.Insert(true) then
+ exit(false);
+
+ OnAfterCreateCustomer(Customer);
+ exit(true);
+ end;
+
+ [IntegrationEvent(false, false)]
+ procedure OnBeforeCreateCustomer(var Customer: Record Customer; var IsHandled: Boolean)
+ begin
+ // Allow extensions to modify customer data before creation
+ // Set IsHandled to true to skip default processing
+ end;
+
+ [IntegrationEvent(false, false)]
+ procedure OnAfterCreateCustomer(var Customer: Record Customer)
+ begin
+ // Allow extensions to perform additional actions after customer creation
+ end;
+}
+```
+
+```al
+// Extension subscribing to integration events with Handler suffix
+codeunit 50102 "Customer Validation Handler"
+{
+ [EventSubscriber(ObjectType::Codeunit, Codeunit::"Customer Management", OnBeforeCreateCustomer, '', false, false)]
+ local procedure ValidateCustomerOnBeforeCreate(var Customer: Record Customer; var IsHandled: Boolean)
+ begin
+ // Custom validation logic
+ ValidateCustomerCreditLimit(Customer);
+
+ // Optionally handle the event to skip default processing
+ if ShouldSkipDefaultProcessing(Customer) then
+ IsHandled := true;
+ end;
+}
+```
+
+## Rule 3: Event Parameter Best Practices
+
+### Intent
+Design event parameters that provide sufficient context while maintaining performance and usability. Pass record variables by reference when possible, include relevant context parameters, use meaningful parameter names, consider performance implications of parameter passing, and implement handled patterns where appropriate. When creating events, ensure parameters provide sufficient context for subscribers while maintaining good performance and use descriptive parameter names that clearly indicate their purpose.
+
+### Examples
+
+```al
+// Good example - Well-designed event parameters with handled pattern
+codeunit 50103 "Document Posting Events"
+{
+ [IntegrationEvent(false, false)]
+ procedure OnBeforePostDocument(var DocumentHeader: Record "Sales Header"; var DocumentLines: Record "Sales Line"; PostingDate: Date; var IsHandled: Boolean)
+ begin
+ // Comprehensive context for document posting
+ // - Document header and lines for full context
+ // - Posting date for temporal context
+ // - IsHandled flag for control flow
+ end;
+
+ [IntegrationEvent(false, false)]
+ procedure OnAfterPostDocument(DocumentHeader: Record "Sales Header"; PostedDocumentNo: Code[20]; PostingResult: Boolean)
+ begin
+ // Results context after posting
+ // - Original document for reference
+ // - Posted document number for tracking
+ // - Success/failure indication
+ end;
+}
+```
\ No newline at end of file
diff --git a/content/docs/vibe-coding/al-guidelines-rules.md b/content/docs/vibe-coding/al-guidelines-rules.md
new file mode 100644
index 00000000..f0caf625
--- /dev/null
+++ b/content/docs/vibe-coding/al-guidelines-rules.md
@@ -0,0 +1,58 @@
+---
+description: AL Guidelines - Comprehensive AI-optimized coding rules for Microsoft Dynamics 365 Business Central development
+globs: ["*.al", "*.json", "app.json", "launch.json"]
+alwaysApply: true
+---
+
+# AL Guidelines - Vibe Coding Rules
+
+You are an AI assistant designed to aid in AL development, particularly for Microsoft Dynamics 365 Business Central. Your role is to assist developers in writing efficient, maintainable code following established patterns and best practices.
+
+## Core Principles
+
+- Follow event-driven programming model; never modify standard application objects
+- Use clear, meaningful names and maintain consistent code structure
+- Prioritize performance optimization and proper error handling
+- Focus on main application implementation by default
+- Only generate test code when explicitly requested
+- Maintain proper AL-Go workspace structure separation
+
+## Rule Categories
+
+The following rule sets provide comprehensive guidance for AL development:
+
+@al-code-style.md
+
+@al-naming-conventions.md
+
+@al-performance.md
+
+@al-error-handling.md
+
+@al-events.md
+
+@al-testing.md
+
+## Key Guidelines Summary
+
+- **File Naming**: Use `..al` pattern consistently
+- **Code Style**: Use two space indentation and PascalCase for variables, PascalCase for objects
+- **Folder Structure**: Organize by feature (`src/feature/subfeature/`) not by object type
+- **Performance**: Filter data early, use temporary tables, avoid unnecessary loops
+- **Events**: Prefer integration events over direct modifications for extensibility
+- **Testing**: Separate App and Test projects, generate tests only when requested
+- **Error Handling**: Use TryFunctions, provide meaningful error messages, implement telemetry
+
+## AL-Go Workspace Structure
+
+When working in AL-Go environments:
+- **App project**: Contains all application logic (tables, pages, codeunits, reports)
+- **Test project**: Contains all test code and references App project as dependency
+- **Never mix**: Application code stays in App, test code stays in Test project
+
+## AI Response Behavior
+
+- Provide concise, actionable advice with specific AL method references
+- Always explain the reasoning behind recommendations
+- Reference Business Central architecture patterns and established best practices
+- Focus on practical implementation guidance that can be immediately applied
\ No newline at end of file
diff --git a/content/docs/vibe-coding/al-naming-conventions.md b/content/docs/vibe-coding/al-naming-conventions.md
new file mode 100644
index 00000000..f5bed480
--- /dev/null
+++ b/content/docs/vibe-coding/al-naming-conventions.md
@@ -0,0 +1,133 @@
+---
+title: "Naming Conventions Rules"
+description: >
+ Comprehensive naming conventions for AL files, objects, variables, and functions
+globs: ["*.al"]
+alwaysApply: true
+---
+
+# Naming Conventions Rules
+
+Consistent naming conventions improve code readability, maintainability, and help AI assistants understand code structure and intent.
+
+## Rule 1: Object Naming Conventions
+
+### Intent
+Use consistent naming patterns for all AL objects to improve discoverability and maintain professional standards. Use PascalCase for object names (tables, pages, reports, codeunits) and meaningful, descriptive names that clearly indicate the object's purpose. Object names must not exceed 30 characters total, with a maximum of 26 characters for the name itself to reserve space for prefixes/affixes (3 characters + 1 space).
+
+### Examples
+
+```al
+// Good examples (within 26 character limit)
+table 50100 "Customer Ledger Entry" // 20 chars
+page 50101 "Sales Invoice" // 13 chars
+codeunit 50102 "Sales Invoice Posting" // 21 chars
+report 50103 "Customer Statement" // 18 chars
+```
+
+```al
+// Bad examples (avoid abbreviations, unclear names, or length violations)
+table 50100 "CustLE" // Too abbreviated
+page 50101 "SalesInv" // Too abbreviated
+table 50104 "Very Long Customer Ledger Entry" // 32 chars - exceeds limit
+codeunit 50102 "SIPoster" // Unclear abbreviation
+```
+
+## Rule 2: File Naming Conventions
+
+### Intent
+Establish consistent file naming patterns that clearly identify object types and facilitate organized development. Use pattern `..al` and maintain consistency across all file names. Ensure file names are descriptive and match the AL object name within the files.
+
+### Examples
+
+```al
+// Good examples
+NoSeries.Page.al
+NoSeries.Table.al
+NoSeriesErrorsImpl.Codeunit.al
+NoSeriesSetup.Codeunit.al
+CustomerCard.Page.al
+SalesHeader.Table.al
+PostSalesInvoice.Codeunit.al
+ItemLedgerEntry.Report.al
+InventorySetup.PageExt.al
+SalesHeader.TableExt.al
+
+// For implementations and interfaces
+INoSeries.Interface.al
+NoSeriesImpl.Codeunit.al
+
+// For test files
+NoSeriesTests.Codeunit.al
+SalesPostingTests.Codeunit.al
+```
+
+## Rule 3: Variable and Function Naming
+
+### Intent
+Use consistent naming conventions for variables and functions to improve code readability. Use PascalCase for variable and function names, descriptive names that clearly indicate purpose, and avoid abbreviations unless they are well-known business terms. Use consistent parameter naming in procedures.
+
+### Examples
+
+```al
+// Good examples - Variables
+var
+ CustomerLedgerEntry: Record "Cust. Ledger Entry";
+ TotalAmount: Decimal;
+ DiscountPercentage: Decimal;
+ IsValidTransaction: Boolean;
+```
+
+```al
+// Good examples - Functions
+procedure CalculateCustomerBalance(CustomerNo: Code[20]): Decimal
+procedure ValidateSalesDocument(var SalesHeader: Record "Sales Header")
+procedure UpdateInventoryQuantity(ItemNo: Code[20]; Quantity: Decimal)
+```
+
+## Rule 4: Parameter Naming in Event Subscribers
+
+### Intent
+Use meaningful parameter names in event subscribers to improve code clarity and maintainability. Use descriptive parameter names that clearly indicate their purpose, follow Business Central conventions for common parameter types, and maintain consistency across similar event subscribers. Avoid unclear generic names like "Rec" - use specific descriptive names.
+
+### Examples
+
+```al
+// Good example - Descriptive parameter names
+[EventSubscriber(ObjectType::Table, Database::"Sales Header", OnBeforeInsert, '', false, false)]
+local procedure AddDefaultValuesOnBeforeInsertSalesHeader(var SalesHeader: Record "Sales Header"; RunTrigger: Boolean)
+begin
+ // Event handling logic
+end;
+
+[EventSubscriber(ObjectType::Table, Database::Customer, OnBeforeModify, '', false, false)]
+local procedure CheckBalanceOnBeforeModifyCustomer(var Customer: Record Customer; var xCustomer: Record Customer)
+begin
+ // Event handling logic
+end;
+```
+
+## Rule 5: Interface and Implementation Naming
+
+### Intent
+Clearly distinguish between interfaces and their implementations using consistent naming patterns. Prefix interfaces with "I" (e.g., `INoSeries`), use "Impl" suffix for implementation codeunits, and keep interface and implementation names closely related. Ensure names stay within the 26-character limit.
+
+### Examples
+
+```al
+// Good examples (within character limits)
+// Interface file: ICustomerService.Interface.al
+interface ICustomerService
+{
+ procedure GetCustomerBalance(CustomerNo: Code[20]): Decimal;
+}
+
+// Implementation file: CustomerServiceImpl.Codeunit.al
+codeunit 50100 "Customer Service Impl" implements ICustomerService
+{
+ procedure GetCustomerBalance(CustomerNo: Code[20]): Decimal
+ begin
+ // Implementation logic
+ end;
+}
+```
\ No newline at end of file
diff --git a/content/docs/vibe-coding/al-performance.md b/content/docs/vibe-coding/al-performance.md
new file mode 100644
index 00000000..9e4aa7aa
--- /dev/null
+++ b/content/docs/vibe-coding/al-performance.md
@@ -0,0 +1,225 @@
+---
+title: "AL Performance Optimization Rules"
+description: >
+ Performance optimization guidelines and best practices for AL development
+globs: ["*.al"]
+alwaysApply: true
+---
+
+# AL Performance Optimization Rules
+
+These rules focus on writing performant AL code that scales well and provides optimal user experience in Business Central environments.
+
+## AL Performance Guidelines Summary
+- Always analyze performance impact when adding new features
+- Optimize queries by filtering data as early as possible
+- Avoid unnecessary loops; use set-based operations when possible
+- Use SetLoadFields to minimize data retrieval
+- Use temporary tables, dictionaries, or lists for temporary data storage
+
+## Rule 1: Early Data Filtering and Query Optimization
+
+### Intent
+Optimize queries by filtering data as early as possible to reduce data transfer and processing overhead. Apply filters before processing records, use appropriate table keys and sorting, minimize the amount of data retrieved from the database, and use SetRange and SetFilter methods effectively.
+
+### Examples
+
+```al
+// Good example - Early filtering
+procedure GetNumberOfCustomersByCity(CityFilter: Text): Integer
+var
+ Customer: Record Customer;
+begin
+ Customer.SetRange(City, CityFilter);
+ Customer.SetRange(Blocked, Customer.Blocked::" ");
+ if Customer.FindSet() then
+ repeat
+ // Process only filtered customers
+ until Customer.Next() = 0;
+
+ exit(Customer.Count);
+end;
+```
+
+```al
+// Bad example (avoid processing all records)
+procedure GetNumberOfCustomersByCity(CityFilter: Text): Integer
+var
+ Customer: Record Customer;
+ Count: Integer;
+begin
+ if Customer.FindSet() then
+ repeat
+ // Processing all customers then filtering
+ if (Customer.City = CityFilter) and (Customer.Blocked = Customer.Blocked::" ") then
+ Count += 1;
+ until Customer.Next() = 0;
+
+ exit(Count);
+end;
+```
+
+## Rule 2: Use SetLoadFields for Optimal Data Retrieval
+
+### Intent
+Use SetLoadFields to minimize data retrieval from the database by loading only the fields you need. Place SetLoadFields before the Get or Find operation, and include only the fields that will be used in your code.
+
+### Examples
+
+```al
+// Good example - SetLoadFields with filtering
+Item.SetRange("Third Party Item Exists", false);
+Item.SetLoadFields("Item Category Code");
+Item.FindFirst();
+```
+
+```al
+// Bad example (avoid SetLoadFields after filtering)
+Item.SetLoadFields("Item Category Code");
+Item.SetRange("Third Party Item Exists", false);
+Item.FindFirst();
+```
+
+## Rule 3: Use Temporary Tables, Dictionaries, and Lists for Performance
+
+### Intent
+Leverage temporary tables, dictionaries, and lists to improve performance in read-heavy scenarios and complex data processing. Use temporary tables for structured record data, dictionaries for key-value pairs, and lists for simple collections that are only temporarily needed.
+
+### Examples
+
+```al
+// Good example - Using temporary tables for structured data
+procedure ProcessSalesData(var TempSalesLine: Record "Sales Line" temporary)
+var
+ SalesLine: Record "Sales Line";
+begin
+ // Load data into temporary table once
+ if SalesLine.FindSet() then
+ repeat
+ TempSalesLine := SalesLine;
+ TempSalesLine.Insert();
+ until SalesLine.Next() = 0;
+
+ // Process temporary data multiple times without database hits
+ ProcessDiscounts(TempSalesLine);
+ CalculateTotals(TempSalesLine);
+ ValidateInventory(TempSalesLine);
+end;
+```
+
+```al
+// Good example - Using dictionaries for key-value temporary data
+procedure CacheCustomerData()
+var
+ Customer: Record Customer;
+ CustomerCache: Dictionary of [Code[20], Text];
+begin
+ if Customer.FindSet() then
+ repeat
+ CustomerCache.Add(Customer."No.", Customer.Name);
+ until Customer.Next() = 0;
+
+ // Use cached data for lookups
+ ProcessOrdersWithCache(CustomerCache);
+end;
+```
+
+```al
+// Good example - Using lists for simple collections
+procedure GetBlockedCustomers(): List of [Code[20]]
+var
+ Customer: Record Customer;
+ BlockedCustomers: List of [Code[20]];
+begin
+ Customer.SetRange(Blocked, Customer.Blocked::All);
+ if Customer.FindSet() then
+ repeat
+ BlockedCustomers.Add(Customer."No.");
+ until Customer.Next() = 0;
+
+ exit(BlockedCustomers);
+end;
+```
+
+## Rule 4: Avoid Unnecessary Loops - Use Set-Based Operations
+
+### Intent
+Minimize looping operations and favor set-based approaches when possible to improve performance. Use built-in aggregation methods (CalcSums, CalcFields), leverage SQL-based operations through AL, avoid nested loops when possible, and use batch operations for multiple record updates.
+
+### Examples
+
+```al
+// Good example - Set-based operation
+procedure GetTotalSalesAmount(CustomerNo: Code[20]): Decimal
+var
+ CustLedgerEntry: Record "Cust. Ledger Entry";
+begin
+ CustLedgerEntry.SetRange("Customer No.", CustomerNo);
+ CustLedgerEntry.CalcSums(Amount);
+ exit(CustLedgerEntry.Amount);
+end;
+```
+
+```al
+// Bad example (avoid manual loops for aggregation)
+procedure GetTotalSalesAmount(CustomerNo: Code[20]): Decimal
+var
+ CustLedgerEntry: Record "Cust. Ledger Entry";
+ TotalAmount: Decimal;
+begin
+ CustLedgerEntry.SetRange("Customer No.", CustomerNo);
+ if CustLedgerEntry.FindSet() then
+ repeat
+ TotalAmount += CustLedgerEntry.Amount;
+ until CustLedgerEntry.Next() = 0;
+
+ exit(TotalAmount);
+end;
+```
+
+## Rule 5: Performance Impact Analysis
+
+### Intent
+Always analyze and consider performance impact when adding new features or modifying existing code. While the AL compiler does not have direct access to performance profilers, you should implement performance-optimal code patterns from the start and consider scalability implications of code changes.
+
+### Examples
+
+```al
+// Good example - Performance-conscious implementation
+procedure UpdatePricesForItems(var Item: Record Item)
+var
+ ItemCount: Integer;
+begin
+ // Check data volume before processing
+ ItemCount := Item.Count();
+
+ if ItemCount > 1000 then begin
+ // Use batch processing for large datasets
+ UpdatePricesInBatches(Item);
+ end else begin
+ // Direct processing for smaller datasets
+ UpdatePricesDirectly(Item);
+ end;
+end;
+```
+
+```al
+// Good example - Batched modifications to minimize database writes
+procedure UpdateCustomerStatistics(CustomerNo: Code[20])
+var
+ Customer: Record Customer;
+ TotalBalance: Decimal;
+ LastPaymentDate: Date;
+begin
+ // Calculate all values first
+ CalculateCustomerTotals(CustomerNo, TotalBalance, LastPaymentDate);
+
+ // Single database write with all changes
+ Customer.SetLoadFields("Balance (LCY)", "Last Payment Date");
+ if Customer.Get(CustomerNo) then begin
+ Customer."Balance (LCY)" := TotalBalance;
+ Customer."Last Payment Date" := LastPaymentDate;
+ Customer.Modify(true);
+ end;
+end;
+```
\ No newline at end of file
diff --git a/content/docs/vibe-coding/al-testing.md b/content/docs/vibe-coding/al-testing.md
new file mode 100644
index 00000000..2c09aff0
--- /dev/null
+++ b/content/docs/vibe-coding/al-testing.md
@@ -0,0 +1,194 @@
+---
+title: "AL Testing & Project Structure Rules"
+description: >
+ AL-Go workspace structure, test generation guidelines, and project organization rules
+globs: ["*.al", "app.json"]
+alwaysApply: false
+---
+
+# AL Testing & Project Structure Rules
+
+These rules ensure proper project organization, test implementation, and workspace structure in AL-Go based development environments.
+
+## Rule 1: AL-Go Workspace Structure Guidelines
+
+### Intent
+Establish clear separation between application code and test code in AL-Go workspace environments.
+
+- App project contains Tables, Pages, Codeunits, Reports, APIs, Enums, etc.
+- Test project contains Test Codeunits, Test Pages, Mock objects, Test data.
+- Each project has its own app.json with appropriate dependencies.
+- Test project references the App project as a dependency.
+- Use App project ONLY for main application implementation, use Test project ONLY for test implementation, never include test files in the main App folder, and never include application logic in the Test folder.
+- When working in AL-Go workspace, always place files in the correct project based on their purpose.
+
+### Examples
+
+```
+// Good example - Proper AL-Go workspace structure
+Repository/
+├── .AL-Go/
+├── .github/
+├── App/
+│ ├── src/
+│ │ ├── Setup/
+│ │ ├── Feature1/
+│ │ ├── Feature2/
+│ │ ├── APIs/
+│ ├── app.json
+│ └── launch.json
+├── Test/
+│ ├── src/
+│ │ ├── SetupTests/
+│ │ ├── Feature1Tests/
+│ │ ├── Feature2Tests/
+│ │ ├── IntegrationTests/
+│ ├── app.json
+│ └── launch.json
+└── al.code-workspace
+```
+
+## Rule 2: Test Generation Guidelines
+
+### Intent
+Control when and how test code is generated to maintain focus on main application implementation.
+
+- DO NOT automatically generate test code unless explicitly requested
+- Focus on main application implementation by default
+- When user asks for implementation create only the main application objects
+- Only generate test files when user specifically requests "Create tests for...", "Generate unit tests...", "Add test coverage...", or "Write tests..."
+- If tests are requested, place them in the Test project following the folder structure where test files should mirror the App project structure but in the Test project
+- Unless the user explicitly requests tests, focus only on main application implementation
+
+## Rule 3: Project Dependencies Configuration
+
+### Intent
+Establish correct dependency relationships between App and Test projects.
+
+- App project app.json should NOT reference Test project
+- Test project app.json MUST reference App project as dependency
+- Test project should include testing frameworks (e.g., "Any", "Library Assert"), and each project maintains its own dependencies.
+- When configuring project dependencies, ensure Test project references App project but never the reverse and include appropriate testing frameworks in Test project.
+
+### Examples
+
+```json
+// Good example - Test project app.json
+{
+ "dependencies": [
+ {
+ "id": "your-app-id",
+ "name": "Your App Name",
+ "publisher": "Your Publisher",
+ "version": "1.0.0.0"
+ },
+ {
+ "id": "dd0be2ea-f733-4d65-bb34-a28f4624fb14",
+ "name": "Library Assert",
+ "publisher": "Microsoft",
+ "version": "20.0.0.0"
+ }
+ ]
+}
+```
+
+## Rule 4: Unit Testing Best Practices
+
+### Intent
+Write comprehensive unit tests that ensure reliability of business logic.
+
+- Write unit tests for all business logic
+- Follow given/when/then structure for test naming
+- Use Assert statements for validating critical conditions
+- Create test data factories for consistent test setup
+- Always try to use standard library codeunits to create data and post documents
+- When creating tests, use descriptive names that follow given/when/then pattern and include comprehensive assertions to validate expected behavior.
+
+### Examples
+
+```al
+// Good example - Well-structured unit test with standard library codeunits
+codeunit 50200 "Customer Management Tests"
+{
+ Subtype = Test;
+
+ var
+ Assert: Codeunit Assert;
+ LibrarySales: Codeunit "Library - Sales";
+ LibraryInventory: Codeunit "Library - Inventory";
+ LibraryRandom: Codeunit "Library - Random";
+ LibraryERM: Codeunit "Library - ERM";
+
+ [Test]
+ procedure GivenValidCustomer_WhenCreatingCustomer_ThenCustomerIsCreated()
+ var
+ Customer: Record Customer;
+ CustomerManagement: Codeunit "Customer Management";
+ CustomerNo: Code[20];
+ begin
+ // Given - Valid customer data using library
+ LibrarySales.CreateCustomer(Customer);
+ Customer."Credit Limit (LCY)" := LibraryRandom.RandDec(10000, 2);
+
+ // When - Creating customer
+ CustomerNo := CustomerManagement.CreateCustomer(Customer);
+
+ // Then - Customer is created successfully
+ Assert.IsTrue(Customer.Get(CustomerNo), 'Customer should be created');
+ Assert.AreEqual(Customer.Name, Customer.Name, 'Customer name should match');
+ end;
+
+ [Test]
+ procedure GivenSalesOrder_WhenPostingOrder_ThenInvoiceIsCreated()
+ var
+ SalesHeader: Record "Sales Header";
+ SalesLine: Record "Sales Line";
+ Item: Record Item;
+ PostedInvoiceNo: Code[20];
+ begin
+ // Given - Sales order with library-created data
+ LibraryInventory.CreateItem(Item);
+ LibrarySales.CreateSalesHeader(SalesHeader, SalesHeader."Document Type"::Order, '');
+ LibrarySales.CreateSalesLine(SalesLine, SalesHeader, SalesLine.Type::Item, Item."No.", LibraryRandom.RandInt(10));
+
+ // When - Posting sales order
+ PostedInvoiceNo := LibrarySales.PostSalesDocument(SalesHeader, true, true);
+
+ // Then - Posted invoice exists
+ Assert.AreNotEqual('', PostedInvoiceNo, 'Posted invoice should be created');
+ end;
+}
+```
+
+## Rule 5: Feature-Based Test Organization
+
+### Intent
+Organize test files to mirror the application structure while maintaining clear separation.
+
+- Test files should mirror App project structure in Test project
+- Use same feature-based organization for tests
+- Place shared test utilities in Common folder
+- Maintain consistent naming patterns
+
+### Examples
+
+```
+// Good example - Mirrored test structure
+App/src/
+├── NoSeries/
+│ ├── NoSeries.Table.al
+│ └── NoSeries.Page.al
+└── Sales/
+ └── Invoice/
+ └── SalesInvoice.Page.al
+
+Test/src/
+├── NoSeries/
+│ └── NoSeriesTests.Codeunit.al
+├── Sales/
+│ └── Invoice/
+│ └── SalesInvoiceTests.Codeunit.al
+└── Common/
+ └── TestHelpers/
+ └── TestDataFactory.Codeunit.al
+```
\ No newline at end of file
diff --git a/go.mod b/go.mod
new file mode 100644
index 00000000..c8b8727f
--- /dev/null
+++ b/go.mod
@@ -0,0 +1,5 @@
+module github.com/microsoft/alguidelines
+
+go 1.23.0
+
+require github.com/google/docsy v0.10.0 // indirect
diff --git a/go.sum b/go.sum
new file mode 100644
index 00000000..78bc9349
--- /dev/null
+++ b/go.sum
@@ -0,0 +1,4 @@
+github.com/FortAwesome/Font-Awesome v0.0.0-20240402185447-c0f460dca7f7/go.mod h1:IUgezN/MFpCDIlFezw3L8j83oeiIuYoj28Miwr/KUYo=
+github.com/google/docsy v0.10.0 h1:6tMDacPwAyRWNCfvsn/9qGOZDQ8b0aRzjRZvnZPY5dg=
+github.com/google/docsy v0.10.0/go.mod h1:c0nIAqmRTOuJ01F85U/wJPQtc3Zj9N58Kea9bOT2AJc=
+github.com/twbs/bootstrap v5.3.3+incompatible/go.mod h1:fZTSrkpSf0/HkL0IIJzvVspTt1r9zuf7XlZau8kpcY0=
diff --git a/config.toml b/hugo.toml
similarity index 96%
rename from config.toml
rename to hugo.toml
index 3901c86d..0163dbbc 100644
--- a/config.toml
+++ b/hugo.toml
@@ -4,7 +4,7 @@ languageCode = 'en-us'
title = 'AL Guidelines'
# Hugo allows theme composition (and inheritance). The precedence is from left to right.
-theme = ["docsy"]
+theme = ["github.com/google/docsy"]
# Will give values to .Lastmod etc.
enableGitInfo = true
@@ -72,11 +72,12 @@ id = ""
[languages]
[languages.en]
-description = "Guidelines for when Developing AL for Microsoft Dynamics 365 Business Central"
-languageName = "English"
title = "alguidelines.dev - Business Central Design Patterns"
+languageName = "English"
# Weight used for sorting.
weight = 1
+[languages.en.params]
+description = "Guidelines for when Developing AL for Microsoft Dynamics 365 Business Central"
# Everything below this are Site Params
@@ -84,12 +85,14 @@ weight = 1
copyright = "alguidelines.dev Project"
github_repo = "https://github.com/microsoft/alguidelines"
github_project_repo = "https://github.com/microsoft/alguidelines"
+github_branch = "main"
privacy_policy = ""
shorttitle = "alguidelines.dev"
# Google Custom Search Engine ID. Remove or comment out to disable search.
# gcs_engine_id = "c632b781ffe71b197"
#algolia_docsearch = true
+offlineSearch = true
# current release branch - could be rc
release_branch = "master"
@@ -107,6 +110,10 @@ notoc = true
# First one is picked as the Twitter card image if not set on page.
images = ["images/og-image-fission.png"]
+[params.mermaid]
+enable = true
+theme = "neutral"
+
[params.social]
#github = ''
#slackurl = ""
@@ -143,8 +150,8 @@ url = '/blog/serverless-next.js-example-blog-with-fission/'
[params.ui]
# Set to true to disable breadcrumb navigation.
breadcrumb_disable = false
-# Set to true to disable the About link in the site footer
-footer_about_disable = false
+# Set to true to show an About link in the site footer
+footer_about_enable = true
# Set to false if you don't want to display a logo (/assets/icons/logo.svg) in the top navbar
navbar_logo = true
# Set to true if you don't want the top navbar to be translucent when over a `block/cover`, like on the homepage.
@@ -214,4 +221,4 @@ weight = 13
name = "Discord"
pre = ""
url = "https://discord.gg/4wbfNv3"
-weight = 14
\ No newline at end of file
+weight = 14
diff --git a/layouts/partials/navbar.html b/layouts/partials/navbar.html
index 49259162..8cff9995 100644
--- a/layouts/partials/navbar.html
+++ b/layouts/partials/navbar.html
@@ -1,9 +1,9 @@
{{ $cover := and (.HasShortcode "blocks/cover") (not .Site.Params.ui.navbar_translucent_over_cover_disable) }}