Compare commits
No commits in common. "main" and "docsy-theme-redo" have entirely different histories.
main
...
docsy-them
|
|
@ -1,27 +0,0 @@
|
|||
# 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
|
||||
|
|
@ -1,35 +0,0 @@
|
|||
// 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": {}
|
||||
}
|
||||
}
|
||||
8
.gitattributes
vendored
|
|
@ -1,8 +0,0 @@
|
|||
* 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
|
||||
73
.github/workflows/hugo.yml
vendored
|
|
@ -1,73 +0,0 @@
|
|||
# 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.151.0
|
||||
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@v4
|
||||
49
.github/workflows/regen-gh-pages.yml
vendored
Normal file
|
|
@ -0,0 +1,49 @@
|
|||
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
|
||||
3
.gitmodules
vendored
Normal file
|
|
@ -0,0 +1,3 @@
|
|||
[submodule "themes/docsy"]
|
||||
path = themes/docsy
|
||||
url = https://github.com/google/docsy.git
|
||||
11
.vscode/tasks.json
vendored
|
|
@ -1,11 +0,0 @@
|
|||
{
|
||||
"version": "2.0.0",
|
||||
"tasks": [
|
||||
{
|
||||
"label": "Start local Hugo server",
|
||||
"type": "shell",
|
||||
"command": "hugo serve",
|
||||
"problemMatcher": []
|
||||
}
|
||||
]
|
||||
}
|
||||
23
CITATION.cff
|
|
@ -1,23 +0,0 @@
|
|||
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"
|
||||
|
|
@ -40,7 +40,14 @@ contact [opencode@microsoft.com](mailto:opencode@microsoft.com) with any additio
|
|||
|
||||
### Setup
|
||||
|
||||
**Run Hugo server**
|
||||
1. Clone and setup
|
||||
```sh
|
||||
# Clone all submodules
|
||||
git submodule update --init --recursive --depth 1
|
||||
# Install NPM dependencies
|
||||
npm install
|
||||
```
|
||||
2. Run Hugo server
|
||||
```
|
||||
$ hugo server
|
||||
Web Server is available at http://localhost:1313/ (bind address 127.0.0.1)
|
||||
|
|
|
|||
|
|
@ -4,7 +4,7 @@ languageCode = 'en-us'
|
|||
title = 'AL Guidelines'
|
||||
|
||||
# Hugo allows theme composition (and inheritance). The precedence is from left to right.
|
||||
theme = ["github.com/google/docsy"]
|
||||
theme = ["docsy"]
|
||||
|
||||
# Will give values to .Lastmod etc.
|
||||
enableGitInfo = true
|
||||
|
|
@ -72,12 +72,11 @@ id = ""
|
|||
|
||||
[languages]
|
||||
[languages.en]
|
||||
title = "alguidelines.dev - Business Central Design Patterns"
|
||||
description = "Guidelines for when Developing AL for Microsoft Dynamics 365 Business Central"
|
||||
languageName = "English"
|
||||
title = "alguidelines.dev - Business Central Design Patterns"
|
||||
# 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
|
||||
|
||||
|
|
@ -110,10 +109,6 @@ 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 = ""
|
||||
|
|
@ -150,8 +145,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 show an About link in the site footer
|
||||
footer_about_enable = true
|
||||
# Set to true to disable the About link in the site footer
|
||||
footer_about_disable = false
|
||||
# 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.
|
||||
21
content/Discussions/_index.md
Normal file
|
|
@ -0,0 +1,21 @@
|
|||
+++
|
||||
chapter = true
|
||||
pre = "<b><i class='fas fa-clone'></i> </b>"
|
||||
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
|
||||
|
||||
|
|
@ -123,16 +123,16 @@ images: ["images/og-image-fission.png"]
|
|||
</div>
|
||||
<div class="col-lg-4">
|
||||
<div class="card-shadow">
|
||||
<span class="card-badge">NEW</span>
|
||||
<span class="card-badge">PATTERN</span>
|
||||
<h4 class="section-text-bold mt-4">
|
||||
Getting Started With Agentic Coding
|
||||
Event Bridge
|
||||
</h4>
|
||||
<div class="card-shadow-content">
|
||||
<p class="section-text">
|
||||
Essential concepts and practices for working with AI coding assistants in your AL development workflow.
|
||||
In the world of interfaces, it is important to preserve (certain) events over multiple implementation of the interface.
|
||||
</p>
|
||||
<a href="/docs/agentic-coding/gettingstarted/">
|
||||
<button class="hero-mid-2 align-self-end">Get Started</button>
|
||||
<a href="/docs/patterns/event-bridge-pattern/">
|
||||
<button class="hero-mid align-self-end">Read Now</button>
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
|
|
@ -140,16 +140,16 @@ images: ["images/og-image-fission.png"]
|
|||
|
||||
<div class="col-lg-4">
|
||||
<div class="card-shadow">
|
||||
<span class="card-badge">GUIDE</span>
|
||||
<span class="card-badge">BEST PRACTICE</span>
|
||||
<h4 class="section-text-bold mt-4">
|
||||
Vibe Coding Rules
|
||||
CASE Action on next line
|
||||
</h4>
|
||||
<div class="card-shadow-content">
|
||||
<p class="section-text">
|
||||
AI-optimized coding rules and guidelines designed to enhance the AL developer experience in modern AI-powered IDEs.
|
||||
A CASE action should start on a line after the possibility.
|
||||
</p>
|
||||
<a href="/docs/agentic-coding/vibe-coding-rules/"
|
||||
><button class="hero-mid-2 align-self-end">Explore Rules</button></a
|
||||
<a href="/docs/bestpractices/case-actions/"
|
||||
><button class="hero-mid-2 align-self-end">Read Now</button></a
|
||||
>
|
||||
</div>
|
||||
</div>
|
||||
|
|
@ -157,17 +157,17 @@ images: ["images/og-image-fission.png"]
|
|||
|
||||
<div class="col-lg-4">
|
||||
<div class="card-shadow">
|
||||
<span class="card-badge">TOOLS</span>
|
||||
<span class="card-badge">PATTERN</span>
|
||||
<h4 class="section-text-bold mt-4">
|
||||
Agentic Tools
|
||||
Façade
|
||||
</h4>
|
||||
<div class="card-shadow-content">
|
||||
<p class="section-text">
|
||||
Model Context Protocol (MCP) servers that enhance AI assistants for Business Central development workflows.
|
||||
The intent of this pattern is to provide a unified API to a single or a collection of potentially complex subsystems.
|
||||
</p>
|
||||
<a
|
||||
href="/docs/agentic-coding/communityresources/tools/"
|
||||
><button class="hero-mid-2 align-self-end">Explore Tools</button></a
|
||||
href="/docs/patterns/facade-pattern/"
|
||||
><button class="hero-mid-2 align-self-end">Read Now</button></a
|
||||
>
|
||||
</div>
|
||||
</div>
|
||||
|
|
@ -210,6 +210,17 @@ images: ["images/og-image-fission.png"]
|
|||
</p>
|
||||
</div>
|
||||
</a>
|
||||
<a href="https://twitter.com/search?q=%23bcalhelp">
|
||||
<div class="card-shadow twitter-card" style="margin-left: auto">
|
||||
<div class="card-header-custom">
|
||||
<h2 style="padding-right: 0.8rem;"><i class="fab fa-twitter"></i></h2>
|
||||
<h2 class="section-text-bold">Twitter</h2>
|
||||
</div>
|
||||
<p class="section-text">
|
||||
Chat about this with the <b class="text-highlight">#bcalhelp</b> hashtag.
|
||||
</p>
|
||||
</div>
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
|
@ -243,6 +254,22 @@ images: ["images/og-image-fission.png"]
|
|||
</div></a
|
||||
>
|
||||
</div>
|
||||
<div class="col-lg-6">
|
||||
<a href="https://twitter.com/search?q=%23bcalhelp"
|
||||
><div
|
||||
class="card-shadow twitter-card-desktop"
|
||||
style="margin-left: auto"
|
||||
>
|
||||
<div class="card-header-custom">
|
||||
<h2 style="padding-right: 0.8rem;"><i class="fab fa-twitter"></i></h2>
|
||||
<h2 class="section-text-bold">Twitter</h2>
|
||||
</div>
|
||||
<p class="section-text">
|
||||
Chat about this with the <b class="text-highlight">#bcalhelp</b> hashtag.
|
||||
</p>
|
||||
</div></a
|
||||
>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -1,96 +0,0 @@
|
|||
---
|
||||
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.
|
||||
|
|
@ -1,10 +1,10 @@
|
|||
---
|
||||
title: "DeleteAll"
|
||||
tags: ["AL","Performance"]
|
||||
tags: ["Performance"]
|
||||
categories: ["Best Practice"]
|
||||
---
|
||||
|
||||
_Created by waldo, Described by waldo_
|
||||
<_Created by waldo, Described by waldo_\>
|
||||
|
||||
## Description
|
||||
|
||||
|
|
@ -25,3 +25,11 @@ 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.
|
||||
|
|
@ -1,69 +0,0 @@
|
|||
---
|
||||
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;
|
||||
```
|
||||
|
|
@ -1,6 +1,6 @@
|
|||
---
|
||||
title: "Subscriber Codeunits"
|
||||
tags: ["AL","Performance"]
|
||||
tags: ["Performance"]
|
||||
categories: ["Best Practice"]
|
||||
---
|
||||
|
||||
|
|
@ -18,18 +18,15 @@ 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/docs/patterns/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/bcpatterns/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"
|
||||
{
|
||||
|
|
@ -76,7 +73,6 @@ codeunit 2037325 "Setup Subs"
|
|||
end;
|
||||
}
|
||||
```
|
||||
|
||||
### Good code
|
||||
|
||||
Split into 2 codeunits, and move the business logic out.
|
||||
|
|
@ -116,7 +112,6 @@ 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"
|
||||
{
|
||||
|
|
@ -129,9 +124,7 @@ codeunit 2037324 "RHE Setup Subs"
|
|||
end;
|
||||
}
|
||||
```
|
||||
|
||||
### Good code
|
||||
|
||||
```AL
|
||||
codeunit 2037324 "RHE Setup Subs"
|
||||
{
|
||||
|
|
@ -152,7 +145,6 @@ 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)]
|
||||
|
|
@ -170,9 +162,7 @@ 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
|
||||
|
|
@ -187,15 +177,21 @@ 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/docs/patterns/generic-method-pattern/)
|
||||
The [Generic Method Pattern](https://alguidelines.dev/bcpatterns/generic-method-pattern/)
|
||||
|
|
@ -5,6 +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
|
||||
|
|
|
|||
|
|
@ -1,187 +0,0 @@
|
|||
---
|
||||
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. While the value is case insensitive for HTTP operations (GET, POST, etc.), it is case sensitive when checking for active subscriptions.
|
||||
|
||||
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. While the value is case insensitive for HTTP operations (GET, POST, etc.), it is case sensitive when checking for active subscriptions.
|
||||
|
||||
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) { }
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
|
@ -1,6 +1,6 @@
|
|||
---
|
||||
title: "begin as an afterword"
|
||||
tags: ["AL","Readability"]
|
||||
tags: ["Readability"]
|
||||
categories: ["Best Practice"]
|
||||
---
|
||||
|
||||
|
|
@ -15,7 +15,7 @@ When `begin` follows `then`, `else`, `do`, it should be on the same line, preced
|
|||
```al
|
||||
if ICPartnerRefType = ICPartnerRefType::"Common Item No." then
|
||||
begin
|
||||
...
|
||||
...
|
||||
end;
|
||||
```
|
||||
|
||||
|
|
@ -23,6 +23,12 @@ 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.
|
||||
|
|
@ -1,6 +1,6 @@
|
|||
---
|
||||
title: "Begin-End - Compound Only"
|
||||
tags: ["AL","Readability"]
|
||||
tags: ["Readability"]
|
||||
categories: ["Best Practice"]
|
||||
---
|
||||
|
||||
|
|
@ -8,30 +8,26 @@ _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.).
|
||||
|
||||
## Example 1
|
||||
|
||||
### Bad code
|
||||
## Bad code
|
||||
|
||||
```AL
|
||||
if FindSet() then begin
|
||||
repeat
|
||||
...
|
||||
...
|
||||
until next() = 0;
|
||||
end;
|
||||
```
|
||||
|
||||
### Good code
|
||||
## Good code
|
||||
|
||||
```AL
|
||||
if FindSet() then
|
||||
repeat
|
||||
...
|
||||
...
|
||||
until next() = 0;
|
||||
```
|
||||
|
||||
## Example 2
|
||||
|
||||
### Bad code
|
||||
## Bad code
|
||||
|
||||
```AL
|
||||
if IsAssemblyOutputLine then begin
|
||||
|
|
@ -39,7 +35,7 @@ if IsAssemblyOutputLine then begin
|
|||
end;
|
||||
```
|
||||
|
||||
### Good code
|
||||
## Good code
|
||||
|
||||
```AL
|
||||
if IsAssemblyOutputLine then
|
||||
|
|
@ -57,9 +53,8 @@ end else
|
|||
(not X)
|
||||
```
|
||||
|
||||
## Tips
|
||||
## [Discussions](https://github.com/microsoft/alguidelines/discussions/categories/bc-best-practices?discussions_q=begin+end+compound+only+category%3A%22BC+Best+Practices%22)
|
||||
|
||||
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.
|
||||
You can find discussions on all "Best Practices" [here](https://github.com/microsoft/alguidelines/discussions/categories/bc-best-practices).
|
||||
|
||||
- `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
|
||||
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.
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
---
|
||||
title: "Binary Operator to Start Line"
|
||||
tags: ["AL","Readability"]
|
||||
tags: ["Readability"]
|
||||
categories: ["Best Practice"]
|
||||
---
|
||||
|
||||
|
|
@ -14,14 +14,20 @@ Do not start a line with a binary operator.
|
|||
|
||||
```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.
|
||||
|
|
|
|||
|
|
@ -1,115 +0,0 @@
|
|||
---
|
||||
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
|
||||
|
|
@ -1,6 +1,6 @@
|
|||
---
|
||||
title: "CASE Action on next line"
|
||||
tags: ["AL","Readability"]
|
||||
tags: ["Readability"]
|
||||
categories: ["Best Practice"]
|
||||
---
|
||||
|
||||
|
|
@ -29,3 +29,9 @@ 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.
|
||||
|
|
|
|||
|
|
@ -1,13 +1,12 @@
|
|||
---
|
||||
title: "Comment Spacing"
|
||||
tags: ["AL","Readability"]
|
||||
tags: ["Readability"]
|
||||
categories: ["Best Practice"]
|
||||
---
|
||||
|
||||
_Created by Microsoft, Described by waldo_
|
||||
|
||||
## Description
|
||||
|
||||
Always start comments with // followed by one space character.
|
||||
|
||||
## Bad code
|
||||
|
|
@ -16,8 +15,15 @@ Always start comments with // followed by one space character.
|
|||
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.
|
||||
|
|
@ -1,6 +1,6 @@
|
|||
---
|
||||
title: "end else pair"
|
||||
tags: ["AL","Readability"]
|
||||
tags: ["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,8 +28,14 @@ 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.
|
||||
|
|
|
|||
|
|
@ -1,109 +0,0 @@
|
|||
---
|
||||
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;
|
||||
```
|
||||
|
|
@ -1,60 +0,0 @@
|
|||
---
|
||||
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;
|
||||
```
|
||||
|
|
@ -1,39 +0,0 @@
|
|||
---
|
||||
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 + <Arrow Left/Right> |
|
||||
| Move Line Up/Down | Editor | ALT + <Arrow Up/Down> |
|
||||
| Copy Line Below/Above | Editor | ALT + SHIFT + <Arrow Up/Down> |
|
||||
| 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 + <Arrow Up/Down> |
|
||||
| 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 + <Arrow Left/Right> |
|
||||
|
||||
|
|
@ -1,13 +1,12 @@
|
|||
---
|
||||
title: "Keyword Pairs - Indentation"
|
||||
tags: ["AL","Readability"]
|
||||
tags: ["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
|
||||
|
|
@ -24,3 +23,9 @@ 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.
|
||||
|
|
@ -1,12 +1,11 @@
|
|||
---
|
||||
title: "Line Start Keywords"
|
||||
tags: ["AL","Readability"]
|
||||
tags: ["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
|
||||
|
|
@ -29,3 +28,10 @@ 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.
|
||||
|
|
@ -1,13 +1,12 @@
|
|||
---
|
||||
title: "Lonely Repeat"
|
||||
tags: ["AL","Readability"]
|
||||
tags: ["Readability"]
|
||||
categories: ["Best Practice"]
|
||||
---
|
||||
|
||||
_Created by Microsoft, Described by waldo_
|
||||
|
||||
## Description
|
||||
|
||||
The `repeat` statement should always be alone on a line.
|
||||
|
||||
## Bad code
|
||||
|
|
@ -22,3 +21,9 @@ The `repeat` statement should always be alone on a line.
|
|||
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.
|
||||
|
|
@ -1,13 +1,12 @@
|
|||
---
|
||||
title: "Named Invocations"
|
||||
tags: ["AL","Readability"]
|
||||
tags: ["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
|
||||
|
|
@ -22,8 +21,8 @@ When calling an object statically use the Object Name, not the Object Id.
|
|||
Page.RunModal(Page::"Posted Sales Shipment Lines", SalesShptLine);
|
||||
```
|
||||
|
||||
## Tips
|
||||
## [Discussions](https://github.com/microsoft/alguidelines/discussions/categories/bc-best-practices?discussions_q=named+invocations+category%3A%22BC+Best+Practices%22)
|
||||
|
||||
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.
|
||||
You can find discussions on all "Best Practices" [here](https://github.com/microsoft/alguidelines/discussions/categories/bc-best-practices).
|
||||
|
||||
- [LC0012](https://github.com/StefanMaron/BusinessCentral.LinterCop/wiki/LC0012): Using hardcoded IDs in functions like Codeunit.Run() is not allowed.
|
||||
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.
|
||||
|
|
|
|||
|
|
@ -1,41 +1,44 @@
|
|||
---
|
||||
title: "One Statement per Line"
|
||||
tags: ["AL","Readability"]
|
||||
tags: ["Readability"]
|
||||
categories: ["Best Practice"]
|
||||
---
|
||||
|
||||
_Created by Microsoft, Described by waldo_
|
||||
|
||||
## Description
|
||||
|
||||
A line of code should not have more than one statement.
|
||||
|
||||
## Example 1
|
||||
|
||||
### Bad code
|
||||
## Bad code
|
||||
|
||||
```al
|
||||
if OppEntry.Find('-') then exit;
|
||||
```
|
||||
|
||||
### Good code
|
||||
|
||||
## Good code
|
||||
|
||||
```al
|
||||
if OppEntry.Find('-') then
|
||||
exit;
|
||||
```
|
||||
|
||||
## Example 2
|
||||
|
||||
### Bad code
|
||||
## 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.
|
||||
|
|
|
|||
|
|
@ -1,29 +1,35 @@
|
|||
---
|
||||
title: "Seperate if and else"
|
||||
tags: ["AL","Readability"]
|
||||
tags: ["Readability"]
|
||||
categories: ["Best Practice"]
|
||||
---
|
||||
|
||||
_Created by Microsoft, Described by waldo_
|
||||
|
||||
## Description
|
||||
|
||||
`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.
|
||||
|
|
|
|||
|
|
@ -1,52 +1,45 @@
|
|||
---
|
||||
title: "Spacing Binary Operators"
|
||||
tags: ["AL","Readability"]
|
||||
tags: ["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.
|
||||
|
||||
## Example 1
|
||||
|
||||
### Bad code
|
||||
## Bad code
|
||||
|
||||
```al
|
||||
"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
|
||||
## Bad code
|
||||
|
||||
```al
|
||||
StartDate := CalcDate('<+'+Format(Days+i)+'D\>',StartDate);
|
||||
```
|
||||
|
||||
### Good code
|
||||
## Good code
|
||||
|
||||
```al
|
||||
StartDate := CalcDate('<+' + Format(Days + i) + 'D\>', StartDate);
|
||||
```
|
||||
|
||||
## Example 3
|
||||
|
||||
### Bad code
|
||||
## Bad code
|
||||
|
||||
```al
|
||||
StartDate:=0D; // Initialize
|
||||
```
|
||||
|
||||
### Good code
|
||||
## Good code
|
||||
|
||||
```al
|
||||
StartDate := 0D; // Initialize
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
---
|
||||
title: "Suggested Abbreviations"
|
||||
tags: ["AL","Readability"]
|
||||
tags: ["Readability"]
|
||||
categories: ["Best Practice"]
|
||||
---
|
||||
|
||||
|
|
@ -359,3 +359,9 @@ 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.
|
||||
|
|
@ -1,6 +1,6 @@
|
|||
---
|
||||
title: "Unnecessary else"
|
||||
tags: ["AL","Readability"]
|
||||
tags: ["Readability"]
|
||||
categories: ["Best Practice"]
|
||||
---
|
||||
|
||||
|
|
@ -32,3 +32,9 @@ _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.
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
---
|
||||
title: "Unnecessary true/false"
|
||||
tags: ["AL","Readability"]
|
||||
tags: ["Readability"]
|
||||
categories: ["Best Practice"]
|
||||
---
|
||||
|
||||
|
|
@ -9,30 +9,33 @@ _Created by Microsoft, Described by waldo_
|
|||
## Description
|
||||
Do not use `true` or `false` keywords unnecessarily if the expression is already an logical expression.
|
||||
|
||||
## Example 1
|
||||
|
||||
### Bad code
|
||||
## Bad code
|
||||
|
||||
```al
|
||||
if IsPositive() = true then
|
||||
```
|
||||
|
||||
### Good code
|
||||
## Good code
|
||||
|
||||
```al
|
||||
if IsPositive() then
|
||||
```
|
||||
|
||||
## Example 2
|
||||
|
||||
### Bad code
|
||||
## 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.
|
||||
|
|
@ -1,13 +1,12 @@
|
|||
---
|
||||
title: "Variable Naming"
|
||||
tags: ["AL","Readability"]
|
||||
tags: ["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.
|
||||
|
|
@ -16,48 +15,34 @@ 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.
|
||||
|
||||
## Example 1
|
||||
|
||||
### Bad code
|
||||
## Bad code
|
||||
|
||||
```al
|
||||
WIPBuffer: Record "Job WIP Buffer"
|
||||
```
|
||||
|
||||
### Good code
|
||||
|
||||
## Good code
|
||||
```al
|
||||
JobWIPBuffer: Record "Job WIP Buffer"
|
||||
```
|
||||
|
||||
## Example 2
|
||||
|
||||
### Bad code
|
||||
|
||||
## Bad code
|
||||
```al
|
||||
Postline: Codeunit "Gen. Jnl.-Post Line";
|
||||
```
|
||||
|
||||
### Good code
|
||||
|
||||
## Good code
|
||||
```al
|
||||
GenJnlPostLine: Codeunit "Gen. Jnl.-Post Line";
|
||||
```
|
||||
|
||||
## Example 3
|
||||
|
||||
### Bad code
|
||||
|
||||
## Bad code
|
||||
```al
|
||||
"Amount (LCY)": Decimal;
|
||||
```
|
||||
|
||||
### Good code
|
||||
|
||||
## Good code
|
||||
```al
|
||||
AmountLCY: Decimal;
|
||||
```
|
||||
|
||||
## Tips
|
||||
## [Discussions](https://github.com/microsoft/alguidelines/discussions/categories/bc-best-practices?discussions_q=one+variable+naming+category%3A%22BC+Best+Practices%22)
|
||||
|
||||
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.
|
||||
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.
|
||||
|
|
|
|||
|
|
@ -1,13 +1,12 @@
|
|||
---
|
||||
title: "Variables Declarations Order"
|
||||
tags: ["AL","Readability"]
|
||||
tags: ["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
|
||||
|
|
@ -26,6 +25,7 @@ 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,9 +40,8 @@ Variables declarations should be ordered by type. In general, object and complex
|
|||
StartingDateFilter: Text;
|
||||
```
|
||||
|
||||
## Tips
|
||||
## [Discussions](https://github.com/microsoft/alguidelines/discussions/categories/bc-best-practices?discussions_q=one+variables+declarations+order+category%3A%22BC+Best+Practices%22)
|
||||
|
||||
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.
|
||||
You can find discussions on all "Best Practices" [here](https://github.com/microsoft/alguidelines/discussions/categories/bc-best-practices).
|
||||
|
||||
- `Sort Variables in the Active Editor` : sorts variables in the current editor
|
||||
- `Sort Variables in the Active Project` : sorts variables in the current project
|
||||
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.
|
||||
|
|
|
|||
|
|
@ -38,66 +38,6 @@ 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/)
|
||||
|
||||
|
|
|
|||
|
Before Width: | Height: | Size: 44 KiB |
|
Before Width: | Height: | Size: 22 KiB |
|
Before Width: | Height: | Size: 54 KiB |
|
Before Width: | Height: | Size: 10 KiB |
|
Before Width: | Height: | Size: 96 KiB |
|
Before Width: | Height: | Size: 128 KiB |
|
Before Width: | Height: | Size: 6 KiB |
|
Before Width: | Height: | Size: 18 KiB |
|
|
@ -1,57 +0,0 @@
|
|||
---
|
||||
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 <https://gohugo.io/gettingstarted/installing/>.
|
||||
|
||||
## 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 <http://localhost:1313> by opening a command promt, and open the `C:\Hugo\Sites\alguidelines` folder and execute `Hugo Serve`
|
||||
|
||||

|
||||
|
|
@ -1,35 +0,0 @@
|
|||
---
|
||||
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:
|
||||
|
||||
<video width=100% controls>
|
||||
<source src="alguidelines walkthrough.mp4" type="video/mp4">
|
||||
Your browser does not support the video tag.
|
||||
</video>
|
||||
|
||||
## 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:
|
||||
|
||||
<video width=100% controls>
|
||||
<source src="alguidelines codespace.mp4" type="video/mp4">
|
||||
Your browser does not support the video tag.
|
||||
</video>
|
||||
|
|
@ -1,22 +0,0 @@
|
|||
---
|
||||
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
|
||||
```
|
||||
|
|
@ -1,9 +0,0 @@
|
|||
---
|
||||
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 <https://gohugo.io/gettingstarted/installing/>
|
||||
|
||||
{{< youtube G7umPCU-8xc >}}
|
||||
|
|
@ -1,6 +1,6 @@
|
|||
---
|
||||
title: "Title Here"
|
||||
tags: ["AL"]
|
||||
tags: []
|
||||
categories: ["Best Practice"]
|
||||
---
|
||||
|
||||
|
|
@ -29,3 +29,11 @@ PutCodeblocksHere()
|
|||
```al
|
||||
PutCodeblocksHere()
|
||||
```
|
||||
|
||||
## Discussions
|
||||
|
||||
Please discuss this guideline <!--[here](https://github.com/microsoft/alguidelines/discussions/42) Fix the link -->
|
||||
|
||||
You can find discussions on all "Best Practices" [here](https://github.com/microsoft/alguidelines/discussions/categories/bc-best-practices).
|
||||
|
||||
<!-- 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. -->
|
||||
|
|
@ -1,6 +1,6 @@
|
|||
---
|
||||
title: "Title Here"
|
||||
tags: ["AL"]
|
||||
title: "Pattern Name"
|
||||
tags: []
|
||||
categories: ["Pattern"]
|
||||
---
|
||||
|
||||
|
|
@ -44,3 +44,11 @@ Usually, there are occasions where NOT to implement the pattern. List the disad
|
|||
## List of references
|
||||
|
||||
Youtube-link? BaseApp? Tweet? ...
|
||||
|
||||
## Discussions
|
||||
|
||||
Please discuss this guideline <!--[here](https://github.com/microsoft/alguidelines/discussions/42) Fix the link -->
|
||||
|
||||
You can find discussions on all "Best Practices" [here](https://github.com/microsoft/alguidelines/discussions/categories/bc-patterns).
|
||||
|
||||
<!-- 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. -->
|
||||
|
|
|
|||
|
|
@ -2,7 +2,6 @@
|
|||
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.
|
||||
|
||||
|
|
|
|||
|
|
@ -1,7 +1,6 @@
|
|||
+++
|
||||
title = "Nav Upgrade"
|
||||
weight = 840
|
||||
tags = ["C/AL"]
|
||||
+++
|
||||
## Anti-Patterns in NAV Upgrade
|
||||
|
||||
|
|
|
|||
|
|
@ -1,7 +1,6 @@
|
|||
+++
|
||||
title = "Reusable Bugs"
|
||||
weight = 1020
|
||||
tags = ["C/AL"]
|
||||
+++
|
||||
_By Bogdana Botez, Andreas Moth, Eric Wauters (waldo), Elly Nkya, Nikola Kukrika_
|
||||
|
||||
|
|
|
|||
|
|
@ -2,8 +2,6 @@
|
|||
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).
|
||||
|
||||
|
|
@ -23,6 +21,6 @@ We're looking forward to your comments. Where you can, do provide concrete examp
|
|||
|
||||
|
||||
|
||||
[anchor0]: /files/CAL-Coding-Guidelines-at-Microsoft-Development-Center-Copenhagen.pdf "download the C/AL coding guidelines as pdf"
|
||||
[anchor0]: https://blogs.msdn.microsoft.com/nav/2015/01/09/cal-coding-guidelines-used-at-microsoft-development-center-copenhagen "download the C/AL coding guidelines as pdf"
|
||||
[anchor1]: /members/waldo/default.aspx "waldo"
|
||||
[anchor2]: https://www.youtube.com/watch?v=z6skKy0pkmU&list=PLhZ3P-LY7CqmVszuvtJLujFyHpsVN0U_w&index=26
|
||||
|
|
|
|||
|
|
@ -1,8 +1,6 @@
|
|||
+++
|
||||
title = "Design"
|
||||
weight = 490
|
||||
tags = ["C/AL"]
|
||||
categories = ["Best Practice"]
|
||||
+++
|
||||
## C/AL Coding Guidelines
|
||||
|
||||
|
|
|
|||
|
|
@ -1,8 +1,6 @@
|
|||
+++
|
||||
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.
|
||||
|
||||
|
|
|
|||
|
|
@ -1,8 +1,6 @@
|
|||
+++
|
||||
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.
|
||||
|
||||
|
|
|
|||
|
|
@ -1,8 +1,6 @@
|
|||
+++
|
||||
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.
|
||||
|
||||
|
|
|
|||
|
|
@ -1,7 +1,5 @@
|
|||
+++
|
||||
title = "Encapsulate Local Functionality"
|
||||
weight = 530
|
||||
tags = ["C/AL"]
|
||||
categories = ["Best Practice"]
|
||||
+++
|
||||
Any function used local must be defined as local.
|
||||
|
|
|
|||
|
|
@ -1,8 +1,6 @@
|
|||
+++
|
||||
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.
|
||||
|
||||
|
|
|
|||
|
|
@ -1,8 +1,6 @@
|
|||
+++
|
||||
title = "Initialized Variables"
|
||||
weight = 660
|
||||
tags = ["C/AL"]
|
||||
categories = ["Best Practice"]
|
||||
+++
|
||||
Variables should always be set to a specific value, before they are used.
|
||||
|
||||
|
|
|
|||
|
|
@ -1,8 +1,6 @@
|
|||
+++
|
||||
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.
|
||||
|
||||
|
|
|
|||
|
|
@ -1,8 +1,6 @@
|
|||
+++
|
||||
title = "Parameter Placeholders"
|
||||
weight = 920
|
||||
tags = ["C/AL"]
|
||||
categories = ["Best Practice"]
|
||||
+++
|
||||
The number of parameters passed to a string must match the placeholders.
|
||||
|
||||
|
|
|
|||
|
|
@ -1,8 +1,6 @@
|
|||
+++
|
||||
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.
|
||||
|
||||
|
|
|
|||
|
|
@ -1,8 +1,6 @@
|
|||
+++
|
||||
title = "Unreachable Code"
|
||||
weight = 1310
|
||||
tags = ["C/AL"]
|
||||
categories = ["Best Practice"]
|
||||
+++
|
||||
Do not write code that will never be hit.
|
||||
|
||||
|
|
|
|||
|
|
@ -1,8 +1,6 @@
|
|||
+++
|
||||
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.
|
||||
|
||||
|
|
|
|||
|
|
@ -1,8 +1,6 @@
|
|||
+++
|
||||
title = "Unused Variables"
|
||||
weight = 1330
|
||||
tags = ["C/AL"]
|
||||
categories = ["Best Practice"]
|
||||
+++
|
||||
Do not declare variables that are unused.
|
||||
|
||||
|
|
|
|||
|
|
@ -1,8 +1,6 @@
|
|||
+++
|
||||
title = "Variable Capacity Mismatch"
|
||||
weight = 1410
|
||||
tags = ["C/AL"]
|
||||
categories = ["Best Practice"]
|
||||
+++
|
||||
Do not assign a value to a variable whose capacity is smaller.
|
||||
|
||||
|
|
|
|||
|
|
@ -1,8 +1,6 @@
|
|||
+++
|
||||
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.
|
||||
|
||||
|
|
|
|||
|
|
@ -1,8 +1,6 @@
|
|||
+++
|
||||
title = "Internally used DotNet Types"
|
||||
weight = 690
|
||||
tags = ["C/AL"]
|
||||
categories = ["Best Practice"]
|
||||
+++
|
||||
_(Dynamics NAV 2015)_
|
||||
|
||||
|
|
|
|||
|
|
@ -1,8 +1,6 @@
|
|||
+++
|
||||
title = "Internationalization"
|
||||
weight = 700
|
||||
tags = ["C/AL"]
|
||||
categories = ["Best Practice"]
|
||||
+++
|
||||
## C/AL Coding Guidelines
|
||||
|
||||
|
|
|
|||
|
|
@ -1,8 +1,6 @@
|
|||
+++
|
||||
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.
|
||||
|
||||
|
|
|
|||
|
|
@ -1,8 +1,6 @@
|
|||
+++
|
||||
title = "Localizability"
|
||||
weight = 750
|
||||
tags = ["C/AL"]
|
||||
categories = ["Best Practice"]
|
||||
+++
|
||||
## C/AL Coding Guidelines
|
||||
|
||||
|
|
|
|||
|
|
@ -1,8 +1,6 @@
|
|||
+++
|
||||
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.
|
||||
|
||||
|
|
|
|||
|
|
@ -1,8 +1,6 @@
|
|||
+++
|
||||
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.
|
||||
|
||||
|
|
|
|||
|
|
@ -1,8 +1,6 @@
|
|||
+++
|
||||
title = "Global Text Constants"
|
||||
weight = 610
|
||||
tags = ["C/AL"]
|
||||
categories = ["Best Practice"]
|
||||
+++
|
||||
Declare Text Constant as global variables.
|
||||
|
||||
|
|
|
|||
|
|
@ -1,8 +1,6 @@
|
|||
+++
|
||||
title = "Use Text Constants"
|
||||
weight = 1360
|
||||
tags = ["C/AL"]
|
||||
categories = ["Best Practice"]
|
||||
+++
|
||||
Pass user messages using Text Constants. It makes translation easy.
|
||||
|
||||
|
|
|
|||
|
|
@ -1,8 +1,6 @@
|
|||
+++
|
||||
title = "Using OptionCaptionML"
|
||||
weight = 1380
|
||||
tags = ["C/AL"]
|
||||
categories = ["Best Practice"]
|
||||
+++
|
||||
The OptionCaptionML should be filled in for sourceexpression using option data types.
|
||||
|
||||
|
|
|
|||
|
|
@ -1,8 +1,6 @@
|
|||
+++
|
||||
title = "Readability"
|
||||
weight = 980
|
||||
tags = ["C/AL","Readability"]
|
||||
categories = ["Best Practice"]
|
||||
+++
|
||||
## C/AL Coding Guidelines
|
||||
|
||||
|
|
|
|||
|
|
@ -1,8 +1,6 @@
|
|||
+++
|
||||
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.
|
||||
|
||||
|
|
|
|||
|
|
@ -1,8 +1,6 @@
|
|||
+++
|
||||
title = "Begin-End - Compound Only"
|
||||
weight = 240
|
||||
tags = ["C/AL","Readability"]
|
||||
categories = ["Best Practice"]
|
||||
+++
|
||||
Only use BEGIN..END to enclose compound statements.
|
||||
|
||||
|
|
|
|||
|
|
@ -1,8 +1,6 @@
|
|||
+++
|
||||
title = "Binary Operator to Start Line"
|
||||
weight = 250
|
||||
tags = ["C/AL","Readability"]
|
||||
categories = ["Best Practice"]
|
||||
+++
|
||||
Do not start a line with a binary operator.
|
||||
|
||||
|
|
|
|||
|
|
@ -1,8 +1,6 @@
|
|||
+++
|
||||
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.
|
||||
|
||||
|
|
|
|||
|
|
@ -1,8 +1,6 @@
|
|||
+++
|
||||
title = "CASE Action"
|
||||
weight = 310
|
||||
tags = ["C/AL","Readability"]
|
||||
categories = ["Best Practice"]
|
||||
+++
|
||||
A CASE action should start on a line after the possibility.
|
||||
|
||||
|
|
|
|||
|
|
@ -1,8 +1,6 @@
|
|||
+++
|
||||
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.
|
||||
|
||||
|
|
|
|||
|
|
@ -1,8 +1,6 @@
|
|||
+++
|
||||
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.
|
||||
|
||||
|
|
|
|||
|
|
@ -1,8 +1,6 @@
|
|||
+++
|
||||
title = "Comment Spacing"
|
||||
weight = 360
|
||||
tags = ["C/AL","Readability"]
|
||||
categories = ["Best Practice"]
|
||||
+++
|
||||
Always start comments with // followed by one space character.
|
||||
|
||||
|
|
|
|||
|
|
@ -1,8 +1,6 @@
|
|||
+++
|
||||
title = "END ELSE Pair"
|
||||
weight = 540
|
||||
tags = ["C/AL","Readability"]
|
||||
categories = ["Best Practice"]
|
||||
+++
|
||||
The END ELSE pair should always appear on the same line.
|
||||
|
||||
|
|
|
|||
|
|
@ -1,8 +1,6 @@
|
|||
+++
|
||||
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.
|
||||
|
||||
|
|
|
|||
|
|
@ -1,8 +1,6 @@
|
|||
+++
|
||||
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.
|
||||
|
||||
|
|
|
|||
|
|
@ -1,8 +1,6 @@
|
|||
+++
|
||||
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.
|
||||
|
||||
|
|
|
|||
|
|
@ -1,8 +1,6 @@
|
|||
+++
|
||||
title = "Lonely Repeat"
|
||||
weight = 760
|
||||
tags = ["C/AL","Readability"]
|
||||
categories = ["Best Practice"]
|
||||
+++
|
||||
The REPEAT statement should always be alone on a line.
|
||||
|
||||
|
|
|
|||
|
|
@ -1,8 +1,6 @@
|
|||
+++
|
||||
title = "Named Invocations"
|
||||
weight = 830
|
||||
tags = ["C/AL","Readability"]
|
||||
categories = ["Best Practice"]
|
||||
+++
|
||||
When calling an object statically use the name, not the number
|
||||
|
||||
|
|
|
|||