Compare commits
113 commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
94d33e1145 | ||
|
|
4778523436 | ||
|
|
be278fced3 | ||
|
|
2914a84a66 | ||
|
|
35e175dacf | ||
|
|
52fa2ea99a | ||
|
|
9389386ec6 | ||
|
|
4e485be0cd | ||
|
|
a7a418f6e2 | ||
|
|
0fb8ca7a14 | ||
|
|
f65c30129b | ||
|
|
76c6fda05c | ||
|
|
f25c8d4845 | ||
|
|
2598db2520 | ||
|
|
7f2b60c5ec | ||
|
|
90010b11a8 | ||
|
|
cec32cb293 | ||
|
|
6c1961d5d6 | ||
|
|
4a3d4d2111 | ||
|
|
d9d8493aa3 | ||
|
|
9d94c5d6d3 | ||
|
|
045558769d | ||
|
|
7bf102e041 | ||
|
|
fb9ca18a04 | ||
|
|
3680a904e2 | ||
|
|
9f8ffa7eca | ||
|
|
9e1954b1b2 | ||
|
|
34079491f7 | ||
|
|
2f8078f0df | ||
|
|
6f08d67402 | ||
|
|
7e9d4c18d3 | ||
|
|
d5790b8abb | ||
|
|
7006c9a415 | ||
|
|
7686ef4d97 | ||
|
|
68deff1d24 | ||
|
|
90867b3595 | ||
|
|
b943ef6fed | ||
|
|
5af1e37539 | ||
|
|
a527835756 | ||
|
|
a35732a260 | ||
|
|
bb8cd97537 | ||
|
|
990b56fee9 | ||
|
|
72d0328fa0 | ||
|
|
77937b249a | ||
|
|
6101d38b0d | ||
|
|
573b8d1ca3 | ||
|
|
8653165eed | ||
|
|
c0a0c2b0d1 | ||
|
|
fff62a9a12 | ||
|
|
2d866c6316 | ||
|
|
34af3362da | ||
|
|
7274dae657 | ||
|
|
e4f08bf9a7 | ||
|
|
de09820554 | ||
|
|
07f7d3fad3 | ||
|
|
9c6169cd6c | ||
|
|
d4c41c7a00 | ||
|
|
cbb4b02680 | ||
|
|
723ebe0f8c | ||
|
|
a135d8b92c | ||
|
|
359c95bfac | ||
|
|
a1725fca7b | ||
|
|
51853c60ee | ||
|
|
6ca52ea91a | ||
|
|
c636361aaf | ||
|
|
ab0f87f1e5 | ||
|
|
816d38948d | ||
|
|
910c3b92b3 | ||
|
|
d9c8e0231b | ||
|
|
ca19ee8d56 | ||
|
|
c354076240 | ||
|
|
ac41363f39 | ||
|
|
2c5e80a8bf | ||
|
|
ce10cf9e96 | ||
|
|
099d17c0d2 | ||
|
|
e9d640d50a | ||
|
|
b46ee5b225 | ||
|
|
d60c48d8a0 | ||
|
|
2834098a70 | ||
|
|
5a801fae69 | ||
|
|
3923b7fdf4 | ||
|
|
78925a791a | ||
|
|
1e734f8858 | ||
|
|
929cf13e1e | ||
|
|
6a0769ecdf | ||
|
|
04bf19baad | ||
|
|
64810dd590 | ||
|
|
a01eae5835 | ||
|
|
4c36721a0a | ||
|
|
52ba0d2c26 | ||
|
|
aca2511c9a | ||
|
|
fd4c4e6198 | ||
|
|
002f3c1d18 | ||
|
|
fc2093e43d | ||
|
|
e0ebd5cb4a | ||
|
|
63ca70861e | ||
|
|
205450b587 | ||
|
|
a3eb251835 | ||
|
|
74b575a122 | ||
|
|
62b3194f3b | ||
|
|
740dbf4e5a | ||
|
|
27483c8f45 | ||
|
|
b3164ca8f0 | ||
|
|
4d6fcee21a | ||
|
|
829e0acf57 | ||
|
|
cb4e8e2194 | ||
|
|
158b902b20 | ||
|
|
de51cb0ad5 | ||
|
|
90cf73d9e5 | ||
|
|
4f1faf3e84 | ||
|
|
d23ccf86e1 | ||
|
|
bd0e425174 | ||
|
|
3941105a09 |
798 changed files with 52049 additions and 30401 deletions
|
|
@ -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
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
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
|
||||
7
.gitignore
vendored
7
.gitignore
vendored
|
|
@ -1,7 +0,0 @@
|
|||
public/
|
||||
/node_modules/
|
||||
/resources/_gen/
|
||||
|
||||
## Locking files for when working with local servers
|
||||
.hugo_build.lock
|
||||
package-lock.json
|
||||
0
.nojekyll
Normal file
0
.nojekyll
Normal file
11
.vscode/tasks.json
vendored
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": []
|
||||
}
|
||||
]
|
||||
}
|
||||
8
404.html
Normal file
8
404.html
Normal file
|
|
@ -0,0 +1,8 @@
|
|||
<!doctype html><html lang=en class=no-js><head><meta charset=utf-8><meta name=viewport content="width=device-width,initial-scale=1,shrink-to-fit=no"><meta name=generator content="Hugo 0.119.0"><meta name=robots content="index, follow"><link rel="shortcut icon" href=/favicons/favicon.ico><link rel=apple-touch-icon href=/favicons/apple-touch-icon-180x180.png sizes=180x180><link rel=icon type=image/png href=/favicons/favicon-16x16.png sizes=16x16><link rel=icon type=image/png href=/favicons/favicon-32x32.png sizes=32x32><link rel=icon type=image/png href=/favicons/android-36x36.png sizes=36x36><link rel=icon type=image/png href=/favicons/android-48x48.png sizes=48x48><link rel=icon type=image/png href=/favicons/android-72x72.png sizes=72x72><link rel=icon type=image/png href=/favicons/android-96x96.png sizes=96x96><link rel=icon type=image/png href=/favicons/android-144x144.png sizes=144x144><link rel=icon type=image/png href=/favicons/android-192x192.png sizes=192x192><title>404 Page not found | alguidelines.dev - Business Central Design Patterns</title><meta name=description content><meta property="og:title" content="404 Page not found"><meta property="og:description" content="Guidelines for when Developing AL for Microsoft Dynamics 365 Business Central"><meta property="og:type" content="website"><meta property="og:url" content="https://alguidelines.dev/404.html"><meta property="og:image" content="https://alguidelines.dev/images/og-image-fission.png"><meta itemprop=name content="404 Page not found"><meta itemprop=description content="Guidelines for when Developing AL for Microsoft Dynamics 365 Business Central"><meta name=twitter:card content="summary_large_image"><meta name=twitter:image content="https://alguidelines.dev/images/og-image-fission.png"><meta name=twitter:title content="404 Page not found"><meta name=twitter:description content="Guidelines for when Developing AL for Microsoft Dynamics 365 Business Central"><link rel=preload href=/scss/main.min.84a617ff0c0265eb62b5cd2929c572f4ed37dc4907d0cf068be4eb2974f6d60f.css as=style><link href=/scss/main.min.84a617ff0c0265eb62b5cd2929c572f4ed37dc4907d0cf068be4eb2974f6d60f.css rel=stylesheet integrity><script src=https://code.jquery.com/jquery-3.6.0.min.js integrity=sha384-vtXRMe3mGCbOeY7l30aIg8H9p3GdeSe4IFlP6G8JMa7o7lXvnz3GFKzPxzJdPfGK crossorigin=anonymous></script>
|
||||
<script src=https://unpkg.com/lunr@2.3.9/lunr.min.js integrity=sha384-203J0SNzyqHby3iU6hzvzltrWi/M41wOP5Gu+BiJMz5nwKykbkUx8Kp7iti0Lpli crossorigin=anonymous></script></head><body class=td-404><header><nav class="js-navbar-scroll navbar navbar-expand navbar-dark flex-column flex-md-row td-navbar"><a class=navbar-brand href=/><img src=/images/SiteLogo.png height=32px>
|
||||
<span class="font-weight-bold show-desktop">alguidelines.dev - Business Central Design Patterns</span>
|
||||
<span class="font-weight-bold show-mobile-inline">alguidelines.dev</span></a><div class="td-navbar-nav-scroll ml-md-auto" id=main_navbar><ul class="navbar-nav mt-2 mt-lg-0"><li class="nav-item mr-4 mb-2 mb-lg-0"><a class=nav-link href=/docs/><i class='fas fa-book'></i><span>Docs</span></a></li><li class="nav-item mr-4 mb-2 mb-lg-0"><a class=nav-link href=https://github.com/microsoft/alguidelines/discussions/ target=_blank><i class='fas fa-comments'></i><span>Discussions</span></a></li><li class="nav-item mr-4 mb-2 mb-lg-0"><a class=nav-link href=https://github.com/microsoft/alguidelines target=_blank><i class='fab fa-github'></i><span>GitHub</span></a></li><li class="nav-item mr-4 mb-2 mb-lg-0"><a class=nav-link href=https://discord.gg/4wbfNv3 target=_blank><i class='fab fa-discord'></i><span>Discord</span></a></li></ul></div><div class="navbar-nav d-none d-lg-block"><input type=search class="form-control td-search-input" placeholder=" Search this site…" aria-label="Search this site…" autocomplete=off data-offline-search-index-json-src=/offline-search-index.f57facda2998f11fdc2549a36425d2f6.json data-offline-search-base-href=/ data-offline-search-max-results=10></div></nav></header><div class="container-fluid td-default td-outer"><main role=main class=td-main><main id=main><div><h1 id=title>Not found</h1><p>Oops! This page doesn't exist. Try going back to our <a href=/>home page</a>.</p></div></main></main><footer class="bg-dark py-5 row d-print-none"><div class="container-fluid mx-sm-5"><div class=row><div class="col-6 col-sm-4 text-xs-center order-sm-2"><ul class="list-inline mb-0"><li class="list-inline-item mx-2 h3" data-toggle=tooltip data-placement=top title=GitHub aria-label=GitHub><a class=text-white target=_blank rel=noopener href=https://github.com/microsoft/alguidelines aria-label=GitHub><i class="fab fa-github"></i></a></li><li class="list-inline-item mx-2 h3" data-toggle=tooltip data-placement=top title=Twitter aria-label=Twitter><a class=text-white target=_blank rel=noopener href="https://twitter.com/search?q=%23bcalhelp" aria-label=Twitter><i class="fab fa-twitter"></i></a></li><li class="list-inline-item mx-2 h3" data-toggle=tooltip data-placement=top title=Discord aria-label=Discord><a class=text-white target=_blank rel=noopener href=https://discord.gg/4wbfNv3 aria-label=Discord><i class="fab fa-discord"></i></a></li></ul></div><div class="col-6 col-sm-4 text-right text-xs-center order-sm-3"></div><div class="col-12 col-sm-4 text-center py-2 order-sm-2"><small class=text-white>© 2023 alguidelines.dev Project All Rights Reserved</small></div></div></div></footer></div><script src=https://cdn.jsdelivr.net/npm/popper.js@1.16.1/dist/umd/popper.min.js integrity=sha384-9/reFTGAW83EW2RDu2S0VKaIzap3H66lZH81PoYlFhbGU+6BZp6G7niu735Sk7lN crossorigin=anonymous></script>
|
||||
<script src=https://cdn.jsdelivr.net/npm/bootstrap@4.6.1/dist/js/bootstrap.min.js integrity="sha512-UR25UO94eTnCVwjbXozyeVd6ZqpaAE9naiEUBK/A+QDbfSTQFhPGj5lOR6d8tsgbBk84Ggb5A3EkjsOgPRPcKA==" crossorigin=anonymous></script>
|
||||
<script src=https://cdn.jsdelivr.net/npm/mermaid@8.13.4/dist/mermaid.min.js integrity="sha512-JERecFUBbsm75UpkVheAuDOE8NdHjQBrPACfEQYPwvPG+fjgCpHAz1Jw2ci9EXmd3DdfiWth3O3CQvcfEg8gsA==" crossorigin=anonymous></script>
|
||||
<script src=/js/tabpane-persist.js></script>
|
||||
<script src=/js/main.min.9021b54d402798091af52a2fc8b747e6c5076e5f1323b276dc25e0774fb46c2d.js integrity="sha256-kCG1TUAnmAka9SovyLdH5sUHbl8TI7J23CXgd0+0bC0=" crossorigin=anonymous></script></body></html>
|
||||
23
CITATION.cff
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"
|
||||
|
|
@ -1,9 +0,0 @@
|
|||
# Microsoft Open Source Code of Conduct
|
||||
|
||||
This project has adopted the [Microsoft Open Source Code of Conduct](https://opensource.microsoft.com/codeofconduct/).
|
||||
|
||||
Resources:
|
||||
|
||||
- [Microsoft Open Source Code of Conduct](https://opensource.microsoft.com/codeofconduct/)
|
||||
- [Microsoft Code of Conduct FAQ](https://opensource.microsoft.com/codeofconduct/faq/)
|
||||
- Contact [opencode@microsoft.com](mailto:opencode@microsoft.com) with questions or concerns
|
||||
21
LICENSE
21
LICENSE
|
|
@ -1,21 +0,0 @@
|
|||
MIT License
|
||||
|
||||
Copyright (c) Microsoft Corporation.
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE
|
||||
66
README.md
66
README.md
|
|
@ -1,66 +0,0 @@
|
|||
# alguidelines.dev Site Source Repo
|
||||
|
||||
This is the source for the [alguidelines.dev](https://alguidelines.dev)
|
||||
website.
|
||||
# Business Central Design Patterns & Best Practices
|
||||
|
||||
This site is meant to house some of the community's knowledge about Microsoft Dynamics 365 Business Central AL Development best practices, particularly around hosting Design Patterns.
|
||||
|
||||
Please see the [ALGuidelines.dev](https://alguidelines.dev/) site for the full information about this project.
|
||||
|
||||
## Repo organization
|
||||
|
||||
This is a [hugo](https://gohugo.io) statically-generated site, hosted
|
||||
on [GitHub Pages](https://https://pages.github.com). The site is automatically built by
|
||||
GitHub Actions (see regen-gh-pages.yml).
|
||||
|
||||
All site content is stored in the `content` directory in markdown format.
|
||||
|
||||
```text
|
||||
content/en
|
||||
├── _index.html # Landing page
|
||||
├── author # Info about blog authors
|
||||
├── blog # Blog posts
|
||||
├── docs # Documentation pages
|
||||
└── search.md
|
||||
```
|
||||
## Contributing
|
||||
|
||||
This project welcomes contributions and suggestions. Most contributions require you to agree to a
|
||||
Contributor License Agreement (CLA) declaring that you have the right to, and actually do, grant us
|
||||
the rights to use your contribution. For details, visit https://cla.opensource.microsoft.com.
|
||||
|
||||
When you submit a pull request, a CLA bot will automatically determine whether you need to provide
|
||||
a CLA and decorate the PR appropriately (e.g., status check, comment). Simply follow the instructions
|
||||
provided by the bot. You will only need to do this once across all repos using our CLA.
|
||||
|
||||
This project has adopted the [Microsoft Open Source Code of Conduct](https://opensource.microsoft.com/codeofconduct/).
|
||||
For more information see the [Code of Conduct FAQ](https://opensource.microsoft.com/codeofconduct/faq/) or
|
||||
contact [opencode@microsoft.com](mailto:opencode@microsoft.com) with any additional questions or comments.
|
||||
|
||||
### Setup
|
||||
|
||||
**Run Hugo server**
|
||||
```
|
||||
$ hugo server
|
||||
Web Server is available at http://localhost:1313/ (bind address 127.0.0.1)
|
||||
Press Ctrl+C to stop
|
||||
```
|
||||
You can visit [localhost:1313](http://localhost:1313/) in browser to preview website.
|
||||
|
||||
### Making changes
|
||||
|
||||
1. Create a new branch
|
||||
2. Make your changes
|
||||
3. Verify your changes locally with Hugo server
|
||||
4. Commit and push your changes to the branch
|
||||
5. Raise a PR to main branch
|
||||
6. Once PR is merged, your changes would be live on the site
|
||||
|
||||
## Trademarks
|
||||
|
||||
This project may contain trademarks or logos for projects, products, or services. Authorized use of Microsoft
|
||||
trademarks or logos is subject to and must follow
|
||||
[Microsoft's Trademark & Brand Guidelines](https://www.microsoft.com/en-us/legal/intellectualproperty/trademarks/usage/general).
|
||||
Use of Microsoft trademarks or logos in modified versions of this project must not cause confusion or imply Microsoft sponsorship.
|
||||
Any use of third-party trademarks or logos are subject to those third-party's policies.
|
||||
41
SECURITY.md
41
SECURITY.md
|
|
@ -1,41 +0,0 @@
|
|||
<!-- BEGIN MICROSOFT SECURITY.MD V0.0.5 BLOCK -->
|
||||
|
||||
## Security
|
||||
|
||||
Microsoft takes the security of our software products and services seriously, which includes all source code repositories managed through our GitHub organizations, which include [Microsoft](https://github.com/Microsoft), [Azure](https://github.com/Azure), [DotNet](https://github.com/dotnet), [AspNet](https://github.com/aspnet), [Xamarin](https://github.com/xamarin), and [our GitHub organizations](https://opensource.microsoft.com/).
|
||||
|
||||
If you believe you have found a security vulnerability in any Microsoft-owned repository that meets [Microsoft's definition of a security vulnerability](https://docs.microsoft.com/en-us/previous-versions/tn-archive/cc751383(v=technet.10)), please report it to us as described below.
|
||||
|
||||
## Reporting Security Issues
|
||||
|
||||
**Please do not report security vulnerabilities through public GitHub issues.**
|
||||
|
||||
Instead, please report them to the Microsoft Security Response Center (MSRC) at [https://msrc.microsoft.com/create-report](https://msrc.microsoft.com/create-report).
|
||||
|
||||
If you prefer to submit without logging in, send email to [secure@microsoft.com](mailto:secure@microsoft.com). If possible, encrypt your message with our PGP key; please download it from the [Microsoft Security Response Center PGP Key page](https://www.microsoft.com/en-us/msrc/pgp-key-msrc).
|
||||
|
||||
You should receive a response within 24 hours. If for some reason you do not, please follow up via email to ensure we received your original message. Additional information can be found at [microsoft.com/msrc](https://www.microsoft.com/msrc).
|
||||
|
||||
Please include the requested information listed below (as much as you can provide) to help us better understand the nature and scope of the possible issue:
|
||||
|
||||
* Type of issue (e.g. buffer overflow, SQL injection, cross-site scripting, etc.)
|
||||
* Full paths of source file(s) related to the manifestation of the issue
|
||||
* The location of the affected source code (tag/branch/commit or direct URL)
|
||||
* Any special configuration required to reproduce the issue
|
||||
* Step-by-step instructions to reproduce the issue
|
||||
* Proof-of-concept or exploit code (if possible)
|
||||
* Impact of the issue, including how an attacker might exploit the issue
|
||||
|
||||
This information will help us triage your report more quickly.
|
||||
|
||||
If you are reporting for a bug bounty, more complete reports can contribute to a higher bounty award. Please visit our [Microsoft Bug Bounty Program](https://microsoft.com/msrc/bounty) page for more details about our active programs.
|
||||
|
||||
## Preferred Languages
|
||||
|
||||
We prefer all communications to be in English.
|
||||
|
||||
## Policy
|
||||
|
||||
Microsoft follows the principle of [Coordinated Vulnerability Disclosure](https://www.microsoft.com/en-us/msrc/cvd).
|
||||
|
||||
<!-- END MICROSOFT SECURITY.MD BLOCK -->
|
||||
25
SUPPORT.md
25
SUPPORT.md
|
|
@ -1,25 +0,0 @@
|
|||
# TODO: The maintainer of this repo has not yet edited this file
|
||||
|
||||
**REPO OWNER**: Do you want Customer Service & Support (CSS) support for this product/project?
|
||||
|
||||
- **No CSS support:** Fill out this template with information about how to file issues and get help.
|
||||
- **Yes CSS support:** Fill out an intake form at [aka.ms/spot](https://aka.ms/spot). CSS will work with/help you to determine next steps. More details also available at [aka.ms/onboardsupport](https://aka.ms/onboardsupport).
|
||||
- **Not sure?** Fill out a SPOT intake as though the answer were "Yes". CSS will help you decide.
|
||||
|
||||
*Then remove this first heading from this SUPPORT.MD file before publishing your repo.*
|
||||
|
||||
# Support
|
||||
|
||||
## How to file issues and get help
|
||||
|
||||
This project uses GitHub Issues to track bugs and feature requests. Please search the existing
|
||||
issues before filing new issues to avoid duplicates. For new issues, file your bug or
|
||||
feature request as a new Issue.
|
||||
|
||||
For help and questions about using this project, please **REPO MAINTAINER: INSERT INSTRUCTIONS HERE
|
||||
FOR HOW TO ENGAGE REPO OWNERS OR COMMUNITY FOR HELP. COULD BE A STACK OVERFLOW TAG OR OTHER
|
||||
CHANNEL. WHERE WILL YOU HELP PEOPLE?**.
|
||||
|
||||
## Microsoft Support Policy
|
||||
|
||||
Support for this **PROJECT or PRODUCT** is limited to the resources listed above.
|
||||
|
|
@ -1,6 +0,0 @@
|
|||
---
|
||||
title: "{{ replace .Name "-" " " | title }}"
|
||||
date: {{ .Date }}
|
||||
draft: true
|
||||
---
|
||||
|
||||
Binary file not shown.
|
Before Width: | Height: | Size: 1.1 KiB |
|
|
@ -1 +0,0 @@
|
|||
<svg id="Layer_1" data-name="Layer 1" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 255 255"><defs><style>.cls-1{fill:#2ea98e;}</style></defs><title>AL logo_VS_smallest version_v3</title><path class="cls-1" d="M92.71,61.16a12.38,12.38,0,0,0-11.56-8h-.44a12.38,12.38,0,0,0-11.56,8l-52.79,138H46.58l8.06-23h52.58l8.06,23h30.21Zm-29.16,89,17.38-49.58L98.3,150.2Z"/><path class="cls-1" d="M189.7,172.2V54.2h-28V180.92c0,12.21,11.34,18.28,18,18.28h59v-27Z"/></svg>
|
||||
|
Before Width: | Height: | Size: 461 B |
|
|
@ -1,218 +0,0 @@
|
|||
//
|
||||
// Main navbar
|
||||
//
|
||||
|
||||
.td-navbar-cover {
|
||||
background: $primary;
|
||||
|
||||
@include media-breakpoint-up(md) {
|
||||
background: transparent !important;
|
||||
|
||||
.nav-link {
|
||||
text-shadow: 1px 1px 2px $dark;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
&.navbar-bg-onscroll .nav-link {
|
||||
text-shadow: none;
|
||||
}
|
||||
}
|
||||
|
||||
.navbar-bg-onscroll {
|
||||
background: $primary !important;
|
||||
opacity: inherit;
|
||||
}
|
||||
|
||||
.td-navbar {
|
||||
background: #212121;
|
||||
min-height: 4rem;
|
||||
margin: 0;
|
||||
z-index: 32;
|
||||
|
||||
@include media-breakpoint-up(md) {
|
||||
position: fixed;
|
||||
top: 0;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
|
||||
.navbar-brand {
|
||||
text-transform: none;
|
||||
text-align: middle;
|
||||
|
||||
.nav-link {
|
||||
display: inline-block;
|
||||
margin-right: -30px;
|
||||
}
|
||||
|
||||
svg {
|
||||
display: inline-block;
|
||||
margin: 0 10px;
|
||||
height: 30px;
|
||||
}
|
||||
}
|
||||
|
||||
.nav-link {
|
||||
text-transform: none;
|
||||
font-weight: $font-weight-bold;
|
||||
}
|
||||
|
||||
.td-search-input {
|
||||
border: none;
|
||||
color: $navbar-dark-color;
|
||||
@include placeholder {
|
||||
color: $navbar-dark-color;
|
||||
}
|
||||
}
|
||||
|
||||
.dropdown {
|
||||
min-width: 100px;
|
||||
}
|
||||
|
||||
@include media-breakpoint-down(md) {
|
||||
padding-right: .5rem;
|
||||
padding-left: .75rem;
|
||||
|
||||
.td-navbar-nav-scroll {
|
||||
max-width: 100%;
|
||||
height: 2.5rem;
|
||||
margin-top: .25rem;
|
||||
overflow: hidden;
|
||||
font-size: .875rem;
|
||||
|
||||
.nav-link {
|
||||
padding-right: .25rem;
|
||||
padding-left: 0;
|
||||
}
|
||||
|
||||
.navbar-nav {
|
||||
padding-bottom: 2rem;
|
||||
overflow-x: auto;
|
||||
white-space: nowrap;
|
||||
-webkit-overflow-scrolling: touch;
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Icons
|
||||
#main_navbar {
|
||||
li i {
|
||||
padding-right: 0.5em;
|
||||
|
||||
&:before {
|
||||
display: inline-block;
|
||||
text-align: center;
|
||||
min-width: 1em;
|
||||
}
|
||||
}
|
||||
.alert {
|
||||
background-color: inherit;
|
||||
padding:0;
|
||||
color: $secondary;
|
||||
border: 0;
|
||||
font-weight: inherit;
|
||||
|
||||
&:before {
|
||||
display: inline-block;
|
||||
font-style: normal;
|
||||
font-variant: normal;
|
||||
text-rendering: auto;
|
||||
-webkit-font-smoothing: antialiased;
|
||||
font-family: "Font Awesome 5 Free";
|
||||
font-weight: 900;
|
||||
content: "\f0d9";
|
||||
padding-left: 0.5em;
|
||||
padding-right: 0.5em;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Foldable sidebar menu
|
||||
nav.foldable-nav {
|
||||
|
||||
&#td-section-nav {
|
||||
position: relative;
|
||||
}
|
||||
|
||||
&#td-section-nav label {
|
||||
margin-bottom: 0;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.td-sidebar-nav__section, .with-child ul {
|
||||
list-style: none;
|
||||
padding: 0;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
.ul-1 > li {
|
||||
padding-left: 1.5em;
|
||||
}
|
||||
|
||||
ul.foldable {
|
||||
max-height: 0;
|
||||
overflow: hidden;
|
||||
transition: max-height 0.5s cubic-bezier(0, 1, 0, 1);
|
||||
}
|
||||
|
||||
input:checked ~ ul.foldable {
|
||||
max-height: 100000vmax;
|
||||
transition: max-height 1s ease-in-out;
|
||||
}
|
||||
|
||||
input[type=checkbox] { display: none; }
|
||||
|
||||
.with-child, .without-child {
|
||||
position: relative;
|
||||
padding-left: 1.5em;
|
||||
}
|
||||
|
||||
.ul-1 .with-child > label:before {
|
||||
display: inline-block;
|
||||
font-style: normal;
|
||||
font-variant: normal;
|
||||
text-rendering: auto;
|
||||
-webkit-font-smoothing: antialiased;
|
||||
font-family: "Font Awesome 5 Free"; font-weight: 900; content: "\f0da";
|
||||
position: absolute;
|
||||
left: 0.1em;
|
||||
padding-left: 0.4em;
|
||||
padding-right: 0.4em;
|
||||
font-size: 1em;
|
||||
color: $gray-900;
|
||||
transition: all 0.5s;
|
||||
&:hover{
|
||||
transform: rotate(90deg);
|
||||
}
|
||||
}
|
||||
|
||||
.ul-1 .with-child > input:checked ~ label:before {
|
||||
color: $primary;
|
||||
transform: rotate(90deg);
|
||||
transition: transform 0.5s;
|
||||
}
|
||||
|
||||
.with-child ul { margin-top: 0.1em; }
|
||||
|
||||
}
|
||||
|
||||
@media (hover: hover) and (pointer: fine) {
|
||||
|
||||
nav.foldable-nav {
|
||||
|
||||
.ul-1 .with-child > label:hover:before {
|
||||
color: $primary;
|
||||
transform: rotate(30deg);
|
||||
transition: transform 0.5s;
|
||||
}
|
||||
|
||||
.ul-1 .with-child > input:checked ~ label:hover:before {
|
||||
color: $primary;
|
||||
transform: rotate(60deg) !important;
|
||||
transition: transform 0.5s;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,65 +0,0 @@
|
|||
//
|
||||
// Right side toc
|
||||
//
|
||||
.td-sidebar-toc {
|
||||
border-left: 1px solid $border-color;
|
||||
|
||||
@supports (position: sticky) {
|
||||
position: sticky;
|
||||
top: 4rem;
|
||||
height: calc(100vh - 4rem);
|
||||
overflow-y: auto;
|
||||
}
|
||||
|
||||
order: 2;
|
||||
padding-top: 0.75rem;
|
||||
padding-bottom: 1.5rem;
|
||||
vertical-align: top;
|
||||
|
||||
}
|
||||
|
||||
.td-page-meta {
|
||||
a {
|
||||
display: block;
|
||||
font-weight: $font-weight-medium;
|
||||
}
|
||||
}
|
||||
|
||||
.td-toc {
|
||||
padding-top: 5px;
|
||||
margin-top: 10px;
|
||||
border-top: 1px solid $black;
|
||||
|
||||
a {
|
||||
display: block;
|
||||
font-weight: $font-weight-medium;
|
||||
padding-bottom: .25rem;
|
||||
}
|
||||
|
||||
li {
|
||||
list-style: none;
|
||||
display: block;
|
||||
}
|
||||
|
||||
li li {
|
||||
margin-left: 0.5rem;
|
||||
}
|
||||
|
||||
#TableOfContents {
|
||||
// Hugo's ToC is a mouthful, this can be used to style the top level h2 entries.
|
||||
> ul > li > ul > li > a {}
|
||||
|
||||
a {
|
||||
color: $gray-600;
|
||||
|
||||
&:hover {
|
||||
color: $blue;
|
||||
text-decoration: none;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ul {
|
||||
padding-left: 0;
|
||||
}
|
||||
}
|
||||
|
|
@ -1,7 +0,0 @@
|
|||
.td-page-meta--child { display: none !important; }
|
||||
.td-page-meta--project-issue { display: none !important; }
|
||||
|
||||
.td-content pre code {
|
||||
font-family: Consolas, "Courier New", monospace;
|
||||
size: 115%;
|
||||
}
|
||||
|
|
@ -1,728 +0,0 @@
|
|||
/*
|
||||
|
||||
Add styles or override variables from the theme here.
|
||||
|
||||
*/
|
||||
|
||||
@import url('https://fonts.googleapis.com/css2?family=Lato:wght@300;400;700;900&display=swap');
|
||||
|
||||
body {
|
||||
font-family: "Segoe UI", Frutiger, "Frutiger Linotype", "Dejavu Sans", "Helvetica Neue", Arial, sans-serif !important;
|
||||
font-weight:500 !important;
|
||||
}
|
||||
|
||||
$display1-weight: 500 !default;
|
||||
$display2-weight: 100 !default;
|
||||
|
||||
$primary: #1f2a43 !default;
|
||||
$primary-light: lighten($primary, 90%) !default;
|
||||
$secondary: #2d70de !default;
|
||||
$light: rgb(255, 255, 255) !default;
|
||||
$grey: #888 !default;
|
||||
$orange: $secondary;
|
||||
|
||||
ul, ol {
|
||||
padding-left: 2em;
|
||||
}
|
||||
|
||||
.navbar-logo {
|
||||
height: 2em;
|
||||
}
|
||||
|
||||
.nav-shadow {
|
||||
box-shadow: 0 2px 2px -2px rgba(0,0,0,.2);
|
||||
}
|
||||
|
||||
.navbar-brand {
|
||||
color: #008489 !important
|
||||
}
|
||||
|
||||
.navbar-bg-onscroll {
|
||||
box-shadow: 0 2px 2px -2px rgba(0,0,0,.2);
|
||||
#docsy-top {
|
||||
display: block !important;
|
||||
}
|
||||
}
|
||||
|
||||
.control-bar {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.td-content pre {
|
||||
word-wrap: normal;
|
||||
background-color: #f8f9fa;
|
||||
padding: 1rem;
|
||||
min-width: 0;
|
||||
word-wrap: break-word;
|
||||
background-color: #fff;
|
||||
background-clip: border-box;
|
||||
border: 1px solid rgba(0, 0, 0, 0.125);
|
||||
border-radius: 0.25rem;
|
||||
}
|
||||
|
||||
.highlight > pre {
|
||||
border: 0px;
|
||||
}
|
||||
|
||||
.td-content blockquote {
|
||||
border-radius: .15rem;
|
||||
}
|
||||
|
||||
.notice.info {
|
||||
border-left-color: #6bf;
|
||||
background-color: rgba(102,187,255,.1);
|
||||
}
|
||||
|
||||
.notice.warning {
|
||||
border-left-color: #fd6;
|
||||
background-color: rgba(255,221,102,.1);
|
||||
}
|
||||
|
||||
.notice.danger {
|
||||
border-left-color: #f66;
|
||||
background-color: rgba(255,102,102,.1);
|
||||
}
|
||||
|
||||
.notice > ul {
|
||||
margin-bottom: 0em;
|
||||
}
|
||||
|
||||
#docsy-search {
|
||||
background: rgba(190, 185, 185, 0.3);
|
||||
}
|
||||
input[type="search"]::placeholder {
|
||||
color: #9B9595 !important;
|
||||
}
|
||||
|
||||
.td-box--secondary p > a:hover {
|
||||
color: #121314 !important;
|
||||
}
|
||||
|
||||
#community a {
|
||||
color: #121314 !important;
|
||||
}
|
||||
|
||||
.showcase img {
|
||||
margin: 0 30px;
|
||||
}
|
||||
|
||||
.td-content > h1 { font-weight: 700; }
|
||||
.td-content > h2 { font-weight: 700; }
|
||||
.td-content > h3 { font-weight: 700; }
|
||||
.td-content > h4 { font-weight: 700; }
|
||||
.td-content > h5 { font-weight: 700; }
|
||||
.td-content > h6 { font-weight: 700; }
|
||||
.td-content > h7 { font-weight: 700; }
|
||||
|
||||
// Custom CSS for homepage
|
||||
|
||||
.hero {
|
||||
padding: 20px 0px 25px 0px;
|
||||
color: white;
|
||||
background: rgb(65, 127, 143);
|
||||
background: linear-gradient(130deg, #4487a1 50%, #0188a3 70%);
|
||||
margin-left: -15px;
|
||||
margin-right: -15px;
|
||||
position: relative;
|
||||
overflow: hidden;
|
||||
|
||||
.container {
|
||||
position: relative;
|
||||
z-index: 2;
|
||||
}
|
||||
}
|
||||
|
||||
@media (min-width: 992px) {
|
||||
.hero {
|
||||
padding: 130px 0 100px;
|
||||
}
|
||||
|
||||
.hero-banner {
|
||||
transform: scale(1.4) translateY(10%);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@media (min-width: 1170px) {
|
||||
.hero {
|
||||
&:before {
|
||||
content: '';
|
||||
width: 60vw;
|
||||
height: 60vw;
|
||||
display: block;
|
||||
background: linear-gradient(90deg, rgb(0, 129, 155) 59%, rgba(0,145,193,1) 70%);
|
||||
position: absolute;
|
||||
left: -20vw;
|
||||
top: -20vw;
|
||||
transform: rotate(45deg);
|
||||
border-radius: 10%;
|
||||
}
|
||||
|
||||
&:after {
|
||||
content: '';
|
||||
width: 20vw;
|
||||
height: 100vw;
|
||||
display: block;
|
||||
background: linear-gradient(-74deg, #007e97 50%, #0091c1 70%);
|
||||
position: absolute;
|
||||
left: 55vw;
|
||||
bottom: -40vw;
|
||||
z-index: 1;
|
||||
transform: rotate(45deg);
|
||||
}
|
||||
|
||||
.container {
|
||||
position: relative;
|
||||
z-index: 2;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.hero-title {
|
||||
font-family: "Segoe UI", Frutiger, "Frutiger Linotype", "Dejavu Sans", "Helvetica Neue", Arial, sans-serif !important;
|
||||
font-weight: 700 !important;
|
||||
// margin-top: 80px;
|
||||
font-size: 56px;
|
||||
}
|
||||
|
||||
// .hero-banner {
|
||||
// margin-top: 80px;
|
||||
// }
|
||||
|
||||
.hero-big {
|
||||
background: rgb(0,145,193);
|
||||
background: linear-gradient(
|
||||
140deg,
|
||||
rgba(0,145,193, 1) 0%,
|
||||
rgba(0,94,113, 1) 100%
|
||||
);
|
||||
padding: 12px 38px;
|
||||
border: none;
|
||||
border-radius: 4px;
|
||||
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
|
||||
font-size: 22px;
|
||||
font-weight: 600;
|
||||
color: white;
|
||||
margin: 10px 0px 40px 0px;
|
||||
}
|
||||
|
||||
.hero-mid {
|
||||
background: rgb(0,145,193);
|
||||
background: linear-gradient(140deg,rgba(0,145,193, 1) 0%, rgba(0,94,113, 1) 100%);
|
||||
padding: 16px 30px;
|
||||
border-radius: 4px;
|
||||
border: none;
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
font-size: 18px;
|
||||
font-weight: 600;
|
||||
color: white;
|
||||
margin: 30px 0px 10px 0px;
|
||||
&:hover {
|
||||
box-shadow: 0 3px 7px #b7b7b7;
|
||||
}
|
||||
}
|
||||
|
||||
.body-container {
|
||||
margin-left: -15px;
|
||||
margin-right: -15px;
|
||||
}
|
||||
|
||||
.body-container .row {
|
||||
padding: 30px 0px;
|
||||
margin: 20px auto;
|
||||
}
|
||||
|
||||
.td-navbar {
|
||||
.nav-link {
|
||||
font-weight: normal !important;
|
||||
color: #f4f4f4 !important;
|
||||
border-bottom: 1px solid transparent;
|
||||
&:hover {
|
||||
color: #fff !important;
|
||||
border-bottom: 1px solid #00b1f7;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.td-search-input::placeholder {
|
||||
color: #fff !important;
|
||||
}
|
||||
|
||||
.section-title {
|
||||
color: #3b3c4c;
|
||||
font-weight: 700;
|
||||
font-size: 46px;
|
||||
margin: 12px 0px;
|
||||
}
|
||||
|
||||
.section-text-bold {
|
||||
color: #3b3c4c;
|
||||
font-weight: 700;
|
||||
margin: 12px 0px;
|
||||
font-size: 18px;
|
||||
line-height: 1.2;
|
||||
}
|
||||
|
||||
.section-text {
|
||||
color: #5a5b75;
|
||||
font-weight: 600;
|
||||
margin: 12px 0px;
|
||||
font-size: 18px;
|
||||
}
|
||||
|
||||
.wide-image {
|
||||
// width: 100%;
|
||||
margin-left: -15px;
|
||||
margin-right: -15px;
|
||||
}
|
||||
.big-waves {
|
||||
width: 100% !important;
|
||||
position: relative;
|
||||
top: -40px;
|
||||
}
|
||||
|
||||
.push-up {
|
||||
margin-top: -50px;
|
||||
}
|
||||
|
||||
.center-text {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
.push-up .col-lg-4, .whats-new .col-lg-4,
|
||||
.community-cards .card-shadow, .community-cards.show-desktop .col-lg-6{
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
|
||||
.card-shadow {
|
||||
padding: 35px 30px 30px 30px;
|
||||
text-align: center;
|
||||
box-shadow: 0 5px 10px 0 rgba(41,26,204,0.12);
|
||||
border: none;
|
||||
border-radius: 6px;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
.card-shadow .section-text-bold {
|
||||
font-size: 22px;
|
||||
}
|
||||
|
||||
.card-shadow img {
|
||||
margin: 2px auto;
|
||||
}
|
||||
|
||||
.card-shadow .section-text {
|
||||
color: #5a5b75;
|
||||
font-size: 16px;
|
||||
line-height: 1.5;
|
||||
}
|
||||
|
||||
.feature-card {
|
||||
padding: 20px;
|
||||
border-radius: 0;
|
||||
height: 100%;
|
||||
border: 1px solid #E8E8E8;
|
||||
background-color: #FFFFFF;
|
||||
}
|
||||
|
||||
.feature-card .card-header-custom .section-text-bold {
|
||||
font-size: 20px;
|
||||
margin: 4px 0 0;
|
||||
}
|
||||
|
||||
.feature-card p.section-text {
|
||||
font-size: 15px;
|
||||
}
|
||||
|
||||
.feature-card p:last-child {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
|
||||
.card-header-custom {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
align-items: flex-start;
|
||||
min-height: 58px;
|
||||
}
|
||||
|
||||
.card-header-custom img {
|
||||
margin-right: 16px;
|
||||
}
|
||||
|
||||
.card-header-custom .section-text-bold {
|
||||
font-size: 18px;
|
||||
color: #3b3c4c;
|
||||
}
|
||||
|
||||
.card .section-text {
|
||||
font-size: 14px;
|
||||
color: #5a5b75;
|
||||
}
|
||||
|
||||
.info-card {
|
||||
text-align: center;
|
||||
margin: 0px auto 30px auto;
|
||||
}
|
||||
|
||||
.info-card .section-text-bold {
|
||||
font-size: 22px;
|
||||
}
|
||||
|
||||
.info-card .section-text {
|
||||
font-size: 17px;
|
||||
}
|
||||
|
||||
.info-card img {
|
||||
margin: 10px auto;
|
||||
}
|
||||
|
||||
.whats-new {
|
||||
background: url("/images/whats-new-bg.webp") no-repeat;
|
||||
background-size: cover;
|
||||
background-position: 0 -150px;
|
||||
}
|
||||
|
||||
.whats-new .card-shadow {
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
.whats-new .section-title {
|
||||
color: white;
|
||||
margin-bottom: 5px;
|
||||
margin-top: 4rem
|
||||
}
|
||||
|
||||
.whats-new .card-shadow {
|
||||
background: white;
|
||||
padding: 25px 15px 15px 15px;
|
||||
min-height: unset;
|
||||
box-shadow: 0 5px 10px 0 rgba(41,26,204,0.12);
|
||||
}
|
||||
|
||||
.whats-new .card-shadow:hover {
|
||||
box-shadow: 5px 10px 30px 0 rgba(41,26,204,0.12);
|
||||
}
|
||||
|
||||
.whats-new .card-shadow-content {
|
||||
padding: 0px 12px;
|
||||
}
|
||||
|
||||
.whats-new .card-shadow button {
|
||||
padding: 12px 22px !important;
|
||||
}
|
||||
|
||||
@media (min-width: 992px) {
|
||||
.whats-new .card-shadow .section-text-bold {
|
||||
min-height: 80px;
|
||||
}
|
||||
|
||||
.whats-new .card-shadow .section-text {
|
||||
min-height: 122px;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
.card-badge {
|
||||
background: #f2f0fb;
|
||||
padding: 6px 15px;
|
||||
text-transform: uppercase;
|
||||
border-radius: 5px;
|
||||
color: #5a5b75;
|
||||
font-weight: 600;
|
||||
font-size: 16px;
|
||||
}
|
||||
|
||||
.card-shadow .hero-mid {
|
||||
width: 100%;
|
||||
position: relative;
|
||||
bottom: 0px;
|
||||
}
|
||||
|
||||
.card-shadow .hero-mid-2 {
|
||||
width: 100%;
|
||||
position: relative;
|
||||
bottom: 0px;
|
||||
}
|
||||
|
||||
.hero-mid-2 {
|
||||
background: white;
|
||||
padding: 12px 22px;
|
||||
border-radius: 4px;
|
||||
border: 1px solid #00b1f7;
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
font-size: 18px;
|
||||
font-weight: 600;
|
||||
color: #00b1f7;
|
||||
margin: 30px 0px 10px 0px;
|
||||
font-weight: 700;
|
||||
|
||||
&:hover {
|
||||
background: #55d7ff;
|
||||
background: linear-gradient(140deg, #55d7ff 0%, #3d98b4 100%);
|
||||
color: #fff;
|
||||
box-shadow: 0 3px 7px #b7b7b7;
|
||||
}
|
||||
}
|
||||
|
||||
.community-cards {
|
||||
background: url("/images/color-bg.webp") no-repeat center;
|
||||
background-size: contain;
|
||||
}
|
||||
|
||||
.community-cards .card-shadow {
|
||||
background: white;
|
||||
min-height: 150px;
|
||||
padding: 16px 22px;
|
||||
}
|
||||
|
||||
.community-cards .card-header-custom {
|
||||
margin-right: auto;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.community-cards .card-header-custom p.section-text-bold {
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
.community-cards .card-shadow img {
|
||||
margin-left: unset;
|
||||
}
|
||||
|
||||
.community-cards .card-shadow .section-text {
|
||||
text-align: left;
|
||||
font-size: 14px;
|
||||
margin-top: 4px;
|
||||
}
|
||||
|
||||
.community-cards a:hover .card-shadow {
|
||||
box-shadow: 0 10px 11px rgba(41, 26, 204, .12);
|
||||
}
|
||||
|
||||
.text-highlight {
|
||||
color: #00b1f7;
|
||||
}
|
||||
|
||||
.community-cards .row {
|
||||
padding: 30px 0px;
|
||||
margin: 20px 2px;
|
||||
}
|
||||
|
||||
.twitter-card-desktop {
|
||||
position: relative;
|
||||
left: 150px;
|
||||
top: 10px;
|
||||
}
|
||||
|
||||
.show-mobile {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.show-mobile-inline {
|
||||
display: none;
|
||||
}
|
||||
|
||||
@media (max-width: 1299px) {
|
||||
.hero {
|
||||
background: url("/images/hero-bg.webp") no-repeat;
|
||||
background-size: cover;
|
||||
}
|
||||
}
|
||||
|
||||
@media (max-width: 575px) {
|
||||
.row {
|
||||
display: inherit;
|
||||
}
|
||||
}
|
||||
|
||||
@media (min-width: 320px) and (max-width: 990px) {
|
||||
.show-desktop {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.show-mobile {
|
||||
display: block;
|
||||
}
|
||||
|
||||
.show-mobile-inline {
|
||||
display: inline;
|
||||
}
|
||||
|
||||
.hero {
|
||||
background: url("/images/hero-bg.webp") no-repeat;
|
||||
background-size: cover;
|
||||
}
|
||||
|
||||
.hero-section-mobile .hero-title {
|
||||
font-size: 39px;
|
||||
}
|
||||
|
||||
.show-mobile-margin {
|
||||
margin-top: 30px;
|
||||
}
|
||||
|
||||
.card {
|
||||
min-height: unset;
|
||||
padding-bottom: 25px;
|
||||
}
|
||||
|
||||
.card-shadow {
|
||||
min-height: unset;
|
||||
padding-bottom: 25px;
|
||||
}
|
||||
|
||||
.whats-new {
|
||||
background: url("/images/whats-new-bg.webp") no-repeat;
|
||||
background-size: cover;
|
||||
}
|
||||
|
||||
.community-cards {
|
||||
background: unset;
|
||||
}
|
||||
}
|
||||
|
||||
.feature-section .card {
|
||||
margin-bottom: 26px;
|
||||
}
|
||||
|
||||
.feature-section .col-lg-4{
|
||||
margin-bottom: 25px;
|
||||
}
|
||||
|
||||
@media (max-width: 480px) {
|
||||
.body-container .row {
|
||||
padding: 30px 8px;
|
||||
}
|
||||
|
||||
.community-cards {
|
||||
background: unset;
|
||||
}
|
||||
}
|
||||
|
||||
.show-mobile-big {
|
||||
display: none;
|
||||
}
|
||||
|
||||
@media (max-width: 767px) {
|
||||
.show-mobile-big {
|
||||
display: block;
|
||||
}
|
||||
|
||||
.show-desktop-big {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.hero-section-mobile {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
text-align: center;
|
||||
}
|
||||
}
|
||||
|
||||
.hero-section-mobile .show-mobile-big {
|
||||
margin-top: 10px;
|
||||
}
|
||||
|
||||
// {{< tabs >}}
|
||||
// from https://github.com/alex-shpak/hugo-book
|
||||
.hidden {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.book-tabs {
|
||||
margin-top: 1rem;
|
||||
margin-bottom: 1rem;
|
||||
|
||||
border: 1px solid rgba(0, 0, 0, 0.125);
|
||||
border-radius:.15rem;
|
||||
|
||||
overflow: hidden;
|
||||
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
|
||||
label {
|
||||
display: inline-block;
|
||||
padding: 0.5rem 1rem;
|
||||
border-bottom: 1px transparent;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.book-tabs-content {
|
||||
order: 999;
|
||||
width: 100%;
|
||||
border-top: 1px solid #f8f9fa;
|
||||
padding: 1rem;
|
||||
display: none;
|
||||
}
|
||||
|
||||
input[type="radio"]:checked + label {
|
||||
border-bottom: 2px solid #05b;
|
||||
}
|
||||
input[type="radio"]:checked + label + .book-tabs-content {
|
||||
display: block;
|
||||
}
|
||||
|
||||
.book-tabs-content > .highlight {
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
.book-tabs-content > blockquote.notice {
|
||||
padding: 1.2em 0em 1.2em 1em;
|
||||
margin-bottom: 1rem;
|
||||
color: #616161;
|
||||
border-left: 6px solid #2D70DE;
|
||||
}
|
||||
|
||||
.book-tabs-content > blockquote.notice.notice.info { border-left-color: #6bf; }
|
||||
.book-tabs-content > blockquote.notice.notice.warning { border-left-color: #fd6; }
|
||||
.book-tabs-content > blockquote.notice.notice.danger { border-left-color: #f66; }
|
||||
}
|
||||
|
||||
a {
|
||||
text-decoration: inherit;
|
||||
}
|
||||
|
||||
.post-img {
|
||||
display: block;
|
||||
|
||||
> p {
|
||||
text-align: center;
|
||||
font-style: italic;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
.DocSearch-Hit-title mark {
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
.DocSearch-Hit-icon svg {
|
||||
vertical-align: top;
|
||||
}
|
||||
|
||||
.DocSearch-Hits mark {
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
.DocSearch-Hit-content-wrapper {
|
||||
overflow-y: hidden;
|
||||
}
|
||||
8
author/index.html
Normal file
8
author/index.html
Normal file
|
|
@ -0,0 +1,8 @@
|
|||
<!doctype html><html lang=en class=no-js><head><meta charset=utf-8><meta name=viewport content="width=device-width,initial-scale=1,shrink-to-fit=no"><meta name=generator content="Hugo 0.119.0"><link rel=alternate type=application/rss+xml href=https://alguidelines.dev/author/index.xml><meta name=robots content="index, follow"><link rel="shortcut icon" href=/favicons/favicon.ico><link rel=apple-touch-icon href=/favicons/apple-touch-icon-180x180.png sizes=180x180><link rel=icon type=image/png href=/favicons/favicon-16x16.png sizes=16x16><link rel=icon type=image/png href=/favicons/favicon-32x32.png sizes=32x32><link rel=icon type=image/png href=/favicons/android-36x36.png sizes=36x36><link rel=icon type=image/png href=/favicons/android-48x48.png sizes=48x48><link rel=icon type=image/png href=/favicons/android-72x72.png sizes=72x72><link rel=icon type=image/png href=/favicons/android-96x96.png sizes=96x96><link rel=icon type=image/png href=/favicons/android-144x144.png sizes=144x144><link rel=icon type=image/png href=/favicons/android-192x192.png sizes=192x192><title>Author | alguidelines.dev - Business Central Design Patterns</title><meta name=description content><meta property="og:title" content="Author"><meta property="og:description" content="Guidelines for when Developing AL for Microsoft Dynamics 365 Business Central"><meta property="og:type" content="website"><meta property="og:url" content="https://alguidelines.dev/author/"><meta property="og:image" content="https://alguidelines.dev/images/og-image-fission.png"><meta itemprop=name content="Author"><meta itemprop=description content="Guidelines for when Developing AL for Microsoft Dynamics 365 Business Central"><meta name=twitter:card content="summary_large_image"><meta name=twitter:image content="https://alguidelines.dev/images/og-image-fission.png"><meta name=twitter:title content="Author"><meta name=twitter:description content="Guidelines for when Developing AL for Microsoft Dynamics 365 Business Central"><link rel=preload href=/scss/main.min.84a617ff0c0265eb62b5cd2929c572f4ed37dc4907d0cf068be4eb2974f6d60f.css as=style><link href=/scss/main.min.84a617ff0c0265eb62b5cd2929c572f4ed37dc4907d0cf068be4eb2974f6d60f.css rel=stylesheet integrity><script src=https://code.jquery.com/jquery-3.6.0.min.js integrity=sha384-vtXRMe3mGCbOeY7l30aIg8H9p3GdeSe4IFlP6G8JMa7o7lXvnz3GFKzPxzJdPfGK crossorigin=anonymous></script>
|
||||
<script src=https://unpkg.com/lunr@2.3.9/lunr.min.js integrity=sha384-203J0SNzyqHby3iU6hzvzltrWi/M41wOP5Gu+BiJMz5nwKykbkUx8Kp7iti0Lpli crossorigin=anonymous></script></head><body class=td-taxonomy><header><nav class="js-navbar-scroll navbar navbar-expand navbar-dark flex-column flex-md-row td-navbar"><a class=navbar-brand href=/><img src=/images/SiteLogo.png height=32px>
|
||||
<span class="font-weight-bold show-desktop">alguidelines.dev - Business Central Design Patterns</span>
|
||||
<span class="font-weight-bold show-mobile-inline">alguidelines.dev</span></a><div class="td-navbar-nav-scroll ml-md-auto" id=main_navbar><ul class="navbar-nav mt-2 mt-lg-0"><li class="nav-item mr-4 mb-2 mb-lg-0"><a class=nav-link href=/docs/><i class='fas fa-book'></i><span>Docs</span></a></li><li class="nav-item mr-4 mb-2 mb-lg-0"><a class=nav-link href=https://github.com/microsoft/alguidelines/discussions/ target=_blank><i class='fas fa-comments'></i><span>Discussions</span></a></li><li class="nav-item mr-4 mb-2 mb-lg-0"><a class=nav-link href=https://github.com/microsoft/alguidelines target=_blank><i class='fab fa-github'></i><span>GitHub</span></a></li><li class="nav-item mr-4 mb-2 mb-lg-0"><a class=nav-link href=https://discord.gg/4wbfNv3 target=_blank><i class='fab fa-discord'></i><span>Discord</span></a></li></ul></div><div class="navbar-nav d-none d-lg-block"><input type=search class="form-control td-search-input" placeholder=" Search this site…" aria-label="Search this site…" autocomplete=off data-offline-search-index-json-src=/offline-search-index.f57facda2998f11fdc2549a36425d2f6.json data-offline-search-base-href=/ data-offline-search-max-results=10></div></nav></header><div class="container-fluid td-default td-outer"><main role=main class=td-main><div class=td-content><main class=taxonomy-terms-page><h1>Author</h1></main></div></main><footer class="bg-dark py-5 row d-print-none"><div class="container-fluid mx-sm-5"><div class=row><div class="col-6 col-sm-4 text-xs-center order-sm-2"><ul class="list-inline mb-0"><li class="list-inline-item mx-2 h3" data-toggle=tooltip data-placement=top title=GitHub aria-label=GitHub><a class=text-white target=_blank rel=noopener href=https://github.com/microsoft/alguidelines aria-label=GitHub><i class="fab fa-github"></i></a></li><li class="list-inline-item mx-2 h3" data-toggle=tooltip data-placement=top title=Twitter aria-label=Twitter><a class=text-white target=_blank rel=noopener href="https://twitter.com/search?q=%23bcalhelp" aria-label=Twitter><i class="fab fa-twitter"></i></a></li><li class="list-inline-item mx-2 h3" data-toggle=tooltip data-placement=top title=Discord aria-label=Discord><a class=text-white target=_blank rel=noopener href=https://discord.gg/4wbfNv3 aria-label=Discord><i class="fab fa-discord"></i></a></li></ul></div><div class="col-6 col-sm-4 text-right text-xs-center order-sm-3"></div><div class="col-12 col-sm-4 text-center py-2 order-sm-2"><small class=text-white>© 2023 alguidelines.dev Project All Rights Reserved</small></div></div></div></footer></div><script src=https://cdn.jsdelivr.net/npm/popper.js@1.16.1/dist/umd/popper.min.js integrity=sha384-9/reFTGAW83EW2RDu2S0VKaIzap3H66lZH81PoYlFhbGU+6BZp6G7niu735Sk7lN crossorigin=anonymous></script>
|
||||
<script src=https://cdn.jsdelivr.net/npm/bootstrap@4.6.1/dist/js/bootstrap.min.js integrity="sha512-UR25UO94eTnCVwjbXozyeVd6ZqpaAE9naiEUBK/A+QDbfSTQFhPGj5lOR6d8tsgbBk84Ggb5A3EkjsOgPRPcKA==" crossorigin=anonymous></script>
|
||||
<script src=https://cdn.jsdelivr.net/npm/mermaid@8.13.4/dist/mermaid.min.js integrity="sha512-JERecFUBbsm75UpkVheAuDOE8NdHjQBrPACfEQYPwvPG+fjgCpHAz1Jw2ci9EXmd3DdfiWth3O3CQvcfEg8gsA==" crossorigin=anonymous></script>
|
||||
<script src=/js/tabpane-persist.js></script>
|
||||
<script src=/js/main.min.9021b54d402798091af52a2fc8b747e6c5076e5f1323b276dc25e0774fb46c2d.js integrity="sha256-kCG1TUAnmAka9SovyLdH5sUHbl8TI7J23CXgd0+0bC0=" crossorigin=anonymous></script></body></html>
|
||||
1
author/index.xml
Normal file
1
author/index.xml
Normal file
|
|
@ -0,0 +1 @@
|
|||
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom"><channel><title>alguidelines.dev - Business Central Design Patterns – Author</title><link>https://alguidelines.dev/author/</link><description>Recent content in Author on alguidelines.dev - Business Central Design Patterns</description><generator>Hugo -- gohugo.io</generator><language>en</language><atom:link href="https://alguidelines.dev/author/index.xml" rel="self" type="application/rss+xml"/></channel></rss>
|
||||
14
blog/index.html
Normal file
14
blog/index.html
Normal file
|
|
@ -0,0 +1,14 @@
|
|||
<!doctype html><html lang=en class=no-js><head><meta charset=utf-8><meta name=viewport content="width=device-width,initial-scale=1,shrink-to-fit=no"><meta name=generator content="Hugo 0.119.0"><link rel=alternate type=application/rss+xml href=https://alguidelines.dev/blog/index.xml><meta name=robots content="index, follow"><link rel="shortcut icon" href=/favicons/favicon.ico><link rel=apple-touch-icon href=/favicons/apple-touch-icon-180x180.png sizes=180x180><link rel=icon type=image/png href=/favicons/favicon-16x16.png sizes=16x16><link rel=icon type=image/png href=/favicons/favicon-32x32.png sizes=32x32><link rel=icon type=image/png href=/favicons/android-36x36.png sizes=36x36><link rel=icon type=image/png href=/favicons/android-48x48.png sizes=48x48><link rel=icon type=image/png href=/favicons/android-72x72.png sizes=72x72><link rel=icon type=image/png href=/favicons/android-96x96.png sizes=96x96><link rel=icon type=image/png href=/favicons/android-144x144.png sizes=144x144><link rel=icon type=image/png href=/favicons/android-192x192.png sizes=192x192><title>AL Guidelines Blog | alguidelines.dev - Business Central Design Patterns</title><meta name=description content><meta property="og:title" content="AL Guidelines Blog"><meta property="og:description" content="Guidelines for when Developing AL for Microsoft Dynamics 365 Business Central"><meta property="og:type" content="website"><meta property="og:url" content="https://alguidelines.dev/blog/"><meta property="og:image" content="https://alguidelines.dev/images/og-image-fission.png"><meta itemprop=name content="AL Guidelines Blog"><meta itemprop=description content="Guidelines for when Developing AL for Microsoft Dynamics 365 Business Central"><meta name=twitter:card content="summary_large_image"><meta name=twitter:image content="https://alguidelines.dev/images/og-image-fission.png"><meta name=twitter:title content="AL Guidelines Blog"><meta name=twitter:description content="Guidelines for when Developing AL for Microsoft Dynamics 365 Business Central"><link rel=preload href=/scss/main.min.84a617ff0c0265eb62b5cd2929c572f4ed37dc4907d0cf068be4eb2974f6d60f.css as=style><link href=/scss/main.min.84a617ff0c0265eb62b5cd2929c572f4ed37dc4907d0cf068be4eb2974f6d60f.css rel=stylesheet integrity><script src=https://code.jquery.com/jquery-3.6.0.min.js integrity=sha384-vtXRMe3mGCbOeY7l30aIg8H9p3GdeSe4IFlP6G8JMa7o7lXvnz3GFKzPxzJdPfGK crossorigin=anonymous></script>
|
||||
<script src=https://unpkg.com/lunr@2.3.9/lunr.min.js integrity=sha384-203J0SNzyqHby3iU6hzvzltrWi/M41wOP5Gu+BiJMz5nwKykbkUx8Kp7iti0Lpli crossorigin=anonymous></script></head><body class="td-section td-blog"><header><nav class="js-navbar-scroll navbar navbar-expand navbar-dark flex-column flex-md-row td-navbar"><a class=navbar-brand href=/><img src=/images/SiteLogo.png height=32px>
|
||||
<span class="font-weight-bold show-desktop">alguidelines.dev - Business Central Design Patterns</span>
|
||||
<span class="font-weight-bold show-mobile-inline">alguidelines.dev</span></a><div class="td-navbar-nav-scroll ml-md-auto" id=main_navbar><ul class="navbar-nav mt-2 mt-lg-0"><li class="nav-item mr-4 mb-2 mb-lg-0"><a class=nav-link href=/docs/><i class='fas fa-book'></i><span>Docs</span></a></li><li class="nav-item mr-4 mb-2 mb-lg-0"><a class=nav-link href=https://github.com/microsoft/alguidelines/discussions/ target=_blank><i class='fas fa-comments'></i><span>Discussions</span></a></li><li class="nav-item mr-4 mb-2 mb-lg-0"><a class=nav-link href=https://github.com/microsoft/alguidelines target=_blank><i class='fab fa-github'></i><span>GitHub</span></a></li><li class="nav-item mr-4 mb-2 mb-lg-0"><a class=nav-link href=https://discord.gg/4wbfNv3 target=_blank><i class='fab fa-discord'></i><span>Discord</span></a></li></ul></div><div class="navbar-nav d-none d-lg-block"><input type=search class="form-control td-search-input" placeholder=" Search this site…" aria-label="Search this site…" autocomplete=off data-offline-search-index-json-src=/offline-search-index.f57facda2998f11fdc2549a36425d2f6.json data-offline-search-base-href=/ data-offline-search-max-results=10></div></nav></header><div class="container-fluid td-outer"><div class=td-main><div class="row flex-xl-nowrap"><aside class="col-12 col-md-3 col-xl-2 td-sidebar d-print-none"><div id=td-sidebar-menu class=td-sidebar__inner><div id=content-mobile><form class="td-sidebar__search d-flex align-items-center"><input type=search class="form-control td-search-input" placeholder=" Search this site…" aria-label="Search this site…" autocomplete=off data-offline-search-index-json-src=/offline-search-index.f57facda2998f11fdc2549a36425d2f6.json data-offline-search-base-href=/ data-offline-search-max-results=10>
|
||||
<button class="btn btn-link td-sidebar__toggle d-md-none p-0 ml-3 fas fa-bars" type=button data-toggle=collapse data-target=#td-section-nav aria-controls=td-docs-nav aria-expanded=false aria-label="Toggle section navigation"></button></form></div><div id=content-desktop></div><nav class="collapse td-sidebar-nav" id=td-section-nav><ul class="td-sidebar-nav__section pr-md-3 ul-0"><li class="td-sidebar-nav__section-title td-sidebar-nav__section without-child" id=m-blog-li><a href=/blog/ title="AL Guidelines Blog" class="align-left pl-0 active td-sidebar-link td-sidebar-link__section tree-root" id=m-blog><span class=td-sidebar-nav-active-item>Blog</span></a></li></ul></nav></div></aside><aside class="d-none d-xl-block col-xl-2 td-sidebar-toc d-print-none"><div class="td-page-meta ml-2 pb-1 pt-2 mb-0"><a href=https://github.com/microsoft/alguidelines/tree/main/content/blog/_index.md class=td-page-meta--view target=_blank rel=noopener><i class="fa fa-file-alt fa-fw"></i> View page source</a>
|
||||
<a href=https://github.com/microsoft/alguidelines/edit/main/content/blog/_index.md class=td-page-meta--edit target=_blank rel=noopener><i class="fa fa-edit fa-fw"></i> Edit this page</a>
|
||||
<a href="https://github.com/microsoft/alguidelines/new/main/content/blog/_index.md?filename=change-me.md&value=---%0Atitle%3A+%22Long+Page+Title%22%0AlinkTitle%3A+%22Short+Nav+Title%22%0Aweight%3A+100%0Adescription%3A+%3E-%0A+++++Page+description+for+heading+and+indexes.%0A---%0A%0A%23%23+Heading%0A%0AEdit+this+template+to+create+your+new+page.%0A%0A%2A+Give+it+a+good+name%2C+ending+in+%60.md%60+-+e.g.+%60getting-started.md%60%0A%2A+Edit+the+%22front+matter%22+section+at+the+top+of+the+page+%28weight+controls+how+its+ordered+amongst+other+pages+in+the+same+directory%3B+lowest+number+first%29.%0A%2A+Add+a+good+commit+message+at+the+bottom+of+the+page+%28%3C80+characters%3B+use+the+extended+description+field+for+more+detail%29.%0A%2A+Create+a+new+branch+so+you+can+preview+your+new+file+and+request+a+review+via+Pull+Request.%0A" class=td-page-meta--child target=_blank rel=noopener><i class="fa fa-edit fa-fw"></i> Create child page</a>
|
||||
<a href="https://github.com/microsoft/alguidelines/issues/new?title=AL%20Guidelines%20Blog" class=td-page-meta--issue target=_blank rel=noopener><i class="fab fa-github fa-fw"></i> Create Issue</a>
|
||||
<a href="https://github.com/microsoft/alguidelines/discussions?discussions_q=AL%20Guidelines%20Blog" class=td-page-meta--issue target=_blank rel=noopener><i class="fab fa-github fa-fw"></i> Discussions</a>
|
||||
<a href=https://github.com/microsoft/alguidelines/issues/new class=td-page-meta--project-issue target=_blank rel=noopener><i class="fas fa-tasks fa-fw"></i> Create project issue</a></div><div class="taxonomy taxonomy-terms-cloud taxo-tags"><h5 class=taxonomy-title>Tag Cloud</h5><ul class=taxonomy-terms><li><a class=taxonomy-term href=https://alguidelines.dev/tags/al/ data-taxonomy-term=al><span class=taxonomy-label>AL</span><span class=taxonomy-count>37</span></a></li><li><a class=taxonomy-term href=https://alguidelines.dev/tags/api/ data-taxonomy-term=api><span class=taxonomy-label>API</span><span class=taxonomy-count>3</span></a></li><li><a class=taxonomy-term href=https://alguidelines.dev/tags/c/al/ data-taxonomy-term=c/al><span class=taxonomy-label>C/AL</span><span class=taxonomy-count>136</span></a></li><li><a class=taxonomy-term href=https://alguidelines.dev/tags/decoupling/ data-taxonomy-term=decoupling><span class=taxonomy-label>Decoupling</span><span class=taxonomy-count>2</span></a></li><li><a class=taxonomy-term href=https://alguidelines.dev/tags/extendability/ data-taxonomy-term=extendability><span class=taxonomy-label>Extendability</span><span class=taxonomy-count>3</span></a></li><li><a class=taxonomy-term href=https://alguidelines.dev/tags/interface/ data-taxonomy-term=interface><span class=taxonomy-label>Interface</span><span class=taxonomy-count>2</span></a></li><li><a class=taxonomy-term href=https://alguidelines.dev/tags/performance/ data-taxonomy-term=performance><span class=taxonomy-label>Performance</span><span class=taxonomy-count>2</span></a></li><li><a class=taxonomy-term href=https://alguidelines.dev/tags/productivity/ data-taxonomy-term=productivity><span class=taxonomy-label>Productivity</span><span class=taxonomy-count>1</span></a></li><li><a class=taxonomy-term href=https://alguidelines.dev/tags/readability/ data-taxonomy-term=readability><span class=taxonomy-label>Readability</span><span class=taxonomy-count>56</span></a></li><li><a class=taxonomy-term href=https://alguidelines.dev/tags/telemetry/ data-taxonomy-term=telemetry><span class=taxonomy-label>Telemetry</span><span class=taxonomy-count>1</span></a></li><li><a class=taxonomy-term href=https://alguidelines.dev/tags/testability/ data-taxonomy-term=testability><span class=taxonomy-label>Testability</span><span class=taxonomy-count>2</span></a></li></ul></div><div class="taxonomy taxonomy-terms-cloud taxo-categories"><h5 class=taxonomy-title>Categories</h5><ul class=taxonomy-terms><li><a class=taxonomy-term href=https://alguidelines.dev/categories/best-practice/ data-taxonomy-term=best-practice><span class=taxonomy-label>Best Practice</span><span class=taxonomy-count>91</span></a></li><li><a class=taxonomy-term href=https://alguidelines.dev/categories/pattern/ data-taxonomy-term=pattern><span class=taxonomy-label>Pattern</span><span class=taxonomy-count>73</span></a></li></ul></div></aside><main class="col-12 col-md-9 col-xl-8 pl-md-5 pr-md-4" role=main><a class="btn btn-lg -bg-orange td-rss-button d-none d-lg-block" href=https://alguidelines.dev/blog/index.xml target=_blank>RSS <i class="fa fa-rss ml-2"></i></a><div class=row><div class=col-12></div></div><div class="row pl-2 pt-2"><div class=col></div></div></main></div></div><footer class="bg-dark py-5 row d-print-none"><div class="container-fluid mx-sm-5"><div class=row><div class="col-6 col-sm-4 text-xs-center order-sm-2"><ul class="list-inline mb-0"><li class="list-inline-item mx-2 h3" data-toggle=tooltip data-placement=top title=GitHub aria-label=GitHub><a class=text-white target=_blank rel=noopener href=https://github.com/microsoft/alguidelines aria-label=GitHub><i class="fab fa-github"></i></a></li><li class="list-inline-item mx-2 h3" data-toggle=tooltip data-placement=top title=Twitter aria-label=Twitter><a class=text-white target=_blank rel=noopener href="https://twitter.com/search?q=%23bcalhelp" aria-label=Twitter><i class="fab fa-twitter"></i></a></li><li class="list-inline-item mx-2 h3" data-toggle=tooltip data-placement=top title=Discord aria-label=Discord><a class=text-white target=_blank rel=noopener href=https://discord.gg/4wbfNv3 aria-label=Discord><i class="fab fa-discord"></i></a></li></ul></div><div class="col-6 col-sm-4 text-right text-xs-center order-sm-3"></div><div class="col-12 col-sm-4 text-center py-2 order-sm-2"><small class=text-white>© 2023 alguidelines.dev Project All Rights Reserved</small></div></div></div></footer></div><script src=https://cdn.jsdelivr.net/npm/popper.js@1.16.1/dist/umd/popper.min.js integrity=sha384-9/reFTGAW83EW2RDu2S0VKaIzap3H66lZH81PoYlFhbGU+6BZp6G7niu735Sk7lN crossorigin=anonymous></script>
|
||||
<script src=https://cdn.jsdelivr.net/npm/bootstrap@4.6.1/dist/js/bootstrap.min.js integrity="sha512-UR25UO94eTnCVwjbXozyeVd6ZqpaAE9naiEUBK/A+QDbfSTQFhPGj5lOR6d8tsgbBk84Ggb5A3EkjsOgPRPcKA==" crossorigin=anonymous></script>
|
||||
<script src=https://cdn.jsdelivr.net/npm/mermaid@8.13.4/dist/mermaid.min.js integrity="sha512-JERecFUBbsm75UpkVheAuDOE8NdHjQBrPACfEQYPwvPG+fjgCpHAz1Jw2ci9EXmd3DdfiWth3O3CQvcfEg8gsA==" crossorigin=anonymous></script>
|
||||
<script src=/js/tabpane-persist.js></script>
|
||||
<script src=/js/main.min.9021b54d402798091af52a2fc8b747e6c5076e5f1323b276dc25e0774fb46c2d.js integrity="sha256-kCG1TUAnmAka9SovyLdH5sUHbl8TI7J23CXgd0+0bC0=" crossorigin=anonymous></script></body></html>
|
||||
1
blog/index.xml
Normal file
1
blog/index.xml
Normal file
|
|
@ -0,0 +1 @@
|
|||
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom"><channel><title>alguidelines.dev - Business Central Design Patterns – AL Guidelines Blog</title><link>https://alguidelines.dev/blog/</link><description>Recent content in AL Guidelines Blog on alguidelines.dev - Business Central Design Patterns</description><generator>Hugo -- gohugo.io</generator><language>en</language><atom:link href="https://alguidelines.dev/blog/index.xml" rel="self" type="application/rss+xml"/></channel></rss>
|
||||
8
categories/best-practice/index.html
Normal file
8
categories/best-practice/index.html
Normal file
File diff suppressed because one or more lines are too long
1507
categories/best-practice/index.xml
Normal file
1507
categories/best-practice/index.xml
Normal file
File diff suppressed because it is too large
Load diff
8
categories/index.html
Normal file
8
categories/index.html
Normal file
|
|
@ -0,0 +1,8 @@
|
|||
<!doctype html><html lang=en class=no-js><head><meta charset=utf-8><meta name=viewport content="width=device-width,initial-scale=1,shrink-to-fit=no"><meta name=generator content="Hugo 0.119.0"><link rel=alternate type=application/rss+xml href=https://alguidelines.dev/categories/index.xml><meta name=robots content="index, follow"><link rel="shortcut icon" href=/favicons/favicon.ico><link rel=apple-touch-icon href=/favicons/apple-touch-icon-180x180.png sizes=180x180><link rel=icon type=image/png href=/favicons/favicon-16x16.png sizes=16x16><link rel=icon type=image/png href=/favicons/favicon-32x32.png sizes=32x32><link rel=icon type=image/png href=/favicons/android-36x36.png sizes=36x36><link rel=icon type=image/png href=/favicons/android-48x48.png sizes=48x48><link rel=icon type=image/png href=/favicons/android-72x72.png sizes=72x72><link rel=icon type=image/png href=/favicons/android-96x96.png sizes=96x96><link rel=icon type=image/png href=/favicons/android-144x144.png sizes=144x144><link rel=icon type=image/png href=/favicons/android-192x192.png sizes=192x192><title>Categories | alguidelines.dev - Business Central Design Patterns</title><meta name=description content><meta property="og:title" content="Categories"><meta property="og:description" content="Guidelines for when Developing AL for Microsoft Dynamics 365 Business Central"><meta property="og:type" content="website"><meta property="og:url" content="https://alguidelines.dev/categories/"><meta property="og:image" content="https://alguidelines.dev/images/og-image-fission.png"><meta itemprop=name content="Categories"><meta itemprop=description content="Guidelines for when Developing AL for Microsoft Dynamics 365 Business Central"><meta name=twitter:card content="summary_large_image"><meta name=twitter:image content="https://alguidelines.dev/images/og-image-fission.png"><meta name=twitter:title content="Categories"><meta name=twitter:description content="Guidelines for when Developing AL for Microsoft Dynamics 365 Business Central"><link rel=preload href=/scss/main.min.84a617ff0c0265eb62b5cd2929c572f4ed37dc4907d0cf068be4eb2974f6d60f.css as=style><link href=/scss/main.min.84a617ff0c0265eb62b5cd2929c572f4ed37dc4907d0cf068be4eb2974f6d60f.css rel=stylesheet integrity><script src=https://code.jquery.com/jquery-3.6.0.min.js integrity=sha384-vtXRMe3mGCbOeY7l30aIg8H9p3GdeSe4IFlP6G8JMa7o7lXvnz3GFKzPxzJdPfGK crossorigin=anonymous></script>
|
||||
<script src=https://unpkg.com/lunr@2.3.9/lunr.min.js integrity=sha384-203J0SNzyqHby3iU6hzvzltrWi/M41wOP5Gu+BiJMz5nwKykbkUx8Kp7iti0Lpli crossorigin=anonymous></script></head><body class=td-taxonomy><header><nav class="js-navbar-scroll navbar navbar-expand navbar-dark flex-column flex-md-row td-navbar"><a class=navbar-brand href=/><img src=/images/SiteLogo.png height=32px>
|
||||
<span class="font-weight-bold show-desktop">alguidelines.dev - Business Central Design Patterns</span>
|
||||
<span class="font-weight-bold show-mobile-inline">alguidelines.dev</span></a><div class="td-navbar-nav-scroll ml-md-auto" id=main_navbar><ul class="navbar-nav mt-2 mt-lg-0"><li class="nav-item mr-4 mb-2 mb-lg-0"><a class=nav-link href=/docs/><i class='fas fa-book'></i><span>Docs</span></a></li><li class="nav-item mr-4 mb-2 mb-lg-0"><a class=nav-link href=https://github.com/microsoft/alguidelines/discussions/ target=_blank><i class='fas fa-comments'></i><span>Discussions</span></a></li><li class="nav-item mr-4 mb-2 mb-lg-0"><a class=nav-link href=https://github.com/microsoft/alguidelines target=_blank><i class='fab fa-github'></i><span>GitHub</span></a></li><li class="nav-item mr-4 mb-2 mb-lg-0"><a class=nav-link href=https://discord.gg/4wbfNv3 target=_blank><i class='fab fa-discord'></i><span>Discord</span></a></li></ul></div><div class="navbar-nav d-none d-lg-block"><input type=search class="form-control td-search-input" placeholder=" Search this site…" aria-label="Search this site…" autocomplete=off data-offline-search-index-json-src=/offline-search-index.f57facda2998f11fdc2549a36425d2f6.json data-offline-search-base-href=/ data-offline-search-max-results=10></div></nav></header><div class="container-fluid td-default td-outer"><main role=main class=td-main><div class=td-content><main class=taxonomy-terms-page><h1>Categories</h1><div class="taxonomy taxonomy-terms-cloud taxo-categories"><ul class=taxonomy-terms><li><a class=taxonomy-term href=https://alguidelines.dev/categories/best-practice/ data-taxonomy-term=best-practice><span class=taxonomy-label>Best Practice</span><span class=taxonomy-count>91</span></a></li><li><a class=taxonomy-term href=https://alguidelines.dev/categories/pattern/ data-taxonomy-term=pattern><span class=taxonomy-label>Pattern</span><span class=taxonomy-count>73</span></a></li></ul></div></main></div></main><footer class="bg-dark py-5 row d-print-none"><div class="container-fluid mx-sm-5"><div class=row><div class="col-6 col-sm-4 text-xs-center order-sm-2"><ul class="list-inline mb-0"><li class="list-inline-item mx-2 h3" data-toggle=tooltip data-placement=top title=GitHub aria-label=GitHub><a class=text-white target=_blank rel=noopener href=https://github.com/microsoft/alguidelines aria-label=GitHub><i class="fab fa-github"></i></a></li><li class="list-inline-item mx-2 h3" data-toggle=tooltip data-placement=top title=Twitter aria-label=Twitter><a class=text-white target=_blank rel=noopener href="https://twitter.com/search?q=%23bcalhelp" aria-label=Twitter><i class="fab fa-twitter"></i></a></li><li class="list-inline-item mx-2 h3" data-toggle=tooltip data-placement=top title=Discord aria-label=Discord><a class=text-white target=_blank rel=noopener href=https://discord.gg/4wbfNv3 aria-label=Discord><i class="fab fa-discord"></i></a></li></ul></div><div class="col-6 col-sm-4 text-right text-xs-center order-sm-3"></div><div class="col-12 col-sm-4 text-center py-2 order-sm-2"><small class=text-white>© 2023 alguidelines.dev Project All Rights Reserved</small></div></div></div></footer></div><script src=https://cdn.jsdelivr.net/npm/popper.js@1.16.1/dist/umd/popper.min.js integrity=sha384-9/reFTGAW83EW2RDu2S0VKaIzap3H66lZH81PoYlFhbGU+6BZp6G7niu735Sk7lN crossorigin=anonymous></script>
|
||||
<script src=https://cdn.jsdelivr.net/npm/bootstrap@4.6.1/dist/js/bootstrap.min.js integrity="sha512-UR25UO94eTnCVwjbXozyeVd6ZqpaAE9naiEUBK/A+QDbfSTQFhPGj5lOR6d8tsgbBk84Ggb5A3EkjsOgPRPcKA==" crossorigin=anonymous></script>
|
||||
<script src=https://cdn.jsdelivr.net/npm/mermaid@8.13.4/dist/mermaid.min.js integrity="sha512-JERecFUBbsm75UpkVheAuDOE8NdHjQBrPACfEQYPwvPG+fjgCpHAz1Jw2ci9EXmd3DdfiWth3O3CQvcfEg8gsA==" crossorigin=anonymous></script>
|
||||
<script src=/js/tabpane-persist.js></script>
|
||||
<script src=/js/main.min.9021b54d402798091af52a2fc8b747e6c5076e5f1323b276dc25e0774fb46c2d.js integrity="sha256-kCG1TUAnmAka9SovyLdH5sUHbl8TI7J23CXgd0+0bC0=" crossorigin=anonymous></script></body></html>
|
||||
1
categories/index.xml
Normal file
1
categories/index.xml
Normal file
|
|
@ -0,0 +1 @@
|
|||
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom"><channel><title>alguidelines.dev - Business Central Design Patterns – Categories</title><link>https://alguidelines.dev/categories/</link><description>Recent content in Categories on alguidelines.dev - Business Central Design Patterns</description><generator>Hugo -- gohugo.io</generator><language>en</language><atom:link href="https://alguidelines.dev/categories/index.xml" rel="self" type="application/rss+xml"/></channel></rss>
|
||||
8
categories/pattern/index.html
Normal file
8
categories/pattern/index.html
Normal file
File diff suppressed because one or more lines are too long
4481
categories/pattern/index.xml
Normal file
4481
categories/pattern/index.xml
Normal file
File diff suppressed because one or more lines are too long
|
|
@ -1,250 +0,0 @@
|
|||
---
|
||||
title: "ALGuidelines.dev"
|
||||
linkTitle: "ALGuidelines.dev"
|
||||
description: "Design Patterns and Guidelines for AL development for Microsoft Dynamics 365 Business Central"
|
||||
images: ["images/og-image-fission.png"]
|
||||
---
|
||||
|
||||
<div class="hero">
|
||||
<div class="container">
|
||||
<div class="row justify-content-center align-items-center">
|
||||
<div class="col-xl-7 col-lg-6 col-md-6 hero-section-mobile">
|
||||
<h1 class="hero-title">
|
||||
Guidelines and Patterns<br />
|
||||
for Development for<br />
|
||||
Microsoft Dynamics 365 Business Central
|
||||
</h1>
|
||||
<img
|
||||
class="img-fluid hero-banner show-mobile-big"
|
||||
src="/images/brainstorming-team-meeting_38894532.png"
|
||||
alt-text="Teamwork Graphic"
|
||||
/>
|
||||
<br />
|
||||
<a href="https://www.youtube.com/watch?v=CWpaD9RUa6U"
|
||||
><button class="hero-big">
|
||||
<i class="fab fa-youtube"></i> Watch the Launch
|
||||
</button></a
|
||||
>
|
||||
</div>
|
||||
|
||||
<div class="col-xl-5 col-lg-6 col-md-6 show-desktop-big">
|
||||
<img
|
||||
class="img-fluid hero-banner"
|
||||
src="/images/brainstorming-team-meeting_38894532.png"
|
||||
alt-text="Teamwork Graphic"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="body-container">
|
||||
<div class="container">
|
||||
<br />
|
||||
<div class="row">
|
||||
<div class="col-lg-5">
|
||||
<h2 class="section-title">Design Patterns?</h2>
|
||||
<p class="section-text-bold">
|
||||
A "Design Pattern" is a software design template to solve common development needs.
|
||||
</p>
|
||||
<p class="section-text">
|
||||
This site is community run and Microsoft endorsed.
|
||||
</p>
|
||||
<a href="/docs"
|
||||
><button class="hero-mid">Learn More</button></a
|
||||
>
|
||||
</div>
|
||||
|
||||
<div class="col-lg-7">
|
||||
<img
|
||||
class="img-fluid show-mobile-margin"
|
||||
src="/images/body-img-1.png"
|
||||
alt-text="Design Patterns Banner"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="wide-image show-desktop">
|
||||
<img src="/images/wave.webp" class="big-waves" />
|
||||
</div>
|
||||
|
||||
<div class="body-container push-up">
|
||||
<div class="container">
|
||||
<div class="row">
|
||||
<div class="col-lg-12 center-text" style="margin-bottom: 50px">
|
||||
<h2 class="section-title">Benefits</h2>
|
||||
</div>
|
||||
|
||||
<div class="col-lg-4">
|
||||
<div class="card-shadow">
|
||||
<h2 class="section-text-bold"><i class="fas fa-retweet"></i></h2>
|
||||
<h4 class="section-text-bold">Repeatability</h4>
|
||||
<p class="section-text">
|
||||
By using consistent patterns and best practices, code is far more reusable across diverse projects.
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-lg-4">
|
||||
<div class="card-shadow">
|
||||
<h2><i class="fas fa-people-carry"></i></h2>
|
||||
<h4 class="section-text-bold">Teamwork</h4>
|
||||
<p class="section-text">
|
||||
If all code uses design patterns and best practices, it's simpler for new team members to ensure
|
||||
that code is correct, as well as making it easier to jump into a project.
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-lg-4">
|
||||
<div class="card-shadow">
|
||||
<h2><i class="fas fa-rocket"></i></h2>
|
||||
<h4 class="section-text-bold">Performance</h4>
|
||||
<p class="section-text">
|
||||
Many design patterns and best practices are also heavily focused on ensuring compliance with
|
||||
the latest performance recommendations to get the most out of every system.
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
<div class="wide-image show-desktop">
|
||||
<img src="/images/wave.webp" class="big-waves" />
|
||||
</div>
|
||||
|
||||
<div class="body-container whats-new">
|
||||
<div class="container">
|
||||
<div class="row">
|
||||
<div class="col-lg-12 center-text" style="margin-bottom: 40px">
|
||||
<h2 class="section-title">What's New</h2>
|
||||
</div>
|
||||
<div class="col-lg-4">
|
||||
<div class="card-shadow">
|
||||
<span class="card-badge">NEW</span>
|
||||
<h4 class="section-text-bold mt-4">
|
||||
Getting Started With Agentic Coding
|
||||
</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.
|
||||
</p>
|
||||
<a href="/docs/agentic-coding/gettingstarted/">
|
||||
<button class="hero-mid-2 align-self-end">Get Started</button>
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="col-lg-4">
|
||||
<div class="card-shadow">
|
||||
<span class="card-badge">GUIDE</span>
|
||||
<h4 class="section-text-bold mt-4">
|
||||
Vibe Coding Rules
|
||||
</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.
|
||||
</p>
|
||||
<a href="/docs/agentic-coding/vibe-coding-rules/"
|
||||
><button class="hero-mid-2 align-self-end">Explore Rules</button></a
|
||||
>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="col-lg-4">
|
||||
<div class="card-shadow">
|
||||
<span class="card-badge">TOOLS</span>
|
||||
<h4 class="section-text-bold mt-4">
|
||||
Agentic Tools
|
||||
</h4>
|
||||
<div class="card-shadow-content">
|
||||
<p class="section-text">
|
||||
Model Context Protocol (MCP) servers that enhance AI assistants for Business Central development workflows.
|
||||
</p>
|
||||
<a
|
||||
href="/docs/agentic-coding/communityresources/tools/"
|
||||
><button class="hero-mid-2 align-self-end">Explore Tools</button></a
|
||||
>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="body-container">
|
||||
<div class="container">
|
||||
<div class="row">
|
||||
<div class="col-lg-4">
|
||||
<br />
|
||||
<br />
|
||||
<h1 class="section-title">Join the Community</h1>
|
||||
<br />
|
||||
|
||||
<div class="show-mobile">
|
||||
<div class="community-cards">
|
||||
<a href="https://discord.gg/4wbfNv3">
|
||||
<div class="card-shadow">
|
||||
<div class="card-header-custom">
|
||||
<h2 style="padding-right: 0.8rem;"><i class="fab fa-discord"></i></h2>
|
||||
<h2 class="section-text-bold">Discord</h2>
|
||||
</div>
|
||||
<p class="section-text">
|
||||
Join the BC Community to engage with other BC Professionals.
|
||||
</p>
|
||||
</div>
|
||||
</a>
|
||||
<a href="https://github.com/microsoft/alguidelines">
|
||||
<div class="card-shadow">
|
||||
<div class="card-header-custom">
|
||||
<h2 style="padding-right: 0.8rem;"><i class="fab fa-github"></i></h2>
|
||||
<h2 class="section-text-bold">GitHub</h2>
|
||||
</div>
|
||||
<p class="section-text">
|
||||
Dive into the code. Submit PRs
|
||||
and collaborate with other like-minded contributors.
|
||||
</p>
|
||||
</div>
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="col-lg-8 community-cards show-desktop">
|
||||
<div class="row">
|
||||
<div class="col-lg-6">
|
||||
<a href="https://discord.gg/4wbfNv3"
|
||||
><div class="card-shadow">
|
||||
<div class="card-header-custom">
|
||||
<h2 style="padding-right: 0.8rem;"><i class="fab fa-discord"></i></h2>
|
||||
<h2 class="section-text-bold">Discord</h2>
|
||||
</div>
|
||||
<p class="section-text">
|
||||
Join the BC Community to engage with other BC Professionals.
|
||||
</p>
|
||||
</div></a
|
||||
>
|
||||
</div>
|
||||
<div class="col-lg-6">
|
||||
<a href="https://github.com/microsoft/alguidelines"
|
||||
><div class="card-shadow">
|
||||
<div class="card-header-custom">
|
||||
<h2 style="padding-right: 0.8rem;"><i class="fab fa-github"></i></h2>
|
||||
<h2 class="section-text-bold">GitHub</h2>
|
||||
</div>
|
||||
<p class="section-text">
|
||||
Dive into the code. Submit PRs
|
||||
and collaborate with other like-minded contributors.
|
||||
</p>
|
||||
</div></a
|
||||
>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
|
@ -1,5 +0,0 @@
|
|||
---
|
||||
title: "AL Guidelines Blog"
|
||||
linkTitle: "Blog"
|
||||
type: "blog"
|
||||
---
|
||||
|
|
@ -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,27 +0,0 @@
|
|||
---
|
||||
title: "DeleteAll"
|
||||
tags: ["AL","Performance"]
|
||||
categories: ["Best Practice"]
|
||||
---
|
||||
|
||||
_Created by waldo, Described by waldo_
|
||||
|
||||
## Description
|
||||
|
||||
When you perform a "DeleteAll" when there is nothing to delete, it will still perform a lock. When you for example perform a DeleteAll on an empty table, it will result in a table lock.
|
||||
Therefore it's good practice to always check if the table is empty when performing a DeleteAll.
|
||||
|
||||
## Bad code
|
||||
|
||||
```al
|
||||
EmptyTableWLD.SetRange(Code, 'AJ');
|
||||
EmptyTableWLD.DeleteAll(true);
|
||||
```
|
||||
|
||||
## Good code
|
||||
|
||||
```al
|
||||
EmptyTableWLD.SetRange(Code, 'AJ');
|
||||
if not EmptyTableWLD.IsEmpty() then
|
||||
EmptyTableWLD.DeleteAll(true);
|
||||
```
|
||||
|
|
@ -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,201 +0,0 @@
|
|||
---
|
||||
title: "Subscriber Codeunits"
|
||||
tags: ["AL","Performance"]
|
||||
categories: ["Best Practice"]
|
||||
---
|
||||
|
||||
_Created by waldo, Described by waldo_
|
||||
|
||||
## Description
|
||||
|
||||
In general, subscribers have to be put in codeunits. There are a few performance considerations that you should keep in the back of your minds, when designing such a codeunit.
|
||||
|
||||
- Keep the codeunit as small as possible
|
||||
- Work with a single instance codeunit
|
||||
- only subscribe when necessary
|
||||
- Avoid generic OnInsert/OnModify/OnDelete
|
||||
|
||||
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/)".
|
||||
|
||||
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"
|
||||
{
|
||||
SingleInstance = true;
|
||||
|
||||
[EventSubscriber(ObjectType::Codeunit, Codeunit::"Manual Setup", 'OnRegisterManualSetup', '', false, false)]
|
||||
local procedure OnRegisterManualSetup(sender: Codeunit "Manual Setup")
|
||||
var
|
||||
AppId: ModuleInfo;
|
||||
NameListLbl: Label 'Linked Texts Framework - List', Locked = true;
|
||||
DescriptionListLbl: Label 'Edit linked texts', Locked = true;
|
||||
KeyWordListLbl: Label 'LT,Distri,Technical,Functional,Reports', Locked = true;
|
||||
NameReportLbl: Label 'Linked Texts Framework - Reports', Locked = true;
|
||||
DescriptionReportLbl: Label 'View linked texts reports', Locked = true;
|
||||
KeyWordReportLbl: Label 'LT,Distri,Technical,Functional,Reports', Locked = true;
|
||||
begin
|
||||
navapp.GetCurrentModuleInfo(AppId);
|
||||
Sender.Insert(NameListLbl, DescriptionListLbl, KeyWordListLbl, page::"LTE Linked Text List", AppId.Id(), "Manual Setup Category"::General);
|
||||
Sender.Insert(NameReportLbl, DescriptionReportLbl, KeyWordReportLbl, page::"LTE Linked Texts Reports", AppId.Id(), "Manual Setup Category"::General);
|
||||
end;
|
||||
|
||||
[EventSubscriber(ObjectType::Codeunit, codeunit::"Manual Setup", 'OnRegisterManualSetup', '', false, false)]
|
||||
local procedure OnRegisterManualSetup(sender: Codeunit "Manual Setup")
|
||||
var
|
||||
AppId: ModuleInfo;
|
||||
NameLayoutLbl: Label 'Report Helper - Layout', Locked = true;
|
||||
DescriptionLayoutLbl: Label 'Set up or update report layout list', Locked = true;
|
||||
KeyWordLayoutLbl: Label 'RH,Distri,Technical,Functional,Reports,Layout', Locked = true;
|
||||
NameCaptionsLbl: Label 'Report Helper - Captions', Locked = true;
|
||||
DescriptionCaptionsLbl: Label 'Set up or update captions list', Locked = true;
|
||||
KeyWordCaptionsLbl: Label 'RH,Distri,Technical,Functional,Reports,Captions', Locked = true;
|
||||
NameFunctionsLbl: Label 'Report Helper - Functions', Locked = true;
|
||||
DescriptionFunctionsLbl: Label 'Set up or disable functions', Locked = true;
|
||||
KeyWordFunctionsLbl: Label 'RH,Distri,Technical,Functional,Reports,Functions', Locked = true;
|
||||
NameDFCLbl: Label 'Report Helper - Default Footer', Locked = true;
|
||||
DescriptionDFCLbl: Label 'Set up or update default footer', Locked = true;
|
||||
KeyWordDFCLbl: Label 'RH,Distri,Technical,Functional,Reports,Default,Footer', Locked = true;
|
||||
begin
|
||||
navapp.GetCurrentModuleInfo(AppId);
|
||||
Sender.Insert(NameLayoutLbl, DescriptionLayoutLbl, KeyWordLayoutLbl, page::"RHE Report Layout List", AppId.Id(), "Manual Setup Category"::General);
|
||||
Sender.Insert(NameCaptionsLbl, DescriptionCaptionsLbl, KeyWordCaptionsLbl, page::"RHE Captions", AppId.Id(), "Manual Setup Category"::General);
|
||||
Sender.Insert(NameFunctionsLbl, DescriptionFunctionsLbl, KeyWordFunctionsLbl, page::"RHE Functions", AppId.Id(), "Manual Setup Category"::General);
|
||||
Sender.Insert(NameDFCLbl, DescriptionDFCLbl, KeyWordDFCLbl, page::"RHE Default Footer Card", AppId.Id(), "Manual Setup Category"::General);
|
||||
end;
|
||||
}
|
||||
```
|
||||
|
||||
### Good code
|
||||
|
||||
Split into 2 codeunits, and move the business logic out.
|
||||
|
||||
```AL
|
||||
codeunit 2037325 "LTE Setup Subs"
|
||||
{
|
||||
SingleInstance = true;
|
||||
|
||||
[EventSubscriber(ObjectType::Codeunit, Codeunit::"Manual Setup", 'OnRegisterManualSetup', '', false, false)]
|
||||
local procedure OnRegisterManualSetup(sender: Codeunit "Manual Setup")
|
||||
var
|
||||
RegisterLTEManualSetup: codeunit "Register LTE Manual Setup";
|
||||
begin
|
||||
RegisterLTEManualSetup.RegisterLTEManualSetup();
|
||||
end;
|
||||
}
|
||||
|
||||
codeunit 2037324 "RHE Setup Subs"
|
||||
{
|
||||
SingleInstance = true;
|
||||
|
||||
[EventSubscriber(ObjectType::Codeunit, Codeunit::"Manual Setup", 'OnRegisterManualSetup', '', false, false)]
|
||||
local procedure OnRegisterManualSetup(sender: Codeunit "Manual Setup")
|
||||
var
|
||||
RegisterRHEManualSetup: codeunit "Register RHE Manual Setup";
|
||||
begin
|
||||
RegisterRHEManualSetup.RegisterRHEManualSetup();
|
||||
end;
|
||||
}
|
||||
|
||||
|
||||
```
|
||||
|
||||
## Work with a single instance codeunit
|
||||
|
||||
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"
|
||||
{
|
||||
[EventSubscriber(ObjectType::Codeunit, Codeunit::"Manual Setup", 'OnRegisterManualSetup', '', false, false)]
|
||||
local procedure OnRegisterManualSetup(sender: Codeunit "Manual Setup")
|
||||
var
|
||||
RegisterRHEManualSetup: codeunit "Register RHE Manual Setup";
|
||||
begin
|
||||
RegisterRHEManualSetup.RegisterRHEManualSetup();
|
||||
end;
|
||||
}
|
||||
```
|
||||
|
||||
### Good code
|
||||
|
||||
```AL
|
||||
codeunit 2037324 "RHE Setup Subs"
|
||||
{
|
||||
SingleInstance = true;
|
||||
|
||||
[EventSubscriber(ObjectType::Codeunit, Codeunit::"Manual Setup", 'OnRegisterManualSetup', '', false, false)]
|
||||
local procedure OnRegisterManualSetup(sender: Codeunit "Manual Setup")
|
||||
var
|
||||
RegisterRHEManualSetup: codeunit "Register RHE Manual Setup";
|
||||
begin
|
||||
RegisterRHEManualSetup.RegisterRHEManualSetup();
|
||||
end;
|
||||
}
|
||||
```
|
||||
|
||||
## only subscribe when necessary
|
||||
|
||||
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)]
|
||||
local procedure JustDoSomthing(var Rec: Record "Just Some Table WLD"; var xRec: Record "Just Some Table WLD")
|
||||
begin
|
||||
if Rec.color <> 'RED' then
|
||||
exit; //only execute when necessary
|
||||
|
||||
...
|
||||
end;
|
||||
|
||||
//business logic
|
||||
if JustSomeTable.FindSet() then
|
||||
repeat
|
||||
JustSomeTable.Validate("Message 2", format(Random(1000)));
|
||||
until JustSomeTable.Next() < 1;
|
||||
```
|
||||
|
||||
### Good code
|
||||
|
||||
```AL
|
||||
if JustSomeTable.FindSet() then
|
||||
repeat
|
||||
if JustSomeTable.Color = 'RED' then
|
||||
BindSubscription(DemoSubs);
|
||||
|
||||
JustSomeTable.Validate("Message 2", format(Random(1000)));
|
||||
|
||||
if JustSomeTable.Color = 'RED' then
|
||||
UnbindSubscription(DemoSubs);
|
||||
until JustSomeTable.Next() < 1;
|
||||
```
|
||||
|
||||
## 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.
|
||||
|
||||
## References
|
||||
|
||||
The [Generic Method Pattern](https://alguidelines.dev/docs/patterns/generic-method-pattern/)
|
||||
|
|
@ -1,24 +0,0 @@
|
|||
---
|
||||
title: "Best Practices"
|
||||
weight: 3
|
||||
description: >
|
||||
AL Code Best Practices
|
||||
---
|
||||
|
||||
This section will be cover things that aren't as simple as Design Patterns, but will help make sure your development is:
|
||||
|
||||
- high-performance
|
||||
- complies with good designs
|
||||
- has high maintainability
|
||||
|
||||
## Readability
|
||||
|
||||
Generally, all readability rules are Microsoft style choices only. You can use them to keep consistency with the existing code.
|
||||
|
||||
## Performance
|
||||
|
||||
Some guidelines are simply better for performance considerations rather than readability or anything else. In this section, let's look into some..
|
||||
|
||||
## Discussion
|
||||
|
||||
All discussion related to Best Practice are to be found on the Github Repo's Discussion pages, found [here](https://github.com/microsoft/alguidelines/discussions/categories/bc-best-practices)
|
||||
|
|
@ -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,28 +0,0 @@
|
|||
---
|
||||
title: "begin as an afterword"
|
||||
tags: ["AL","Readability"]
|
||||
categories: ["Best Practice"]
|
||||
---
|
||||
|
||||
_Created by Microsoft, Described by waldo_
|
||||
|
||||
## Description
|
||||
|
||||
When `begin` follows `then`, `else`, `do`, it should be on the same line, preceded by one space character.
|
||||
|
||||
## Bad code
|
||||
|
||||
```al
|
||||
if ICPartnerRefType = ICPartnerRefType::"Common Item No." then
|
||||
begin
|
||||
...
|
||||
end;
|
||||
```
|
||||
|
||||
## Good code
|
||||
|
||||
```al
|
||||
if ICPartnerRefType = ICPartnerRefType::"Common Item No." then begin
|
||||
...
|
||||
end;
|
||||
```
|
||||
|
|
@ -1,65 +0,0 @@
|
|||
---
|
||||
title: "Begin-End - Compound Only"
|
||||
tags: ["AL","Readability"]
|
||||
categories: ["Best Practice"]
|
||||
---
|
||||
|
||||
_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
|
||||
|
||||
```AL
|
||||
if FindSet() then begin
|
||||
repeat
|
||||
...
|
||||
until next() = 0;
|
||||
end;
|
||||
```
|
||||
|
||||
### Good code
|
||||
|
||||
```AL
|
||||
if FindSet() then
|
||||
repeat
|
||||
...
|
||||
until next() = 0;
|
||||
```
|
||||
|
||||
## Example 2
|
||||
|
||||
### Bad code
|
||||
|
||||
```AL
|
||||
if IsAssemblyOutputLine then begin
|
||||
TestField("Order Line No.", 0);
|
||||
end;
|
||||
```
|
||||
|
||||
### Good code
|
||||
|
||||
```AL
|
||||
if IsAssemblyOutputLine then
|
||||
TestField("Order Line No.", 0);
|
||||
```
|
||||
|
||||
## Exception
|
||||
|
||||
```AL
|
||||
// Except for this case
|
||||
if X then begin
|
||||
if Y then
|
||||
//DO SOMETHING;
|
||||
end else
|
||||
(not X)
|
||||
```
|
||||
|
||||
## 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 begin..end around single statements.
|
||||
|
||||
- `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
|
||||
|
|
@ -1,27 +0,0 @@
|
|||
---
|
||||
title: "Binary Operator to Start Line"
|
||||
tags: ["AL","Readability"]
|
||||
categories: ["Best Practice"]
|
||||
---
|
||||
|
||||
_Created by Microsoft, Described by waldo_
|
||||
|
||||
## Description
|
||||
|
||||
Do not start a line with a binary operator.
|
||||
|
||||
## Bad code
|
||||
|
||||
```AL
|
||||
"Quantity to Ship" :=
|
||||
Quantity
|
||||
- "Quantity Shipped"
|
||||
```
|
||||
|
||||
## Good code
|
||||
|
||||
```AL
|
||||
"Quantity to Ship" :=
|
||||
Quantity -
|
||||
"Quantity Shipped"
|
||||
```
|
||||
|
|
@ -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,31 +0,0 @@
|
|||
---
|
||||
title: "CASE Action on next line"
|
||||
tags: ["AL","Readability"]
|
||||
categories: ["Best Practice"]
|
||||
---
|
||||
|
||||
_Created by Microsoft, Described by waldo_
|
||||
|
||||
## Description
|
||||
|
||||
A CASE action should start on a line after the possibility.
|
||||
|
||||
## Bad code
|
||||
|
||||
```AL
|
||||
case Letter of
|
||||
'A': Letter2 := '10';
|
||||
'B': Letter2 := '11';
|
||||
end;
|
||||
```
|
||||
|
||||
## Good code
|
||||
|
||||
```AL
|
||||
case Letter of
|
||||
'A':
|
||||
Letter2 := '10';
|
||||
'B':
|
||||
Letter2 := '11';
|
||||
end;
|
||||
```
|
||||
|
|
@ -1,23 +0,0 @@
|
|||
---
|
||||
title: "Comment Spacing"
|
||||
tags: ["AL","Readability"]
|
||||
categories: ["Best Practice"]
|
||||
---
|
||||
|
||||
_Created by Microsoft, Described by waldo_
|
||||
|
||||
## Description
|
||||
|
||||
Always start comments with // followed by one space character.
|
||||
|
||||
## Bad code
|
||||
|
||||
```al
|
||||
RowNo += 1000; //Move way below the budget
|
||||
```
|
||||
|
||||
## Good code
|
||||
|
||||
```al
|
||||
RowNo += 1000; // Move way below the budget
|
||||
```
|
||||
|
|
@ -1,35 +0,0 @@
|
|||
---
|
||||
title: "end else pair"
|
||||
tags: ["AL","Readability"]
|
||||
categories: ["Best Practice"]
|
||||
---
|
||||
|
||||
_Created by Microsoft, Described by waldo_
|
||||
|
||||
## Description
|
||||
|
||||
The `end else` pair should always appear on the same line.
|
||||
|
||||
## Bad code
|
||||
|
||||
```al
|
||||
if OppEntry.Find('-') then
|
||||
if SalesCycleStage.Find('-') then begin
|
||||
...
|
||||
end
|
||||
else
|
||||
begin
|
||||
...
|
||||
end;
|
||||
```
|
||||
|
||||
## Good code
|
||||
|
||||
```al
|
||||
if OppEntry.Find('-') then
|
||||
if SalesCycleStage.Find('-') then begin
|
||||
...
|
||||
end else begin
|
||||
...
|
||||
end;
|
||||
```
|
||||
|
|
@ -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,26 +0,0 @@
|
|||
---
|
||||
title: "Keyword Pairs - Indentation"
|
||||
tags: ["AL","Readability"]
|
||||
categories: ["Best Practice"]
|
||||
---
|
||||
|
||||
_Created by Microsoft, Described by waldo_
|
||||
|
||||
## Description
|
||||
|
||||
The `if..then` pair, `while..do` pair, and `for..do` pair must appear on the same line or the same level of indentation. If possible, you can align the lines it is even much more readable.
|
||||
|
||||
## Bad code
|
||||
|
||||
```al
|
||||
if (x = y) and
|
||||
(a = b) then
|
||||
```
|
||||
|
||||
## Good code
|
||||
|
||||
```al
|
||||
if (x = y) and
|
||||
(a = b)
|
||||
then
|
||||
```
|
||||
|
|
@ -1,31 +0,0 @@
|
|||
---
|
||||
title: "Line Start Keywords"
|
||||
tags: ["AL","Readability"]
|
||||
categories: ["Best Practice"]
|
||||
---
|
||||
_Created by Microsoft, Described by waldo_
|
||||
|
||||
## Description
|
||||
|
||||
The `end`, `if`, `repeat`, `for`, `while`, `else` and `case` statement should always start a line.
|
||||
|
||||
## Bad code
|
||||
|
||||
```al
|
||||
if IsContactName then ValidateContactName()
|
||||
else if IsSalespersonCode then ValidateSalespersonCode()
|
||||
else if IsSalesCycleCode then ValidatSalesCycleCode();
|
||||
```
|
||||
|
||||
## Good code
|
||||
|
||||
```al
|
||||
if IsContactName then
|
||||
ValidateContactName()
|
||||
else
|
||||
if IsSalespersonCode then
|
||||
ValidateSalespersonCode()
|
||||
else
|
||||
if IsSalesCycleCode then
|
||||
ValidatSalesCycleCode();
|
||||
```
|
||||
|
|
@ -1,24 +0,0 @@
|
|||
---
|
||||
title: "Lonely Repeat"
|
||||
tags: ["AL","Readability"]
|
||||
categories: ["Best Practice"]
|
||||
---
|
||||
|
||||
_Created by Microsoft, Described by waldo_
|
||||
|
||||
## Description
|
||||
|
||||
The `repeat` statement should always be alone on a line.
|
||||
|
||||
## Bad code
|
||||
|
||||
```al
|
||||
if ReservEntry.FindSet() then repeat
|
||||
```
|
||||
|
||||
## Good code
|
||||
|
||||
```al
|
||||
if ReservEntry.FindSet() then
|
||||
repeat
|
||||
```
|
||||
|
|
@ -1,29 +0,0 @@
|
|||
---
|
||||
title: "Named Invocations"
|
||||
tags: ["AL","Readability"]
|
||||
categories: ["Best Practice"]
|
||||
---
|
||||
|
||||
_Created by Microsoft, Described by waldo_
|
||||
|
||||
## Description
|
||||
|
||||
When calling an object statically use the Object Name, not the Object Id.
|
||||
|
||||
## Bad code
|
||||
|
||||
```al
|
||||
Page.RunModal(525, SalesShptLine);
|
||||
```
|
||||
|
||||
## Good code
|
||||
|
||||
```al
|
||||
Page.RunModal(Page::"Posted Sales Shipment Lines", SalesShptLine);
|
||||
```
|
||||
|
||||
## Tips
|
||||
|
||||
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.
|
||||
|
||||
- [LC0012](https://github.com/StefanMaron/BusinessCentral.LinterCop/wiki/LC0012): Using hardcoded IDs in functions like Codeunit.Run() is not allowed.
|
||||
|
|
@ -1,41 +0,0 @@
|
|||
---
|
||||
title: "One Statement per Line"
|
||||
tags: ["AL","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
|
||||
|
||||
```al
|
||||
if OppEntry.Find('-') then exit;
|
||||
```
|
||||
|
||||
### Good code
|
||||
|
||||
```al
|
||||
if OppEntry.Find('-') then
|
||||
exit;
|
||||
```
|
||||
|
||||
## Example 2
|
||||
|
||||
### Bad code
|
||||
|
||||
```al
|
||||
TotalCost += Cost; TotalAmt += Amt;
|
||||
```
|
||||
|
||||
### Good code
|
||||
|
||||
```al
|
||||
TotalCost += Cost;
|
||||
TotalAmt += Amt;
|
||||
```
|
||||
|
|
@ -1,29 +0,0 @@
|
|||
---
|
||||
title: "Seperate if and else"
|
||||
tags: ["AL","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
|
||||
...
|
||||
end;
|
||||
```
|
||||
|
||||
## Good code
|
||||
|
||||
```al
|
||||
if Atom = '>' then
|
||||
HasLogicalOperator := true
|
||||
else begin
|
||||
...
|
||||
end;
|
||||
```
|
||||
|
|
@ -1,53 +0,0 @@
|
|||
---
|
||||
title: "Spacing Binary Operators"
|
||||
tags: ["AL","Readability"]
|
||||
categories: ["Best Practice"]
|
||||
---
|
||||
|
||||
_Created by Microsoft, Described by waldo_
|
||||
|
||||
## Description
|
||||
|
||||
There must be exactly one space character on each side of a binary operator such as = + - AND OR =. The parameter comma operator however, should have a space after the comma.
|
||||
|
||||
## Example 1
|
||||
|
||||
### Bad code
|
||||
|
||||
```al
|
||||
"Line Discount %" := "Line Discount Amount"/"Line Value"*100;
|
||||
```
|
||||
|
||||
### Good code
|
||||
|
||||
```al
|
||||
"Line Discount %" := "Line Discount Amount" / "Line Value" * 100;
|
||||
```
|
||||
|
||||
## Example 2
|
||||
|
||||
### Bad code
|
||||
|
||||
```al
|
||||
StartDate := CalcDate('<+'+Format(Days+i)+'D\>',StartDate);
|
||||
```
|
||||
|
||||
### Good code
|
||||
|
||||
```al
|
||||
StartDate := CalcDate('<+' + Format(Days + i) + 'D\>', StartDate);
|
||||
```
|
||||
|
||||
## Example 3
|
||||
|
||||
### Bad code
|
||||
|
||||
```al
|
||||
StartDate:=0D; // Initialize
|
||||
```
|
||||
|
||||
### Good code
|
||||
|
||||
```al
|
||||
StartDate := 0D; // Initialize
|
||||
```
|
||||
|
|
@ -1,361 +0,0 @@
|
|||
---
|
||||
title: "Suggested Abbreviations"
|
||||
tags: ["AL","Readability"]
|
||||
categories: ["Best Practice"]
|
||||
---
|
||||
|
||||
_Created by Microsoft, Described by waldo_
|
||||
|
||||
## Description
|
||||
|
||||
Whenever possible, do **not** use abbreviations in variables, functions and objects names.
|
||||
|
||||
If there is no other choice, then use the suggestions below.
|
||||
|
||||
| word | Abbreviation |
|
||||
|---|---|
|
||||
| absence | Abs |
|
||||
| account | Acc |
|
||||
| accounting | Acc |
|
||||
| accumulated | Accum |
|
||||
| action | Act |
|
||||
| activity | Activ |
|
||||
| additional | Add |
|
||||
| address | Addr |
|
||||
| adjust | Adj |
|
||||
| adjusted | Adjd |
|
||||
| adjustment | Adjmt |
|
||||
| agreement | Agrmt |
|
||||
| allocation | Alloc |
|
||||
| allowance | Allow |
|
||||
| alternative | Alt |
|
||||
| amount | Amt |
|
||||
| amounts | Amts |
|
||||
| answer | Ans |
|
||||
| applies | Appl |
|
||||
| application | Appln |
|
||||
| arrival | Arriv |
|
||||
| assembly | Asm |
|
||||
| assemble to order | ATO |
|
||||
| assignment | Assgnt |
|
||||
| associated | Assoc |
|
||||
| attachment | Attmt |
|
||||
| authorities | Auth |
|
||||
| automatic | Auto |
|
||||
| availability | Avail |
|
||||
| average | Avg |
|
||||
| ba db. | BA |
|
||||
| balance | Bal |
|
||||
| bill of materials | BOM |
|
||||
| blanket | Blnkt |
|
||||
| budget | Budg |
|
||||
| buffer | Buf |
|
||||
| business | Bus |
|
||||
| business interaction management | BIM |
|
||||
| buying | Buy |
|
||||
| calculate | Calc |
|
||||
| calculated | Calcd |
|
||||
| calculation | Calcu |
|
||||
| calendar | Cal |
|
||||
| capacity | Cap |
|
||||
| capacity requirements planning | CRP |
|
||||
| cash flow | CF |
|
||||
| cashflow | CF |
|
||||
| catalog | ctlg |
|
||||
| category | Cat |
|
||||
| Central Processing Unit | CPU |
|
||||
| center | Ctr |
|
||||
| change | Chg |
|
||||
| changes | Chgs |
|
||||
| character | Char |
|
||||
| characters | Chars |
|
||||
| charge | Chrg |
|
||||
| charges | Chrgs |
|
||||
| check | Chk |
|
||||
| classification | Class |
|
||||
| collection | coll |
|
||||
| column | col |
|
||||
| comment | Cmt |
|
||||
| company | Co |
|
||||
| component | Comp |
|
||||
| completion | Cmpltn |
|
||||
| components | Comps |
|
||||
| composition | Compn |
|
||||
| compression | Compr |
|
||||
| concurrent | Concrnt |
|
||||
| confidential | Conf |
|
||||
| confirmation | Cnfrmn |
|
||||
| conflict | Confl |
|
||||
| consolidate | Consol |
|
||||
| consolidation | Consolid |
|
||||
| consumption | Consump |
|
||||
| contact | Cont |
|
||||
| container | Cntr |
|
||||
| contract | Contr |
|
||||
| contracted | Contrd |
|
||||
| control | Ctrl |
|
||||
| controls | Ctrls |
|
||||
| conversion | Conv |
|
||||
| correction | Cor |
|
||||
| correspondence | Corres |
|
||||
| corresponding | Corresp |
|
||||
| cost | Cst |
|
||||
| sold | COGS |
|
||||
| credit | Cr |
|
||||
| cumulate | Cumul |
|
||||
| currency | Curr |
|
||||
| current | Crnt |
|
||||
| customer | Cust |
|
||||
| customer/vendor | CV |
|
||||
| daily | Dly |
|
||||
| dampener | Damp |
|
||||
| database management system | DBMS |
|
||||
| date | D |
|
||||
| definition | Def |
|
||||
| demonstration | Demo |
|
||||
| department | Dept |
|
||||
| department/project | DP |
|
||||
| depreciation | Depr |
|
||||
| description | Desc |
|
||||
| detail | Dtl |
|
||||
| detailed | Dtld |
|
||||
| details | Dtls |
|
||||
| deviation | Dev |
|
||||
| difference | Diff |
|
||||
| dimension | Dim |
|
||||
| direct | Dir |
|
||||
| discount | Disc |
|
||||
| discrete | Discr |
|
||||
| distribute | Distr |
|
||||
| distributed | Distrd |
|
||||
| distributor | Distbtr |
|
||||
| distribution | Distrn |
|
||||
| document | Doc |
|
||||
| duplicate | Dupl |
|
||||
| entered | Entrd |
|
||||
| engineering | Engin |
|
||||
| exchange | Exch |
|
||||
| excluding | Excl |
|
||||
| execute | Exec |
|
||||
| expected | Expd |
|
||||
| expedited | Exped |
|
||||
| expense | Exp |
|
||||
| expression | Expr |
|
||||
| expiration | Expir |
|
||||
| extended | Ext |
|
||||
| explode | Expl |
|
||||
| export | Expt |
|
||||
| final | Fnl |
|
||||
| finance | Fin |
|
||||
| fiscal | Fisc |
|
||||
| finished | Fnshd |
|
||||
| fixed asset | FA |
|
||||
| forward | Fwd |
|
||||
| freight | Frt |
|
||||
| general | Gen |
|
||||
| general ledger | GL |
|
||||
| group | Gr |
|
||||
| header | Hdr |
|
||||
| history | Hist |
|
||||
| holiday | Hol |
|
||||
| human resource | HR |
|
||||
| identification | ID |
|
||||
| import | Imp |
|
||||
| inbound | Inbnd |
|
||||
| including | Incl |
|
||||
| included | Incld |
|
||||
| incoming | Incmg |
|
||||
| independent software vendor | ISV |
|
||||
| industry | Indust |
|
||||
| information | Info |
|
||||
| initial | Init |
|
||||
| Intrastat | Intra |
|
||||
| interaction | Interact |
|
||||
| integration | Integr |
|
||||
| interest | Int |
|
||||
| Interim | Intm |
|
||||
| internal protocol | IP |
|
||||
| inventory | Invt |
|
||||
| inventoriable | Invtbl |
|
||||
| invoice | Inv |
|
||||
| invoiced | Invd |
|
||||
| item tracking | IT |
|
||||
| journal | Jnl |
|
||||
| language | Lang |
|
||||
| ledger | Ledg |
|
||||
| level | Lvl |
|
||||
| line | Ln |
|
||||
| list | Lt |
|
||||
| local currency | LCY |
|
||||
| location | Loc |
|
||||
| mailing | Mail |
|
||||
| maintenance | Maint |
|
||||
| management | Mgt |
|
||||
| manual | Man |
|
||||
| manufacturing | Mfg |
|
||||
| manufacturer | Mfr |
|
||||
| material | Mat |
|
||||
| marketing | Mktg |
|
||||
| maximum | Max |
|
||||
| measure | Meas |
|
||||
| message | Msg |
|
||||
| minimum | Min |
|
||||
| miscellaneous | Misc |
|
||||
| modify | Mod |
|
||||
| month | Mth |
|
||||
| negative | Neg |
|
||||
| non-inventoriable | NonInvtbl |
|
||||
| notification | Notif |
|
||||
| number | No |
|
||||
| numbers | Nos |
|
||||
| object | Obj |
|
||||
| operating | Oper |
|
||||
| opportunity | Opp |
|
||||
| order | Ord |
|
||||
| orders | Ords |
|
||||
| original | Orig |
|
||||
| organization | Org |
|
||||
| outbound | Outbnd |
|
||||
| Outgoing | Outg |
|
||||
| output | Out |
|
||||
| outstanding | Outstd |
|
||||
| overhead | Ovhd |
|
||||
| payment | Pmt |
|
||||
| percent | Pct |
|
||||
| personnel | Persnl |
|
||||
| physical | Phys |
|
||||
| picture | Pic |
|
||||
| planning | Plng |
|
||||
| posted | Pstd |
|
||||
| posting | Post |
|
||||
| positive | Pos |
|
||||
| precision | Prec |
|
||||
| prepayment | Prepmt |
|
||||
| product | Prod |
|
||||
| production | Prod |
|
||||
| production order | ProdOrd |
|
||||
| project | Proj |
|
||||
| property | Prop |
|
||||
| prospect | Prspct |
|
||||
| purchase | Purch |
|
||||
| purchases | Purch |
|
||||
| purchaser | Purchr |
|
||||
| purchase order | PurchOrd |
|
||||
| quality | Qlty |
|
||||
| quantity | Qty |
|
||||
| questionnaire | Questn |
|
||||
| quote | Qte |
|
||||
| radio frequency | RF |
|
||||
| range | Rng |
|
||||
| receipt | Rcpt |
|
||||
| received | Rcd |
|
||||
| record | Rec |
|
||||
| records | Recs |
|
||||
| reconcile | Recncl |
|
||||
| reconciliation | Recon |
|
||||
| recurring | Recur |
|
||||
| reference | Ref |
|
||||
| register | Reg |
|
||||
| registration | Regn |
|
||||
| registered | Regd |
|
||||
| relation | Rel |
|
||||
| relations | Rels |
|
||||
| relationship | Rlshp |
|
||||
| release | Rlse |
|
||||
| released | Rlsd |
|
||||
| remaining | Rem |
|
||||
| reminder | Rmdr |
|
||||
| replacement | Repl |
|
||||
| replenish | Rplnsh |
|
||||
| replenishment | Rplnsht |
|
||||
| report | Rpt |
|
||||
| represent | Rep |
|
||||
| represented | Repd |
|
||||
| request | Rqst |
|
||||
| required | Reqd |
|
||||
| requirement | Reqt |
|
||||
| requirements | Reqts |
|
||||
| requisition | Req |
|
||||
| reserve | Rsv |
|
||||
| reserved | Rsvd |
|
||||
| reservation | Reserv |
|
||||
| resolution | Resol |
|
||||
| resource | Res |
|
||||
| response | Rsp |
|
||||
| responsibility | Resp |
|
||||
| retain | Rtn |
|
||||
| retained | Rtnd |
|
||||
| return | Ret |
|
||||
| returns | Rets |
|
||||
| revaluation | Revaln |
|
||||
| reverse | Rev |
|
||||
| review | Rvw |
|
||||
| round | Rnd |
|
||||
| rounded | Rndd |
|
||||
| rounding | Rndg |
|
||||
| route | Rte |
|
||||
| routing | Rtng |
|
||||
| routine | Rout |
|
||||
| sales & receivables | Sales |
|
||||
| safety | Saf |
|
||||
| schedule | Sched |
|
||||
| second | Sec |
|
||||
| segment | Seg |
|
||||
| select | Sel |
|
||||
| selection | Selctn |
|
||||
| sequence | Seq |
|
||||
| serial | Ser |
|
||||
| serial number | SN |
|
||||
| service | Serv |
|
||||
| sheet | Sh |
|
||||
| shipment | Shpt |
|
||||
| source | Src |
|
||||
| special | Spcl |
|
||||
| specification | Spec |
|
||||
| specifications | Specs |
|
||||
| standard | Std |
|
||||
| frequency | SF |
|
||||
| statement | Stmt |
|
||||
| statistical | Stat |
|
||||
| statistics | Stats |
|
||||
| stock | Stk |
|
||||
| stockkeeping unit | SKU |
|
||||
| stream | Stm |
|
||||
| structured query language | SQL |
|
||||
| subcontract | Subcontr |
|
||||
| subcontracted | Subcontrd |
|
||||
| subcontracting | Subcontrg |
|
||||
| substitute | Sub |
|
||||
| substitution | Subst |
|
||||
| suggest | Sug |
|
||||
| suggested | Sugd |
|
||||
| suggestion | Sugn |
|
||||
| summary | Sum |
|
||||
| suspended | Suspd |
|
||||
| symptom | Sympt |
|
||||
| synchronize | Synch |
|
||||
| temporary | Temp |
|
||||
| total | Tot |
|
||||
| transaction | Transac |
|
||||
| transfer | Trans |
|
||||
| translation | Transln |
|
||||
| tracking | Trkg |
|
||||
| troubleshoot | Tblsht |
|
||||
| troubleshooting | Tblshtg |
|
||||
| unit of measure | UOM |
|
||||
| unit test | UT |
|
||||
| unrealized | Unreal |
|
||||
| unreserved | Unrsvd |
|
||||
| update | Upd |
|
||||
| valuation | Valn |
|
||||
| value | Val |
|
||||
| value added tax | VAT |
|
||||
| variance | Var |
|
||||
| vendor | Vend |
|
||||
| warehouse | Whse |
|
||||
| web shop | WS |
|
||||
| worksheet | Wksh |
|
||||
| g/l | GL |
|
||||
| % | Pct |
|
||||
| 3-tier | Three-Tier |
|
||||
| Outlook Synch | Osynch |
|
||||
|
|
@ -1,34 +0,0 @@
|
|||
---
|
||||
title: "Unnecessary else"
|
||||
tags: ["AL","Readability"]
|
||||
categories: ["Best Practice"]
|
||||
---
|
||||
|
||||
_Created by Microsoft, Described by waldo_
|
||||
|
||||
## Description
|
||||
|
||||
`else` should not be used when the last action in the `then` part is an `exit`, `break`, `skip`, `quit`, `error`.
|
||||
|
||||
## Bad code
|
||||
|
||||
```al
|
||||
procedure SomeProcedure()
|
||||
begin
|
||||
if IsAdjmtBinCodeChanged() then
|
||||
Error(AdjmtBinCodeChangeNotAllowedErr, ...)
|
||||
else
|
||||
Error(BinCodeChangeNotAllowedErr, ...);
|
||||
end;
|
||||
```
|
||||
|
||||
## Good code
|
||||
|
||||
```al
|
||||
procedure SomeProcedure()
|
||||
begin
|
||||
if IsAdjmtBinCodeChanged() then
|
||||
Error(AdjmtBinCodeChangeNotAllowedErr, ...)
|
||||
Error(BinCodeChangeNotAllowedErr, ...);
|
||||
end;
|
||||
```
|
||||
|
|
@ -1,38 +0,0 @@
|
|||
---
|
||||
title: "Unnecessary true/false"
|
||||
tags: ["AL","Readability"]
|
||||
categories: ["Best Practice"]
|
||||
---
|
||||
|
||||
_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
|
||||
|
||||
```al
|
||||
if IsPositive() = true then
|
||||
```
|
||||
|
||||
### Good code
|
||||
|
||||
```al
|
||||
if IsPositive() then
|
||||
```
|
||||
|
||||
## Example 2
|
||||
|
||||
### Bad code
|
||||
|
||||
```al
|
||||
if Complete <> true then
|
||||
```
|
||||
|
||||
### Good code
|
||||
|
||||
```al
|
||||
if not Complete then
|
||||
```
|
||||
|
|
@ -1,63 +0,0 @@
|
|||
---
|
||||
title: "Variable Naming"
|
||||
tags: ["AL","Readability"]
|
||||
categories: ["Best Practice"]
|
||||
---
|
||||
|
||||
_Created by Microsoft, Described by waldo_
|
||||
|
||||
## Description
|
||||
|
||||
Variables that refer to a AL object must contain the objects name, abbreviated where necessary.
|
||||
|
||||
A variable must begin with a capital letter.
|
||||
|
||||
Blanks, periods, and other characters (such as parentheses) that would make quotation marks around a variable necessary must be omitted.
|
||||
|
||||
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
|
||||
|
||||
```al
|
||||
WIPBuffer: Record "Job WIP Buffer"
|
||||
```
|
||||
|
||||
### Good code
|
||||
|
||||
```al
|
||||
JobWIPBuffer: Record "Job WIP Buffer"
|
||||
```
|
||||
|
||||
## Example 2
|
||||
|
||||
### Bad code
|
||||
|
||||
```al
|
||||
Postline: Codeunit "Gen. Jnl.-Post Line";
|
||||
```
|
||||
|
||||
### Good code
|
||||
|
||||
```al
|
||||
GenJnlPostLine: Codeunit "Gen. Jnl.-Post Line";
|
||||
```
|
||||
|
||||
## Example 3
|
||||
|
||||
### Bad code
|
||||
|
||||
```al
|
||||
"Amount (LCY)": Decimal;
|
||||
```
|
||||
|
||||
### Good code
|
||||
|
||||
```al
|
||||
AmountLCY: Decimal;
|
||||
```
|
||||
|
||||
## Tips
|
||||
|
||||
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.
|
||||
|
|
@ -1,48 +0,0 @@
|
|||
---
|
||||
title: "Variables Declarations Order"
|
||||
tags: ["AL","Readability"]
|
||||
categories: ["Best Practice"]
|
||||
---
|
||||
|
||||
_Created by Microsoft, Described by waldo_
|
||||
|
||||
## Description
|
||||
|
||||
Variables declarations should be ordered by type. In general, object and complex variable types are listed first followed by simple variables. The order should be:
|
||||
|
||||
- Record
|
||||
- Report
|
||||
- Codeunit
|
||||
- XmlPort
|
||||
- Page
|
||||
- Query
|
||||
- Notification
|
||||
- BigText
|
||||
- DateFormula
|
||||
- RecordId
|
||||
- RecordRef
|
||||
- FieldRef
|
||||
- FilterPageBuilder
|
||||
|
||||
(Ref: [Microsoft Docs](https://docs.microsoft.com/en-us/dynamics365/business-central/dev-itpro/developer/analyzers/codecop-aa0021))
|
||||
|
||||
## Bad code
|
||||
|
||||
```al
|
||||
StartingDateFilter: Text;
|
||||
Vendor: Record Vendor;
|
||||
```
|
||||
|
||||
## Good code
|
||||
|
||||
```al
|
||||
Vendor: Record Vendor;
|
||||
StartingDateFilter: Text;
|
||||
```
|
||||
|
||||
## 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 sorts variables.
|
||||
|
||||
- `Sort Variables in the Active Editor` : sorts variables in the current editor
|
||||
- `Sort Variables in the Active Project` : sorts variables in the current project
|
||||
|
|
@ -1,63 +0,0 @@
|
|||
+++
|
||||
chapter = true
|
||||
pre = "<b><i class='fas fa-clone'></i> </b>"
|
||||
title = "Guide to Fork & PR"
|
||||
weight = 100
|
||||
+++
|
||||
|
||||
## Abstract
|
||||
|
||||
AL Guidelines is a community project, and as such YOU are encouraged to submit corrections and new ideas. In order to get your content included, you must submit a pull request to the GitHub Repository (Located here: https://github.com/microsoft/alguidelines\). All Pull Requests are subject to approval by a minimum of three admins.
|
||||
|
||||
If You are toying with an idea, but You aren't ready to create a document just yet, you are encouraged to create a project discussion thread here: https://github.com/microsoft/alguidelines/discussions
|
||||
|
||||
{{% alert title="Warning" color="warning" %}}
|
||||
This is a warning.
|
||||
If You haven't worked in collaboration with "external" GitHub repositories before, please familiarize yourself with that process by visiting:
|
||||
|
||||
https://docs.github.com/en/pull-requests/collaborating-with-pull-requests
|
||||
|
||||
{{% /alert %}}
|
||||
|
||||
## Steps
|
||||
Now that You have decided that You are ready to contribute, these are the steps to take.
|
||||
{{% alert title="Note" color="info" %}}
|
||||
You can read more about this process here:
|
||||
|
||||
https://docs.github.com/en/get-started/quickstart/contributing-to-projects
|
||||
|
||||
{{% /alert %}}
|
||||
|
||||
### Step 1: Fork
|
||||
In order to work on the repository, You must [Fork](https://docs.github.com/en/get-started/quickstart/fork-a-repo) the repository.
|
||||
|
||||
By forking the repository, You essentially create a copy into Your own account.
|
||||
|
||||
Start by going to the GitHub Repository (https://github.com/microsoft/alguidelines\), and press the Fork 
|
||||
|
||||
Once You have successfully forked the repository, go to your own GitHub repository : 
|
||||
|
||||
You are now able to clone your own repository to your local pc and start editing using your favorite editor. [Visual Studio Code](https://code.visualstudio.com/) is perfectly fine for this task.
|
||||
|
||||
{{% alert title="Note" color="info" %}}
|
||||
You can read more about forking here:
|
||||
|
||||
https://docs.github.com/en/get-started/quickstart/fork-a-repo
|
||||
{{% /alert %}}
|
||||
|
||||
### Step 2: Branch
|
||||
While not necessarily a must, it is always good practice to create a branch off of your forked repository. That will allow you to work on multiple contributions at the same time and won't have to wait for pull requests to be approved before you can continue on your next contribution.
|
||||
|
||||
{{% alert title="Note" color="info" %}}
|
||||
You can read more about Branches here:
|
||||
|
||||
https://docs.github.com/en/pull-requests/collaborating-with-pull-requests/proposing-changes-to-your-work-with-pull-requests/about-branches
|
||||
{{% /alert %}}
|
||||
### Step 3: Pull Request
|
||||
Once you are happy with your contribution, it's time to create a pull request to propose changes into the main project! This is the final step in producing a fork of someone else's project, and arguably the most important. If you've made a change that you feel would benefit the community as a whole, you should definitely consider contributing back.
|
||||
|
||||
To do so, head on over to the repository on GitHub where your project lives. For this example, it would be at `https://www.github.com/<your_username>/alguidelines`. You'll see a banner indicating that your branch is one commit ahead of microsoft:main. Click **Contribute** and then **Open a pull request.**
|
||||
|
||||
GitHub will bring you to a page that shows the differences between your fork and the microsoft/alguidelines repository. Click **Create pull request.**
|
||||
|
||||
GitHub will bring you to a page where you can enter a title and a description of your changes. _It's important to provide as much useful information and a rationale for why you're making this pull request in the first place._ The project owners needs to be able to determine whether your change is as useful to everyone as you think it is. Finally, click **Create pull request.**
|
||||
|
|
@ -1,126 +0,0 @@
|
|||
+++
|
||||
chapter = true
|
||||
pre = "<b><i class='fas fa-clone'></i> </b>"
|
||||
title = "Formatting tips"
|
||||
weight = 100
|
||||
+++
|
||||
|
||||
# Tips and tricks in terms of working with MarkDown and Hugo
|
||||
|
||||
## Markdown Cheat Sheet
|
||||
Here you can find a rather interesting Cheat Sheet regarding markdown: [https://www.markdownguide.org/cheat-sheet](https://www.markdownguide.org/cheat-sheet)
|
||||
|
||||
## Code Fences / Syntax highlighting
|
||||
|
||||
The syntax to use codefences is with backticks. If you provide the language after the first block of backticks, github will automatically put that in decent syntax highlighting. So, A simple code fence with AL code, can simply be done by:
|
||||
````
|
||||
```AL
|
||||
procedure ALGuidelinesRock()
|
||||
var
|
||||
Customer: Record Customer;
|
||||
begin
|
||||
Customer.Get('10000');
|
||||
Customer.Name := 'waldo';
|
||||
Customer.Modify(true);
|
||||
end;
|
||||
```
|
||||
````
|
||||
|
||||
Results in:
|
||||
```AL
|
||||
procedure ALGuidelinesRock()
|
||||
var
|
||||
Customer: Record Customer;
|
||||
begin
|
||||
Customer.Get('10000');
|
||||
Customer.Name := 'waldo';
|
||||
Customer.Modify(true);
|
||||
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/)
|
||||
|
||||
It basically means we are able to use easy notations to do cool things. Let's point out some useful shortcodes:
|
||||
|
||||
### Twitter
|
||||
|
||||
```
|
||||
{{</* tweet user="waldo1001" id="1458787011024805892" */>}}
|
||||
```
|
||||
makes:
|
||||
{{< tweet user="waldo1001" id="1458787011024805892" >}}
|
||||
|
||||
### YouTube
|
||||
```
|
||||
{{</* youtube QVOMCYitLEc */>}}
|
||||
```
|
||||
makes:
|
||||
{{< youtube QVOMCYitLEc >}}
|
||||
|
||||
### Figure
|
||||
```
|
||||
{{</* figure src="http://www.waldo.be/wp-content/uploads/2021/11/business-central-logo.png" title="Business Central" */>}}
|
||||
```
|
||||
makes:
|
||||
{{< figure src="http://www.waldo.be/wp-content/uploads/2021/11/business-central-logo.png" title="Business Central" >}}
|
||||
|
|
@ -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,31 +0,0 @@
|
|||
---
|
||||
title: "Title Here"
|
||||
tags: ["AL"]
|
||||
categories: ["Best Practice"]
|
||||
---
|
||||
|
||||
<!-- This is a guideline, some parts are optional (if there's no content, remove the whole paragraph). -->
|
||||
<!-- The "Readability" and "Performance" tags are often used. You can, however, add multiple tags, but please do not change the Category -->
|
||||
|
||||
_Created by <!-- John Smith --><!--, Cronus International.--> Described by <!-- Jane Doe--><!--, Contoso, LLC-->_
|
||||
|
||||
## Description
|
||||
|
||||
In depth description on what this Pattern is all about
|
||||
|
||||
- basic components
|
||||
- how the interact
|
||||
- steps to implement
|
||||
- considerations to take
|
||||
|
||||
## Bad code
|
||||
|
||||
```al
|
||||
PutCodeblocksHere()
|
||||
```
|
||||
|
||||
## Good code
|
||||
|
||||
```al
|
||||
PutCodeblocksHere()
|
||||
```
|
||||
|
|
@ -1,46 +0,0 @@
|
|||
---
|
||||
title: "Title Here"
|
||||
tags: ["AL"]
|
||||
categories: ["Pattern"]
|
||||
---
|
||||
|
||||
<!-- This is a guideline, some parts are optional (if there's no content, remove the whole paragraph). -->
|
||||
|
||||
_Created by <!-- John Smith --><!--, Cronus International.--> Described by <!-- Jane Doe--><!--, Contoso, LLC-->_
|
||||
|
||||
## Abstract
|
||||
|
||||
Short, descriptive and easy to remember description of this pattern.
|
||||
|
||||
## Context
|
||||
|
||||
Sets the stage where the pattern takes place. 1-2 sentences.
|
||||
|
||||
## Problem
|
||||
|
||||
What happens before this pattern is used? How can it go wrong? 1-5 lines.
|
||||
|
||||
## Description
|
||||
|
||||
In depth description on what this Pattern is all about
|
||||
|
||||
- basic components
|
||||
- how the interact
|
||||
- steps to implement
|
||||
- considerations to take
|
||||
|
||||
## Usage
|
||||
|
||||
If applicable: where is it used in an app. You can refer to any app, BaseApp, or a (public) ISV app.
|
||||
|
||||
## Benefits
|
||||
|
||||
1-2 sentences: what did you just solve
|
||||
|
||||
## When not to use
|
||||
|
||||
Usually, there are occasions where NOT to implement the pattern. List the disadvantages of this pattern here.
|
||||
|
||||
## List of references
|
||||
|
||||
Youtube-link? BaseApp? Tweet? ...
|
||||
|
|
@ -1,12 +0,0 @@
|
|||
---
|
||||
title: "Templates"
|
||||
---
|
||||
|
||||
We have created some template-files that you can simply copy and use. Look at them as "Patterns for describing patterns"
|
||||
|
||||
We currently offer the following templates:
|
||||
|
||||
- for [Patterns](/contributing/templates/patterns/) ([raw](https://raw.githubusercontent.com/microsoft/alguidelines/main/content/docs/Contributing/Templates/Patterns/index.md))
|
||||
- for [Best Practice](/contributing/templates/bestpractice/) ([raw](https://raw.githubusercontent.com/microsoft/alguidelines/main/content/docs/Contributing/Templates/BestPractice/index.md))
|
||||
|
||||
opening the "raw" link, will allow for the best copy/paste result.
|
||||
|
|
@ -1,9 +0,0 @@
|
|||
+++
|
||||
chapter = true
|
||||
pre = "<b><i class='fas fa-clone'></i> </b>"
|
||||
title = "Understanding the Approval Process"
|
||||
weight = 100
|
||||
+++
|
||||
|
||||
(coming soon)
|
||||
|
||||
|
|
@ -1,27 +0,0 @@
|
|||
+++
|
||||
chapter = true
|
||||
pre = "<b><i class='fas fa-clone'></i> </b>"
|
||||
title = "Contributing"
|
||||
weight = 100
|
||||
+++
|
||||
|
||||
First off, we're glad you want to help! The project team is kind and helpful, so if you're not sure if you have a good first contribution, make a [Discussion](https://github.com/microsoft/alguidelines/discussions/) about it or even just create your first try. You'll get feedback and we will be happy to help refine it, if it even needs it.
|
||||
|
||||
## Code of Conduct
|
||||
|
||||
This project welcomes contributions and suggestions. Most contributions require you to agree to a Contributor License Agreement (CLA) declaring that you have the right to, and actually do, grant us the rights to use your contribution. For details, visit https://cla.opensource.microsoft.com.
|
||||
|
||||
When you submit a pull request, a CLA bot will automatically determine whether you need to provide a CLA and decorate the PR appropriately (e.g., status check, comment). Simply follow the instructions provided by the bot. You will only need to do this once across all repos using our CLA.
|
||||
|
||||
This project has adopted the Microsoft Open Source Code of Conduct. For more information see the Code of Conduct FAQ or contact opencode@microsoft.com with any additional questions or comments.
|
||||
|
||||
## Steps to Contribute
|
||||
|
||||
* [Guide to Fork & PR](/docs/contributing/forkandpr/)
|
||||
* Templates:
|
||||
- For [patterns](/docs/contributing/templates/patterns/)
|
||||
- For [guidelines](/docs/contributing/templates/guidelines/)
|
||||
* [Understanding the Approval Process](/docs/contributing/theapprovalprocess/)
|
||||
|
||||
Here are some [tips and tricks in terms of working with Markdown and Hugo](/docs/contributing/formattingtips/).
|
||||
|
||||
|
|
@ -1,19 +0,0 @@
|
|||
+++
|
||||
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.
|
||||
|
||||
"An **anti-pattern** (or **antipattern**) is a common response to a recurring problem that is usually ineffective and risks being highly counterproductive" (from [Wikipedia][anchor0])
|
||||
|
||||
Since almost the beginning of the NAV Design Patterns project, we talked about documenting the anti-patterns - but never found the time. Until, spontaneously, the April 1st 2015 article had practically wrote itself in a couple of hours, with priceless contributions coming from Andreas, Elly, Nikola - and last but not least, waldo.
|
||||
|
||||
Best regards,
|
||||
|
||||
Bogdana Botez
|
||||
|
||||
|
||||
|
||||
[anchor0]: http://en.wikipedia.org/wiki/Anti-pattern
|
||||
|
|
@ -1,75 +0,0 @@
|
|||
+++
|
||||
title = "Nav Upgrade"
|
||||
weight = 840
|
||||
tags = ["C/AL"]
|
||||
+++
|
||||
## Anti-Patterns in NAV Upgrade
|
||||
|
||||
_By Carlos Raul Garcia and Bogdana Botez at Microsoft Development Center Copenhagen_
|
||||
|
||||
[![ ][image0]][anchor0]
|
||||
|
||||
**Context**: when NAV is upgraded, whether on-premises or in the cloud, developers have the chance to write upgrade code to move data across changing data structures. Writing good quality code will help successful upgrades.
|
||||
|
||||
### General on upgrade
|
||||
|
||||
**Problem:** assuming that the upgrade table contains data.
|
||||
|
||||
If the table is empty, it means that either the upgrade has run, or there was no data in the original tenant; in both cases, the upgrade code should exit immediately.
|
||||
|
||||
**Solution:** if using an upgrade table, always validate that the table contains data before doing anything.
|
||||
|
||||
### Upgrade code, can it be rerun safely?
|
||||
|
||||
**Problem**: if the upgrade code is not written in a way that makes it runnable twice ([idempotent][anchor1]), then several failures can happen, including something as critical as data corruption.
|
||||
|
||||
In on premise NAV installations, if something fails at upgrade, there is no way to run only the "remaining" tasks. You will need to run the whole upgrade again, and might end-up with data that you cannot trust.
|
||||
|
||||
What about the cloud? In Platform As A Service (PaaS), in some situations, the upgrade code needs to be run twice (for example, when moving tenants from a broken/frozen VM to a healthy one).
|
||||
|
||||
**Solution:** Make sure each of your upgrade procedures only kicks in if it didn't run before.
|
||||
|
||||
**Examples**
|
||||
|
||||
The examples below have happened in real live NAV PaaS upgrade:
|
||||
|
||||
**Table data overwrite**
|
||||
|
||||
* **Problem**: at upgrade, a new column has been added to a table and initialized with default values. In the meanwhile, during production, some of the default values are changed to production real life values. The second time the upgrade runs, those values will be overwritten with defaults, any personalization lost.
|
||||
* **Solution:** before initializing with default values, check if non-default values exist.
|
||||
|
||||
**Crash on math operations**
|
||||
|
||||
* **Problem:** one tenant upgrade managed to divide by zero, by assuming a non-zero value.
|
||||
* **Solution:** don't assume values can never be zero, always check before using them in divisions.
|
||||
|
||||
**Use of external components**
|
||||
|
||||
* **Problem:** a one-time registration through web services to an external service failed when attempting to register a second time.
|
||||
* **Solution:** check if already registered, before attempting again.
|
||||
|
||||
### Parallelism
|
||||
|
||||
**Problem**: Upgrade procedures can be run in parallel, causing issues when different procedures attempt to modify the same table at the same time.
|
||||
|
||||
When modifications to the same table are being made from two or more different procedures, there is no guarantee on sequential run, or on a certain order they will be run in.
|
||||
|
||||
**Solution**: if sequential or ordered execution is needed, make the affected upgrade procedures local and call them all, in the desired sequence, from a public upgrade procedure.
|
||||
|
||||
### Access to cloud machines
|
||||
|
||||
**Problem:** dependencies on manual installation steps do not fit in the cloud.
|
||||
|
||||
If Dynamics NAV is installed on-premises, then any additional setup (like dependencies of external dlls, manual configuration steps etc.) can be done manually or semi-manually by the IT admin, at first setup and upgrade.
|
||||
|
||||
In the cloud, NAV partners don't have access to the machines -- hence they cannot deploy and configure those external dependencies as they did in the old on-premises installations.
|
||||
|
||||
**Solution:** Don't assume you will have access to PaaS or SaaS machines. Build your solution in such a way that it doesn't depend on executing manual configurations on the host machine.
|
||||
|
||||
|
||||
|
||||
[anchor0]: upgrade.png
|
||||
[anchor1]: http://stackoverflow.com/questions/1077412/what-is-an-idempotent-operation#1077421
|
||||
|
||||
|
||||
[image0]: upgrade.png
|
||||
|
|
@ -1,95 +0,0 @@
|
|||
+++
|
||||
title = "Reusable Bugs"
|
||||
weight = 1020
|
||||
tags = ["C/AL"]
|
||||
+++
|
||||
_By Bogdana Botez, Andreas Moth, Eric Wauters (waldo), Elly Nkya, Nikola Kukrika_
|
||||
|
||||
_April 1st, 2015_
|
||||
|
||||
## Abstract
|
||||
|
||||
It is difficult to come up with new and different bugs in each release, and it is a waste of resources to not use the ones which already had proven successful in the past. To avoid reinventing the wheel, we propose to come up with new innovative ways to create bugs that are reusable and generic enough to be used in various places of the application.
|
||||
|
||||
## Examples
|
||||
|
||||
**Option Strings**
|
||||
|
||||
One example of a reusable bug is to find a heavily used table, like table 36 Sales Header, and observe one of the more important fields of type Option, for example Document Type. The OptionString property has the following value: Quote,Order,Invoice,Credit Memo,Blanket Order,Return Order, which you should update to add one option, for example Transport Order in the beginning.
|
||||
|
||||
The main benefit of this reusable bug is that code changed in one place, has impact in multiple sides on the application: document creation and handling, posting etc.
|
||||
|
||||
**Application Management**
|
||||
|
||||
Codeunit 1, ApplicationManagement is a good place for creating reusable bugs. A simple INSERT on the RecRef on the first line of OnDatabaseInsert will create a sure and versatile bug that is reusable all over the application. Redundancy on database insertion ensures that data is surely conveyed to the data storage layer, twice.
|
||||
|
||||
**God objects**
|
||||
|
||||
We are considering to no longer make localization builds for countries. It consumes lab resources to keep running multiple country builds. Instead, we will merge everything into one single build. This is a bigger scale implementation of the ["God object"][anchor0]. Inside this unique build, we will use IF statements and sporadically CASE statements to select each country's behavior. For more help on how to use IFs, see also the IF .. THEN|ELSE C/AL Coding Guideline.
|
||||
|
||||
**Field length economy**
|
||||
|
||||
When you post a document, make sure to transfer data to a field that is smaller than the field you are assigning from. This will not immediately be caught and will only hurt a subset of the customers who uses Microsoft Dynamics NAV to its fullest.
|
||||
|
||||
## Guidelines
|
||||
|
||||
When bug addition is not entirely obvious, there is a second way to approach the problem. By following some general coding best practices like the ones described below, reliable bug innovation is facilitated and can come naturally as a collateral effect.
|
||||
|
||||
**Code structure**
|
||||
|
||||
Put everything in one function and only use comments to explain the structure of your code. And don't use functions - because this only complicates things... having to navigate from function to function, and completely lose track of where you are in the business logic.
|
||||
|
||||
On top of that .. put everything in one codeunit. Because also that will simplify and make your structure more readable.
|
||||
|
||||
Use Hungarian Notation on your variables, because at any time, you need to know what type, and what context your variable is on.
|
||||
|
||||
Declare all your functions and variables global, so they are available at any time.
|
||||
|
||||
**Don't do Unit Testing**
|
||||
|
||||
Unit testing adds complexity and extra time to the stuff you're doing. Also, it eats up extra codeunits which means: it costs money. You will never be able to foresee all scenarios possible, so you're destined to forget and not test everything. So you will save time in not doing unit testing.
|
||||
|
||||
**Never add images to actions**
|
||||
|
||||
Because there is an image by default. When you don't provide an image on an action on a page, the application will foresee a ball... and when you never do it, your application will have a very consistent way of showing your actions, by providing that picture of that ball. On top of that, you'll save time.
|
||||
|
||||
**Do not care about ControlIDs**
|
||||
|
||||
When you're doing development of your product, do not care about ControlIDs, and just leave the Offset ID to the default value of 0\. This way, when merging, you will receive nice notifications, saying both you and Microsoft have added functions in that objects. You can use this feature to document all these places.
|
||||
|
||||
**Hooks**
|
||||
|
||||
Never apply the hook pattern. Hooks will only reduce upgrade time. This means, you will only shortly enjoy using the AMU (Application Merge Utilities). The more you change in default application, the longer it takes to upgrade, the longer you will enjoy the toolkit
|
||||
|
||||
This can be taken one level higher. Simply you are not hardcore if you do not use notepad to resolve all of the merge issues.
|
||||
|
||||
**How to use RecRef**
|
||||
|
||||
Why fuss around declaring specific table variables, just generalize, all you need is one, two, or perhaps three RecRef variables, with a few IFs and CASEs here and there for reflection, to carry you all the way.
|
||||
|
||||
**Arguments**
|
||||
|
||||
Using only a few arguments on the functions is a sign of a weak developer. Stick in as many arguments as possible on the function, even if you are not using them, they could be useful in the future.
|
||||
|
||||
**Just another field / action**
|
||||
|
||||
Thinking of the design is overrated, each problem can be solved by adding an additional field or the table/page or with adding another action. We all know this has worked well in the past.
|
||||
|
||||
**Reusability**
|
||||
|
||||
We have decided that each time we fix a bug, we now also explain how it can be applied as a pattern. We then use anti-virus software to search for these patterns, to make sure we do not re-introduce these bugs anywhere else in NAV.
|
||||
|
||||
**Business logic placement**
|
||||
|
||||
As a best practice, we have also decided to move code into pages. Business logic should no longer be in tables and codeunits, but instead pages should know and be aware of the context and update it accordingly. As opposite to tables and codeunits, pages are aware of the context.
|
||||
|
||||
## Conclusion
|
||||
|
||||
Happy April Fools' Day.
|
||||
|
||||
Disclaimer: this is inspired from IETF documentation published on April 1st, like for example the revolutionizing [IP over Avian Carriers][anchor1] standard.
|
||||
|
||||
|
||||
|
||||
[anchor0]: http://en.wikipedia.org/wiki/God_object
|
||||
[anchor1]: http://en.wikipedia.org/wiki/IP_over_Avian_Carriers
|
||||
|
|
@ -1,28 +0,0 @@
|
|||
+++
|
||||
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).
|
||||
|
||||
You can also [download the C/AL coding guidelines as pdf,][anchor0] all in one document. In contrast, on this wiki, the coding guidelines are published individually. The reason is: give you the chance to comment and share your oppinion on each one. Thanks to [waldo][anchor1] for this idea and for helping out.
|
||||
|
||||
The guidelines are debatable - and it is good when they are generating debate. There is variation of opinion on the rules at Microsoft too. The plan is to simply expose what we use now. And more important, to say that guidelines could be used. Debate on individual guidelines can become heated for any programming language, but the benefit of using some guidelines stays.
|
||||
|
||||
For us, those guidelines are enforced at check-in time - we are using a tool which verifies and only allows compliant check-ins. While this tool is internal and not ready to publish, we had anyways decided to open up and present the rules we use to the community, as inspiration.
|
||||
|
||||
Question: Since we're having the guidelines, how come there is still C/AL code in NAV which doesn't respect them?
|
||||
|
||||
Answer: all new C/AL code is bound to follow the guidelines (else it cannot be checked-in). However, the code that existed before the rules - it does not. We had done cleanup in a certain degree. Now we're gradually improving the old code base as we visit various objects in order to add new functionality, however chances are that code we didn't touch in a long time had remained in its old form.
|
||||
|
||||
We're looking forward to your comments. Where you can, do provide concrete examples of the alternatives, Good and Bad.
|
||||
|
||||
{{< youtube z6skKy0pkmU >}}
|
||||
|
||||
|
||||
|
||||
[anchor0]: /files/CAL-Coding-Guidelines-at-Microsoft-Development-Center-Copenhagen.pdf "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,11 +0,0 @@
|
|||
+++
|
||||
title = "Design"
|
||||
weight = 490
|
||||
tags = ["C/AL"]
|
||||
categories = ["Best Practice"]
|
||||
+++
|
||||
## C/AL Coding Guidelines
|
||||
|
||||
## **Design**
|
||||
|
||||
Find the C/AL guidelines by expanding the menu in the left.
|
||||
|
|
@ -1,27 +0,0 @@
|
|||
+++
|
||||
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.
|
||||
|
||||
Unintentional value changes might propagate. Also, it might lead people to believe that value changes are intended.
|
||||
|
||||
Bad code
|
||||
|
||||
LOCAL PROCEDURE ShowMessage@15(VAR Text@1000 : Text[250]);
|
||||
BEGIN
|
||||
Text := GetMessageText;
|
||||
IF (Text <> '') AND GenJnlLineInserted THEN
|
||||
MESSAGE(Text);
|
||||
END;
|
||||
|
||||
Good code
|
||||
|
||||
LOCAL PROCEDURE ShowMessage@15(Text@1000 : Text[250]);
|
||||
BEGIN
|
||||
Text := GetMessageText;
|
||||
IF (Text <> '') AND GenJnlLineInserted THEN
|
||||
MESSAGE(Text);
|
||||
END;
|
||||
|
|
@ -1,20 +0,0 @@
|
|||
+++
|
||||
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.
|
||||
|
||||
Bad code
|
||||
|
||||
Any procedure / trigger that has class coupling of > 30
|
||||
|
||||
|
||||
|
||||
Good code
|
||||
|
||||
Any procedure / trigger that has class coupling of <= 30\.
|
||||
Class coupling is computed by summing the unique instances of the following in a code block:
|
||||
- every unique usage of a complex C/AL data type (table, codeunit, etc) as 1\.
|
||||
- every unique usage of a DotNet type as 1\.
|
||||
|
|
@ -1,24 +0,0 @@
|
|||
+++
|
||||
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.
|
||||
|
||||
Bad code
|
||||
|
||||
Any procedure / trigger that has a cyclomatic complexity > 25, using the CC3 version mentioned in [this article][anchor0].
|
||||
|
||||
|
||||
|
||||
Good code
|
||||
|
||||
Any procedure / trigger that has a cyclomatic complexity <= 25, using the CC3 version.
|
||||
The CC3 version is computed by summing the following in a code block:
|
||||
- each IF statement as 1\.
|
||||
- each entire CASE as 1\.
|
||||
|
||||
|
||||
|
||||
[anchor0]: http://www.aivosto.com/project/help/pm-complexity.html
|
||||
|
|
@ -1,7 +0,0 @@
|
|||
+++
|
||||
title = "Encapsulate Local Functionality"
|
||||
weight = 530
|
||||
tags = ["C/AL"]
|
||||
categories = ["Best Practice"]
|
||||
+++
|
||||
Any function used local must be defined as local.
|
||||
|
|
@ -1,31 +0,0 @@
|
|||
+++
|
||||
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.
|
||||
|
||||
Bad code
|
||||
|
||||
IF Cust.FIND('-') THEN
|
||||
ERROR(CustIsBlockErr)
|
||||
|
||||
Good code
|
||||
|
||||
IF Cust.FINDFIRST THEN
|
||||
ERROR(CustIsBlockErr)
|
||||
|
||||
Bad code
|
||||
|
||||
IF Cust.FINDFIRST THEN
|
||||
REPEAT
|
||||
...
|
||||
UNTIL Cust.NEXT = 0;
|
||||
|
||||
Good code
|
||||
|
||||
IF Cust.FINDSET THEN
|
||||
REPEAT
|
||||
...
|
||||
UNTIL Cust.NEXT = 0;
|
||||
|
|
@ -1,51 +0,0 @@
|
|||
+++
|
||||
title = "Initialized Variables"
|
||||
weight = 660
|
||||
tags = ["C/AL"]
|
||||
categories = ["Best Practice"]
|
||||
+++
|
||||
Variables should always be set to a specific value, before they are used.
|
||||
|
||||
Bad code
|
||||
|
||||
PROCEDURE SetPurchLine@22(VAR CurrentPurchLine@1000 : Record 39);
|
||||
VAR
|
||||
Pegging@1001 : Boolean;
|
||||
BEGIN
|
||||
IF Pegging THEN
|
||||
CurrQuantity := CurrentPurchLine."Quantity (Base)"
|
||||
ELSE
|
||||
CurrQuantity := CurrentPurchLine."Outstanding Qty. (Base)";
|
||||
END;
|
||||
|
||||
Good code
|
||||
|
||||
PROCEDURE SetPurchLine@22(VAR CurrentPurchLine@1000 : Record 39);
|
||||
VAR
|
||||
Pegging@1001 : Boolean;
|
||||
BEGIN
|
||||
Pegging := IsPegging(CurrentPurchLine);
|
||||
IF Pegging THEN
|
||||
CurrQuantity := CurrentPurchLine."Quantity (Base)"
|
||||
ELSE
|
||||
CurrQuantity := CurrentPurchLine."Outstanding Qty. (Base)";
|
||||
END;
|
||||
|
||||
Bad code
|
||||
|
||||
// In the example below, the function will always return FALSE.
|
||||
PROCEDURE GetItemsToPlan@22() : Boolean;
|
||||
BEGIN
|
||||
SETRANGE("Document Type","Document Type"::Order);
|
||||
...
|
||||
FINDSET
|
||||
END;
|
||||
|
||||
Good code
|
||||
|
||||
PROCEDURE GetItemsToPlan@22() : Boolean;
|
||||
BEGIN
|
||||
SETRANGE("Document Type","Document Type"::Order);
|
||||
...
|
||||
EXIT(FINDSET)
|
||||
END;
|
||||
|
|
@ -1,43 +0,0 @@
|
|||
+++
|
||||
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.
|
||||
|
||||
Bad code
|
||||
|
||||
Any procedure / trigger that has a maintainability index < 20
|
||||
|
||||
|
||||
|
||||
Good code
|
||||
|
||||
Any procedure / trigger that has a maintainability index >= 20\.
|
||||
The Maintainability Index is computed as a function:
|
||||
- Lines Of Code (inverse proportional)
|
||||
- the Halstead Volume
|
||||
- Cyclomatic Complexity (inverse proportional).
|
||||
|
||||
More info
|
||||
|
||||
* [Halstead Volume][anchor1]
|
||||
* [Cyclomatic Complexity][anchor2]
|
||||
|
||||
Bad code
|
||||
|
||||
Any procedure / trigger that is > 100 lines of code
|
||||
|
||||
|
||||
|
||||
Good code
|
||||
|
||||
Any procedure / trigger that is <= 100 lines of code.
|
||||
A full C/AL Statement counts as 1 line of code
|
||||
|
||||
|
||||
|
||||
[anchor0]: http://blogs.msdn.com/b/codeanalysis/archive/2007/11/20/maintainability-index-range-and-meaning.aspx
|
||||
[anchor1]: http://en.wikipedia.org/wiki/Halstead_complexity_measures
|
||||
[anchor2]: http://www.aivosto.com/project/help/pm-complexity.html
|
||||
|
|
@ -1,35 +0,0 @@
|
|||
+++
|
||||
title = "Parameter Placeholders"
|
||||
weight = 920
|
||||
tags = ["C/AL"]
|
||||
categories = ["Best Practice"]
|
||||
+++
|
||||
The number of parameters passed to a string must match the placeholders.
|
||||
|
||||
Bad code
|
||||
|
||||
CannotDeleteLineErr@1005 : TextConst 'ENU=You cannot delete this line because one or more rating values exists.';
|
||||
...
|
||||
ERROR(CannotDeleteLineErr,TABLECAPTION);
|
||||
|
||||
Good code
|
||||
|
||||
CannotDeleteLineErr@1005 : TextConst 'ENU=You cannot delete this line because one or more rating values exists.';
|
||||
...
|
||||
ERROR(CannotDeleteLineErr);
|
||||
|
||||
|
||||
|
||||
Bad code
|
||||
|
||||
CannotUseThisFieldErr@1020 : TextConst 'ENU=You cannot use this field for %2 fields.';
|
||||
...
|
||||
ERROR(CannotUseThisFieldErr,0,Field.Class);
|
||||
|
||||
Good code
|
||||
|
||||
CannotUseThisFieldErr@1020 : TextConst 'ENU=You cannot use this field for %1 fields.';
|
||||
...
|
||||
ERROR(CannotUseThisFieldErr,Field.Class);
|
||||
|
||||
###
|
||||
|
|
@ -1,23 +0,0 @@
|
|||
+++
|
||||
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.
|
||||
|
||||
Bad code
|
||||
|
||||
LOCAL PROCEDURE Code@1();
|
||||
VAR
|
||||
CAJnlPostBatch@1001 : Codeunit 1103;
|
||||
BEGIN
|
||||
CAJnlPostBatch.Run(CostJnlLine);
|
||||
END;
|
||||
|
||||
Good code
|
||||
|
||||
LOCAL PROCEDURE Code@1();
|
||||
BEGIN
|
||||
CODEUNIT.RUN(CODEUNIT::"CA Jnl.-Post Batch",CostJnlLine);
|
||||
END;
|
||||
|
|
@ -1,27 +0,0 @@
|
|||
+++
|
||||
title = "Unreachable Code"
|
||||
weight = 1310
|
||||
tags = ["C/AL"]
|
||||
categories = ["Best Practice"]
|
||||
+++
|
||||
Do not write code that will never be hit.
|
||||
|
||||
It affects code readability and can lead to wrong assumptions.
|
||||
|
||||
Bad code
|
||||
|
||||
IF Type <> Type::FIELD THEN BEGIN
|
||||
...
|
||||
ERROR(...);
|
||||
RecRef.CLOSE;
|
||||
END;
|
||||
|
||||
|
||||
|
||||
Good code
|
||||
|
||||
IF Type <> Type::FIELD THEN BEGIN
|
||||
...
|
||||
RecRef.CLOSE;
|
||||
ERROR(...);
|
||||
END;
|
||||
|
|
@ -1,40 +0,0 @@
|
|||
+++
|
||||
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.
|
||||
|
||||
Bad code
|
||||
|
||||
PROCEDURE AddEntities@1(FilterStr@1000 : Text[250]);
|
||||
VAR
|
||||
Vendor@1001 : Record 23;
|
||||
Count@1002 : Integer;
|
||||
BEGIN
|
||||
Count := 0;
|
||||
Vendor.SETFILTER("No.",FilterStr);
|
||||
IF Vendor.FINDSET THEN
|
||||
REPEAT
|
||||
"User ID" := USERID;
|
||||
"Vendor No." := Vendor."No.";
|
||||
IF INSERT THEN
|
||||
Count += 1;
|
||||
UNTIL Vendor.NEXT = 0;
|
||||
END;
|
||||
|
||||
Good code
|
||||
|
||||
PROCEDURE AddEntities@1(FilterStr@1000 : Text[250]);
|
||||
VAR
|
||||
Vendor@1001 : Record 23;
|
||||
BEGIN
|
||||
Vendor.SETFILTER("No.",FilterStr);
|
||||
IF Vendor.FINDSET THEN
|
||||
REPEAT
|
||||
"User ID" := USERID;
|
||||
"Vendor No." := Vendor."No.";
|
||||
IF INSERT THEN;
|
||||
UNTIL Vendor.NEXT = 0;
|
||||
END;
|
||||
|
|
@ -1,46 +0,0 @@
|
|||
+++
|
||||
title = "Unused Variables"
|
||||
weight = 1330
|
||||
tags = ["C/AL"]
|
||||
categories = ["Best Practice"]
|
||||
+++
|
||||
Do not declare variables that are unused.
|
||||
|
||||
Unused variables affect readability.
|
||||
|
||||
Bad code
|
||||
|
||||
PROCEDURE CheckPostingDate@23(CaptionEntryNo@1005 : Text[50]);
|
||||
BEGIN
|
||||
IF GenJnlCheckLine.DateNotAllowed(PostingDate) THEN
|
||||
ERROR(DateNotAllowedErr,Caption,EntryNo)
|
||||
IF PostingDate > MaxPostingDate THEN
|
||||
MaxPostingDate := PostingDate;
|
||||
END
|
||||
|
||||
Good code
|
||||
|
||||
PROCEDURE CheckPostingDate@23();
|
||||
BEGIN
|
||||
IF GenJnlCheckLine.DateNotAllowed(PostingDate) THEN
|
||||
ERROR(DateNotAllowedErr,Caption,EntryNo);
|
||||
IF PostingDate > MaxPostingDate THEN
|
||||
MaxPostingDate := PostingDate;
|
||||
END;
|
||||
|
||||
Bad code
|
||||
|
||||
PROCEDURE IsReturned@14(EntryNo@1002 : Integer) : Decimal;
|
||||
VAR
|
||||
ItemEntry@1000 : Record 32;
|
||||
Quantity@1003 : Integer;
|
||||
BEGIN
|
||||
EXIT(-OutboundApplied(EntryNo,TRUE) - InboundApplied(EntryNo,TRUE));
|
||||
END;
|
||||
|
||||
Good code
|
||||
|
||||
PROCEDURE IsReturned@14(EntryNo@1002 : Integer) : Decimal;
|
||||
BEGIN
|
||||
EXIT(-OutboundApplied(EntryNo,TRUE) - InboundApplied(EntryNo,TRUE));
|
||||
END;
|
||||
|
|
@ -1,61 +0,0 @@
|
|||
+++
|
||||
title = "Variable Capacity Mismatch"
|
||||
weight = 1410
|
||||
tags = ["C/AL"]
|
||||
categories = ["Best Practice"]
|
||||
+++
|
||||
Do not assign a value to a variable whose capacity is smaller.
|
||||
|
||||
It will throw an error at runtime.
|
||||
|
||||
Bad code
|
||||
|
||||
FileName@1010 : Text[250];
|
||||
...
|
||||
UploadedFileName@1016 : Text[1024];
|
||||
...
|
||||
FileName := UploadedFileName;
|
||||
|
||||
Good code
|
||||
|
||||
FileName@1010 : Text[1024];
|
||||
...
|
||||
UploadedFileName@1016 : Text[1024];
|
||||
...
|
||||
FileName := UploadedFileName;
|
||||
|
||||
Bad code
|
||||
|
||||
FileName@1010 : Text[250];
|
||||
...
|
||||
UploadedFileName@1016 : Text[1024];
|
||||
...
|
||||
FileName := UploadedFileName;
|
||||
|
||||
Good code
|
||||
|
||||
FileName@1010 : Text[250];
|
||||
...
|
||||
UploadedFileName@1016 : Text[1024];
|
||||
...
|
||||
FileName := COPYSTR(UploadedFileName,1,250); // In case only the first 250 chars are needed. Similar for fields
|
||||
|
||||
Bad code
|
||||
|
||||
VAR
|
||||
ExceededNumberTxt@001 : 'ENU=Warning: Exceeded number of unsent documents/requests'
|
||||
Subject@1002 : Text[50];
|
||||
...
|
||||
BEGIN
|
||||
...
|
||||
Subject := ExceededNumberTxt;
|
||||
|
||||
Good code
|
||||
|
||||
VAR
|
||||
ExceededNumberTxt@001 : 'ENU=Warning: Exceeded number of unsent documents/requests'
|
||||
Subject@1002 : Text[100];
|
||||
...
|
||||
BEGIN
|
||||
...
|
||||
Subject := ExceededNumberTxt';
|
||||
|
|
@ -1,32 +0,0 @@
|
|||
+++
|
||||
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.
|
||||
|
||||
**Given that**
|
||||
"Contract Type" is a field on table ServiceContractHeader, then in the following example there is a parameter name clash with the field name. Which one will be used?
|
||||
|
||||
Bad code
|
||||
|
||||
PROCEDURE InsertData@1("Contract Type"@1000 : Option...);
|
||||
...
|
||||
BEGIN
|
||||
...
|
||||
WITH ServiceContractHeader DO BEGIN
|
||||
...
|
||||
DimMgt.InsertServContractDim(...,"Contract Type","Contract No.",0,...);
|
||||
END;
|
||||
|
||||
Good code
|
||||
|
||||
PROCEDURE InsertData@1(ContractType@1000 : Option...);
|
||||
...
|
||||
BEGIN
|
||||
...
|
||||
WITH ServiceContractHeader DO BEGIN
|
||||
...
|
||||
DimMgt.InsertServContractDim(...,ContractType,"Contract No.",0,...);
|
||||
END;
|
||||
|
|
@ -1,208 +0,0 @@
|
|||
+++
|
||||
title = "Internally used DotNet Types"
|
||||
weight = 690
|
||||
tags = ["C/AL"]
|
||||
categories = ["Best Practice"]
|
||||
+++
|
||||
_(Dynamics NAV 2015)_
|
||||
|
||||
|
||||
**Dot Net Types**
|
||||
|
||||
'mscorlib'.System.Convert
|
||||
|
||||
'mscorlib'.System.Globalization.CultureInfo
|
||||
|
||||
'mscorlib'.System.Globalization.DateTimeStyles
|
||||
|
||||
'mscorlib'.System.Globalization.NumberStyles
|
||||
|
||||
'mscorlib'.System.Type
|
||||
|
||||
'mscorlib'.System.Array
|
||||
|
||||
'mscorlib'.System.EventArgs
|
||||
|
||||
'mscorlib'.System.Security.Cryptography.SHA512Managed
|
||||
|
||||
'mscorlib'.System.Security.Cryptography.HashAlgorithm
|
||||
|
||||
'mscorlib'.System.Text.Encoding
|
||||
|
||||
'mscorlib'.System.Text.UTF8Encoding
|
||||
|
||||
'mscorlib'.System.Environment
|
||||
|
||||
'mscorlib'.System.IO.Directory
|
||||
|
||||
'mscorlib'.System.IO.Path
|
||||
|
||||
'mscorlib'.System.IO.File
|
||||
|
||||
'mscorlib'.System.IO.FileAttributes
|
||||
|
||||
'mscorlib'.System.Collections.ArrayList
|
||||
|
||||
'mscorlib'.System.Collections.IEnumerator
|
||||
|
||||
'mscorlib'.System.Collections.Generic.IEnumerator\`1
|
||||
|
||||
'mscorlib'.System.TimeSpan
|
||||
|
||||
'mscorlib'.System.DateTime
|
||||
|
||||
'mscorlib'.System.DateTimeKind
|
||||
|
||||
'mscorlib'.System.DateTimeOffset
|
||||
|
||||
'mscorlib'.System.Decimal
|
||||
|
||||
'mscorlib'.System.String
|
||||
|
||||
'System'.System.Diagnostics.Process
|
||||
|
||||
'System'.System.Diagnostics.ProcessStartInfo
|
||||
|
||||
'System'.System.IO.Compression.CompressionMode
|
||||
|
||||
'System'.System.IO.Compression.GZipStream
|
||||
|
||||
'System'.System.Uri
|
||||
|
||||
'System'.System.UriPartial
|
||||
|
||||
'System.Data'.System.Data.DataColumn
|
||||
|
||||
'System.Data'.System.Data.DataTable
|
||||
|
||||
'System.Data'.System.Data.DataRow
|
||||
|
||||
'System.Web'.System.Web.HttpUtility
|
||||
|
||||
'System.Windows.Forms'.System.Windows.Forms.DialogResult
|
||||
|
||||
'System.Windows.Forms'.System.Windows.Forms.FileDialog
|
||||
|
||||
'System.Windows.Forms'.System.Windows.Forms.OpenFileDialog
|
||||
|
||||
'System.Windows.Forms'.System.Windows.Forms.SaveFileDialog
|
||||
|
||||
'System.Windows.Forms'.System.Windows.Forms.FolderBrowserDialog
|
||||
|
||||
'System.Xml'.\*
|
||||
|
||||
'DocumentFormat.OpenXml'.\*
|
||||
|
||||
'mscorlib'.System.IO.DirectoryInfo
|
||||
|
||||
'mscorlib'.System.IO.FileInfo
|
||||
|
||||
'Microsoft.Dynamics.Nav.Client.CodeViewerTypes'.Microsoft.Dynamics.Nav.Client.CodeViewerTypes.BreakpointCollection
|
||||
|
||||
'Microsoft.Dynamics.Nav.Client.CodeViewerTypes'.Microsoft.Dynamics.Nav.Client.CodeViewerTypes.VariableCollection
|
||||
|
||||
'Microsoft.Dynamics.Nav.SMTP'.Microsoft.Dynamics.Nav.SMTP.SmtpMessage
|
||||
|
||||
'Microsoft.Dynamics.Nav.Management.DSObjectPickerWrapper'.\*
|
||||
|
||||
'Microsoft.Dynamics.Nav.Timer'.\*
|
||||
|
||||
'Microsoft.Dynamics.Nav.DO.ClientProxyWrapper'.\*
|
||||
|
||||
'Microsoft.Dynamics.Nav.Client.BusinessChart'.\*
|
||||
|
||||
'Microsoft.Dynamics.Nav.Client.BusinessChart.Model'.\*
|
||||
|
||||
'Microsoft.Dynamics.Nav.Integration.Office'.\*
|
||||
|
||||
'Microsoft.Dynamics.Nav.Integration.Office.Mock'.\*
|
||||
|
||||
'Microsoft.Dynamics.Nav.EwsWrapper'.\*
|
||||
|
||||
'Microsoft.Dynamics.Nav.EwsWrapper.ALTestHelper'.\*
|
||||
|
||||
'Microsoft.Dynamics.NAV.OLSync.OLSyncSupplier'.\*
|
||||
|
||||
'Microsoft.Dynamics.Nav.OLSync.Common'.\*
|
||||
|
||||
'Microsoft.Dynamics.Nav.NavUserAccount'.\*
|
||||
|
||||
'Microsoft.Dynamics.Nav.OpenXml'.\*
|
||||
|
||||
'Microsoft.Dynamics.Nav.RapidStart'.\*
|
||||
|
||||
'Microsoft.Dynamics.Framework.RapidStart.Common'.\*
|
||||
|
||||
'Microsoft.Dynamics.Nav.Client.TimelineVisualization'.Microsoft.Dynamics.Nav.Client.TimelineVisualization.
|
||||
|
||||
VisualizationScenarios
|
||||
|
||||
'Microsoft.Dynamics.Framework.UI.WinForms.DataVisualization.Timeline'.Microsoft.Dynamics.Framework.UI.
|
||||
|
||||
WinForms.DataVisualization.TimelineVisualization.DataModel+TransactionChangesRow
|
||||
|
||||
'Microsoft.Dynamics.Framework.UI.WinForms.DataVisualization.Timeline'.Microsoft.Dynamics.Framework.UI.
|
||||
|
||||
WinForms.DataVisualization.TimelineVisualization.DataModel+TransactionChangesDataTable
|
||||
|
||||
'Microsoft.Dynamics.Framework.UI.WinForms.DataVisualization.Timeline'.Microsoft.Dynamics.Framework.UI.
|
||||
|
||||
WinForms.DataVisualization.TimelineVisualization.DataModel+TransactionRow
|
||||
|
||||
'Microsoft.Dynamics.Framework.UI.WinForms.DataVisualization.Timeline'.Microsoft.Dynamics.Framework.UI.
|
||||
|
||||
WinForms.DataVisualization.TimelineVisualization.DataModel+TransactionDataTable
|
||||
|
||||
'Microsoft.Office.Interop.Word'.\*
|
||||
|
||||
'Microsoft.Office.Interop.Excel'.\*
|
||||
|
||||
'Microsoft.Dynamics.BAPIWrapper'.\*
|
||||
|
||||
'Microsoft.Dynamics.Nav.Types'.Microsoft.Dynamics.Nav.Types.ConfigSettings
|
||||
|
||||
'Microsoft.Dynamics.Nav.DocumentService'.\*
|
||||
|
||||
'Microsoft.Dynamics.Nav.DocumentService.Types'.\*
|
||||
|
||||
'mscorlib'.System.IO.StreamWriter
|
||||
|
||||
'Microsoft.Dynamics.Nav.Client.TimelineVisualization'.Microsoft.Dynamics.Nav.Client.TimelineVisualization.
|
||||
|
||||
InteractiveTimelineVisualizationAddIn
|
||||
|
||||
'System'.System.ComponentModel.CancelEventArgs
|
||||
|
||||
'System'.System.Text.RegularExpressions.Regex
|
||||
|
||||
'System'.System.Text.RegularExpressions.RegexOptions
|
||||
|
||||
'mscorlib'.System.IO.StreamReader
|
||||
|
||||
'System.Windows.Forms'.System.Windows.Forms.Control
|
||||
|
||||
'System.Windows.Forms'.System.Windows.Forms.ControlEventArgs
|
||||
|
||||
'System.Windows.Forms'.System.Windows.Forms.DragEventArgs
|
||||
|
||||
'System.Windows.Forms'.System.Windows.Forms.GiveFeedbackEventArgs
|
||||
|
||||
'System.Windows.Forms'.System.Windows.Forms.HelpEventArgs
|
||||
|
||||
'System.Windows.Forms'.System.Windows.Forms.InvalidateEventArgs
|
||||
|
||||
'System.Windows.Forms'.System.Windows.Forms.KeyEventArgs
|
||||
|
||||
'System.Windows.Forms'.System.Windows.Forms.KeyPressEventArgs
|
||||
|
||||
'System.Windows.Forms'.System.Windows.Forms.LayoutEventArgs
|
||||
|
||||
'System.Windows.Forms'.System.Windows.Forms.MouseEventArgs
|
||||
|
||||
'System.Windows.Forms'.System.Windows.Forms.PaintEventArgs
|
||||
|
||||
'System.Windows.Forms'.System.Windows.Forms.PreviewKeyDownEventArgs
|
||||
|
||||
'System.Windows.Forms'.System.Windows.Forms.QueryAccessibilityHelpEventArgs
|
||||
|
||||
'System.Windows.Forms'.System.Windows.Forms.UICuesEventArgs
|
||||
|
|
@ -1,12 +0,0 @@
|
|||
+++
|
||||
title = "Internationalization"
|
||||
weight = 700
|
||||
tags = ["C/AL"]
|
||||
categories = ["Best Practice"]
|
||||
+++
|
||||
## C/AL Coding Guidelines
|
||||
|
||||
## **Internationalization**
|
||||
|
||||
|
||||
Find the C/AL guidelines by expanding the menu in the left.
|
||||
|
|
@ -1,19 +0,0 @@
|
|||
+++
|
||||
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.
|
||||
|
||||
Bad code
|
||||
|
||||
IF ReservEntry."Expected Receipt Date" >
|
||||
CALCDATE('-' + FORMAT("Dampener (Time)") + FirstDate)
|
||||
THEN
|
||||
|
||||
Good code
|
||||
|
||||
IF ReservEntry."Expected Receipt Date" >
|
||||
CALCDATE('<-' + FORMAT("Dampener (Time)") + FirstDate + '>')
|
||||
THEN
|
||||
|
|
@ -1,11 +0,0 @@
|
|||
+++
|
||||
title = "Localizability"
|
||||
weight = 750
|
||||
tags = ["C/AL"]
|
||||
categories = ["Best Practice"]
|
||||
+++
|
||||
## C/AL Coding Guidelines
|
||||
|
||||
## **Localizability**
|
||||
|
||||
Find the C/AL guidelines by expanding the menu in the left.
|
||||
|
|
@ -1,28 +0,0 @@
|
|||
+++
|
||||
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.
|
||||
|
||||
Bad code
|
||||
|
||||
...
|
||||
{ 2 ;2 ;Field ;
|
||||
SourceExpr=Name }
|
||||
...
|
||||
OBJECT Table 2000000000 User
|
||||
...
|
||||
{ 2 ; ;Name ;Text50 }
|
||||
|
||||
Good code
|
||||
|
||||
...
|
||||
{ 2 ;2 ;Field ;
|
||||
CaptionML=ENU=Name;
|
||||
SourceExpr=Name }
|
||||
...
|
||||
OBJECT Table 2000000000 User
|
||||
...
|
||||
{ 2 ; ;Name ;Text50 }
|
||||
|
|
@ -1,24 +0,0 @@
|
|||
+++
|
||||
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.
|
||||
|
||||
Reason:
|
||||
|
||||
1. The correct translation will be automatically used.
|
||||
2. If the caption/name changes, then there will be a single point of change needed.
|
||||
|
||||
Bad code
|
||||
|
||||
```al
|
||||
IF NOT CONFIRM(UpdateLocationQst,TRUE,FIELDNAME("Location Code"),...)
|
||||
```
|
||||
|
||||
Good code
|
||||
|
||||
```al
|
||||
IF NOT CONFIRM(UpdateLocationQst,TRUE,FIELDCAPTION("Location Code"),...)
|
||||
```
|
||||
|
|
@ -1,26 +0,0 @@
|
|||
+++
|
||||
title = "Global Text Constants"
|
||||
weight = 610
|
||||
tags = ["C/AL"]
|
||||
categories = ["Best Practice"]
|
||||
+++
|
||||
Declare Text Constant as global variables.
|
||||
|
||||
Bad code
|
||||
|
||||
```al
|
||||
PROCEDURE GetRequirementText@6(...) : Text[50];
|
||||
VAR
|
||||
RequirementOptionsTxt@1002 : TextConst 'ENU=Shipment,Receive,Pick,Put-Away';
|
||||
BEGIN
|
||||
```
|
||||
|
||||
Good code
|
||||
|
||||
```al
|
||||
VAR
|
||||
RequirementOptionsTxt@1002 : TextConst 'ENU=Shipment,Receive,Pick,Put-Away';
|
||||
...
|
||||
PROCEDURE GetRequirementText@6(...) : Text[50];
|
||||
BEGIN
|
||||
```
|
||||
|
|
@ -1,47 +0,0 @@
|
|||
+++
|
||||
title = "Use Text Constants"
|
||||
weight = 1360
|
||||
tags = ["C/AL"]
|
||||
categories = ["Best Practice"]
|
||||
+++
|
||||
Pass user messages using Text Constants. It makes translation easy.
|
||||
|
||||
Bad code
|
||||
|
||||
```al
|
||||
ImportAttachmentQst@1021 : TextConst 'ENU="Import attachment "';
|
||||
...
|
||||
IF CONFIRM(ImportAttachmentQst + Caption +'?',TRUE) THEN BEGIN
|
||||
```
|
||||
|
||||
Good code
|
||||
|
||||
```al
|
||||
ImportAttachmentQst@1021 : TextConst 'ENU="Import attachment %1?"';
|
||||
...
|
||||
IF CONFIRM(STRSUBSTNO(ImportAttachmentQst, Caption),TRUE) THEN BEGIN
|
||||
```
|
||||
|
||||
Bad code
|
||||
|
||||
```al
|
||||
...
|
||||
IF NOT
|
||||
CONFIRM(
|
||||
STRSUBSTNO(
|
||||
'Difference on Periodic entries: %1 on %2' +
|
||||
'Do you want to continue?',Balance,Date),
|
||||
TRUE)
|
||||
THEN
|
||||
ERROR('Program terminated by the user');
|
||||
```
|
||||
|
||||
Good code
|
||||
|
||||
```al
|
||||
DiffOnPeriodEntiesQst@100 : TextConst 'ENU="Difference on Periodic entries: %1 on %2\\ Do you want to continue?"';
|
||||
ProgramTerminatedErr@200 : TextConst 'ENU="Program terminated by the user"';
|
||||
...
|
||||
IF NOT CONFIRM(STRSUBSTNO(DiffOnPeriodEntiesQst,Balance,Date),TRUE) THEN
|
||||
ERROR(ProgramTerminatedErr);
|
||||
```
|
||||
Some files were not shown because too many files have changed in this diff Show more
Loading…
Add table
Add a link
Reference in a new issue