diff --git a/.devcontainer/Dockerfile b/.devcontainer/Dockerfile deleted file mode 100644 index b5d6dfb3..00000000 --- a/.devcontainer/Dockerfile +++ /dev/null @@ -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 diff --git a/.devcontainer/devcontainer.json b/.devcontainer/devcontainer.json deleted file mode 100644 index 12d6163f..00000000 --- a/.devcontainer/devcontainer.json +++ /dev/null @@ -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": {} - } -} diff --git a/.gitattributes b/.gitattributes deleted file mode 100644 index 1fbdaf78..00000000 --- a/.gitattributes +++ /dev/null @@ -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 \ No newline at end of file diff --git a/.github/workflows/hugo.yml b/.github/workflows/hugo.yml deleted file mode 100644 index 149fb31b..00000000 --- a/.github/workflows/hugo.yml +++ /dev/null @@ -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 diff --git a/.github/workflows/regen-gh-pages.yml b/.github/workflows/regen-gh-pages.yml new file mode 100644 index 00000000..36ff7fa8 --- /dev/null +++ b/.github/workflows/regen-gh-pages.yml @@ -0,0 +1,49 @@ +name: hugo CI + +on: + push: + branches: + - main # Set a branch name to trigger deployment + pull_request: + +jobs: + deploy: + runs-on: ubuntu-20.04 + concurrency: + group: ${{ github.workflow }}-${{ github.ref }} + steps: + - uses: actions/checkout@v2 + with: + submodules: recursive # Fetch Hugo themes (true OR recursive) + fetch-depth: 0 # Fetch all history for .GitInfo and .Lastmod + + - name: Setup Hugo + uses: peaceiris/actions-hugo@v2 + with: + hugo-version: 'latest' + extended: true # Use extended Hugo + + - name: Setup Node + uses: actions/setup-node@v2 + with: + node-version: '14' + + - name: Cache dependencies + uses: actions/cache@v1 + with: + path: ~/.npm + key: ${{ runner.os }}-node-${{ hashFiles('**/package-lock.json') }} + restore-keys: | + ${{ runner.os }}-node- + - name: Prepare postcss-cli + run: npm ci + + - name: Build + run: hugo --minify + + - name: Deploy + uses: peaceiris/actions-gh-pages@v3 + if: ${{ github.ref == 'refs/heads/main' }} + with: + github_token: ${{ secrets.GITHUB_TOKEN }} + publish_dir: ./public diff --git a/.gitmodules b/.gitmodules new file mode 100644 index 00000000..b76244d3 --- /dev/null +++ b/.gitmodules @@ -0,0 +1,3 @@ +[submodule "themes/docsy"] + path = themes/docsy + url = https://github.com/google/docsy.git diff --git a/.vscode/tasks.json b/.vscode/tasks.json deleted file mode 100644 index d4117c41..00000000 --- a/.vscode/tasks.json +++ /dev/null @@ -1,11 +0,0 @@ -{ - "version": "2.0.0", - "tasks": [ - { - "label": "Start local Hugo server", - "type": "shell", - "command": "hugo serve", - "problemMatcher": [] - } - ] -} \ No newline at end of file diff --git a/CITATION.cff b/CITATION.cff deleted file mode 100644 index 51c15c7e..00000000 --- a/CITATION.cff +++ /dev/null @@ -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" \ No newline at end of file diff --git a/README.md b/README.md index e41ff2b1..3e15c226 100644 --- a/README.md +++ b/README.md @@ -40,7 +40,14 @@ contact [opencode@microsoft.com](mailto:opencode@microsoft.com) with any additio ### Setup -**Run Hugo server** +1. Clone and setup +```sh +# Clone all submodules +git submodule update --init --recursive --depth 1 +# Install NPM dependencies +npm install +``` +2. Run Hugo server ``` $ hugo server Web Server is available at http://localhost:1313/ (bind address 127.0.0.1) diff --git a/hugo.toml b/config.toml similarity index 97% rename from hugo.toml rename to config.toml index 0163dbbc..fa73b91b 100644 --- a/hugo.toml +++ b/config.toml @@ -4,7 +4,7 @@ languageCode = 'en-us' title = 'AL Guidelines' # Hugo allows theme composition (and inheritance). The precedence is from left to right. -theme = ["github.com/google/docsy"] +theme = ["docsy"] # Will give values to .Lastmod etc. enableGitInfo = true @@ -72,12 +72,11 @@ id = "" [languages] [languages.en] -title = "alguidelines.dev - Business Central Design Patterns" +description = "Guidelines for when Developing AL for Microsoft Dynamics 365 Business Central" languageName = "English" +title = "alguidelines.dev - Business Central Design Patterns" # Weight used for sorting. weight = 1 -[languages.en.params] -description = "Guidelines for when Developing AL for Microsoft Dynamics 365 Business Central" # Everything below this are Site Params @@ -110,10 +109,6 @@ notoc = true # First one is picked as the Twitter card image if not set on page. images = ["images/og-image-fission.png"] -[params.mermaid] -enable = true -theme = "neutral" - [params.social] #github = '' #slackurl = "" @@ -150,8 +145,8 @@ url = '/blog/serverless-next.js-example-blog-with-fission/' [params.ui] # Set to true to disable breadcrumb navigation. breadcrumb_disable = false -# Set to true to show an About link in the site footer -footer_about_enable = true +# Set to true to disable the About link in the site footer +footer_about_disable = false # Set to false if you don't want to display a logo (/assets/icons/logo.svg) in the top navbar navbar_logo = true # Set to true if you don't want the top navbar to be translucent when over a `block/cover`, like on the homepage. @@ -221,4 +216,4 @@ weight = 13 name = "Discord" pre = "" url = "https://discord.gg/4wbfNv3" -weight = 14 +weight = 14 \ No newline at end of file diff --git a/content/Discussions/_index.md b/content/Discussions/_index.md new file mode 100644 index 00000000..6fdaeb1d --- /dev/null +++ b/content/Discussions/_index.md @@ -0,0 +1,21 @@ ++++ +chapter = true +pre = " " +title = "Discussions" +weight = 200 ++++ + +# [Discussions](https://github.com/microsoft/alguidelines/discussions) + +We enabled the "Discussions" forum on the github-page of our repository for you to discuss anything "Design Pattern" or "Best Practices" related. + +You can find the discussions here: https://github.com/microsoft/alguidelines/discussions. + +Feel free to browse through them, find certain topics and/or participate in the discussions! + +## Create your own discussion + +You are obviously also free to start a new discussion. You can simply do that by using the "New Discussion" button on the top right. + +Or use this link: https://github.com/microsoft/alguidelines/discussions/new?category=bc-patterns + diff --git a/content/_index.md b/content/_index.md index 5d6a83a4..0117a774 100644 --- a/content/_index.md +++ b/content/_index.md @@ -123,16 +123,16 @@ images: ["images/og-image-fission.png"]
- NEW + PATTERN

- Getting Started With Agentic Coding + Event Bridge

- Essential concepts and practices for working with AI coding assistants in your AL development workflow. + In the world of interfaces, it is important to preserve (certain) events over multiple implementation of the interface.

- - + +
@@ -140,16 +140,16 @@ images: ["images/og-image-fission.png"]
- GUIDE + BEST PRACTICE

- Vibe Coding Rules + CASE Action on next line

- AI-optimized coding rules and guidelines designed to enhance the AL developer experience in modern AI-powered IDEs. + A CASE action should start on a line after the possibility.

-
@@ -157,17 +157,17 @@ images: ["images/og-image-fission.png"]
- TOOLS + PATTERN

- Agentic Tools + Façade

- Model Context Protocol (MCP) servers that enhance AI assistants for Business Central development workflows. + The intent of this pattern is to provide a unified API to a single or a collection of potentially complex subsystems.

@@ -210,6 +210,17 @@ images: ["images/og-image-fission.png"]

+ + +
@@ -243,6 +254,22 @@ images: ["images/og-image-fission.png"] +
+ +
diff --git a/content/docs/BestPractices/CustomTelemetry/index.md b/content/docs/BestPractices/CustomTelemetry/index.md deleted file mode 100644 index c2f1eb20..00000000 --- a/content/docs/BestPractices/CustomTelemetry/index.md +++ /dev/null @@ -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. \ No newline at end of file diff --git a/content/docs/BestPractices/DeleteAll/index.md b/content/docs/BestPractices/DeleteAll/index.md index 3ed623a5..802d1e2d 100644 --- a/content/docs/BestPractices/DeleteAll/index.md +++ b/content/docs/BestPractices/DeleteAll/index.md @@ -1,10 +1,10 @@ --- title: "DeleteAll" -tags: ["AL","Performance"] +tags: ["Performance"] categories: ["Best Practice"] --- -_Created by waldo, Described by waldo_ +<_Created by waldo, Described by waldo_\> ## Description @@ -25,3 +25,11 @@ Therefore it's good practice to always check if the table is empty when performi if not EmptyTableWLD.IsEmpty() then EmptyTableWLD.DeleteAll(true); ``` + +## Discussions + +You can discuss the guideline [here](https://github.com/microsoft/alguidelines/discussions/107) + +You can find discussions on all "Best Practices" [here](https://github.com/microsoft/alguidelines/discussions/categories/bc-best-practices). + +If you don't find the discussion of this guideline, please feel free to create a new one with the same title as this article. \ No newline at end of file diff --git a/content/docs/BestPractices/SetLoadFields/Index.md b/content/docs/BestPractices/SetLoadFields/Index.md deleted file mode 100644 index e3aae546..00000000 --- a/content/docs/BestPractices/SetLoadFields/Index.md +++ /dev/null @@ -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; -``` diff --git a/content/docs/BestPractices/SubscriberCodeunits/index.md b/content/docs/BestPractices/SubscriberCodeunits/index.md index 9f4e61ea..2aadfa54 100644 --- a/content/docs/BestPractices/SubscriberCodeunits/index.md +++ b/content/docs/BestPractices/SubscriberCodeunits/index.md @@ -1,6 +1,6 @@ --- title: "Subscriber Codeunits" -tags: ["AL","Performance"] +tags: ["Performance"] categories: ["Best Practice"] --- @@ -18,18 +18,15 @@ In general, subscribers have to be put in codeunits. There are a few performanc Let's discuss all points ## Keep the codeunit as small as possible - Every time a subscriber gets called, a new instance of the codeunit is being loaded in memory, which takes memory and processing power. The smaller the codeunit, the less memory, and the faster it is. -Therefore, it's suggested to split the subscribers by functionality and avoid putting business logic in the actual codeunit. Tip: put all business logic in an "[Method Codeunit](https://alguidelines.dev/docs/patterns/generic-method-pattern/)". +Therefore, it's suggested to split the subscribers by functionality and avoid putting business logic in the actual codeunit. Tip: put all business logic in an "[Method Codeunit](https://alguidelines.dev/bcpatterns/generic-method-pattern/)". Examples: - - if you app does things on Sales and Purchase, create a Sales-subs codeunit, and a Purchase-subs. - if you have multiple functionalities in your app (let's call'm modules), create a subs-codeunit per module, and only add the subscribers in there that are necessary for that module. ### Bad code - ```AL codeunit 2037325 "Setup Subs" { @@ -76,7 +73,6 @@ codeunit 2037325 "Setup Subs" end; } ``` - ### Good code Split into 2 codeunits, and move the business logic out. @@ -116,7 +112,6 @@ codeunit 2037324 "RHE Setup Subs" To avoid the extra "loading of the content" while a subscriber is being executed, use Single Instance codeunit for subscribers. Do take into account, of course, that it would share the state across the entire session. ### Bad code - ```AL codeunit 2037324 "RHE Setup Subs" { @@ -129,9 +124,7 @@ codeunit 2037324 "RHE Setup Subs" end; } ``` - ### Good code - ```AL codeunit 2037324 "RHE Setup Subs" { @@ -152,7 +145,6 @@ codeunit 2037324 "RHE Setup Subs" If possible, only execute the subscriber when really necessary by using Manual Binding. ### Bad code - ```AL //subscriber - code should actually only run when Color=Red. [EventSubscriber(ObjectType::Table, Database::"Just Some Table WLD", 'OnAfterValidateEvent', 'Message 2', false, false)] @@ -170,9 +162,7 @@ If possible, only execute the subscriber when really necessary by using Manual B JustSomeTable.Validate("Message 2", format(Random(1000))); until JustSomeTable.Next() < 1; ``` - ### Good code - ```AL if JustSomeTable.FindSet() then repeat @@ -187,15 +177,21 @@ If possible, only execute the subscriber when really necessary by using Manual B ``` ## Avoid OnInsert/OnModify/OnDelete - The reason for this is, that it breaks the batch-calls: - - Any "OnInsert" subscriber breaks the bulk inserts, simply because it needs to perform an operation after every record that was inserted - Any "OnModify" subscriber slows down the "ModifyAll", simply because it needs to perform an operation after every record that was modified. I fact: 1 SQL call is turned into a loop of SQL calls. - Any "OnDelete" subscriber slows down the "DeleteAll", simply because it needs to perform an operation after every record that was deleted. I fact: 1 SQL call is turned into a loop of SQL calls. Avoid subscribers to these events. +## [Discussions](https://github.com/microsoft/alguidelines/discussions/92) + +You can discuss this guidelines [here](https://github.com/microsoft/alguidelines/discussions/92). + +You can find discussions on all "Best Practices" [here](https://github.com/microsoft/alguidelines/discussions/categories/bc-best-practices). + +If you don't find the discussion of this guideline, please feel free to create a new one with the same title as this article. + ## References -The [Generic Method Pattern](https://alguidelines.dev/docs/patterns/generic-method-pattern/) +The [Generic Method Pattern](https://alguidelines.dev/bcpatterns/generic-method-pattern/) \ No newline at end of file diff --git a/content/docs/BestPractices/_index.md b/content/docs/BestPractices/_index.md index 9684d0df..698e2846 100644 --- a/content/docs/BestPractices/_index.md +++ b/content/docs/BestPractices/_index.md @@ -5,6 +5,8 @@ description: > AL Code Best Practices --- +# Business Central Best Practices + This section will be cover things that aren't as simple as Design Patterns, but will help make sure your development is: - high-performance diff --git a/content/docs/BestPractices/api-page/index.md b/content/docs/BestPractices/api-page/index.md deleted file mode 100644 index cc23701e..00000000 --- a/content/docs/BestPractices/api-page/index.md +++ /dev/null @@ -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) { } - } - } -} -``` diff --git a/content/docs/BestPractices/begin-as-an-afterword/index.md b/content/docs/BestPractices/begin-as-an-afterword/index.md index 5b6bac47..190bae4f 100644 --- a/content/docs/BestPractices/begin-as-an-afterword/index.md +++ b/content/docs/BestPractices/begin-as-an-afterword/index.md @@ -1,6 +1,6 @@ --- title: "begin as an afterword" -tags: ["AL","Readability"] +tags: ["Readability"] categories: ["Best Practice"] --- @@ -8,14 +8,14 @@ _Created by Microsoft, Described by waldo_ ## Description -When `begin` follows `then`, `else`, `do`, it should be on the same line, preceded by one space character. +When `begin` follows `then`, `else`, `do`, it should be on the same line, preceded by one space character. ## Bad code ```al if ICPartnerRefType = ICPartnerRefType::"Common Item No." then begin - ... + ... end; ``` @@ -23,6 +23,12 @@ When `begin` follows `then`, `else`, `do`, it should be on the same line, preced ```al if ICPartnerRefType = ICPartnerRefType::"Common Item No." then begin - ... + ... end; ``` + +## [Discussions](https://github.com/microsoft/alguidelines/discussions/categories/bc-best-practices?discussions_q=begin+as+an+After+Word+category%3A%22BC+Best+Practices%22) + +You can find discussions on all "Best Practices" [here](https://github.com/microsoft/alguidelines/discussions/categories/bc-best-practices). + +If you don't find the discussion of this guideline, please feel free to create a new one with the same title as this article. \ No newline at end of file diff --git a/content/docs/BestPractices/begin-end/index.md b/content/docs/BestPractices/begin-end/index.md index d35dea99..22433d65 100644 --- a/content/docs/BestPractices/begin-end/index.md +++ b/content/docs/BestPractices/begin-end/index.md @@ -1,6 +1,6 @@ --- title: "Begin-End - Compound Only" -tags: ["AL","Readability"] +tags: ["Readability"] categories: ["Best Practice"] --- @@ -8,30 +8,26 @@ _Created by Microsoft, Described by waldo_ Only use begin..end to enclose [compound statements](https://docs.microsoft.com/en-us/cpp/c-language/compound-statement-c?view=msvc-170#:~:text=A%20compound%20statement%20%28also%20called%20a%20%22block%22%29%20typically,appear%20at%20the%20head%20of%20a%20compound%20statement.). -## Example 1 - -### Bad code +## Bad code ```AL if FindSet() then begin repeat - ... + ... until next() = 0; end; ``` -### Good code +## Good code ```AL if FindSet() then repeat - ... + ... until next() = 0; ``` -## Example 2 - -### Bad code +## Bad code ```AL if IsAssemblyOutputLine then begin @@ -39,7 +35,7 @@ if IsAssemblyOutputLine then begin end; ``` -### Good code +## Good code ```AL if IsAssemblyOutputLine then @@ -57,9 +53,8 @@ end else (not X) ``` -## Tips +## [Discussions](https://github.com/microsoft/alguidelines/discussions/categories/bc-best-practices?discussions_q=begin+end+compound+only+category%3A%22BC+Best+Practices%22) -The [AZ AL Dev Tools/AL Code Outline](https://marketplace.visualstudio.com/items?itemName=andrzejzwierzchowski.al-code-outline) extension adds two new commands to Visual Studio Code to remove begin..end around single statements. +You can find discussions on all "Best Practices" [here](https://github.com/microsoft/alguidelines/discussions/categories/bc-best-practices). -- `Remove Begin..End around Single Statements from the Active Editor` : removes begin..end around single statement from the current editor -- `Remove Begin..End around Single Statements from the Active Project` : removes begin..end around single statement from the current project +If you don't find the discussion of this guideline, please feel free to create a new one with the same title as this article. diff --git a/content/docs/BestPractices/binary-operator-line-start/index.md b/content/docs/BestPractices/binary-operator-line-start/index.md index 2f7ecae8..7408ef63 100644 --- a/content/docs/BestPractices/binary-operator-line-start/index.md +++ b/content/docs/BestPractices/binary-operator-line-start/index.md @@ -1,6 +1,6 @@ --- title: "Binary Operator to Start Line" -tags: ["AL","Readability"] +tags: ["Readability"] categories: ["Best Practice"] --- @@ -8,20 +8,26 @@ _Created by Microsoft, Described by waldo_ ## Description -Do not start a line with a binary operator. +Do not start a line with a binary operator. ## Bad code ```AL "Quantity to Ship" := - Quantity - - "Quantity Shipped" +Quantity +- "Quantity Shipped" ``` ## Good code ```AL "Quantity to Ship" := - Quantity - - "Quantity Shipped" +Quantity - +"Quantity Shipped" ``` + +## [Discussions](https://github.com/microsoft/alguidelines/discussions/categories/bc-best-practices?discussions_q=binary+operator+to+start+line+category%3A%22BC+Best+Practices%22) + +You can find discussions on all "Best Practices" [here](https://github.com/microsoft/alguidelines/discussions/categories/bc-best-practices). + +If you don't find the discussion of this guideline, please feel free to create a new one with the same title as this article. diff --git a/content/docs/BestPractices/blank-lines/index.md b/content/docs/BestPractices/blank-lines/index.md deleted file mode 100644 index 7f1b8efe..00000000 --- a/content/docs/BestPractices/blank-lines/index.md +++ /dev/null @@ -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 diff --git a/content/docs/BestPractices/case-actions/index.md b/content/docs/BestPractices/case-actions/index.md index 5062e53e..fb041368 100644 --- a/content/docs/BestPractices/case-actions/index.md +++ b/content/docs/BestPractices/case-actions/index.md @@ -1,6 +1,6 @@ --- title: "CASE Action on next line" -tags: ["AL","Readability"] +tags: ["Readability"] categories: ["Best Practice"] --- @@ -8,7 +8,7 @@ _Created by Microsoft, Described by waldo_ ## Description -A CASE action should start on a line after the possibility. +A CASE action should start on a line after the possibility. ## Bad code @@ -29,3 +29,9 @@ A CASE action should start on a line after the possibility. Letter2 := '11'; end; ``` + +## [Discussions](https://github.com/microsoft/alguidelines/discussions/categories/bc-best-practices?discussions_q=case+action+on+next+line+category%3A%22BC+Best+Practices%22) + +You can find discussions on all "Best Practices" [here](https://github.com/microsoft/alguidelines/discussions/categories/bc-best-practices). + +If you don't find the discussion of this guideline, please feel free to create a new one with the same title as this article. diff --git a/content/docs/BestPractices/comments-spacing/index.md b/content/docs/BestPractices/comments-spacing/index.md index 615f9e69..e8e2c07f 100644 --- a/content/docs/BestPractices/comments-spacing/index.md +++ b/content/docs/BestPractices/comments-spacing/index.md @@ -1,13 +1,12 @@ --- title: "Comment Spacing" -tags: ["AL","Readability"] +tags: ["Readability"] categories: ["Best Practice"] --- _Created by Microsoft, Described by waldo_ ## Description - Always start comments with // followed by one space character. ## Bad code @@ -15,9 +14,16 @@ Always start comments with // followed by one space character. ```al RowNo += 1000; //Move way below the budget ``` - + + ## Good code ```al RowNo += 1000; // Move way below the budget ``` + +## [Discussions](https://github.com/microsoft/alguidelines/discussions/categories/bc-best-practices?discussions_q=comment+spacing+category%3A%22BC+Best+Practices%22) + +You can find discussions on all "Best Practices" [here](https://github.com/microsoft/alguidelines/discussions/categories/bc-best-practices). + +If you don't find the discussion of this guideline, please feel free to create a new one with the same title as this article. \ No newline at end of file diff --git a/content/docs/BestPractices/end-else-pair/index.md b/content/docs/BestPractices/end-else-pair/index.md index 29313176..9c339d36 100644 --- a/content/docs/BestPractices/end-else-pair/index.md +++ b/content/docs/BestPractices/end-else-pair/index.md @@ -1,6 +1,6 @@ --- title: "end else pair" -tags: ["AL","Readability"] +tags: ["Readability"] categories: ["Best Practice"] --- @@ -15,11 +15,11 @@ The `end else` pair should always appear on the same line. ```al if OppEntry.Find('-') then if SalesCycleStage.Find('-') then begin - ... + ... end else begin - ... + ... end; ``` @@ -28,8 +28,14 @@ The `end else` pair should always appear on the same line. ```al if OppEntry.Find('-') then if SalesCycleStage.Find('-') then begin - ... + ... end else begin - ... + ... end; ``` + +## [Discussions](https://github.com/microsoft/alguidelines/discussions/categories/bc-best-practices?discussions_q=end+else/pair+category%3A%22BC+Best+Practices%22) + +You can find discussions on all "Best Practices" [here](https://github.com/microsoft/alguidelines/discussions/categories/bc-best-practices). + +If you don't find the discussion of this guideline, please feel free to create a new one with the same title as this article. diff --git a/content/docs/BestPractices/if-not-find-then-exit/index.md b/content/docs/BestPractices/if-not-find-then-exit/index.md deleted file mode 100644 index 6c097864..00000000 --- a/content/docs/BestPractices/if-not-find-then-exit/index.md +++ /dev/null @@ -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; -``` diff --git a/content/docs/BestPractices/istemporary-table-safeguard/index.md b/content/docs/BestPractices/istemporary-table-safeguard/index.md deleted file mode 100644 index 7331b34c..00000000 --- a/content/docs/BestPractices/istemporary-table-safeguard/index.md +++ /dev/null @@ -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; -``` diff --git a/content/docs/BestPractices/keyboard-shortcuts/index.md b/content/docs/BestPractices/keyboard-shortcuts/index.md deleted file mode 100644 index a9b15863..00000000 --- a/content/docs/BestPractices/keyboard-shortcuts/index.md +++ /dev/null @@ -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 + | -| Move Line Up/Down | Editor | ALT + | -| Copy Line Below/Above | Editor | ALT + SHIFT + | -| Delete Line | Editor | CTRL + X (without selection) | -| Add Selection To Next Match | Editor | CTRL + D | -| Select All Occurences Of Find Match | Editor | CTRL + SHIFT + L | -| Add Cursors To Line Ends | Editor | SHIFT + ALT + I (after selecting lines) | -| Add Cursor Above/Below | Editor | CTRL + ALT + | -| Place Multiple Cursors Manually | Editor | ALT + Click | -| Fast Scrolling | Editor | ALT + Mouse Wheel | -| Go To Symbol In File | Editor | CTRL + SHIFT + O | -| Breadcrumbs - Open And Select | Editor | CTRL + SHIFT + . | -| Go Back / Forward | Go To Definition | ALT + | - diff --git a/content/docs/BestPractices/keyword-pairs-indentation/index.md b/content/docs/BestPractices/keyword-pairs-indentation/index.md index b25ea498..625310f4 100644 --- a/content/docs/BestPractices/keyword-pairs-indentation/index.md +++ b/content/docs/BestPractices/keyword-pairs-indentation/index.md @@ -1,13 +1,12 @@ --- title: "Keyword Pairs - Indentation" -tags: ["AL","Readability"] +tags: ["Readability"] categories: ["Best Practice"] --- _Created by Microsoft, Described by waldo_ ## Description - The `if..then` pair, `while..do` pair, and `for..do` pair must appear on the same line or the same level of indentation. If possible, you can align the lines it is even much more readable. ## Bad code @@ -24,3 +23,9 @@ The `if..then` pair, `while..do` pair, and `for..do` pair must appear on the sam (a = b) then ``` + +## [Discussions](https://github.com/microsoft/alguidelines/discussions/categories/bc-best-practices?discussions_q=keyword+pair+indentation+category%3A%22BC+Best+Practices%22) + +You can find discussions on all "Best Practices" [here](https://github.com/microsoft/alguidelines/discussions/categories/bc-best-practices). + +If you don't find the discussion of this guideline, please feel free to create a new one with the same title as this article. \ No newline at end of file diff --git a/content/docs/BestPractices/line-start-keywords/index.md b/content/docs/BestPractices/line-start-keywords/index.md index 113e587c..51cee4ee 100644 --- a/content/docs/BestPractices/line-start-keywords/index.md +++ b/content/docs/BestPractices/line-start-keywords/index.md @@ -1,12 +1,11 @@ --- title: "Line Start Keywords" -tags: ["AL","Readability"] +tags: ["Readability"] categories: ["Best Practice"] --- -_Created by Microsoft, Described by waldo_ +<_Created by Microsoft, Described by waldo_\> ## Description - The `end`, `if`, `repeat`, `for`, `while`, `else` and `case` statement should always start a line. ## Bad code @@ -29,3 +28,10 @@ The `end`, `if`, `repeat`, `for`, `while`, `else` and `case` statement should al if IsSalesCycleCode then ValidatSalesCycleCode(); ``` + + +## [Discussions](https://github.com/microsoft/alguidelines/discussions/categories/bc-best-practices?discussions_q=line+start+keyword+category%3A%22BC+Best+Practices%22) + +You can find discussions on all "Best Practices" [here](https://github.com/microsoft/alguidelines/discussions/categories/bc-best-practices). + +If you don't find the discussion of this guideline, please feel free to create a new one with the same title as this article. \ No newline at end of file diff --git a/content/docs/BestPractices/lonely-repeat/index.md b/content/docs/BestPractices/lonely-repeat/index.md index d15c0945..87551a9e 100644 --- a/content/docs/BestPractices/lonely-repeat/index.md +++ b/content/docs/BestPractices/lonely-repeat/index.md @@ -1,13 +1,12 @@ --- title: "Lonely Repeat" -tags: ["AL","Readability"] +tags: ["Readability"] categories: ["Best Practice"] --- _Created by Microsoft, Described by waldo_ ## Description - The `repeat` statement should always be alone on a line. ## Bad code @@ -15,10 +14,16 @@ The `repeat` statement should always be alone on a line. ```al if ReservEntry.FindSet() then repeat ``` - + ## Good code ```al if ReservEntry.FindSet() then repeat ``` + +## [Discussions](https://github.com/microsoft/alguidelines/discussions/categories/bc-best-practices?discussions_q=lonely+repeat+category%3A%22BC+Best+Practices%22) + +You can find discussions on all "Best Practices" [here](https://github.com/microsoft/alguidelines/discussions/categories/bc-best-practices). + +If you don't find the discussion of this guideline, please feel free to create a new one with the same title as this article. \ No newline at end of file diff --git a/content/docs/BestPractices/named-invocations/index.md b/content/docs/BestPractices/named-invocations/index.md index 395848e4..9d817731 100644 --- a/content/docs/BestPractices/named-invocations/index.md +++ b/content/docs/BestPractices/named-invocations/index.md @@ -1,13 +1,12 @@ --- title: "Named Invocations" -tags: ["AL","Readability"] +tags: ["Readability"] categories: ["Best Practice"] --- _Created by Microsoft, Described by waldo_ ## Description - When calling an object statically use the Object Name, not the Object Id. ## Bad code @@ -22,8 +21,8 @@ When calling an object statically use the Object Name, not the Object Id. Page.RunModal(Page::"Posted Sales Shipment Lines", SalesShptLine); ``` -## Tips +## [Discussions](https://github.com/microsoft/alguidelines/discussions/categories/bc-best-practices?discussions_q=named+invocations+category%3A%22BC+Best+Practices%22) -The [BusinessCentral.LinterCop](https://marketplace.visualstudio.com/items?itemName=StefanMaron.businesscentral-lintercop) extension adds a new rule to check your code for hardcoded object IDs. +You can find discussions on all "Best Practices" [here](https://github.com/microsoft/alguidelines/discussions/categories/bc-best-practices). -- [LC0012](https://github.com/StefanMaron/BusinessCentral.LinterCop/wiki/LC0012): Using hardcoded IDs in functions like Codeunit.Run() is not allowed. +If you don't find the discussion of this guideline, please feel free to create a new one with the same title as this article. diff --git a/content/docs/BestPractices/one-statement-per-line/index.md b/content/docs/BestPractices/one-statement-per-line/index.md index 822c05c1..a8508ff5 100644 --- a/content/docs/BestPractices/one-statement-per-line/index.md +++ b/content/docs/BestPractices/one-statement-per-line/index.md @@ -1,41 +1,44 @@ --- title: "One Statement per Line" -tags: ["AL","Readability"] +tags: ["Readability"] categories: ["Best Practice"] --- _Created by Microsoft, Described by waldo_ ## Description - A line of code should not have more than one statement. -## Example 1 - -### Bad code +## Bad code ```al if OppEntry.Find('-') then exit; -``` +``` + -### Good code +## Good code ```al if OppEntry.Find('-') then exit; -``` - -## Example 2 - -### Bad code +``` + +## Bad code ```al TotalCost += Cost; TotalAmt += Amt; -``` +``` + -### Good code +## Good code ```al TotalCost += Cost; TotalAmt += Amt; ``` + +## [Discussions](https://github.com/microsoft/alguidelines/discussions/categories/bc-best-practices?discussions_q=one+statement+per+line+category%3A%22BC+Best+Practices%22) + +You can find discussions on all "Best Practices" [here](https://github.com/microsoft/alguidelines/discussions/categories/bc-best-practices). + +If you don't find the discussion of this guideline, please feel free to create a new one with the same title as this article. diff --git a/content/docs/BestPractices/separate-if-and-else/index.md b/content/docs/BestPractices/separate-if-and-else/index.md index 805ff90c..0cfe515b 100644 --- a/content/docs/BestPractices/separate-if-and-else/index.md +++ b/content/docs/BestPractices/separate-if-and-else/index.md @@ -1,29 +1,35 @@ --- title: "Seperate if and else" -tags: ["AL","Readability"] +tags: ["Readability"] categories: ["Best Practice"] --- _Created by Microsoft, Described by waldo_ ## Description - -`if` and `else` statements should be on separate lines. +`if` and `else` statements should be on separate lines. ## Bad code ```al - if Atom = '>' then HasLogicalOperator := true else begin - ... + if Atom = '\>' then HasLogicalOperator := true else begin + ... end; ``` ## Good code ```al - if Atom = '>' then + if Atom = '\>' then HasLogicalOperator := true else begin ... end; ``` + + +## [Discussions](https://github.com/microsoft/alguidelines/discussions/categories/bc-best-practices?discussions_q=separate+if+and+else+category%3A%22BC+Best+Practices%22) + +You can find discussions on all "Best Practices" [here](https://github.com/microsoft/alguidelines/discussions/categories/bc-best-practices). + +If you don't find the discussion of this guideline, please feel free to create a new one with the same title as this article. diff --git a/content/docs/BestPractices/spacing-binary-operators/index.md b/content/docs/BestPractices/spacing-binary-operators/index.md index e0bfaac8..48bbbbe5 100644 --- a/content/docs/BestPractices/spacing-binary-operators/index.md +++ b/content/docs/BestPractices/spacing-binary-operators/index.md @@ -1,53 +1,46 @@ --- title: "Spacing Binary Operators" -tags: ["AL","Readability"] +tags: ["Readability"] categories: ["Best Practice"] --- _Created by Microsoft, Described by waldo_ ## Description - There must be exactly one space character on each side of a binary operator such as = + - AND OR =. The parameter comma operator however, should have a space after the comma. -## Example 1 - -### Bad code +## Bad code ```al - "Line Discount %" := "Line Discount Amount"/"Line Value"*100; -``` + "Line Discount %" := "Line Discount Amount"/"Line Value"*100; +``` -### Good code +## Good code ```al - "Line Discount %" := "Line Discount Amount" / "Line Value" * 100; -``` + "Line Discount %" := "Line Discount Amount" / "Line Value" * 100; +``` -## Example 2 - -### Bad code +## Bad code ```al - StartDate := CalcDate('<+'+Format(Days+i)+'D\>',StartDate); -``` + StartDate := CalcDate('<+'+Format(Days+i)+'D\>',StartDate); +``` -### Good code +## Good code ```al - StartDate := CalcDate('<+' + Format(Days + i) + 'D\>', StartDate); -``` + StartDate := CalcDate('<+' + Format(Days + i) + 'D\>', StartDate); +``` -## Example 3 - -### Bad code +## Bad code ```al - StartDate:=0D; // Initialize + StartDate:=0D; // Initialize ``` - -### Good code + +## Good code ```al StartDate := 0D; // Initialize -``` +``` \ No newline at end of file diff --git a/content/docs/BestPractices/suggested-abbreviations/index.md b/content/docs/BestPractices/suggested-abbreviations/index.md index 722f1561..7ff5136d 100644 --- a/content/docs/BestPractices/suggested-abbreviations/index.md +++ b/content/docs/BestPractices/suggested-abbreviations/index.md @@ -1,6 +1,6 @@ --- title: "Suggested Abbreviations" -tags: ["AL","Readability"] +tags: ["Readability"] categories: ["Best Practice"] --- @@ -359,3 +359,9 @@ If there is no other choice, then use the suggestions below. | % | Pct | | 3-tier | Three-Tier | | Outlook Synch | Osynch | + +## [Discussions](https://github.com/microsoft/alguidelines/discussions/categories/bc-best-practices?discussions_q=abbreviations+category%3A%22BC+Best+Practices%22) + +You can find discussions on all "Best Practices" [here](https://github.com/microsoft/alguidelines/discussions/categories/bc-best-practices). + +If you don't find the discussion of this guideline, please feel free to create a new one with the same title as this article. \ No newline at end of file diff --git a/content/docs/BestPractices/unnecessary-else/index.md b/content/docs/BestPractices/unnecessary-else/index.md index 4dd6d1bd..d190bfdf 100644 --- a/content/docs/BestPractices/unnecessary-else/index.md +++ b/content/docs/BestPractices/unnecessary-else/index.md @@ -1,6 +1,6 @@ --- title: "Unnecessary else" -tags: ["AL","Readability"] +tags: ["Readability"] categories: ["Best Practice"] --- @@ -32,3 +32,9 @@ _Created by Microsoft, Described by waldo_ Error(BinCodeChangeNotAllowedErr, ...); end; ``` + +## [Discussions](https://github.com/microsoft/alguidelines/discussions/categories/bc-best-practices?discussions_q=unnecessary+else+category%3A%22BC+Best+Practices%22) + +You can find discussions on all "Best Practices" [here](https://github.com/microsoft/alguidelines/discussions/categories/bc-best-practices). + +If you don't find the discussion of this guideline, please feel free to create a new one with the same title as this article. diff --git a/content/docs/BestPractices/unnecessary-truefalse/index.md b/content/docs/BestPractices/unnecessary-truefalse/index.md index 57d6864b..8e020968 100644 --- a/content/docs/BestPractices/unnecessary-truefalse/index.md +++ b/content/docs/BestPractices/unnecessary-truefalse/index.md @@ -1,6 +1,6 @@ --- title: "Unnecessary true/false" -tags: ["AL","Readability"] +tags: ["Readability"] categories: ["Best Practice"] --- @@ -9,30 +9,33 @@ _Created by Microsoft, Described by waldo_ ## Description Do not use `true` or `false` keywords unnecessarily if the expression is already an logical expression. -## Example 1 - -### Bad code +## Bad code ```al if IsPositive() = true then -``` - -### Good code +``` + +## Good code ```al if IsPositive() then -``` +``` + -## Example 2 - -### Bad code +## Bad code ```al if Complete <> true then -``` - -### Good code +``` + +## Good code ```al if not Complete then ``` + +## [Discussions](https://github.com/microsoft/alguidelines/discussions/categories/bc-best-practices?discussions_q=unnecessary+true+false+category%3A%22BC+Best+Practices%22) + +You can find discussions on all "Best Practices" [here](https://github.com/microsoft/alguidelines/discussions/categories/bc-best-practices). + +If you don't find the discussion of this guideline, please feel free to create a new one with the same title as this article. \ No newline at end of file diff --git a/content/docs/BestPractices/variable-naming/index.md b/content/docs/BestPractices/variable-naming/index.md index 242d1d15..e722b779 100644 --- a/content/docs/BestPractices/variable-naming/index.md +++ b/content/docs/BestPractices/variable-naming/index.md @@ -1,13 +1,12 @@ --- title: "Variable Naming" -tags: ["AL","Readability"] +tags: ["Readability"] categories: ["Best Practice"] --- _Created by Microsoft, Described by waldo_ ## Description - Variables that refer to a AL object must contain the objects name, abbreviated where necessary. A variable must begin with a capital letter. @@ -16,48 +15,34 @@ Blanks, periods, and other characters (such as parentheses) that would make quot If a variable is a compound of two or more words or abbreviations, each word or abbreviation should begin with a capital letter. -## Example 1 - -### Bad code +## Bad code ```al WIPBuffer: Record "Job WIP Buffer" ``` - -### Good code - +## Good code ```al JobWIPBuffer: Record "Job WIP Buffer" ``` - -## Example 2 - -### Bad code - +## Bad code ```al Postline: Codeunit "Gen. Jnl.-Post Line"; ``` - -### Good code - +## Good code ```al GenJnlPostLine: Codeunit "Gen. Jnl.-Post Line"; ``` - -## Example 3 - -### Bad code - +## Bad code ```al "Amount (LCY)": Decimal; ``` - -### Good code - +## Good code ```al AmountLCY: Decimal; ``` -## Tips +## [Discussions](https://github.com/microsoft/alguidelines/discussions/categories/bc-best-practices?discussions_q=one+variable+naming+category%3A%22BC+Best+Practices%22) -The [AL Variable Helper](https://marketplace.visualstudio.com/items?itemName=rasmus.al-var-helper) extension provides Intellisense support to assign correct variable names in AL. +You can find discussions on all "Best Practices" [here](https://github.com/microsoft/alguidelines/discussions/categories/bc-best-practices). + +If you don't find the discussion of this guideline, please feel free to create a new one with the same title as this article. diff --git a/content/docs/BestPractices/variables-declarations-order/index.md b/content/docs/BestPractices/variables-declarations-order/index.md index 8f7e2131..1701e044 100644 --- a/content/docs/BestPractices/variables-declarations-order/index.md +++ b/content/docs/BestPractices/variables-declarations-order/index.md @@ -1,13 +1,12 @@ --- title: "Variables Declarations Order" -tags: ["AL","Readability"] +tags: ["Readability"] categories: ["Best Practice"] --- _Created by Microsoft, Described by waldo_ ## Description - Variables declarations should be ordered by type. In general, object and complex variable types are listed first followed by simple variables. The order should be: - Record @@ -26,6 +25,7 @@ Variables declarations should be ordered by type. In general, object and complex (Ref: [Microsoft Docs](https://docs.microsoft.com/en-us/dynamics365/business-central/dev-itpro/developer/analyzers/codecop-aa0021)) + ## Bad code ```al @@ -40,9 +40,8 @@ Variables declarations should be ordered by type. In general, object and complex StartingDateFilter: Text; ``` -## Tips +## [Discussions](https://github.com/microsoft/alguidelines/discussions/categories/bc-best-practices?discussions_q=one+variables+declarations+order+category%3A%22BC+Best+Practices%22) -The [AZ AL Dev Tools/AL Code Outline](https://marketplace.visualstudio.com/items?itemName=andrzejzwierzchowski.al-code-outline) extension adds two new commands to Visual Studio Code to sorts variables. +You can find discussions on all "Best Practices" [here](https://github.com/microsoft/alguidelines/discussions/categories/bc-best-practices). -- `Sort Variables in the Active Editor` : sorts variables in the current editor -- `Sort Variables in the Active Project` : sorts variables in the current project +If you don't find the discussion of this guideline, please feel free to create a new one with the same title as this article. diff --git a/content/docs/Contributing/FormattingTips/index.md b/content/docs/Contributing/FormattingTips/index.md index bf6f6a18..4f4e525e 100644 --- a/content/docs/Contributing/FormattingTips/index.md +++ b/content/docs/Contributing/FormattingTips/index.md @@ -38,66 +38,6 @@ Results in: end; ``` -## Diagrams with Mermaid - -When providing a documentation, diagrams come in handy. [Mermaid](https://mermaid-js.github.io/mermaid/#/) lets you create diagrams and visualizations using text and code. - -For example the following markdown section: - -```` -```mermaid -classDiagram - Animal <|-- Duck - Animal <|-- Fish - Animal <|-- Zebra - Animal : +int age - Animal : +String gender - Animal: +isMammal() - Animal: +mate() - class Duck{ - +String beakColor - +swim() - +quack() - } - class Fish{ - -int sizeInFeet - -canEat() - } - class Zebra{ - +bool is_wild - +run() - } -``` -```` - -Results in: - -```mermaid -classDiagram - Animal <|-- Duck - Animal <|-- Fish - Animal <|-- Zebra - Animal : +int age - Animal : +String gender - Animal: +isMammal() - Animal: +mate() - class Duck{ - +String beakColor - +swim() - +quack() - } - class Fish{ - -int sizeInFeet - -canEat() - } - class Zebra{ - +bool is_wild - +run() - } -``` - -Can't wait to get started? Use the Mermaid [Live Editor](https://mermaid.live/edit). - ## Hugo Shortcodes Since we're using "Hugo", we can use it's shortcode. Here is a reference: [https://gohugo.io/content-management/shortcodes/](https://gohugo.io/content-management/shortcodes/) diff --git a/content/docs/Contributing/InstallHugo/ManuallyOnWindows11/EnvironmentVariables.png b/content/docs/Contributing/InstallHugo/ManuallyOnWindows11/EnvironmentVariables.png deleted file mode 100644 index 1f76acbe..00000000 Binary files a/content/docs/Contributing/InstallHugo/ManuallyOnWindows11/EnvironmentVariables.png and /dev/null differ diff --git a/content/docs/Contributing/InstallHugo/ManuallyOnWindows11/EnvironmentVariablesPath.png b/content/docs/Contributing/InstallHugo/ManuallyOnWindows11/EnvironmentVariablesPath.png deleted file mode 100644 index 7c4bb5f6..00000000 Binary files a/content/docs/Contributing/InstallHugo/ManuallyOnWindows11/EnvironmentVariablesPath.png and /dev/null differ diff --git a/content/docs/Contributing/InstallHugo/ManuallyOnWindows11/Extract.png b/content/docs/Contributing/InstallHugo/ManuallyOnWindows11/Extract.png deleted file mode 100644 index 35cb5f98..00000000 Binary files a/content/docs/Contributing/InstallHugo/ManuallyOnWindows11/Extract.png and /dev/null differ diff --git a/content/docs/Contributing/InstallHugo/ManuallyOnWindows11/ExtractToBin.png b/content/docs/Contributing/InstallHugo/ManuallyOnWindows11/ExtractToBin.png deleted file mode 100644 index ba466115..00000000 Binary files a/content/docs/Contributing/InstallHugo/ManuallyOnWindows11/ExtractToBin.png and /dev/null differ diff --git a/content/docs/Contributing/InstallHugo/ManuallyOnWindows11/HugoServe.png b/content/docs/Contributing/InstallHugo/ManuallyOnWindows11/HugoServe.png deleted file mode 100644 index 569b5098..00000000 Binary files a/content/docs/Contributing/InstallHugo/ManuallyOnWindows11/HugoServe.png and /dev/null differ diff --git a/content/docs/Contributing/InstallHugo/ManuallyOnWindows11/SearchForEnv.png b/content/docs/Contributing/InstallHugo/ManuallyOnWindows11/SearchForEnv.png deleted file mode 100644 index 881136cd..00000000 Binary files a/content/docs/Contributing/InstallHugo/ManuallyOnWindows11/SearchForEnv.png and /dev/null differ diff --git a/content/docs/Contributing/InstallHugo/ManuallyOnWindows11/SelectExtended.png b/content/docs/Contributing/InstallHugo/ManuallyOnWindows11/SelectExtended.png deleted file mode 100644 index a8adec9c..00000000 Binary files a/content/docs/Contributing/InstallHugo/ManuallyOnWindows11/SelectExtended.png and /dev/null differ diff --git a/content/docs/Contributing/InstallHugo/ManuallyOnWindows11/SystemProperties.png b/content/docs/Contributing/InstallHugo/ManuallyOnWindows11/SystemProperties.png deleted file mode 100644 index ae8adee9..00000000 Binary files a/content/docs/Contributing/InstallHugo/ManuallyOnWindows11/SystemProperties.png and /dev/null differ diff --git a/content/docs/Contributing/InstallHugo/ManuallyOnWindows11/index.md b/content/docs/Contributing/InstallHugo/ManuallyOnWindows11/index.md deleted file mode 100644 index 7e4b8105..00000000 --- a/content/docs/Contributing/InstallHugo/ManuallyOnWindows11/index.md +++ /dev/null @@ -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 . - -## 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 %}} - -![image](SelectExtended.png) - -After downloading the .zip file, extract the zip-file to `c:\Hugo\Bin` - -![image](Extract.png) - -![image](ExtractToBin.png) - -## 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` - -![image](SearchForEnv.png) - -once you see the `Edit the system environment variables`, open it and select `Environment Variables` - -![image](SystemProperties.png) - -Once the Environment Variables screen is open, highlight the `Path` lines and press the `Edit...` button - -![image](EnvironmentVariables.png) - -Now press `New` and add the `C:\Hugo\Bin` to the path. Press `OK` and `OK` to save the new `Path` - -![image](EnvironmentVariablesPath.png) - -Once complete. You should now be able to preview the Hugo site on by opening a command promt, and open the `C:\Hugo\Sites\alguidelines` folder and execute `Hugo Serve` - -![image](HugoServe.png) \ No newline at end of file diff --git a/content/docs/Contributing/InstallHugo/TheShortcut/alguidelines codespace.mp4 b/content/docs/Contributing/InstallHugo/TheShortcut/alguidelines codespace.mp4 deleted file mode 100644 index 173ea8d5..00000000 Binary files a/content/docs/Contributing/InstallHugo/TheShortcut/alguidelines codespace.mp4 and /dev/null differ diff --git a/content/docs/Contributing/InstallHugo/TheShortcut/alguidelines walkthrough.mp4 b/content/docs/Contributing/InstallHugo/TheShortcut/alguidelines walkthrough.mp4 deleted file mode 100644 index 269de8a0..00000000 Binary files a/content/docs/Contributing/InstallHugo/TheShortcut/alguidelines walkthrough.mp4 and /dev/null differ diff --git a/content/docs/Contributing/InstallHugo/TheShortcut/index.md b/content/docs/Contributing/InstallHugo/TheShortcut/index.md deleted file mode 100644 index 9f46fc7f..00000000 --- a/content/docs/Contributing/InstallHugo/TheShortcut/index.md +++ /dev/null @@ -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: - - - -## Use GitHub Codespaces - -What is also great about this, is that you can also use [GitHub Codespaces](https://github.com/features/codespaces) with that setup. In that case, steps 1-5 become two clicks... Here is another full walkthrough: - - diff --git a/content/docs/Contributing/InstallHugo/UsingPowershellOnWindows11/index.md b/content/docs/Contributing/InstallHugo/UsingPowershellOnWindows11/index.md deleted file mode 100644 index 3b64af8f..00000000 --- a/content/docs/Contributing/InstallHugo/UsingPowershellOnWindows11/index.md +++ /dev/null @@ -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 -``` diff --git a/content/docs/Contributing/InstallHugo/_index.md b/content/docs/Contributing/InstallHugo/_index.md deleted file mode 100644 index 30a2f7f6..00000000 --- a/content/docs/Contributing/InstallHugo/_index.md +++ /dev/null @@ -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 - -{{< youtube G7umPCU-8xc >}} diff --git a/content/docs/Contributing/Templates/BestPractice/index.md b/content/docs/Contributing/Templates/BestPractice/index.md index 7a82fbe5..dbb9e0d3 100644 --- a/content/docs/Contributing/Templates/BestPractice/index.md +++ b/content/docs/Contributing/Templates/BestPractice/index.md @@ -1,6 +1,6 @@ --- title: "Title Here" -tags: ["AL"] +tags: [] categories: ["Best Practice"] --- @@ -29,3 +29,11 @@ PutCodeblocksHere() ```al PutCodeblocksHere() ``` + +## Discussions + +Please discuss this guideline + +You can find discussions on all "Best Practices" [here](https://github.com/microsoft/alguidelines/discussions/categories/bc-best-practices). + + \ No newline at end of file diff --git a/content/docs/Contributing/Templates/Patterns/index.md b/content/docs/Contributing/Templates/Patterns/index.md index 7e39e182..031973e1 100644 --- a/content/docs/Contributing/Templates/Patterns/index.md +++ b/content/docs/Contributing/Templates/Patterns/index.md @@ -1,6 +1,6 @@ --- -title: "Title Here" -tags: ["AL"] +title: "Pattern Name" +tags: [] categories: ["Pattern"] --- @@ -44,3 +44,11 @@ Usually, there are occasions where NOT to implement the pattern. List the disad ## List of references Youtube-link? BaseApp? Tweet? ... + +## Discussions + +Please discuss this guideline + +You can find discussions on all "Best Practices" [here](https://github.com/microsoft/alguidelines/discussions/categories/bc-patterns). + + diff --git a/content/docs/NAVPatterns/2-anti-patterns/_index.md b/content/docs/NAVPatterns/2-anti-patterns/_index.md index e7695291..e0c88db2 100644 --- a/content/docs/NAVPatterns/2-anti-patterns/_index.md +++ b/content/docs/NAVPatterns/2-anti-patterns/_index.md @@ -2,7 +2,6 @@ chapter = true title = "2. Anti-Patterns" weight = 130 -tags = ["C/AL"] +++ Some of the software development practices, had **not** stood the test of time. Despite that, some are still being used today by developers everywhere. diff --git a/content/docs/NAVPatterns/2-anti-patterns/nav-upgrade/index.md b/content/docs/NAVPatterns/2-anti-patterns/nav-upgrade/index.md index 244abe96..2c827cdf 100644 --- a/content/docs/NAVPatterns/2-anti-patterns/nav-upgrade/index.md +++ b/content/docs/NAVPatterns/2-anti-patterns/nav-upgrade/index.md @@ -1,7 +1,6 @@ +++ title = "Nav Upgrade" weight = 840 -tags = ["C/AL"] +++ ## Anti-Patterns in NAV Upgrade diff --git a/content/docs/NAVPatterns/2-anti-patterns/reusable-bugs/index.md b/content/docs/NAVPatterns/2-anti-patterns/reusable-bugs/index.md index 09d31469..8809e65a 100644 --- a/content/docs/NAVPatterns/2-anti-patterns/reusable-bugs/index.md +++ b/content/docs/NAVPatterns/2-anti-patterns/reusable-bugs/index.md @@ -1,7 +1,6 @@ +++ title = "Reusable Bugs" weight = 1020 -tags = ["C/AL"] +++ _By Bogdana Botez, Andreas Moth, Eric Wauters (waldo), Elly Nkya, Nikola Kukrika_ diff --git a/content/docs/NAVPatterns/3-cal-coding-guidelines/_index.md b/content/docs/NAVPatterns/3-cal-coding-guidelines/_index.md index 54e113cf..e5d3b5ba 100644 --- a/content/docs/NAVPatterns/3-cal-coding-guidelines/_index.md +++ b/content/docs/NAVPatterns/3-cal-coding-guidelines/_index.md @@ -2,8 +2,6 @@ chapter = true title = "3. CAL Coding Guidelines" weight = 150 -tags = ["C/AL"] -categories = ["Best Practice"] +++ We've decided to publish our current C/AL coding guidelines. They are actual, as per January 2015 when this is published (but might fall out of sync as time goes by). @@ -23,6 +21,6 @@ We're looking forward to your comments. Where you can, do provide concrete examp -[anchor0]: /files/CAL-Coding-Guidelines-at-Microsoft-Development-Center-Copenhagen.pdf "download the C/AL coding guidelines as pdf" +[anchor0]: https://blogs.msdn.microsoft.com/nav/2015/01/09/cal-coding-guidelines-used-at-microsoft-development-center-copenhagen "download the C/AL coding guidelines as pdf" [anchor1]: /members/waldo/default.aspx "waldo" [anchor2]: https://www.youtube.com/watch?v=z6skKy0pkmU&list=PLhZ3P-LY7CqmVszuvtJLujFyHpsVN0U_w&index=26 diff --git a/content/docs/NAVPatterns/3-cal-coding-guidelines/design/_index.md b/content/docs/NAVPatterns/3-cal-coding-guidelines/design/_index.md index f77a18f4..e6278922 100644 --- a/content/docs/NAVPatterns/3-cal-coding-guidelines/design/_index.md +++ b/content/docs/NAVPatterns/3-cal-coding-guidelines/design/_index.md @@ -1,8 +1,6 @@ +++ title = "Design" weight = 490 -tags = ["C/AL"] -categories = ["Best Practice"] +++ ## C/AL Coding Guidelines diff --git a/content/docs/NAVPatterns/3-cal-coding-guidelines/design/by-reference-parameters/index.md b/content/docs/NAVPatterns/3-cal-coding-guidelines/design/by-reference-parameters/index.md index c58006b1..d3689a6e 100644 --- a/content/docs/NAVPatterns/3-cal-coding-guidelines/design/by-reference-parameters/index.md +++ b/content/docs/NAVPatterns/3-cal-coding-guidelines/design/by-reference-parameters/index.md @@ -1,8 +1,6 @@ +++ title = "By Reference Parameters" weight = 280 -tags = ["C/AL"] -categories = ["Best Practice"] +++ Do not declare parameters by reference if their values are not intended to be changed. diff --git a/content/docs/NAVPatterns/3-cal-coding-guidelines/design/class-coupling/index.md b/content/docs/NAVPatterns/3-cal-coding-guidelines/design/class-coupling/index.md index aa47a30e..9219ad2b 100644 --- a/content/docs/NAVPatterns/3-cal-coding-guidelines/design/class-coupling/index.md +++ b/content/docs/NAVPatterns/3-cal-coding-guidelines/design/class-coupling/index.md @@ -1,8 +1,6 @@ +++ title = "Class Coupling" weight = 320 -tags = ["C/AL"] -categories = ["Best Practice"] +++ Do not write functions that have high class coupling. This makes the code hard to maintain. diff --git a/content/docs/NAVPatterns/3-cal-coding-guidelines/design/cyclomatic-complexity/index.md b/content/docs/NAVPatterns/3-cal-coding-guidelines/design/cyclomatic-complexity/index.md index 5a2aa8ac..ffa2a71b 100644 --- a/content/docs/NAVPatterns/3-cal-coding-guidelines/design/cyclomatic-complexity/index.md +++ b/content/docs/NAVPatterns/3-cal-coding-guidelines/design/cyclomatic-complexity/index.md @@ -1,8 +1,6 @@ +++ title = "Cyclomatic Complexity" weight = 460 -tags = ["C/AL"] -categories = ["Best Practice"] +++ Do not write functions that have high cyclomatic complexity. This makes the code hard to maintain. diff --git a/content/docs/NAVPatterns/3-cal-coding-guidelines/design/encapsulate-local-functionality/index.md b/content/docs/NAVPatterns/3-cal-coding-guidelines/design/encapsulate-local-functionality/index.md index 04d84879..b52d9285 100644 --- a/content/docs/NAVPatterns/3-cal-coding-guidelines/design/encapsulate-local-functionality/index.md +++ b/content/docs/NAVPatterns/3-cal-coding-guidelines/design/encapsulate-local-functionality/index.md @@ -1,7 +1,5 @@ +++ title = "Encapsulate Local Functionality" weight = 530 -tags = ["C/AL"] -categories = ["Best Practice"] +++ Any function used local must be defined as local. diff --git a/content/docs/NAVPatterns/3-cal-coding-guidelines/design/findset-findfirst-findlast/index.md b/content/docs/NAVPatterns/3-cal-coding-guidelines/design/findset-findfirst-findlast/index.md index 2dc93c17..5112e849 100644 --- a/content/docs/NAVPatterns/3-cal-coding-guidelines/design/findset-findfirst-findlast/index.md +++ b/content/docs/NAVPatterns/3-cal-coding-guidelines/design/findset-findfirst-findlast/index.md @@ -1,8 +1,6 @@ +++ title = "FINDSET FINDFIRST FINDLAST" weight = 600 -tags = ["C/AL"] -categories = ["Best Practice"] +++ FINDSET, FIND('+') or FIND('-') should only be used when NEXT is used and vice versa. diff --git a/content/docs/NAVPatterns/3-cal-coding-guidelines/design/initialized-variables/index.md b/content/docs/NAVPatterns/3-cal-coding-guidelines/design/initialized-variables/index.md index 35a05a85..15903ec7 100644 --- a/content/docs/NAVPatterns/3-cal-coding-guidelines/design/initialized-variables/index.md +++ b/content/docs/NAVPatterns/3-cal-coding-guidelines/design/initialized-variables/index.md @@ -1,8 +1,6 @@ +++ title = "Initialized Variables" weight = 660 -tags = ["C/AL"] -categories = ["Best Practice"] +++ Variables should always be set to a specific value, before they are used. diff --git a/content/docs/NAVPatterns/3-cal-coding-guidelines/design/maintainability-index/index.md b/content/docs/NAVPatterns/3-cal-coding-guidelines/design/maintainability-index/index.md index 051ac2fb..0d6a486e 100644 --- a/content/docs/NAVPatterns/3-cal-coding-guidelines/design/maintainability-index/index.md +++ b/content/docs/NAVPatterns/3-cal-coding-guidelines/design/maintainability-index/index.md @@ -1,8 +1,6 @@ +++ title = "Maintainability Index" weight = 770 -tags = ["C/AL"] -categories = ["Best Practice"] +++ [Maintainability Index][anchor0]: Do not write functions that have a very low maintainability index. This makes the code hard to maintain. diff --git a/content/docs/NAVPatterns/3-cal-coding-guidelines/design/parameter-placeholders/index.md b/content/docs/NAVPatterns/3-cal-coding-guidelines/design/parameter-placeholders/index.md index 8811e459..de4ee689 100644 --- a/content/docs/NAVPatterns/3-cal-coding-guidelines/design/parameter-placeholders/index.md +++ b/content/docs/NAVPatterns/3-cal-coding-guidelines/design/parameter-placeholders/index.md @@ -1,8 +1,6 @@ +++ title = "Parameter Placeholders" weight = 920 -tags = ["C/AL"] -categories = ["Best Practice"] +++ The number of parameters passed to a string must match the placeholders. diff --git a/content/docs/NAVPatterns/3-cal-coding-guidelines/design/static-object-invocation/index.md b/content/docs/NAVPatterns/3-cal-coding-guidelines/design/static-object-invocation/index.md index 97e89c8a..54aaf03b 100644 --- a/content/docs/NAVPatterns/3-cal-coding-guidelines/design/static-object-invocation/index.md +++ b/content/docs/NAVPatterns/3-cal-coding-guidelines/design/static-object-invocation/index.md @@ -1,8 +1,6 @@ +++ title = "Static Object Invocation" weight = 1160 -tags = ["C/AL"] -categories = ["Best Practice"] +++ Call objects statically whenever possible. It reduces extra noise and removes extra variables. Downside: changing the name of the object which is called statically will need a code update. diff --git a/content/docs/NAVPatterns/3-cal-coding-guidelines/design/unreachable-code/index.md b/content/docs/NAVPatterns/3-cal-coding-guidelines/design/unreachable-code/index.md index 69f713e1..6046b986 100644 --- a/content/docs/NAVPatterns/3-cal-coding-guidelines/design/unreachable-code/index.md +++ b/content/docs/NAVPatterns/3-cal-coding-guidelines/design/unreachable-code/index.md @@ -1,8 +1,6 @@ +++ title = "Unreachable Code" weight = 1310 -tags = ["C/AL"] -categories = ["Best Practice"] +++ Do not write code that will never be hit. diff --git a/content/docs/NAVPatterns/3-cal-coding-guidelines/design/unused-initialized-variables/index.md b/content/docs/NAVPatterns/3-cal-coding-guidelines/design/unused-initialized-variables/index.md index 7e8f6738..898bac08 100644 --- a/content/docs/NAVPatterns/3-cal-coding-guidelines/design/unused-initialized-variables/index.md +++ b/content/docs/NAVPatterns/3-cal-coding-guidelines/design/unused-initialized-variables/index.md @@ -1,8 +1,6 @@ +++ title = "Unused Initialized Variables" weight = 1320 -tags = ["C/AL"] -categories = ["Best Practice"] +++ The value assigned to a variable must be used. Else the variable is not necessary. diff --git a/content/docs/NAVPatterns/3-cal-coding-guidelines/design/unused-variables/index.md b/content/docs/NAVPatterns/3-cal-coding-guidelines/design/unused-variables/index.md index d7bd6da8..c0e5e8b3 100644 --- a/content/docs/NAVPatterns/3-cal-coding-guidelines/design/unused-variables/index.md +++ b/content/docs/NAVPatterns/3-cal-coding-guidelines/design/unused-variables/index.md @@ -1,8 +1,6 @@ +++ title = "Unused Variables" weight = 1330 -tags = ["C/AL"] -categories = ["Best Practice"] +++ Do not declare variables that are unused. diff --git a/content/docs/NAVPatterns/3-cal-coding-guidelines/design/variable-capacity-mismatch/index.md b/content/docs/NAVPatterns/3-cal-coding-guidelines/design/variable-capacity-mismatch/index.md index 0f3d5aab..87bbe3d6 100644 --- a/content/docs/NAVPatterns/3-cal-coding-guidelines/design/variable-capacity-mismatch/index.md +++ b/content/docs/NAVPatterns/3-cal-coding-guidelines/design/variable-capacity-mismatch/index.md @@ -1,8 +1,6 @@ +++ title = "Variable Capacity Mismatch" weight = 1410 -tags = ["C/AL"] -categories = ["Best Practice"] +++ Do not assign a value to a variable whose capacity is smaller. diff --git a/content/docs/NAVPatterns/3-cal-coding-guidelines/design/with-scope-name-collision/index.md b/content/docs/NAVPatterns/3-cal-coding-guidelines/design/with-scope-name-collision/index.md index d14ab704..74bbdd31 100644 --- a/content/docs/NAVPatterns/3-cal-coding-guidelines/design/with-scope-name-collision/index.md +++ b/content/docs/NAVPatterns/3-cal-coding-guidelines/design/with-scope-name-collision/index.md @@ -1,8 +1,6 @@ +++ title = "WITH Scope Name Collision" weight = 1450 -tags = ["C/AL"] -categories = ["Best Practice"] +++ Do not use the WITH scope when it has a variable whose name is the same as a local variable. This can lead to wrong code assumptions. diff --git a/content/docs/NAVPatterns/3-cal-coding-guidelines/internally-used-dot-net-types/index.md b/content/docs/NAVPatterns/3-cal-coding-guidelines/internally-used-dot-net-types/index.md index cb1f1493..b543f50b 100644 --- a/content/docs/NAVPatterns/3-cal-coding-guidelines/internally-used-dot-net-types/index.md +++ b/content/docs/NAVPatterns/3-cal-coding-guidelines/internally-used-dot-net-types/index.md @@ -1,8 +1,6 @@ +++ title = "Internally used DotNet Types" weight = 690 -tags = ["C/AL"] -categories = ["Best Practice"] +++ _(Dynamics NAV 2015)_ diff --git a/content/docs/NAVPatterns/3-cal-coding-guidelines/internationalization/_index.md b/content/docs/NAVPatterns/3-cal-coding-guidelines/internationalization/_index.md index 6edfff5a..1881ceea 100644 --- a/content/docs/NAVPatterns/3-cal-coding-guidelines/internationalization/_index.md +++ b/content/docs/NAVPatterns/3-cal-coding-guidelines/internationalization/_index.md @@ -1,8 +1,6 @@ +++ title = "Internationalization" weight = 700 -tags = ["C/AL"] -categories = ["Best Practice"] +++ ## C/AL Coding Guidelines diff --git a/content/docs/NAVPatterns/3-cal-coding-guidelines/internationalization/using-calcdate/index.md b/content/docs/NAVPatterns/3-cal-coding-guidelines/internationalization/using-calcdate/index.md index cf18657b..4387da52 100644 --- a/content/docs/NAVPatterns/3-cal-coding-guidelines/internationalization/using-calcdate/index.md +++ b/content/docs/NAVPatterns/3-cal-coding-guidelines/internationalization/using-calcdate/index.md @@ -1,8 +1,6 @@ +++ title = "Using Calcdate" weight = 1370 -tags = ["C/AL"] -categories = ["Best Practice"] +++ CALCDATE should only be used with DateFormula variables. Alternatively the string should be enclosed using the <> symbols. diff --git a/content/docs/NAVPatterns/3-cal-coding-guidelines/localizability/_index.md b/content/docs/NAVPatterns/3-cal-coding-guidelines/localizability/_index.md index 714eeeb3..9b16cefa 100644 --- a/content/docs/NAVPatterns/3-cal-coding-guidelines/localizability/_index.md +++ b/content/docs/NAVPatterns/3-cal-coding-guidelines/localizability/_index.md @@ -1,8 +1,6 @@ +++ title = "Localizability" weight = 750 -tags = ["C/AL"] -categories = ["Best Practice"] +++ ## C/AL Coding Guidelines diff --git a/content/docs/NAVPatterns/3-cal-coding-guidelines/localizability/captionml-for-system-tables/index.md b/content/docs/NAVPatterns/3-cal-coding-guidelines/localizability/captionml-for-system-tables/index.md index 4cad77f9..ad50b16c 100644 --- a/content/docs/NAVPatterns/3-cal-coding-guidelines/localizability/captionml-for-system-tables/index.md +++ b/content/docs/NAVPatterns/3-cal-coding-guidelines/localizability/captionml-for-system-tables/index.md @@ -1,8 +1,6 @@ +++ title = "CaptionML on System Pages" weight = 300 -tags = ["C/AL"] -categories = ["Best Practice"] +++ CaptionML should always be specified on a page field for a system table. By default, system tables do not have captions, so if you need to use them in the UI then captions need to be added. diff --git a/content/docs/NAVPatterns/3-cal-coding-guidelines/localizability/fieldcaption-and-tablecaption/index.md b/content/docs/NAVPatterns/3-cal-coding-guidelines/localizability/fieldcaption-and-tablecaption/index.md index ae4c8bdc..9b05906f 100644 --- a/content/docs/NAVPatterns/3-cal-coding-guidelines/localizability/fieldcaption-and-tablecaption/index.md +++ b/content/docs/NAVPatterns/3-cal-coding-guidelines/localizability/fieldcaption-and-tablecaption/index.md @@ -1,8 +1,6 @@ +++ title = "FIELDCAPTION and TABLECAPTION" weight = 580 -tags = ["C/AL"] -categories = ["Best Practice"] +++ For user messages, errors etc., use FIELDCAPTION not FIELDNAME and TABLECAPTION not TABLENAME. diff --git a/content/docs/NAVPatterns/3-cal-coding-guidelines/localizability/global-text-constants/index.md b/content/docs/NAVPatterns/3-cal-coding-guidelines/localizability/global-text-constants/index.md index 0f8c2f63..579e57df 100644 --- a/content/docs/NAVPatterns/3-cal-coding-guidelines/localizability/global-text-constants/index.md +++ b/content/docs/NAVPatterns/3-cal-coding-guidelines/localizability/global-text-constants/index.md @@ -1,8 +1,6 @@ +++ title = "Global Text Constants" weight = 610 -tags = ["C/AL"] -categories = ["Best Practice"] +++ Declare Text Constant as global variables. diff --git a/content/docs/NAVPatterns/3-cal-coding-guidelines/localizability/use-text-constants/index.md b/content/docs/NAVPatterns/3-cal-coding-guidelines/localizability/use-text-constants/index.md index 29129263..a6209174 100644 --- a/content/docs/NAVPatterns/3-cal-coding-guidelines/localizability/use-text-constants/index.md +++ b/content/docs/NAVPatterns/3-cal-coding-guidelines/localizability/use-text-constants/index.md @@ -1,8 +1,6 @@ +++ title = "Use Text Constants" weight = 1360 -tags = ["C/AL"] -categories = ["Best Practice"] +++ Pass user messages using Text Constants. It makes translation easy. diff --git a/content/docs/NAVPatterns/3-cal-coding-guidelines/localizability/using-optioncaptionml/index.md b/content/docs/NAVPatterns/3-cal-coding-guidelines/localizability/using-optioncaptionml/index.md index c02403b5..1de0a0c8 100644 --- a/content/docs/NAVPatterns/3-cal-coding-guidelines/localizability/using-optioncaptionml/index.md +++ b/content/docs/NAVPatterns/3-cal-coding-guidelines/localizability/using-optioncaptionml/index.md @@ -1,8 +1,6 @@ +++ title = "Using OptionCaptionML" weight = 1380 -tags = ["C/AL"] -categories = ["Best Practice"] +++ The OptionCaptionML should be filled in for sourceexpression using option data types. diff --git a/content/docs/NAVPatterns/3-cal-coding-guidelines/readability/_index.md b/content/docs/NAVPatterns/3-cal-coding-guidelines/readability/_index.md index 0a930440..18360f86 100644 --- a/content/docs/NAVPatterns/3-cal-coding-guidelines/readability/_index.md +++ b/content/docs/NAVPatterns/3-cal-coding-guidelines/readability/_index.md @@ -1,8 +1,6 @@ +++ title = "Readability" weight = 980 -tags = ["C/AL","Readability"] -categories = ["Best Practice"] +++ ## C/AL Coding Guidelines diff --git a/content/docs/NAVPatterns/3-cal-coding-guidelines/readability/begin-as-an-afterword/index.md b/content/docs/NAVPatterns/3-cal-coding-guidelines/readability/begin-as-an-afterword/index.md index 498e7b86..9e967112 100644 --- a/content/docs/NAVPatterns/3-cal-coding-guidelines/readability/begin-as-an-afterword/index.md +++ b/content/docs/NAVPatterns/3-cal-coding-guidelines/readability/begin-as-an-afterword/index.md @@ -1,8 +1,6 @@ +++ title = "Begin as an 'After Word'" weight = 230 -tags = ["C/AL","Readability"] -categories = ["Best Practice"] +++ When BEGIN follows THEN, ELSE, DO, it should be on the same line, preceded by one space character. diff --git a/content/docs/NAVPatterns/3-cal-coding-guidelines/readability/begin-end/index.md b/content/docs/NAVPatterns/3-cal-coding-guidelines/readability/begin-end/index.md index a0caf6ad..c3351b48 100644 --- a/content/docs/NAVPatterns/3-cal-coding-guidelines/readability/begin-end/index.md +++ b/content/docs/NAVPatterns/3-cal-coding-guidelines/readability/begin-end/index.md @@ -1,8 +1,6 @@ +++ title = "Begin-End - Compound Only" weight = 240 -tags = ["C/AL","Readability"] -categories = ["Best Practice"] +++ Only use BEGIN..END to enclose compound statements. diff --git a/content/docs/NAVPatterns/3-cal-coding-guidelines/readability/binary-operator-line-start/index.md b/content/docs/NAVPatterns/3-cal-coding-guidelines/readability/binary-operator-line-start/index.md index 8ff81042..feea3be3 100644 --- a/content/docs/NAVPatterns/3-cal-coding-guidelines/readability/binary-operator-line-start/index.md +++ b/content/docs/NAVPatterns/3-cal-coding-guidelines/readability/binary-operator-line-start/index.md @@ -1,8 +1,6 @@ +++ title = "Binary Operator to Start Line" weight = 250 -tags = ["C/AL","Readability"] -categories = ["Best Practice"] +++ Do not start a line with a binary operator. diff --git a/content/docs/NAVPatterns/3-cal-coding-guidelines/readability/blank-lines/index.md b/content/docs/NAVPatterns/3-cal-coding-guidelines/readability/blank-lines/index.md index 5929b91e..5ead097e 100644 --- a/content/docs/NAVPatterns/3-cal-coding-guidelines/readability/blank-lines/index.md +++ b/content/docs/NAVPatterns/3-cal-coding-guidelines/readability/blank-lines/index.md @@ -1,8 +1,6 @@ +++ title = "Blank Lines" weight = 260 -tags = ["C/AL","Readability"] -categories = ["Best Practice"] +++ Do not use blank lines at the beginning or end of any functions, after BEGIN, before END, or inside multiline expressions. diff --git a/content/docs/NAVPatterns/3-cal-coding-guidelines/readability/case-actions/index.md b/content/docs/NAVPatterns/3-cal-coding-guidelines/readability/case-actions/index.md index d4bbea8c..dc76df2f 100644 --- a/content/docs/NAVPatterns/3-cal-coding-guidelines/readability/case-actions/index.md +++ b/content/docs/NAVPatterns/3-cal-coding-guidelines/readability/case-actions/index.md @@ -1,8 +1,6 @@ +++ title = "CASE Action" weight = 310 -tags = ["C/AL","Readability"] -categories = ["Best Practice"] +++ A CASE action should start on a line after the possibility. diff --git a/content/docs/NAVPatterns/3-cal-coding-guidelines/readability/colon-usage-in-case/index.md b/content/docs/NAVPatterns/3-cal-coding-guidelines/readability/colon-usage-in-case/index.md index e02393fe..137c127c 100644 --- a/content/docs/NAVPatterns/3-cal-coding-guidelines/readability/colon-usage-in-case/index.md +++ b/content/docs/NAVPatterns/3-cal-coding-guidelines/readability/colon-usage-in-case/index.md @@ -1,8 +1,6 @@ +++ title = "Colon usage in CASE" weight = 340 -tags = ["C/AL","Readability"] -categories = ["Best Practice"] +++ The last possibility on a CASE statement must be immediately followed by a colon. diff --git a/content/docs/NAVPatterns/3-cal-coding-guidelines/readability/comments-curly-brackets/index.md b/content/docs/NAVPatterns/3-cal-coding-guidelines/readability/comments-curly-brackets/index.md index 049cbd86..5c4ea36c 100644 --- a/content/docs/NAVPatterns/3-cal-coding-guidelines/readability/comments-curly-brackets/index.md +++ b/content/docs/NAVPatterns/3-cal-coding-guidelines/readability/comments-curly-brackets/index.md @@ -1,8 +1,6 @@ +++ title = "Comments inside Curly Brackets" weight = 350 -tags = ["C/AL","Readability"] -categories = ["Best Practice"] +++ Never use curly bracket comments. During development, the "Block comment" functionality can be used instead. However, in production code, block comments are not recommended. diff --git a/content/docs/NAVPatterns/3-cal-coding-guidelines/readability/comments-spacing/index.md b/content/docs/NAVPatterns/3-cal-coding-guidelines/readability/comments-spacing/index.md index 7203208c..9ffc3802 100644 --- a/content/docs/NAVPatterns/3-cal-coding-guidelines/readability/comments-spacing/index.md +++ b/content/docs/NAVPatterns/3-cal-coding-guidelines/readability/comments-spacing/index.md @@ -1,8 +1,6 @@ +++ title = "Comment Spacing" weight = 360 -tags = ["C/AL","Readability"] -categories = ["Best Practice"] +++ Always start comments with // followed by one space character. diff --git a/content/docs/NAVPatterns/3-cal-coding-guidelines/readability/end-else-pair/index.md b/content/docs/NAVPatterns/3-cal-coding-guidelines/readability/end-else-pair/index.md index a31fb67e..520c1b95 100644 --- a/content/docs/NAVPatterns/3-cal-coding-guidelines/readability/end-else-pair/index.md +++ b/content/docs/NAVPatterns/3-cal-coding-guidelines/readability/end-else-pair/index.md @@ -1,8 +1,6 @@ +++ title = "END ELSE Pair" weight = 540 -tags = ["C/AL","Readability"] -categories = ["Best Practice"] +++ The END ELSE pair should always appear on the same line. diff --git a/content/docs/NAVPatterns/3-cal-coding-guidelines/readability/indentation/index.md b/content/docs/NAVPatterns/3-cal-coding-guidelines/readability/indentation/index.md index 614aab16..9b15844a 100644 --- a/content/docs/NAVPatterns/3-cal-coding-guidelines/readability/indentation/index.md +++ b/content/docs/NAVPatterns/3-cal-coding-guidelines/readability/indentation/index.md @@ -1,8 +1,6 @@ +++ title = "Indentation" weight = 650 -tags = ["C/AL","Readability"] -categories = ["Best Practice"] +++ In general, use an indentation of two space characters. Logical expressions in the IF, WHILE, and UNTIL parts are indented at least 3, 6, and 6 spaces respectively. diff --git a/content/docs/NAVPatterns/3-cal-coding-guidelines/readability/keyword-pairs-indentation/index.md b/content/docs/NAVPatterns/3-cal-coding-guidelines/readability/keyword-pairs-indentation/index.md index e109817a..acd13053 100644 --- a/content/docs/NAVPatterns/3-cal-coding-guidelines/readability/keyword-pairs-indentation/index.md +++ b/content/docs/NAVPatterns/3-cal-coding-guidelines/readability/keyword-pairs-indentation/index.md @@ -1,8 +1,6 @@ +++ title = "Keyword Pairs - Indentation" weight = 730 -tags = ["C/AL","Readability"] -categories = ["Best Practice"] +++ The IF..THEN pair, WHILE..DO pair, and FOR..DO pair must appear on the same line or the same level of indentation. diff --git a/content/docs/NAVPatterns/3-cal-coding-guidelines/readability/line-start-keywords/index.md b/content/docs/NAVPatterns/3-cal-coding-guidelines/readability/line-start-keywords/index.md index 58ce7884..ec7754ec 100644 --- a/content/docs/NAVPatterns/3-cal-coding-guidelines/readability/line-start-keywords/index.md +++ b/content/docs/NAVPatterns/3-cal-coding-guidelines/readability/line-start-keywords/index.md @@ -1,8 +1,6 @@ +++ title = "Line Start Keywords" weight = 740 -tags = ["C/AL","Readability"] -categories = ["Best Practice"] +++ The END, IF, REPEAT, FOR, WHILE, ELSE and CASE statement should always start a line. diff --git a/content/docs/NAVPatterns/3-cal-coding-guidelines/readability/lonely-repeat/index.md b/content/docs/NAVPatterns/3-cal-coding-guidelines/readability/lonely-repeat/index.md index a7bae1df..a5e278e4 100644 --- a/content/docs/NAVPatterns/3-cal-coding-guidelines/readability/lonely-repeat/index.md +++ b/content/docs/NAVPatterns/3-cal-coding-guidelines/readability/lonely-repeat/index.md @@ -1,8 +1,6 @@ +++ title = "Lonely Repeat" weight = 760 -tags = ["C/AL","Readability"] -categories = ["Best Practice"] +++ The REPEAT statement should always be alone on a line. diff --git a/content/docs/NAVPatterns/3-cal-coding-guidelines/readability/named-invocations/index.md b/content/docs/NAVPatterns/3-cal-coding-guidelines/readability/named-invocations/index.md index 64b8fff3..bff692d7 100644 --- a/content/docs/NAVPatterns/3-cal-coding-guidelines/readability/named-invocations/index.md +++ b/content/docs/NAVPatterns/3-cal-coding-guidelines/readability/named-invocations/index.md @@ -1,8 +1,6 @@ +++ title = "Named Invocations" weight = 830 -tags = ["C/AL","Readability"] -categories = ["Best Practice"] +++ When calling an object statically use the name, not the number diff --git a/content/docs/NAVPatterns/3-cal-coding-guidelines/readability/nested-withs/index.md b/content/docs/NAVPatterns/3-cal-coding-guidelines/readability/nested-withs/index.md index 3dcabde2..b1a57b16 100644 --- a/content/docs/NAVPatterns/3-cal-coding-guidelines/readability/nested-withs/index.md +++ b/content/docs/NAVPatterns/3-cal-coding-guidelines/readability/nested-withs/index.md @@ -1,8 +1,6 @@ +++ title = "Nested WITHs" weight = 850 -tags = ["C/AL","Readability"] -categories = ["Best Practice"] +++ Do not nest WITHs that reference different types of objects. diff --git a/content/docs/NAVPatterns/3-cal-coding-guidelines/readability/one-statement-per-line/index.md b/content/docs/NAVPatterns/3-cal-coding-guidelines/readability/one-statement-per-line/index.md index 5d9e6379..348ddaac 100644 --- a/content/docs/NAVPatterns/3-cal-coding-guidelines/readability/one-statement-per-line/index.md +++ b/content/docs/NAVPatterns/3-cal-coding-guidelines/readability/one-statement-per-line/index.md @@ -1,8 +1,6 @@ +++ title = "One Statement Per Line" weight = 910 -tags = ["C/AL","Readability"] -categories = ["Best Practice"] +++ A line of code should not have more than one statement. diff --git a/content/docs/NAVPatterns/3-cal-coding-guidelines/readability/separate-if-and-else/index.md b/content/docs/NAVPatterns/3-cal-coding-guidelines/readability/separate-if-and-else/index.md index f1c05ab4..8b9948fa 100644 --- a/content/docs/NAVPatterns/3-cal-coding-guidelines/readability/separate-if-and-else/index.md +++ b/content/docs/NAVPatterns/3-cal-coding-guidelines/readability/separate-if-and-else/index.md @@ -1,8 +1,6 @@ +++ title = "Separate IF and ELSE" weight = 1050 -tags = ["C/AL","Readability"] -categories = ["Best Practice"] +++ IF and ELSE statements should be on separate lines. diff --git a/content/docs/NAVPatterns/3-cal-coding-guidelines/readability/spacing-binary-operators/index.md b/content/docs/NAVPatterns/3-cal-coding-guidelines/readability/spacing-binary-operators/index.md index ec18a52d..6766688a 100644 --- a/content/docs/NAVPatterns/3-cal-coding-guidelines/readability/spacing-binary-operators/index.md +++ b/content/docs/NAVPatterns/3-cal-coding-guidelines/readability/spacing-binary-operators/index.md @@ -1,8 +1,6 @@ +++ title = "Spacing Binary Operators" weight = 1120 -tags = ["C/AL","Readability"] -categories = ["Best Practice"] +++ There must be exactly one space character on each side of a binary operator such as = + - AND OR =. The parameter comma operator however, should have no spaces. diff --git a/content/docs/NAVPatterns/3-cal-coding-guidelines/readability/spacing-brackets-and/index.md b/content/docs/NAVPatterns/3-cal-coding-guidelines/readability/spacing-brackets-and/index.md index 087b1ac8..1187bcce 100644 --- a/content/docs/NAVPatterns/3-cal-coding-guidelines/readability/spacing-brackets-and/index.md +++ b/content/docs/NAVPatterns/3-cal-coding-guidelines/readability/spacing-brackets-and/index.md @@ -1,8 +1,6 @@ +++ title = "Spacing Brackets and ::" weight = 1130 -tags = ["C/AL","Readability"] -categories = ["Best Practice"] +++ There must be no spaces characters before and after [] dimension brackets symbols or :: option symbols. diff --git a/content/docs/NAVPatterns/3-cal-coding-guidelines/readability/spacing-unary-operators/index.md b/content/docs/NAVPatterns/3-cal-coding-guidelines/readability/spacing-unary-operators/index.md index 970b2d16..63277cff 100644 --- a/content/docs/NAVPatterns/3-cal-coding-guidelines/readability/spacing-unary-operators/index.md +++ b/content/docs/NAVPatterns/3-cal-coding-guidelines/readability/spacing-unary-operators/index.md @@ -1,8 +1,6 @@ +++ title = "Spacing Unary Operators" weight = 1140 -tags = ["C/AL","Readability"] -categories = ["Best Practice"] +++ There must be no space between a unary operator and its argument (except for the NOT keyword). diff --git a/content/docs/NAVPatterns/3-cal-coding-guidelines/readability/suggested-abbreviations/index.md b/content/docs/NAVPatterns/3-cal-coding-guidelines/readability/suggested-abbreviations/index.md index 97b84557..df4af1ec 100644 --- a/content/docs/NAVPatterns/3-cal-coding-guidelines/readability/suggested-abbreviations/index.md +++ b/content/docs/NAVPatterns/3-cal-coding-guidelines/readability/suggested-abbreviations/index.md @@ -1,8 +1,6 @@ +++ title = "Suggested Abbreviations" weight = 1170 -tags = ["C/AL","Readability"] -categories = ["Best Practice"] +++ ### Suggested Abbreviations diff --git a/content/docs/NAVPatterns/3-cal-coding-guidelines/readability/temporary-variable-naming/index.md b/content/docs/NAVPatterns/3-cal-coding-guidelines/readability/temporary-variable-naming/index.md index bbd36a69..53630c54 100644 --- a/content/docs/NAVPatterns/3-cal-coding-guidelines/readability/temporary-variable-naming/index.md +++ b/content/docs/NAVPatterns/3-cal-coding-guidelines/readability/temporary-variable-naming/index.md @@ -1,8 +1,6 @@ +++ title = "Temporary Variable Naming" weight = 1200 -tags = ["C/AL","Readability"] -categories = ["Best Practice"] +++ The name of a temporary variable must be prefixed with the word Temp and not otherwise. diff --git a/content/docs/NAVPatterns/3-cal-coding-guidelines/readability/textconst-suffixes/index.md b/content/docs/NAVPatterns/3-cal-coding-guidelines/readability/textconst-suffixes/index.md index 7de92fbf..09f44459 100644 --- a/content/docs/NAVPatterns/3-cal-coding-guidelines/readability/textconst-suffixes/index.md +++ b/content/docs/NAVPatterns/3-cal-coding-guidelines/readability/textconst-suffixes/index.md @@ -1,8 +1,6 @@ +++ title = "TextConst Suffixes" weight = 1210 -tags = ["C/AL","Readability"] -categories = ["Best Practice"] +++ TextConst variable names should have a suffix (an approved three-letter suffix: Msg, Tok, Err, Qst, Lbl, Txt) describing usage. diff --git a/content/docs/NAVPatterns/3-cal-coding-guidelines/readability/unary-operator-line-end/index.md b/content/docs/NAVPatterns/3-cal-coding-guidelines/readability/unary-operator-line-end/index.md index 11ad406e..c121de69 100644 --- a/content/docs/NAVPatterns/3-cal-coding-guidelines/readability/unary-operator-line-end/index.md +++ b/content/docs/NAVPatterns/3-cal-coding-guidelines/readability/unary-operator-line-end/index.md @@ -1,8 +1,6 @@ +++ title = "Unary Operator Line End" weight = 1250 -tags = ["C/AL","Readability"] -categories = ["Best Practice"] +++ Do not end a line with unary operator. diff --git a/content/docs/NAVPatterns/3-cal-coding-guidelines/readability/unnecessary-compound-parenthesis/index.md b/content/docs/NAVPatterns/3-cal-coding-guidelines/readability/unnecessary-compound-parenthesis/index.md index 127418c2..2e3f3f14 100644 --- a/content/docs/NAVPatterns/3-cal-coding-guidelines/readability/unnecessary-compound-parenthesis/index.md +++ b/content/docs/NAVPatterns/3-cal-coding-guidelines/readability/unnecessary-compound-parenthesis/index.md @@ -1,8 +1,6 @@ +++ title = "Unnecessary Compound Parenthesis" weight = 1260 -tags = ["C/AL","Readability"] -categories = ["Best Practice"] +++ Use parenthesis only to enclose compound expressions inside compound expressions. diff --git a/content/docs/NAVPatterns/3-cal-coding-guidelines/readability/unnecessary-else/index.md b/content/docs/NAVPatterns/3-cal-coding-guidelines/readability/unnecessary-else/index.md index f3755ab2..8ba88518 100644 --- a/content/docs/NAVPatterns/3-cal-coding-guidelines/readability/unnecessary-else/index.md +++ b/content/docs/NAVPatterns/3-cal-coding-guidelines/readability/unnecessary-else/index.md @@ -1,8 +1,6 @@ +++ title = "Unnecessary ELSE" weight = 1270 -tags = ["C/AL","Readability"] -categories = ["Best Practice"] +++ ELSE should not be used when the last action in the THEN part is an EXIT, BREAK, SKIP, QUIT, ERROR. diff --git a/content/docs/NAVPatterns/3-cal-coding-guidelines/readability/unnecessary-function-parenthesis/index.md b/content/docs/NAVPatterns/3-cal-coding-guidelines/readability/unnecessary-function-parenthesis/index.md index 9750286a..44bc6402 100644 --- a/content/docs/NAVPatterns/3-cal-coding-guidelines/readability/unnecessary-function-parenthesis/index.md +++ b/content/docs/NAVPatterns/3-cal-coding-guidelines/readability/unnecessary-function-parenthesis/index.md @@ -1,8 +1,6 @@ +++ title = "Unnecessary Function Parenthesis" weight = 1280 -tags = ["C/AL","Readability"] -categories = ["Best Practice"] +++ Do not use parenthesis in a function call if the function does not have any parameters. diff --git a/content/docs/NAVPatterns/3-cal-coding-guidelines/readability/unnecessary-separators/index.md b/content/docs/NAVPatterns/3-cal-coding-guidelines/readability/unnecessary-separators/index.md index ecefcdf6..2a653922 100644 --- a/content/docs/NAVPatterns/3-cal-coding-guidelines/readability/unnecessary-separators/index.md +++ b/content/docs/NAVPatterns/3-cal-coding-guidelines/readability/unnecessary-separators/index.md @@ -1,8 +1,6 @@ +++ title = "Unnecessary Separators" weight = 1290 -tags = ["C/AL","Readability"] -categories = ["Best Practice"] +++ There should be no unnecessary separators. diff --git a/content/docs/NAVPatterns/3-cal-coding-guidelines/readability/unnecessary-truefalse/index.md b/content/docs/NAVPatterns/3-cal-coding-guidelines/readability/unnecessary-truefalse/index.md index 181b2067..ed6e3679 100644 --- a/content/docs/NAVPatterns/3-cal-coding-guidelines/readability/unnecessary-truefalse/index.md +++ b/content/docs/NAVPatterns/3-cal-coding-guidelines/readability/unnecessary-truefalse/index.md @@ -1,8 +1,6 @@ +++ title = "Unnecessary TRUE/FALSE" weight = 1300 -tags = ["C/AL","Readability"] -categories = ["Best Practice"] +++ Do not use TRUE or FALSE keywords unnecessarily if the expression is already an logical expression. diff --git a/content/docs/NAVPatterns/3-cal-coding-guidelines/readability/variable-already-scoped/index.md b/content/docs/NAVPatterns/3-cal-coding-guidelines/readability/variable-already-scoped/index.md index f24f9358..73e682b5 100644 --- a/content/docs/NAVPatterns/3-cal-coding-guidelines/readability/variable-already-scoped/index.md +++ b/content/docs/NAVPatterns/3-cal-coding-guidelines/readability/variable-already-scoped/index.md @@ -1,8 +1,6 @@ +++ title = "Variable Already Scoped" weight = 1400 -tags = ["C/AL","Readability"] -categories = ["Best Practice"] +++ Do not use scope ''.'' qualifier unnecessarily when a variable is already implicitly or explicitly scoped. It keeps the code simpler. diff --git a/content/docs/NAVPatterns/3-cal-coding-guidelines/readability/variable-naming/index.md b/content/docs/NAVPatterns/3-cal-coding-guidelines/readability/variable-naming/index.md index a678e0a3..9b2b5639 100644 --- a/content/docs/NAVPatterns/3-cal-coding-guidelines/readability/variable-naming/index.md +++ b/content/docs/NAVPatterns/3-cal-coding-guidelines/readability/variable-naming/index.md @@ -1,8 +1,6 @@ +++ title = "Variable Naming" weight = 1420 -tags = ["C/AL","Readability"] -categories = ["Best Practice"] +++ Variables that refer to a C/AL object must contain the objects name, abbreviated where necessary. diff --git a/content/docs/NAVPatterns/3-cal-coding-guidelines/readability/variables-declarations-order/index.md b/content/docs/NAVPatterns/3-cal-coding-guidelines/readability/variables-declarations-order/index.md index d9b7cc08..a578ca7d 100644 --- a/content/docs/NAVPatterns/3-cal-coding-guidelines/readability/variables-declarations-order/index.md +++ b/content/docs/NAVPatterns/3-cal-coding-guidelines/readability/variables-declarations-order/index.md @@ -1,8 +1,6 @@ +++ title = "Variables Declarations Order" weight = 1430 -tags = ["C/AL","Readability"] -categories = ["Best Practice"] +++ Variables declarations should be ordered by type. In general, object and complex variable types are listed first followed by simple variables. The order should be the same as the object list in the object designer for C/AL objects. Afterwards come the complex variables like RecordRef, .NET, FieldRef etc. At the end come all the simple data types in no particular order. diff --git a/content/docs/NAVPatterns/3-cal-coding-guidelines/ux/_index.md b/content/docs/NAVPatterns/3-cal-coding-guidelines/ux/_index.md index cc4c977b..b755e886 100644 --- a/content/docs/NAVPatterns/3-cal-coding-guidelines/ux/_index.md +++ b/content/docs/NAVPatterns/3-cal-coding-guidelines/ux/_index.md @@ -1,8 +1,6 @@ +++ title = "UX" weight = 1390 -tags = ["C/AL"] -categories = ["Best Practice"] +++ ## C/AL Coding Guidelines diff --git a/content/docs/NAVPatterns/3-cal-coding-guidelines/ux/actions-images/index.md b/content/docs/NAVPatterns/3-cal-coding-guidelines/ux/actions-images/index.md index 863d0609..c07df2c0 100644 --- a/content/docs/NAVPatterns/3-cal-coding-guidelines/ux/actions-images/index.md +++ b/content/docs/NAVPatterns/3-cal-coding-guidelines/ux/actions-images/index.md @@ -1,8 +1,6 @@ +++ title = "Actions - Images" weight = 200 -tags = ["C/AL"] -categories = ["Best Practice"] +++ All actions must have an image assigned to them. diff --git a/content/docs/NAVPatterns/3-cal-coding-guidelines/ux/confirm/index.md b/content/docs/NAVPatterns/3-cal-coding-guidelines/ux/confirm/index.md index 155b747d..fb3394f9 100644 --- a/content/docs/NAVPatterns/3-cal-coding-guidelines/ux/confirm/index.md +++ b/content/docs/NAVPatterns/3-cal-coding-guidelines/ux/confirm/index.md @@ -1,8 +1,6 @@ +++ title = "CONFIRM" weight = 380 -tags = ["C/AL"] -categories = ["Best Practice"] +++ Always end CONFIRM with a question mark. diff --git a/content/docs/NAVPatterns/3-cal-coding-guidelines/ux/fielderror/index.md b/content/docs/NAVPatterns/3-cal-coding-guidelines/ux/fielderror/index.md index 1f87ff3a..1e041417 100644 --- a/content/docs/NAVPatterns/3-cal-coding-guidelines/ux/fielderror/index.md +++ b/content/docs/NAVPatterns/3-cal-coding-guidelines/ux/fielderror/index.md @@ -1,8 +1,6 @@ +++ title = "FIELDERROR" weight = 590 -tags = ["C/AL"] -categories = ["Best Practice"] +++ Never use FIELDERROR with a period as it is automatically inserted. diff --git a/content/docs/NAVPatterns/3-cal-coding-guidelines/ux/message-and-error/index.md b/content/docs/NAVPatterns/3-cal-coding-guidelines/ux/message-and-error/index.md index 93bce63a..93ede77d 100644 --- a/content/docs/NAVPatterns/3-cal-coding-guidelines/ux/message-and-error/index.md +++ b/content/docs/NAVPatterns/3-cal-coding-guidelines/ux/message-and-error/index.md @@ -1,8 +1,6 @@ +++ title = "MESSAGE and ERROR" weight = 790 -tags = ["C/AL"] -categories = ["Best Practice"] +++ Always end MESSAGE or ERROR with a period. diff --git a/content/docs/NAVPatterns/4-get-involved/_index.md b/content/docs/NAVPatterns/4-get-involved/_index.md index 43d27a4a..98bf3470 100644 --- a/content/docs/NAVPatterns/4-get-involved/_index.md +++ b/content/docs/NAVPatterns/4-get-involved/_index.md @@ -2,7 +2,6 @@ chapter = true title = "(OLD) Get Involved" weight = 170 -tags = ["C/AL"] +++ **Reminder, this is an ARCHIVE of the Patterns site, this information is not current.** diff --git a/content/docs/NAVPatterns/4-get-involved/code-of-conduct/index.md b/content/docs/NAVPatterns/4-get-involved/code-of-conduct/index.md index 76927c3e..02cdce3a 100644 --- a/content/docs/NAVPatterns/4-get-involved/code-of-conduct/index.md +++ b/content/docs/NAVPatterns/4-get-involved/code-of-conduct/index.md @@ -1,7 +1,6 @@ +++ title = "Code of Conduct" weight = 330 -tags = ["C/AL"] +++ Find below the rules to be used when disseminating or relating to the NAV Design Patterns. diff --git a/content/docs/NAVPatterns/4-get-involved/patterns-authors/index.md b/content/docs/NAVPatterns/4-get-involved/patterns-authors/index.md index 104b6b21..de316d5c 100644 --- a/content/docs/NAVPatterns/4-get-involved/patterns-authors/index.md +++ b/content/docs/NAVPatterns/4-get-involved/patterns-authors/index.md @@ -1,7 +1,6 @@ +++ title = "Patterns Authors" weight = 930 -tags = ["C/AL"] +++ This is the list of people that have been part of the NAV Design Patterns team. If you would like to join the project follow the instructions provided on [Be a NAV Pattern Author][anchor0] page. diff --git a/content/docs/NAVPatterns/4-get-involved/template-for-writing-nav-design-patterns/index.md b/content/docs/NAVPatterns/4-get-involved/template-for-writing-nav-design-patterns/index.md index a47abbd6..77a8b22b 100644 --- a/content/docs/NAVPatterns/4-get-involved/template-for-writing-nav-design-patterns/index.md +++ b/content/docs/NAVPatterns/4-get-involved/template-for-writing-nav-design-patterns/index.md @@ -1,7 +1,6 @@ +++ title = "Template for writing Nav Design Patterns" weight = 1180 -tags = ["C/AL"] +++ This is a guideline, some parts are optional (if there's no content, remove the whole paragraph). diff --git a/content/docs/NAVPatterns/_index.md b/content/docs/NAVPatterns/_index.md index 22ecc24f..5865a369 100644 --- a/content/docs/NAVPatterns/_index.md +++ b/content/docs/NAVPatterns/_index.md @@ -1,7 +1,6 @@ +++ title = "NAV Patterns Archive" -weight = 4 -tags = ["C/AL"] +weight = 20 +++ ## About the archive diff --git a/content/docs/NAVPatterns/patterns/_index.md b/content/docs/NAVPatterns/patterns/_index.md index 527ceb23..075fc693 100644 --- a/content/docs/NAVPatterns/patterns/_index.md +++ b/content/docs/NAVPatterns/patterns/_index.md @@ -1,8 +1,8 @@ --- -title: "1. Patterns" +title: "Patterns" weight: 110 -tags: ["C/AL"] -categories: ["Pattern"] +tags: ["NAV", "C/AL"] +categories: ["Archived Pattern"] description: > Patterns described to be used with Microsoft Dynamics NAV --- diff --git a/content/docs/NAVPatterns/patterns/activity-log/index.md b/content/docs/NAVPatterns/patterns/activity-log/index.md index f6acd62a..37a327a2 100644 --- a/content/docs/NAVPatterns/patterns/activity-log/index.md +++ b/content/docs/NAVPatterns/patterns/activity-log/index.md @@ -1,8 +1,6 @@ +++ title = "Activity Logs" weight = 210 -tags = ["C/AL"] -categories = ["Pattern"] +++ _Originally by Ciprian Iordache at Microsoft Development Center Copenhagen_ diff --git a/content/docs/NAVPatterns/patterns/argument-table/index.md b/content/docs/NAVPatterns/patterns/argument-table/index.md index b413e0c8..13a4c70d 100644 --- a/content/docs/NAVPatterns/patterns/argument-table/index.md +++ b/content/docs/NAVPatterns/patterns/argument-table/index.md @@ -1,8 +1,6 @@ +++ title = "Argument Table" weight = 220 -tags = ["C/AL"] -categories = ["Pattern"] +++ _Originally By Nikola Kukrika and waldo_ diff --git a/content/docs/NAVPatterns/patterns/blocked-entity/_index.md b/content/docs/NAVPatterns/patterns/blocked-entity/_index.md index 402ba137..765791a5 100644 --- a/content/docs/NAVPatterns/patterns/blocked-entity/_index.md +++ b/content/docs/NAVPatterns/patterns/blocked-entity/_index.md @@ -1,8 +1,6 @@ +++ title = "Blocked Entity" weight = 270 -tags = ["C/AL"] -categories = ["Pattern"] +++ _Originally by Abhishek Ghosh at Microsoft Development Center Copenhagen_ @@ -92,9 +90,7 @@ Entities where the Blocked Entity has been implemented include: The [Released Entity][anchor4]. -{{% alert title="Note" %}} -There was previously a video demonstration of this pattern, but it is no longer available. -{{% /alert %}} +{{< youtube O2R-fTSup1o >}} [anchor0]: 2260.BlockedEntityPattern.png [anchor1]: /navpatterns/1-patterns/blocked-entity/data-driven-blocked-entity/ diff --git a/content/docs/NAVPatterns/patterns/blocked-entity/data-driven-blocked-entity/index.md b/content/docs/NAVPatterns/patterns/blocked-entity/data-driven-blocked-entity/index.md index 3e4413b3..295cf07b 100644 --- a/content/docs/NAVPatterns/patterns/blocked-entity/data-driven-blocked-entity/index.md +++ b/content/docs/NAVPatterns/patterns/blocked-entity/data-driven-blocked-entity/index.md @@ -1,8 +1,6 @@ +++ title = "Data Driven Blocked Entity" weight = 470 -tags = ["C/AL"] -categories = ["Pattern"] +++ _Written by Bogdan Andrei Sturzoiu, at Microsoft Development Center Copenhagen_ diff --git a/content/docs/NAVPatterns/patterns/cached-web-service-calls/index.md b/content/docs/NAVPatterns/patterns/cached-web-service-calls/index.md index fd64729e..baa4cea5 100644 --- a/content/docs/NAVPatterns/patterns/cached-web-service-calls/index.md +++ b/content/docs/NAVPatterns/patterns/cached-web-service-calls/index.md @@ -1,8 +1,6 @@ +++ title = "Cached Web Server Calls" weight = 290 -tags = ["C/AL"] -categories = ["Pattern"] +++ _Originally by Mostafa Balat, Microsoft Development Center Copenhagen_ diff --git a/content/docs/NAVPatterns/patterns/conditional-cascading-update/index.md b/content/docs/NAVPatterns/patterns/conditional-cascading-update/index.md index 2b29989d..a690d1ff 100644 --- a/content/docs/NAVPatterns/patterns/conditional-cascading-update/index.md +++ b/content/docs/NAVPatterns/patterns/conditional-cascading-update/index.md @@ -1,8 +1,6 @@ +++ title = "Conditional Cascading Update" weight = 370 -tags = ["C/AL"] -categories = ["Pattern"] +++ _Originally by Jan Hoek at IDYN_ diff --git a/content/docs/NAVPatterns/patterns/copy-document/index.md b/content/docs/NAVPatterns/patterns/copy-document/index.md index 98f1bd0b..324b617a 100644 --- a/content/docs/NAVPatterns/patterns/copy-document/index.md +++ b/content/docs/NAVPatterns/patterns/copy-document/index.md @@ -1,8 +1,6 @@ +++ title = "Copy Document" weight = 390 -tags = ["C/AL"] -categories = ["Pattern"] +++ _By Bogdan Sturzoiu at Microsoft Development Center Copenhagen_ diff --git a/content/docs/NAVPatterns/patterns/create-data-from-templates/index.md b/content/docs/NAVPatterns/patterns/create-data-from-templates/index.md index d51d6c92..9ecad138 100644 --- a/content/docs/NAVPatterns/patterns/create-data-from-templates/index.md +++ b/content/docs/NAVPatterns/patterns/create-data-from-templates/index.md @@ -1,8 +1,6 @@ +++ title = "Create Data from Templates" weight = 400 -tags = ["C/AL"] -categories = ["Pattern"] +++ _Originally by Nikola Kukrika at Microsoft Development Center Copenhagen_ diff --git a/content/docs/NAVPatterns/patterns/create-urls-to-nav-clients/index.md b/content/docs/NAVPatterns/patterns/create-urls-to-nav-clients/index.md index 5f604ef4..fdcbee7c 100644 --- a/content/docs/NAVPatterns/patterns/create-urls-to-nav-clients/index.md +++ b/content/docs/NAVPatterns/patterns/create-urls-to-nav-clients/index.md @@ -1,8 +1,6 @@ +++ title = "Create URLs to NAV Clients" weight = 410 -tags = ["C/AL"] -categories = ["Pattern"] +++ _By Mike Borg Cardona and Bogdana Botez at Microsoft Development Center Copenhagen_ diff --git a/content/docs/NAVPatterns/patterns/creating-custom-charts/index.md b/content/docs/NAVPatterns/patterns/creating-custom-charts/index.md index 64d58fd4..4bd83358 100644 --- a/content/docs/NAVPatterns/patterns/creating-custom-charts/index.md +++ b/content/docs/NAVPatterns/patterns/creating-custom-charts/index.md @@ -1,8 +1,6 @@ +++ title = "Creating Custom Charts" weight = 420 -tags = ["C/AL"] -categories = ["Pattern"] +++ _Originally by Nikola Kukrika at Microsoft Development Center Copenhagen_ diff --git a/content/docs/NAVPatterns/patterns/cross-session-events/index.md b/content/docs/NAVPatterns/patterns/cross-session-events/index.md index b9aecd0b..1a5fae83 100644 --- a/content/docs/NAVPatterns/patterns/cross-session-events/index.md +++ b/content/docs/NAVPatterns/patterns/cross-session-events/index.md @@ -1,8 +1,6 @@ +++ title = "Cross Session Events" weight = 430 -tags = ["C/AL"] -categories = ["Pattern"] +++ _By Nikolai L'Estrange, from TVision Technology Ltd. in the UK_ diff --git a/content/docs/NAVPatterns/patterns/currently-active-record/index.md b/content/docs/NAVPatterns/patterns/currently-active-record/index.md index ad7e9e2e..1f1f56a0 100644 --- a/content/docs/NAVPatterns/patterns/currently-active-record/index.md +++ b/content/docs/NAVPatterns/patterns/currently-active-record/index.md @@ -1,8 +1,6 @@ +++ title = "Currently Active Record" weight = 450 -tags = ["C/AL"] -categories = ["Pattern"] +++ _Authors: Henrik Langbak and Kim Ginnerup, Bording Data_ diff --git a/content/docs/NAVPatterns/patterns/data-migration-facade/index.md b/content/docs/NAVPatterns/patterns/data-migration-facade/index.md index e4e5aa4e..bd0650da 100644 --- a/content/docs/NAVPatterns/patterns/data-migration-facade/index.md +++ b/content/docs/NAVPatterns/patterns/data-migration-facade/index.md @@ -1,8 +1,6 @@ +++ title = "Data Migration Façade" weight = 480 -tags = ["C/AL"] -categories = ["Pattern"] +++ _By David Bastide and Soumya Dutta at Microsoft Development Center Copenhagen_ @@ -108,7 +106,7 @@ begin end; end; -procedure MigrateItemUnitOfMeasure(ItemDataMigrationFacade : Codeunit "Item Data Migration Facade"; ItemJson : Text) +procedure MigrateItemUnitOfMeasure(ItemDataMigrationFacade : Codeunit "Item Data Migration Facade"; ItemJson : Text); var MyUnitCodeStagingTable: Record "My Unit Code Staging Table"; DataMigrationStatusFacade: Codeunit "Data Migration Status Facade"; @@ -154,7 +152,7 @@ _Figure 3: Simplified sequence diagram of the data migration with staging tables Below is a simplified example showing how to create an item: ```al [EventSubscriber(ObjectType::Codeunit, Codeunit::"Item Data Migration Facade", 'OnMigrateItem', '', true, true)] -procedure OnMigrateItem(VAR Sender : Codeunit "Item Data Migration Facade";RecordIdToMigrate : RecordId) +procedure OnMigrateItem(VAR Sender : Codeunit "Item Data Migration Facade";RecordIdToMigrate : RecordId); var MyItemStagingTable : Record "My Item Staging Table"; begin @@ -186,7 +184,7 @@ Below is another example showing how to use additional events to set fields that ```al [EventSubscriber(ObjectType::Codeunit, Codeunit::"Item Data Migration Facade", 'OnMigrateItemUnitOfMeasure', '', true, true)] -procedure OnMigrateItemUnitOfMeasure(VAR Sender : Codeunit "Item Data Migration Facade";RecordIdToMigrate : RecordId) +procedure OnMigrateItemUnitOfMeasure(VAR Sender : Codeunit "Item Data Migration Facade";RecordIdToMigrate : RecordId); var MyItemStagingTable : Record "My Item Staging Table"; MyUnitCodeStagingTable : Record "My Unit Code Staging Table"; diff --git a/content/docs/NAVPatterns/patterns/discovery-event/index.md b/content/docs/NAVPatterns/patterns/discovery-event/index.md index d8cbf195..eba8381a 100644 --- a/content/docs/NAVPatterns/patterns/discovery-event/index.md +++ b/content/docs/NAVPatterns/patterns/discovery-event/index.md @@ -1,8 +1,6 @@ +++ title = "Discovery Event" weight = 500 -tags = ["C/AL"] -categories = ["Pattern"] +++ _by waldo_ diff --git a/content/docs/NAVPatterns/patterns/document/index.md b/content/docs/NAVPatterns/patterns/document/index.md index 66120a29..653ed482 100644 --- a/content/docs/NAVPatterns/patterns/document/index.md +++ b/content/docs/NAVPatterns/patterns/document/index.md @@ -1,8 +1,6 @@ +++ title = "Document" weight = 510 -tags = ["C/AL"] -categories = ["Pattern"] +++ _By Xavier Garonnat, knk Ingénierie (France), xgaronnat@knk.fr_ diff --git a/content/docs/NAVPatterns/patterns/easy-update-of-setup-or-supplementary-information/index.md b/content/docs/NAVPatterns/patterns/easy-update-of-setup-or-supplementary-information/index.md index f652f12f..aea9f8d3 100644 --- a/content/docs/NAVPatterns/patterns/easy-update-of-setup-or-supplementary-information/index.md +++ b/content/docs/NAVPatterns/patterns/easy-update-of-setup-or-supplementary-information/index.md @@ -1,8 +1,6 @@ +++ title = "Easy Update Of Setup Or Supplementary Information" weight = 520 -tags = ["C/AL"] -categories = ["Pattern"] +++ _Originally by Anders Larsen at Microsoft Development Center Copenhagen_ diff --git a/content/docs/NAVPatterns/patterns/error-message-processing/index.md b/content/docs/NAVPatterns/patterns/error-message-processing/index.md index 07d9c27c..2b736c8c 100644 --- a/content/docs/NAVPatterns/patterns/error-message-processing/index.md +++ b/content/docs/NAVPatterns/patterns/error-message-processing/index.md @@ -1,8 +1,6 @@ +++ title = "Error Message Processing" weight = 550 -tags = ["C/AL"] -categories = ["Pattern"] +++ _By Jesper Schulz at Microsoft Development Center Copenhagen_ @@ -84,9 +82,9 @@ BEGIN END ELSE TempErrorMessage.LogSimpleMessage(TempErrorMessage."Message Type"::Error,NoSATAccountDefinedErr); - IF NOT TempErrorMessage.HasErrors(TRUE) THEN - SaveXMLToClient(Document,Year,Month,'CT'); - TempErrorMessage.ShowErrorMessages(FALSE); + IF NOT TempErrorMessage.HasErrors(TRUE) THEN + SaveXMLToClient(Document,Year,Month,'CT'); + TempErrorMessage.ShowErrorMessages(FALSE); END; ``` diff --git a/content/docs/NAVPatterns/patterns/extending-the-role-center-headlines/index.md b/content/docs/NAVPatterns/patterns/extending-the-role-center-headlines/index.md index 3f5b4615..f3006cde 100644 --- a/content/docs/NAVPatterns/patterns/extending-the-role-center-headlines/index.md +++ b/content/docs/NAVPatterns/patterns/extending-the-role-center-headlines/index.md @@ -1,8 +1,6 @@ +++ title = "Extending the Role Center Headlines" weight = 560 -tags = ["C/AL"] -categories = ["Pattern"] +++ _By David Bastide at Microsoft Development Center Copenhagen_ diff --git a/content/docs/NAVPatterns/patterns/feature-localization-for-data-structures/index.md b/content/docs/NAVPatterns/patterns/feature-localization-for-data-structures/index.md index cd110eb1..14ee3b5e 100644 --- a/content/docs/NAVPatterns/patterns/feature-localization-for-data-structures/index.md +++ b/content/docs/NAVPatterns/patterns/feature-localization-for-data-structures/index.md @@ -1,8 +1,6 @@ +++ title = "Feature Localization For Data Structures" weight = 570 -tags = ["C/AL"] -categories = ["Pattern"] +++ _Originally by Bogdan Sturzoiu at Microsoft Development Center Copenhagen_ diff --git a/content/docs/NAVPatterns/patterns/hooks/index.md b/content/docs/NAVPatterns/patterns/hooks/index.md index 9134887d..b23b7e88 100644 --- a/content/docs/NAVPatterns/patterns/hooks/index.md +++ b/content/docs/NAVPatterns/patterns/hooks/index.md @@ -1,8 +1,6 @@ +++ title = "Hooks" weight = 620 -tags = ["C/AL"] -categories = ["Pattern"] +++ _By Eric Wauters ([waldo][anchor0]), Partner-Ready-Software_ diff --git a/content/docs/NAVPatterns/patterns/implementation-of-surrogate-keys-using-autoincrement-pattern/index.md b/content/docs/NAVPatterns/patterns/implementation-of-surrogate-keys-using-autoincrement-pattern/index.md index a89b6b1b..e8bce739 100644 --- a/content/docs/NAVPatterns/patterns/implementation-of-surrogate-keys-using-autoincrement-pattern/index.md +++ b/content/docs/NAVPatterns/patterns/implementation-of-surrogate-keys-using-autoincrement-pattern/index.md @@ -1,8 +1,6 @@ +++ title = "Surrogate keys using Autoincrement Pattern" weight = 630 -tags = ["C/AL"] -categories = ["Pattern"] +++ _By [Soren Klemmensen][anchor0], [_Partner-Ready-Software_ ][anchor1] & [360 Visibility][anchor2]_ diff --git a/content/docs/NAVPatterns/patterns/instructions-in-the-ui/index.md b/content/docs/NAVPatterns/patterns/instructions-in-the-ui/index.md index fdbe7999..a1ec6258 100644 --- a/content/docs/NAVPatterns/patterns/instructions-in-the-ui/index.md +++ b/content/docs/NAVPatterns/patterns/instructions-in-the-ui/index.md @@ -1,8 +1,6 @@ +++ title = "Instructions in the UI" weight = 670 -tags = ["C/AL"] -categories = ["Pattern"] +++ _Originally by Nikola Kukrika at Microsoft Development Center Copenhagen_ diff --git a/content/docs/NAVPatterns/patterns/integration-of-addresses/index.md b/content/docs/NAVPatterns/patterns/integration-of-addresses/index.md index 95521bba..d397581d 100644 --- a/content/docs/NAVPatterns/patterns/integration-of-addresses/index.md +++ b/content/docs/NAVPatterns/patterns/integration-of-addresses/index.md @@ -1,8 +1,6 @@ +++ title = "Integration of Addresses" weight = 680 -tags = ["C/AL"] -categories = ["Pattern"] +++ {{< youtube 60Wrx9N-gfY>}} diff --git a/content/docs/NAVPatterns/patterns/journal-error-processing/index.md b/content/docs/NAVPatterns/patterns/journal-error-processing/index.md index 7dbcd05e..a45fa1b6 100644 --- a/content/docs/NAVPatterns/patterns/journal-error-processing/index.md +++ b/content/docs/NAVPatterns/patterns/journal-error-processing/index.md @@ -1,8 +1,6 @@ +++ title = "Journal Error Processing" weight = 710 -tags = ["C/AL"] -categories = ["Pattern"] +++ _Originally by Bogdana Botez at Microsoft Development Center Copenhagen_ diff --git a/content/docs/NAVPatterns/patterns/journal-template-batch-line/index.md b/content/docs/NAVPatterns/patterns/journal-template-batch-line/index.md index 99fe99af..36cd27de 100644 --- a/content/docs/NAVPatterns/patterns/journal-template-batch-line/index.md +++ b/content/docs/NAVPatterns/patterns/journal-template-batch-line/index.md @@ -1,8 +1,6 @@ +++ title = "Journal Template Batch Line" weight = 720 -tags = ["C/AL"] -categories = ["Pattern"] +++ _Originally by Bogdana Botez at Microsoft Development Center Copenhagen_ diff --git a/content/docs/NAVPatterns/patterns/master-data/index.md b/content/docs/NAVPatterns/patterns/master-data/index.md index f42cbbd4..6dcc814a 100644 --- a/content/docs/NAVPatterns/patterns/master-data/index.md +++ b/content/docs/NAVPatterns/patterns/master-data/index.md @@ -1,8 +1,6 @@ +++ title = "Master Data" weight = 780 -tags = ["C/AL"] -categories = ["Pattern"] +++ _By [Soren Klemmensen][anchor0], [_Partner-Ready-Software_ ][anchor1] & [360 Visibility][anchor2]_ diff --git a/content/docs/NAVPatterns/patterns/multi-file-download/index.md b/content/docs/NAVPatterns/patterns/multi-file-download/index.md index fe7bb5f8..775bb447 100644 --- a/content/docs/NAVPatterns/patterns/multi-file-download/index.md +++ b/content/docs/NAVPatterns/patterns/multi-file-download/index.md @@ -1,8 +1,6 @@ +++ title = "Multi-file Download" weight = 800 -tags = ["C/AL"] -categories = ["Pattern"] +++ _By Martin Dam at Microsoft Development Center Copenhagen_ diff --git a/content/docs/NAVPatterns/patterns/multi-page-list/index.md b/content/docs/NAVPatterns/patterns/multi-page-list/index.md index 3fea0820..bc7b8f68 100644 --- a/content/docs/NAVPatterns/patterns/multi-page-list/index.md +++ b/content/docs/NAVPatterns/patterns/multi-page-list/index.md @@ -1,8 +1,6 @@ +++ title = "Multi-Page List" weight = 810 -tags = ["C/AL"] -categories = ["Pattern"] +++ _By Bogdana Botez at Microsoft Development Center Copenhagen_ diff --git a/content/docs/NAVPatterns/patterns/multilanguage-application-data/index.md b/content/docs/NAVPatterns/patterns/multilanguage-application-data/index.md index 79495c6f..83d06dfd 100644 --- a/content/docs/NAVPatterns/patterns/multilanguage-application-data/index.md +++ b/content/docs/NAVPatterns/patterns/multilanguage-application-data/index.md @@ -1,8 +1,6 @@ +++ title = "Multilanguage Application Data" weight = 820 -tags = ["C/AL"] -categories = ["Pattern"] +++ _Originally by Bogdana Botez at Microsoft Development Center Copenhagen_ diff --git a/content/docs/NAVPatterns/patterns/net-exception-handling-in-cal/_index.md b/content/docs/NAVPatterns/patterns/net-exception-handling-in-cal/_index.md index 9175a3e3..b96c39fc 100644 --- a/content/docs/NAVPatterns/patterns/net-exception-handling-in-cal/_index.md +++ b/content/docs/NAVPatterns/patterns/net-exception-handling-in-cal/_index.md @@ -1,8 +1,6 @@ +++ title = "NET Exception Handling in CAL" weight = 860 -tags = ["C/AL"] -categories = ["Pattern"] +++ _Originally by Mostafa Balat, Microsoft Development Center Copenhagen_ diff --git a/content/docs/NAVPatterns/patterns/net-exception-handling-in-cal/tryfunction-net-exception-handling-in-cal/index.md b/content/docs/NAVPatterns/patterns/net-exception-handling-in-cal/tryfunction-net-exception-handling-in-cal/index.md index 209b5c70..80239de5 100644 --- a/content/docs/NAVPatterns/patterns/net-exception-handling-in-cal/tryfunction-net-exception-handling-in-cal/index.md +++ b/content/docs/NAVPatterns/patterns/net-exception-handling-in-cal/tryfunction-net-exception-handling-in-cal/index.md @@ -1,8 +1,6 @@ +++ title = "TryFunction NET Exception Handling in CAL" weight = 1240 -tags = ["C/AL"] -categories = ["Pattern"] +++ _Originally by Mostafa Balat, Microsoft Development Center Copenhagen_ diff --git a/content/docs/NAVPatterns/patterns/no-series/index.md b/content/docs/NAVPatterns/patterns/no-series/index.md index d59f2b3a..a841e1e1 100644 --- a/content/docs/NAVPatterns/patterns/no-series/index.md +++ b/content/docs/NAVPatterns/patterns/no-series/index.md @@ -1,8 +1,6 @@ +++ title = "No Series" weight = 870 -tags = ["C/AL"] -categories = ["Pattern"] +++ _By Bogdana Botez, at Microsoft Development Center Copenhagen_ diff --git a/content/docs/NAVPatterns/patterns/notifications/_index.md b/content/docs/NAVPatterns/patterns/notifications/_index.md index 52b7d965..270c0fc4 100644 --- a/content/docs/NAVPatterns/patterns/notifications/_index.md +++ b/content/docs/NAVPatterns/patterns/notifications/_index.md @@ -1,7 +1,5 @@ +++ title = "Notifications" weight = 890 -tags = ["C/AL"] -categories = ["Pattern"] +++ A collection of patterns about notifications. diff --git a/content/docs/NAVPatterns/patterns/notifications/in-context-notifications/index.md b/content/docs/NAVPatterns/patterns/notifications/in-context-notifications/index.md index 1efa1a09..9c603272 100644 --- a/content/docs/NAVPatterns/patterns/notifications/in-context-notifications/index.md +++ b/content/docs/NAVPatterns/patterns/notifications/in-context-notifications/index.md @@ -1,8 +1,6 @@ +++ title = "In-context Notifications" weight = 640 -tags = ["C/AL"] -categories = ["Pattern"] +++ _By Soumya Dutta at Microsoft Development Center Copenhagen_ diff --git a/content/docs/NAVPatterns/patterns/notifications/notification-lifecycle-management-pattern/index.md b/content/docs/NAVPatterns/patterns/notifications/notification-lifecycle-management-pattern/index.md index b8d3195d..89cd570c 100644 --- a/content/docs/NAVPatterns/patterns/notifications/notification-lifecycle-management-pattern/index.md +++ b/content/docs/NAVPatterns/patterns/notifications/notification-lifecycle-management-pattern/index.md @@ -1,8 +1,6 @@ +++ title = "Notification Lifecycle Management Pattern" weight = 880 -tags = ["C/AL"] -categories = ["Pattern"] +++ _By David Bastide at Microsoft Development Center Copenhagen_ diff --git a/content/docs/NAVPatterns/patterns/observer/index.md b/content/docs/NAVPatterns/patterns/observer/index.md index 9be9315e..2ebeab8e 100644 --- a/content/docs/NAVPatterns/patterns/observer/index.md +++ b/content/docs/NAVPatterns/patterns/observer/index.md @@ -1,8 +1,6 @@ +++ title = "Observer" weight = 900 -tags = ["C/AL"] -categories = ["Pattern"] +++ _By Nikolai L'Estrange, from TVision Technology Ltd. in the UK_ diff --git a/content/docs/NAVPatterns/patterns/posting-routine-select-behavior/index.md b/content/docs/NAVPatterns/patterns/posting-routine-select-behavior/index.md index 79bc2d92..738afa38 100644 --- a/content/docs/NAVPatterns/patterns/posting-routine-select-behavior/index.md +++ b/content/docs/NAVPatterns/patterns/posting-routine-select-behavior/index.md @@ -1,8 +1,6 @@ +++ title = "Posting Routine - Select Behavior" weight = 940 -tags = ["C/AL"] -categories = ["Pattern"] +++ _By waldo_ diff --git a/content/docs/NAVPatterns/patterns/product-name/index.md b/content/docs/NAVPatterns/patterns/product-name/index.md index f5f5d49c..d3c841b4 100644 --- a/content/docs/NAVPatterns/patterns/product-name/index.md +++ b/content/docs/NAVPatterns/patterns/product-name/index.md @@ -1,8 +1,6 @@ +++ title = "Product Name" weight = 950 -tags = ["C/AL"] -categories = ["Pattern"] +++ #### **Abstract** diff --git a/content/docs/NAVPatterns/patterns/queries/_index.md b/content/docs/NAVPatterns/patterns/queries/_index.md index 30ab3a09..f823a5fd 100644 --- a/content/docs/NAVPatterns/patterns/queries/_index.md +++ b/content/docs/NAVPatterns/patterns/queries/_index.md @@ -1,7 +1,5 @@ +++ title = "Queries" weight = 960 -tags = ["C/AL"] -categories = ["Pattern"] +++ Expand to see NAV design patterns which use queries. diff --git a/content/docs/NAVPatterns/patterns/queries/select-distinct-with-queries/index.md b/content/docs/NAVPatterns/patterns/queries/select-distinct-with-queries/index.md index b0fe885a..6630bca8 100644 --- a/content/docs/NAVPatterns/patterns/queries/select-distinct-with-queries/index.md +++ b/content/docs/NAVPatterns/patterns/queries/select-distinct-with-queries/index.md @@ -1,8 +1,6 @@ +++ title = "SELECT DISTINCT with Queries" weight = 1040 -tags = ["C/AL"] -categories = ["Pattern"] +++ _By Bogdana Botez, at Microsoft Development Center Copenhagen_ diff --git a/content/docs/NAVPatterns/patterns/queries/use-queries-to-detect-duplicate-records/index.md b/content/docs/NAVPatterns/patterns/queries/use-queries-to-detect-duplicate-records/index.md index b2681324..e26e5399 100644 --- a/content/docs/NAVPatterns/patterns/queries/use-queries-to-detect-duplicate-records/index.md +++ b/content/docs/NAVPatterns/patterns/queries/use-queries-to-detect-duplicate-records/index.md @@ -1,8 +1,6 @@ +++ title = "Use Queries to Detect Duplicate Records" weight = 1340 -tags = ["C/AL"] -categories = ["Pattern"] +++ _Originally by Abshishek Ghosh and Bogdan Sturzoiu at Microsoft Development Center Copenhagen_ diff --git a/content/docs/NAVPatterns/patterns/queries/use-queries-to-replace-nested-loops/index.md b/content/docs/NAVPatterns/patterns/queries/use-queries-to-replace-nested-loops/index.md index a61d2583..3caea2a9 100644 --- a/content/docs/NAVPatterns/patterns/queries/use-queries-to-replace-nested-loops/index.md +++ b/content/docs/NAVPatterns/patterns/queries/use-queries-to-replace-nested-loops/index.md @@ -1,8 +1,6 @@ +++ title = "Use Queries to Replace Nested Loops" weight = 1350 -tags = ["C/AL"] -categories = ["Pattern"] +++ _Originally by Bogdan Sturzoiu, Microsoft Development Center Copenhagen_ diff --git a/content/docs/NAVPatterns/patterns/read-once-initialization-and-validation/index.md b/content/docs/NAVPatterns/patterns/read-once-initialization-and-validation/index.md index 580a7489..370b210c 100644 --- a/content/docs/NAVPatterns/patterns/read-once-initialization-and-validation/index.md +++ b/content/docs/NAVPatterns/patterns/read-once-initialization-and-validation/index.md @@ -1,8 +1,6 @@ +++ title = "Read-once Initialization and Validation" weight = 970 -tags = ["C/AL"] -categories = ["Pattern"] +++ _By Tim Grant_ diff --git a/content/docs/NAVPatterns/patterns/released-entity/index.md b/content/docs/NAVPatterns/patterns/released-entity/index.md index f4b2eeb8..89723cb0 100644 --- a/content/docs/NAVPatterns/patterns/released-entity/index.md +++ b/content/docs/NAVPatterns/patterns/released-entity/index.md @@ -1,8 +1,6 @@ +++ title = "Released Entity" weight = 1000 -tags = ["C/AL"] -categories = ["Pattern"] +++ _Authors: Henrik Langbak and Kim Ginnerup, Bording Data_ diff --git a/content/docs/NAVPatterns/patterns/report-selection/index.md b/content/docs/NAVPatterns/patterns/report-selection/index.md index e5b2ff95..7a16d6a8 100644 --- a/content/docs/NAVPatterns/patterns/report-selection/index.md +++ b/content/docs/NAVPatterns/patterns/report-selection/index.md @@ -1,8 +1,6 @@ +++ title = "Report Selection" weight = 1010 -tags = ["C/AL"] -categories = ["Pattern"] +++ From the PRS workshop at NAVTechDays 2013, this pattern was written by 2 work groups diff --git a/content/docs/NAVPatterns/patterns/security/1-sensitive-data-encapsulation/index.md b/content/docs/NAVPatterns/patterns/security/1-sensitive-data-encapsulation/index.md index 89adf3be..3f0c1582 100644 --- a/content/docs/NAVPatterns/patterns/security/1-sensitive-data-encapsulation/index.md +++ b/content/docs/NAVPatterns/patterns/security/1-sensitive-data-encapsulation/index.md @@ -1,8 +1,6 @@ +++ title = "Sensitive Data Encapsulation" weight = 120 -tags = ["C/AL"] -categories = ["Pattern"] +++ _By Bogdana Botez at Microsoft Development Center Copenhagen_ diff --git a/content/docs/NAVPatterns/patterns/security/2-data-encryption/index.md b/content/docs/NAVPatterns/patterns/security/2-data-encryption/index.md index 20c4d26b..1fb363a8 100644 --- a/content/docs/NAVPatterns/patterns/security/2-data-encryption/index.md +++ b/content/docs/NAVPatterns/patterns/security/2-data-encryption/index.md @@ -1,8 +1,6 @@ +++ title = "Data Encryption" weight = 140 -tags = ["C/AL"] -categories = ["Pattern"] +++ _By Bogdana Botez at Microsoft Development Center Copenhagen_ diff --git a/content/docs/NAVPatterns/patterns/security/3-single-point-of-access/index.md b/content/docs/NAVPatterns/patterns/security/3-single-point-of-access/index.md index e2c42eb8..e275700c 100644 --- a/content/docs/NAVPatterns/patterns/security/3-single-point-of-access/index.md +++ b/content/docs/NAVPatterns/patterns/security/3-single-point-of-access/index.md @@ -1,8 +1,6 @@ +++ title = "Single Point of Access" weight = 160 -tags = ["C/AL"] -categories = ["Pattern"] +++ _By Bogdana Botez at Microsoft Development Center Copenhagen_ diff --git a/content/docs/NAVPatterns/patterns/security/4-masked-text/index.md b/content/docs/NAVPatterns/patterns/security/4-masked-text/index.md index 9f08fce5..c8593751 100644 --- a/content/docs/NAVPatterns/patterns/security/4-masked-text/index.md +++ b/content/docs/NAVPatterns/patterns/security/4-masked-text/index.md @@ -1,8 +1,6 @@ +++ title = "Masked Text" weight = 180 -tags = ["C/AL"] -categories = ["Pattern"] +++ _By Bogdana Botez at Microsoft Development Center Copenhagen_ diff --git a/content/docs/NAVPatterns/patterns/security/5-ssl-in-nav/index.md b/content/docs/NAVPatterns/patterns/security/5-ssl-in-nav/index.md index 0c8eaf6e..5a64d5c1 100644 --- a/content/docs/NAVPatterns/patterns/security/5-ssl-in-nav/index.md +++ b/content/docs/NAVPatterns/patterns/security/5-ssl-in-nav/index.md @@ -1,8 +1,6 @@ +++ title = "SSL in NAV" weight = 190 -tags = ["C/AL"] -categories = ["Pattern"] +++ _By Bogdana Botez at Microsoft Development Center Copenhagen_ diff --git a/content/docs/NAVPatterns/patterns/security/_index.md b/content/docs/NAVPatterns/patterns/security/_index.md index 19fab25e..18985e6f 100644 --- a/content/docs/NAVPatterns/patterns/security/_index.md +++ b/content/docs/NAVPatterns/patterns/security/_index.md @@ -1,8 +1,6 @@ +++ title = "Security" weight = 1030 -tags = ["C/AL"] -categories = ["Pattern"] +++ _By Bogdana Botez at Microsoft Development Center Copenhagen_ diff --git a/content/docs/NAVPatterns/patterns/setup-specificity-fallback/index.md b/content/docs/NAVPatterns/patterns/setup-specificity-fallback/index.md index d5ca5897..5022e8c5 100644 --- a/content/docs/NAVPatterns/patterns/setup-specificity-fallback/index.md +++ b/content/docs/NAVPatterns/patterns/setup-specificity-fallback/index.md @@ -1,8 +1,6 @@ +++ title = "Setup Specificity Fallback" weight = 1060 -tags = ["C/AL"] -categories = ["Pattern"] +++ _By Jan Hoek at IDYN_ diff --git a/content/docs/NAVPatterns/patterns/silent-file-upload-and-download/index.md b/content/docs/NAVPatterns/patterns/silent-file-upload-and-download/index.md index d6b21254..440c4b38 100644 --- a/content/docs/NAVPatterns/patterns/silent-file-upload-and-download/index.md +++ b/content/docs/NAVPatterns/patterns/silent-file-upload-and-download/index.md @@ -1,8 +1,6 @@ +++ title = "Silent File Upload and Download" weight = 1080 -tags = ["C/AL"] -categories = ["Pattern"] +++ _By Bogdana Botez, at Microsoft Development Center Copenhagen_ diff --git a/content/docs/NAVPatterns/patterns/singleton/_index.md b/content/docs/NAVPatterns/patterns/singleton/_index.md index 8c61390d..ae9bdf13 100644 --- a/content/docs/NAVPatterns/patterns/singleton/_index.md +++ b/content/docs/NAVPatterns/patterns/singleton/_index.md @@ -1,8 +1,6 @@ +++ title = "Singleton" weight = 1090 -tags = ["C/AL"] -categories = ["Pattern"] +++ _By Bogdana Botez at Microsoft Development Center Copenhagen_ diff --git a/content/docs/NAVPatterns/patterns/singleton/singleton-codeunit/index.md b/content/docs/NAVPatterns/patterns/singleton/singleton-codeunit/index.md index 1d8a7a83..1c7069a7 100644 --- a/content/docs/NAVPatterns/patterns/singleton/singleton-codeunit/index.md +++ b/content/docs/NAVPatterns/patterns/singleton/singleton-codeunit/index.md @@ -1,8 +1,6 @@ +++ title = "Singleton Codeunit" weight = 1100 -tags = ["C/AL"] -categories = ["Pattern"] +++ _By Bogdana Botez at Microsoft Development Center Copenhagen_ diff --git a/content/docs/NAVPatterns/patterns/singleton/singleton-table/_index.md b/content/docs/NAVPatterns/patterns/singleton/singleton-table/_index.md index 31bbebb7..d2cec690 100644 --- a/content/docs/NAVPatterns/patterns/singleton/singleton-table/_index.md +++ b/content/docs/NAVPatterns/patterns/singleton/singleton-table/_index.md @@ -1,8 +1,6 @@ +++ title = "Singleton Table" weight = 1110 -tags = ["C/AL"] -categories = ["Pattern"] +++ ## Singleton Table diff --git a/content/docs/NAVPatterns/patterns/singleton/singleton-table/cue-table/index.md b/content/docs/NAVPatterns/patterns/singleton/singleton-table/cue-table/index.md index eff99fd9..913383a6 100644 --- a/content/docs/NAVPatterns/patterns/singleton/singleton-table/cue-table/index.md +++ b/content/docs/NAVPatterns/patterns/singleton/singleton-table/cue-table/index.md @@ -1,8 +1,6 @@ +++ title = "Cue Table" weight = 440 -tags = ["C/AL"] -categories = ["Pattern"] +++ _By Bogdana Botez at Microsoft Development Center Copenhagen_ diff --git a/content/docs/NAVPatterns/patterns/singleton/singleton-table/setup-table/index.md b/content/docs/NAVPatterns/patterns/singleton/singleton-table/setup-table/index.md index 11ec5be1..fe9800e2 100644 --- a/content/docs/NAVPatterns/patterns/singleton/singleton-table/setup-table/index.md +++ b/content/docs/NAVPatterns/patterns/singleton/singleton-table/setup-table/index.md @@ -1,8 +1,6 @@ +++ title = "Setup Table" weight = 1070 -tags = ["C/AL"] -categories = ["Pattern"] +++ _By Abhishek Ghosh, at Microsoft Development Center Copenhagen_ diff --git a/content/docs/NAVPatterns/patterns/standard-journal/index.md b/content/docs/NAVPatterns/patterns/standard-journal/index.md index 43f4d4fd..1ebb3b12 100644 --- a/content/docs/NAVPatterns/patterns/standard-journal/index.md +++ b/content/docs/NAVPatterns/patterns/standard-journal/index.md @@ -1,8 +1,6 @@ +++ title = "Standard Journal" weight = 1150 -tags = ["C/AL"] -categories = ["Pattern"] +++ _By Bogdana Botez, at Microsoft Development Center Copenhagen_ diff --git a/content/docs/NAVPatterns/patterns/temporary-dataset-report/index.md b/content/docs/NAVPatterns/patterns/temporary-dataset-report/index.md index b54a4c68..72a7d87d 100644 --- a/content/docs/NAVPatterns/patterns/temporary-dataset-report/index.md +++ b/content/docs/NAVPatterns/patterns/temporary-dataset-report/index.md @@ -1,8 +1,6 @@ +++ title = "Temporary Dataset Report" weight = 1190 -tags = ["C/AL"] -categories = ["Pattern"] +++ _Originally by Abhishek Ghosh, at Microsoft Development Center Copenhagen_ diff --git a/content/docs/NAVPatterns/patterns/totals-and-discounts-on-subpages-sales-and-purchases/index.md b/content/docs/NAVPatterns/patterns/totals-and-discounts-on-subpages-sales-and-purchases/index.md index 88d0d8a4..19143556 100644 --- a/content/docs/NAVPatterns/patterns/totals-and-discounts-on-subpages-sales-and-purchases/index.md +++ b/content/docs/NAVPatterns/patterns/totals-and-discounts-on-subpages-sales-and-purchases/index.md @@ -1,8 +1,6 @@ +++ title = "Totals and Discounts on Subpages Sales and Purchases" weight = 1220 -tags = ["C/AL"] -categories = ["Pattern"] +++ _By Nikola Kukrika at Microsoft Development Center Copenhagen_ diff --git a/content/docs/NAVPatterns/patterns/transfer-custom-fields/index.md b/content/docs/NAVPatterns/patterns/transfer-custom-fields/index.md index 7745afe0..3d0d3e78 100644 --- a/content/docs/NAVPatterns/patterns/transfer-custom-fields/index.md +++ b/content/docs/NAVPatterns/patterns/transfer-custom-fields/index.md @@ -1,8 +1,6 @@ +++ title = "Transfer Custom Fields" weight = 1230 -tags = ["C/AL"] -categories = ["Pattern"] +++ {{< youtube cGaBqwfGCws>}} diff --git a/content/docs/NAVPatterns/patterns/variant-facade/index.md b/content/docs/NAVPatterns/patterns/variant-facade/index.md index cb969300..0c377d39 100644 --- a/content/docs/NAVPatterns/patterns/variant-facade/index.md +++ b/content/docs/NAVPatterns/patterns/variant-facade/index.md @@ -1,8 +1,6 @@ +++ title = "Variant Facade" weight = 1440 -tags = ["C/AL"] -categories = ["Pattern"] +++ _By Nikola Kukrika, waldo and Gary Winter_ diff --git a/content/docs/NAVPatterns/related-links/index.md b/content/docs/NAVPatterns/related-links/index.md index dbcd675d..bda8b801 100644 --- a/content/docs/NAVPatterns/related-links/index.md +++ b/content/docs/NAVPatterns/related-links/index.md @@ -1,7 +1,6 @@ +++ title = "Related Links" weight = 990 -tags = ["C/AL"] +++ Find below some related NAV Design Patterns links. diff --git a/content/docs/_index.md b/content/docs/_index.md index e48539bd..4adca734 100644 --- a/content/docs/_index.md +++ b/content/docs/_index.md @@ -3,7 +3,7 @@ title: "AL Guidelines" linkTitle: Docs weight: 20 description: > - Patterns and Best Practices for AL Development + Patterns and Best Practices for AL Develolpment --- ## Business Central Design Patterns & Best Practices @@ -40,4 +40,4 @@ This project is a Microsoft Business Central Community initiative with support f #### Contributing To find out more about contributing, read up here: -[Contributing](/docs/contributing/) +[Contributing](/contributing/) \ No newline at end of file diff --git a/content/docs/agentic-coding/CommunityResources/Agents/_index.md b/content/docs/agentic-coding/CommunityResources/Agents/_index.md deleted file mode 100644 index b463099f..00000000 --- a/content/docs/agentic-coding/CommunityResources/Agents/_index.md +++ /dev/null @@ -1,217 +0,0 @@ ---- -title: "AI Coding Agents" -linkTitle: "Agents" -weight: 5 -description: > - Learn about different AI coding agents and how to use them for AL development ---- - -## Overview - -AI coding agents are intelligent assistants that help you write, review, and improve AL code for Business Central. This section explains the different types of agents available and how to choose the right one for your needs. - -## What are AI Coding Agents? - -AI coding agents are more than simple autocomplete tools. They can: -- Understand natural language instructions -- Generate complete code implementations -- Explain existing code -- Refactor and improve code quality -- Help debug issues -- Provide learning and guidance - -## Available Agents - -Explore detailed information about each AI coding agent: - -### [GitHub Copilot](github-copilot-agent) -Microsoft's AI pair programmer integrated into VS Code. - -**Best For**: -- Inline code completion as you type -- Quick code generation -- Learning AL patterns -- Teams already using GitHub - -**Key Features**: Real-time suggestions, multi-file context, AL-aware completions - ---- - -### [GitHub Copilot Chat](github-copilot-chat-agent) -Conversational AI assistant from GitHub with deep VS Code integration. - -**Best For**: -- Interactive code discussions -- Code explanations and learning -- Debugging assistance -- Complex refactoring tasks - -**Key Features**: Natural language chat, context-aware responses, inline chat - ---- - -### [Claude (via API or Claude.ai)](claude-agent) -Anthropic's AI assistant with strong reasoning capabilities. - -**Best For**: -- Complex problem solving -- Detailed code analysis -- Architecture discussions -- Large codebase understanding - -**Key Features**: Long context window, strong analytical abilities, helpful explanations - ---- - -### [Cursor](cursor-agent) -AI-first code editor built on VS Code with integrated AI assistance. - -**Best For**: -- All-in-one AI coding environment -- Teams wanting deep AI integration -- Multi-model AI access -- Codebase-wide AI context - -**Key Features**: Multiple AI models, composer mode, codebase indexing, chat + autocomplete - ---- - -## Comparison Matrix - -| Feature | GitHub Copilot | Copilot Chat | Claude | Cursor | -|---------|---------------|--------------|---------|---------| -| **Inline Completion** | ✓✓✓ Excellent | Limited | N/A | ✓✓✓ Excellent | -| **Chat Interface** | N/A | ✓✓✓ Excellent | ✓✓✓ Excellent | ✓✓✓ Excellent | -| **AL Awareness** | ✓✓ Good | ✓✓ Good | ✓ Basic | ✓✓ Good | -| **Code Explanation** | Limited | ✓✓✓ Excellent | ✓✓✓ Excellent | ✓✓✓ Excellent | -| **VS Code Integration** | ✓✓✓ Native | ✓✓✓ Native | ✗ Web/API | ✓✓✓ Fork | -| **Context Window** | Medium | Medium | ✓✓✓ Very Large | Large | -| **Multi-file Editing** | Limited | Limited | Manual | ✓✓✓ Excellent | -| **Pricing** | $10-19/mo | Included | Free/Pro | $20/mo | -| **Team Features** | ✓✓ Good | ✓✓ Good | Limited | ✓ Basic | - -**Legend**: ✓✓✓ Excellent | ✓✓ Good | ✓ Basic | Limited | ✗ Not Available | N/A Not Applicable - -## Choosing the Right Agent - -### For Individual Developers - -**Just Starting with AI?** -→ Start with [GitHub Copilot](github-copilot-agent) + [Copilot Chat](github-copilot-chat-agent) -- Easy setup -- Great VS Code integration -- Good AL support -- One subscription for both - -**Want Maximum AI Power?** -→ Try [Cursor](cursor-agent) -- Multiple AI models -- Strong codebase understanding -- All-in-one solution -- Advanced features - -**Need Deep Analysis?** -→ Use [Claude](claude-agent) for complex tasks -- Long context for large codebases -- Excellent reasoning -- Detailed explanations -- Free tier available - -### For Teams - -**Microsoft/GitHub Ecosystem?** -→ GitHub Copilot for Business -- Centralized management -- License management -- Organization policies -- Familiar tools - -**Maximum Flexibility?** -→ Cursor for Teams -- Multiple AI models -- Strong collaboration -- Advanced features -- Modern interface - -**Hybrid Approach?** -→ Combine tools: -- GitHub Copilot for daily coding -- Claude for complex analysis -- Cursor for specific projects - -## Using Multiple Agents - -Many developers use multiple agents for different tasks: - -**Daily Coding**: GitHub Copilot (inline suggestions) -**Learning & Debugging**: Copilot Chat or Cursor Chat -**Complex Problems**: Claude (detailed analysis) -**Refactoring**: Cursor (multi-file editing) - -**Example Workflow**: -1. Write code with GitHub Copilot inline suggestions -2. Ask Copilot Chat to explain complex BC patterns -3. Use Claude for architecture review of large features -4. Use Cursor for complex multi-file refactoring - -## Getting Started - -### New to AI Coding? - -1. **Start Simple**: [GitHub Copilot](github-copilot-agent) -2. **Learn the Basics**: [Effective Prompting](../../gettingstarted/effective-prompting) -3. **Try Examples**: [Getting More](../../gettingmore) -4. **Explore Others**: Try Claude or Cursor for comparison - -### Already Using AI? - -**Expand Your Toolkit**: -- If using Copilot → Try Cursor for advanced features -- If using Claude → Add Copilot for inline completion -- If using Cursor → Use Claude for deep analysis - -## Common Questions - -### Can I use multiple agents? -Yes! Many developers use different agents for different tasks. They complement each other well. - -### Which is best for AL development? -GitHub Copilot has the most AL-specific training, but all agents can be effective with good prompting. - -### Are these expensive? -Most are $10-20/month for individual use. GitHub Copilot offers free access for students and open source maintainers. - -### Do I need internet? -Yes, all current AI agents require internet connectivity to function. - -### Will AI replace AL developers? -No. AI agents are tools to augment your capabilities, not replace your expertise and decision-making. - -## Privacy & Security - -All agents send code to external services. Consider: -- Review your organization's AI usage policy -- Don't include sensitive data in code -- Use business/enterprise plans for better controls -- Understand each tool's data handling policies - -See individual agent pages for specific privacy information. - -## Learning Path - -1. **Read agent pages** to understand capabilities -2. **Choose one** to start with -3. **Follow setup** instructions -4. **Practice** with examples from [Getting More](../../gettingmore) -5. **Experiment** with others as needed - -## Resources - -- [Setup Guide](../../gettingstarted/setup) - Environment configuration -- [Effective Prompting](../../gettingstarted/effective-prompting) - Get better results -- [Best Practices](../../gettingstarted/best-practices) - Use AI effectively -- [Limitations](../../gettingstarted/limitations) - Understand constraints - ---- - -**Questions?** Join the discussion at [GitHub Discussions](https://github.com/microsoft/alguidelines/discussions) diff --git a/content/docs/agentic-coding/CommunityResources/Agents/claude-agent.md b/content/docs/agentic-coding/CommunityResources/Agents/claude-agent.md deleted file mode 100644 index 828c5674..00000000 --- a/content/docs/agentic-coding/CommunityResources/Agents/claude-agent.md +++ /dev/null @@ -1,757 +0,0 @@ ---- -title: "Claude Agent" -linkTitle: "Claude" -weight: 3 -description: > - Anthropic's AI assistant with strong reasoning and large context window for AL development ---- - -## Overview - -Claude is Anthropic's AI assistant known for its strong reasoning capabilities, long context window, and helpful, detailed responses. While not specifically designed for coding, it excels at code analysis, architecture discussions, and complex problem-solving for AL development. - -**Developer**: Anthropic -**Type**: Conversational AI Assistant -**Primary Use**: Code analysis, learning, complex problem-solving -**Integration**: Web interface (Claude.ai) or API - -## What is Claude? - -Claude is a general-purpose AI assistant that: -- Provides detailed, thoughtful analysis -- Handles very large amounts of code -- Reasons through complex problems -- Explains concepts clearly -- Generates well-structured code -- Maintains context across long conversations - -### Access Methods - -**Claude.ai (Web)**: -- Free tier available -- Pro tier ($20/month) for more usage -- Upload files directly -- Conversation interface -- No IDE integration - -**Claude API**: -- Programmatic access -- Can be integrated into tools -- Pay-per-use pricing -- Requires development - -**Note**: Unlike Copilot/Cursor, Claude doesn't have native VS Code integration. - -## Key Capabilities for AL Development - -### Exceptional Context Window - -**Claude 3.5 Sonnet**: ~200K tokens -- Can analyze entire AL projects -- Process multiple large files simultaneously -- Maintain context across long conversations -- Reference earlier parts of discussion - -**Practical Use**: -``` -You can paste: -- Multiple complete AL files -- Entire codeunit implementations -- Full table structures -- Large amounts of documentation - -Claude maintains context and can reference any part. -``` - -### Strong Analytical Abilities - -**Code Analysis**: -- Deep understanding of code structure -- Identifies patterns and anti-patterns -- Suggests architectural improvements -- Explains complex logic clearly - -**Problem Solving**: -- Reasons through complex scenarios -- Considers multiple approaches -- Explains trade-offs -- Provides detailed rationale - -**Learning & Teaching**: -- Patient, clear explanations -- Step-by-step breakdowns -- Answers follow-up questions -- Adapts to your knowledge level - -## Strengths for AL Development - -### ✓ Exceptional At - -**Large Codebase Analysis**: -``` -Paste your entire project: -- All codeunits -- Table structures -- Page definitions -- Integration logic - -Ask: "Review this AL project for architectural improvements" - -Claude can analyze it all and provide comprehensive feedback. -``` - -**Architecture & Design**: -``` -Questions like: -- "Should I use events or direct calls for this integration?" -- "How should I structure this multi-tenant extension?" -- "What's the best way to handle this complex business logic?" - -Claude provides thoughtful, reasoned analysis. -``` - -**Complex Problem Solving**: -``` -Describe complex scenarios: -- Multi-step business processes -- Integration challenges -- Performance optimization -- Refactoring strategies - -Claude reasons through options and trade-offs. -``` - -**Learning & Understanding**: -``` -Questions like: -- "Explain Business Central's posting architecture" -- "How does AL's event system work?" -- "What are the differences between these BC patterns?" - -Claude provides detailed, educational responses. -``` - -### ⚠ Limitations - -**No IDE Integration**: -- Manual copy-paste workflow -- No inline suggestions -- No automatic context -- More context switching - -**AL-Specific Training**: -- Not specifically trained on AL -- May not know latest BC features -- Might suggest non-AL patterns -- Requires clear AL context - -**Real-Time Coding**: -- Not designed for quick completions -- Better for analysis than generation -- Slower workflow than Copilot -- Best for thoughtful tasks - -## Practical AL Examples - -### Architecture Review - -**You** (paste entire project): -``` -I'm building a customer loyalty system for Business Central. -Here are my main files: - -[Paste 5-10 AL files totaling thousands of lines] - -Review the architecture and suggest improvements. -``` - -**Claude Responds**: -``` -I've analyzed your loyalty system implementation. Here's my review: - -Overall Architecture: -Your implementation follows a good separation of concerns with: -- Setup tables (Loyalty Tier, Loyalty Rules) -- Transaction table (Loyalty Points Entry) -- Processing codeunits (Points Calculator, Tier Evaluator) -- Integration through event subscribers - -Strengths: -1. Good use of events for extensibility -2. Clear separation between setup and transactions -3. Proper use of temporary tables for calculations - -Areas for Improvement: - -1. Performance Optimization (Table 50101 "Loyalty Points Entry"): - - Current implementation recalculates totals on every query - - Recommendation: Add a "Total Points" field on Customer table - - Use events to update when points are added/removed - - Reduces database load significantly - -2. Transaction Handling (Codeunit 50100): - Lines 45-67 process points without transaction wrapping. - This could lead to partial updates if an error occurs. - - Suggested pattern: - procedure AwardPoints(...) - begin - // Wrap in Codeunit.Run for transaction - if not AwardPointsInternal(...) then - Error(GetLastErrorText()); - end; - - procedure AwardPointsInternal(...): Boolean - begin - // Processing logic here - end; - -3. Testing Considerations: - - No test codeunits provided - - Critical calculation logic in Points Calculator should have unit tests - - Recommendation: Create test codeunit with scenarios for: - * Points calculation accuracy - * Tier upgrades/downgrades - * Edge cases (negative points, expired points) - -4. Telemetry (Missing): - Consider adding Application Insights telemetry for: - - Points awarded/redeemed - - Tier changes - - Calculation performance - - Error conditions - -5. API Exposure: - For mobile/web integration, consider: - - API page for customer's current points balance - - API page for points history - - OData for reporting - -Would you like me to elaborate on any of these points or show -specific code examples for the improvements? -``` - -### Complex AL Pattern Explanation - -**You**: -``` -I found this code in standard BC. Can you explain in detail how -this posting pattern works and why it's structured this way? - -[Paste complex BC posting code] -``` - -**Claude Explains**: -``` -This is Business Central's standard document posting pattern. -Let me break it down step by step: - -Structure Overview: -1. OnRun trigger (lines 10-15): Entry point -2. Main posting procedure (lines 20-150): Orchestration -3. Helper procedures (lines 155+): Specific tasks -4. Integration events: Extensibility points - -Detailed Explanation: - -Phase 1: Pre-Posting Validation (lines 22-35) --------------------------------------------- -The code first validates the document is ready to post: - - OnRun() - begin - CheckDocumentIsReadyToPost(SalesHeader); - end; - -This separation ensures: -- Validation logic is reusable -- Errors caught before any changes -- Can be tested independently - -Key validations: -- Document not already posted -- Required fields populated -- Valid line items exist -- Customer not blocked - -Phase 2: Table Locking (lines 40-45) ------------------------------------- - SalesHeader.LockTable(); - SalesLine.LockTable(); - -Critical for multi-user scenarios: -- Prevents concurrent modifications -- Ensures data consistency -- Follows database transaction best practices - -This must happen INSIDE the implicit transaction -(started by OnRun trigger). - -Phase 3: Integration Events "Before" (lines 50-55) -------------------------------------------------- - OnBeforePostSalesDocument(SalesHeader, PostingResult); - -Allows extensions to: -- Add additional validation -- Modify posting parameters -- Log custom data -- Integrate with external systems - -Extensions subscribe to these events to add functionality -without modifying base code. - -Phase 4: Core Posting Logic (lines 60-120) ------------------------------------------- -The actual posting work: - - PostSalesLines(SalesHeader); // Create ledger entries - PostCustomerLedger(SalesHeader); // Update customer balance - UpdateInventory(SalesHeader); // Adjust inventory - CreatePostedDocument(SalesHeader);// Archive posted doc - -Each step is a separate procedure because: -- Clarity and maintainability -- Reusability -- Easier testing -- Clear transaction boundaries - -Phase 5: Integration Events "After" (lines 125-130) --------------------------------------------------- - OnAfterPostSalesDocument(SalesHeader, PostingResult); - -Allows extensions to: -- Trigger follow-up actions -- Send notifications -- Update related data -- Log completion - -Phase 6: Cleanup (lines 135-145) --------------------------------- - DeleteAfterPosting(SalesHeader); - Commit; - -- Removes unposted document -- Commits transaction -- Frees locks - -Why This Pattern? - -1. Reliability: - - Explicit transaction boundaries - - Proper locking prevents conflicts - - Validation before any changes - -2. Extensibility: - - Events at every critical point - - Extensions can hook in without modification - - Standard pattern across all posting routines - -3. Maintainability: - - Clear, linear flow - - Each procedure has single responsibility - - Easy to understand and debug - -4. Performance: - - Table locks minimize deadlocks - - Efficient batch processing - - Single transaction reduces overhead - -5. Error Handling: - - OnRun trigger provides implicit error handling - - Transaction rolled back on error - - Clean state maintained - -This is the gold standard for BC posting routines. When creating -your own posting logic, follow this same pattern for: -- Custom documents -- Batch processing -- Any transactional operations - -Would you like me to explain any specific part in more detail, -or show how to implement this pattern for a custom document? -``` - -### Migration Strategy - -**You**: -``` -We have 50,000 lines of NAV 2018 C/AL code to migrate to BC AL. -What's the best strategy? - -[Include examples of your NAV code] -``` - -**Claude Provides**: -``` -Migrating 50K lines of C/AL to AL is substantial. Here's a strategic approach: - -Phase 1: Assessment & Planning (Week 1-2) ------------------------------------------ - -1. Code Analysis: - - Categorize by object type - - Identify custom vs modified standard objects - - Map dependencies - - Find external integrations - -2. Risk Assessment: - HIGH RISK (manual migration priority): - - Heavy .NET interop - - Automation/OCX controls - - Client-side code - - File system access - - MEDIUM RISK (tool + manual): - - Standard modifications - - Reports - - Complex business logic - - LOW RISK (mostly automated): - - Simple tables - - Basic pages - - Straightforward codeunits - -3. Tool Selection: - - AL Object Designer for conversion - - txt2al for initial conversion - - Manual for complex cases - -Phase 2: Automated Conversion (Week 3-4) ----------------------------------------- - -1. Run automated tools on LOW/MEDIUM risk code -2. Fix compilation errors -3. Address obvious issues -4. Don't spend time on perfect conversion yet - -Phase 3: Manual Review & Refactoring (Week 5-12) ------------------------------------------------ - -Priority order: -1. Core business logic (highest value) -2. Integration points (highest risk) -3. Reports (user-facing) -4. Supporting code (lower priority) - -For each module: -a) Understand business purpose -b) Review automated conversion -c) Refactor to AL patterns -d) Add error handling -e) Add telemetry -f) Create tests - -Phase 4: Testing (Week 13-16) ------------------------------ - -Comprehensive testing: -- Unit tests for calculations -- Integration tests for workflows -- User acceptance testing -- Performance testing -- Migration testing (data) - -Phase 5: Incremental Deployment (Week 17+) ------------------------------------------- - -Consider: -- Parallel run period -- Module-by-module rollout -- Rollback procedures -- User training - -Specific Code Migration Tips: - -1. C/AL Automation → AL: - Your NAV code uses Word automation. - Replace with: - - BC Word Report Layouts (preferred) - - External API calls if complex - - Azure Functions for processing - -2. Client-Side Code → AL: - NAV Page code that ran on client. - Move to: - - Page triggers (server-side) - - Page extensions - - Control add-ins (if really needed) - -3. Record.SETCURRENTKEY → AL: - NAV: Rec.SETCURRENTKEY(Date, Amount); - AL: Rec.SetCurrentKey(Date, Amount); - - Note: Case-sensitive in AL! - -4. Reports: - NAV RDLC reports need: - - AL report object - - Updated dataset - - Layout regeneration - - Consider: RDL → Word layout migration - -5. .NET Interop: - NAV: DotNet variables - AL: Check if: - - Available in AL - - Use BC API instead - - Create Azure Function if needed - -AI-Assisted Migration Strategy: - -Use AI tools effectively: -1. GitHub Copilot: - - Quick syntax fixes - - Pattern replacement - - Boilerplate generation - -2. Claude (me!): - - Complex logic understanding - - Architecture decisions - - Refactoring strategies - -3. Manual effort: - - Business logic validation - - Testing - - Critical decisions - -Realistic Timeline: -- Simple migration: 4-6 months -- Complex migration: 6-12 months -- Very complex: 12-18 months - -Budget for: -- 30% automated conversion -- 40% manual refactoring -- 30% testing and validation - -Would you like me to: -1. Review specific NAV code samples? -2. Elaborate on any phase? -3. Discuss specific technical challenges? -4. Create a detailed project plan template? -``` - -## How to Use Claude Effectively - -### Workflow - -**1. Preparation**: -- Gather all relevant AL code -- Prepare specific questions -- Include context and requirements - -**2. Interaction**: -- Paste code into Claude.ai -- Ask clear, specific questions -- Provide examples of what you want - -**3. Iterate**: -- Ask follow-up questions -- Clarify unclear points -- Request alternatives -- Explore trade-offs - -**4. Implementation**: -- Copy suggested code -- Adapt to your project -- Test thoroughly -- Refine as needed - -### Best Practices - -**Provide Full Context**: -``` -Good: -"I'm building an AL extension for BC v22 that integrates with Shopify. -Here's my current implementation: [paste code] -I need to handle rate limiting from the Shopify API. -Show me the best AL pattern for this." - -vs. - -Poor: -"How do I handle rate limiting?" -``` - -**Ask Specific Questions**: -``` -Good: -"Review this AL posting codeunit for performance issues, -specifically focusing on database operations." - -vs. - -Poor: -"Is this good?" -``` - -**Use for Complex Tasks**: -- Architecture decisions -- Code review of large modules -- Learning complex concepts -- Debugging intricate issues - -## How It Differs from Other Agents - -### vs. GitHub Copilot - -**Claude**: -- ✓ Much larger context window -- ✓ Better at analysis and reasoning -- ✓ More detailed explanations -- ✗ No IDE integration -- ✗ No inline suggestions -- ✗ Manual workflow - -**GitHub Copilot**: -- ✓ IDE integration -- ✓ Real-time suggestions -- ✓ Fast workflow -- ✗ Limited context -- ✗ Less analytical - -**Use Both**: Copilot for daily coding, Claude for deep analysis - -### vs. Cursor - -**Claude**: -- ✓ Larger context window -- ✓ Better analytical depth -- ✓ More thoughtful responses -- ✗ No IDE integration -- ✗ Manual copy-paste - -**Cursor**: -- ✓ IDE integration -- ✓ Multiple AI models (including Claude!) -- ✓ Direct code editing -- ✗ Smaller context per interaction -- ✗ Different editor - -**Note**: Cursor can use Claude as its AI model, giving you Claude's capabilities with IDE integration! - -## Access & Pricing - -### Claude.ai (Web) - -**Free Tier**: -- Limited messages per day -- Claude 3.5 Sonnet access -- Good for occasional use -- No payment required - -**Pro Tier** ($20/month): -- 5x more usage -- Priority access -- Claude 3 Opus (most capable model) -- Early feature access - -### Claude API - -**Pay-per-use**: -- Billed by tokens -- Integration into tools -- Programmatic access -- See Anthropic pricing page - -**Link**: [Claude.ai](https://claude.ai) | [Anthropic Pricing](https://www.anthropic.com/pricing) - -## Privacy & Security - -### What Gets Sent -- Your messages and questions -- Code you paste -- Files you upload -- Conversation history - -### Data Usage -- Not used for training (as of current policy) -- Processed for improving responses -- Retained per Anthropic's policy -- Review privacy policy for details - -### Best Practices -- Don't paste sensitive data -- Avoid customer information -- Review organization policies -- Use sanitized code examples - -## When to Use Claude - -### ✓ Ideal For - -- **Large code reviews**: Paste entire projects -- **Architecture discussions**: Complex design decisions -- **Learning**: Detailed explanations of AL/BC concepts -- **Problem solving**: Complex scenarios with many variables -- **Migration planning**: NAV to BC conversions -- **Refactoring strategies**: Large-scale code improvements -- **API design**: Thoughtful API architecture - -### ⚠ Consider Alternatives - -- **Quick completions** → GitHub Copilot -- **IDE integration** → Copilot or Cursor -- **Real-time coding** → Copilot or Cursor -- **Multi-file editing** → Cursor - -## Complementary Tools - -**Use With**: -- GitHub Copilot for daily coding -- VS Code for development -- AL analyzers for code quality -- Version control for safety - -**Workflow Example**: -1. Code with Copilot in VS Code -2. Review architecture with Claude -3. Implement improvements in VS Code -4. Test and validate - -## Tips for AL Development - -**Provide AL Context**: -``` -"I'm working in AL for Business Central version 22. -[Your question]" -``` - -**Reference BC Concepts**: -``` -"Using BC's standard posting pattern..." -"Following BC event subscriber patterns..." -``` - -**Ask for Alternatives**: -``` -"Show me 3 different approaches to this problem, -with pros and cons of each." -``` - -**Request Explanations**: -``` -"Explain this like I'm familiar with C# but new to AL" -``` - -## Resources - -### Official -- [Claude.ai](https://claude.ai) -- [Anthropic Documentation](https://docs.anthropic.com) -- [API Documentation](https://docs.anthropic.com/claude/reference) - -### AL Guidelines -- [Effective Prompting](../../gettingstarted/effective-prompting) -- [Best Practices](../../gettingstarted/best-practices) -- [Code Review Examples](../../gettingmore/code-review) - ---- - -**Next Steps**: -- Try [Claude.ai](https://claude.ai) for free -- Compare with [other AI agents](./) -- Use alongside [GitHub Copilot](github-copilot-agent) - -**Questions?** Join [GitHub Discussions](https://github.com/microsoft/alguidelines/discussions) diff --git a/content/docs/agentic-coding/CommunityResources/Agents/cursor-agent.md b/content/docs/agentic-coding/CommunityResources/Agents/cursor-agent.md deleted file mode 100644 index 4e5fdfd1..00000000 --- a/content/docs/agentic-coding/CommunityResources/Agents/cursor-agent.md +++ /dev/null @@ -1,675 +0,0 @@ ---- -title: "Cursor Agent" -linkTitle: "Cursor" -weight: 4 -description: > - AI-first code editor with integrated AI assistance and multi-file editing for AL development ---- - -## Overview - -Cursor is an AI-first code editor built on VS Code that integrates AI deeply into every aspect of development. It provides inline suggestions, chat assistance, and advanced features like Composer mode for multi-file editing, making it a powerful tool for AL development. - -**Developer**: Anysphere Inc. -**Type**: AI-Integrated Code Editor -**Primary Use**: Complete AI-assisted development environment -**Integration**: Native (is a code editor) - -## What is Cursor? - -Cursor is a fork of Visual Studio Code with integrated AI capabilities: -- Native AI code completion (like Copilot) -- Built-in AI chat (like Copilot Chat) -- Composer mode for multi-file editing -- Codebase indexing for better context -- Multiple AI model support -- All VS Code extensions work - -### Key Differentiator - -Unlike tools that add AI to VS Code, Cursor **is** an AI-first editor built from the ground up with AI integration. - -**Think of it as**: VS Code + GitHub Copilot + Advanced AI Features + Better Context Understanding - -## Key Features - -### 1. Tab Completion (Like Copilot) - -Real-time AI suggestions as you type: -```al -// Type a comment -/// Validate customer credit limit - -// Cursor suggests complete procedure -procedure ValidateCreditLimit(CustomerNo: Code[20]): Boolean -var - Customer: Record Customer; -begin - // Full implementation suggested -end; -``` - -**Plus**: -- Faster than Copilot -- Better context awareness -- Multiple AI models available - -### 2. Chat Interface (Like Copilot Chat) - -Built-in AI chat in the sidebar: -- Ask questions about code -- Get explanations -- Request code generation -- Debug issues - -**Plus**: -- Can use multiple AI models (GPT-4, Claude, etc.) -- Better codebase understanding -- More context awareness - -### 3. Cmd+K (Inline Chat) - -Quick inline AI assistance: -- Press `Cmd+K` (Mac) or `Ctrl+K` (Windows) -- Ask questions or request changes -- AI suggests edits inline -- Accept, reject, or modify - -**Example**: -```al -// Select code, press Cmd+K, type: -"Add error handling and telemetry" - -// Cursor shows diff with changes -// Accept to apply -``` - -### 4. Composer Mode ⭐ - -**Most Powerful Feature**: Multi-file editing with AI - -- Create/edit multiple files simultaneously -- AI understands file relationships -- Generates complete features -- Handles complex refactoring - -**Example**: -``` -Press Cmd+I (Composer) -Type: "Add a loyalty points system with table, page, and codeunit" - -Cursor creates: -- Table 50100 "Loyalty Points Entry" -- Page 50100 "Loyalty Points List" -- Page 50101 "Loyalty Points Card" -- Codeunit 50100 "Loyalty Points Manager" - -All properly connected and following AL patterns -``` - -### 5. Codebase Indexing - -Cursor indexes your entire workspace: -- AI understands your project structure -- References existing code -- Follows your patterns -- Suggests consistent code - -**Benefit for AL**: -- Knows your table structures -- Understands your naming conventions -- Follows your coding patterns -- References your existing codeunits - -### 6. Multiple AI Models - -Choose your AI model: -- GPT-4 (OpenAI) -- GPT-4 Turbo -- Claude 3.5 Sonnet (Anthropic) -- Claude 3 Opus -- (More being added) - -**Why it matters**: -- Different models have different strengths -- Switch based on task -- Try multiple for comparison -- Use best for each scenario - -## Strengths for AL Development - -### ✓ Exceptional At - -**Multi-File Projects**: -``` -Composer: "Create a complete order processing module with: -- Order Header and Line tables -- Card and List pages -- Processing codeunit -- Validation codeunit -- Test codeunit -Follow AL best practices" - -Cursor creates all files properly structured and connected. -``` - -**Large Refactoring**: -``` -"Refactor this project to: -- Add telemetry to all procedures -- Standardize error handling -- Add XML documentation -- Update to use new AL patterns" - -Cursor makes changes across all relevant files. -``` - -**Project Generation**: -``` -"Create a BC extension for customer surveys with: -- Setup tables -- Survey and response tables -- Pages for all tables -- Email sending logic -- API for mobile access" - -Cursor scaffolds entire extension. -``` - -**Codebase Understanding**: -- Knows your entire project -- References existing patterns -- Follows your conventions -- Suggests consistent code - -### ⚠ Consider Limitations - -**Learning Curve**: -- Different from VS Code workflow -- New shortcuts to learn -- Composer mode takes practice -- More features to understand - -**Cost**: -- $20/month (more than Copilot's $10) -- No free tier for individuals -- Worth it for heavy AI use - -**AL-Specific Training**: -- Not specifically trained on AL -- Requires good AL context -- May need explicit AL patterns -- Works best with symbols downloaded - -## Practical AL Examples - -### Composer Mode: Complete Feature - -**Command** (in Composer, `Cmd+I`): -``` -Create a complete customer discount management system: - -Tables: -- Discount Group Setup (code, description, percentage) -- Customer Discount Assignment (customer no, discount group) - -Pages: -- List and Card pages for each table - -Codeunit: -- Calculate discount based on customer's group -- Apply discount to sales lines -- Event subscribers for sales line validation - -Include: -- Proper AL naming conventions -- XML documentation -- Error handling -- Data classification -``` - -**Cursor Creates** (all files): - -``` -Created Files: -✓ Tables/DiscountGroupSetup.Table.al -✓ Tables/CustomerDiscountAssignment.Table.al -✓ Pages/DiscountGroupSetupList.Page.al -✓ Pages/DiscountGroupSetupCard.Page.al -✓ Pages/CustomerDiscountAssignmentList.Page.al -✓ Codeunits/DiscountManager.Codeunit.al - -All files properly structured with: -- AL best practices -- XML documentation -- Event subscribers -- Error handling -``` - -### Chat: Code Review - -**You** (in Chat): -``` -Review this codeunit for performance and best practices: - -[Paste codeunit] - -Specifically check: -- Database operations -- AL patterns -- Error handling -- Code analyzers compliance -``` - -**Cursor Analyzes**: -``` -Found several issues: - -1. Performance Issue (Lines 45-52): - Multiple FindSet without SetLoadFields - - Current: - SalesLine.SetRange("Document No.", OrderNo); - if SalesLine.FindSet() then - - Recommended: - SalesLine.SetRange("Document No.", OrderNo); - SalesLine.SetLoadFields(Quantity, "Unit Price", Amount); - if SalesLine.FindSet() then - -2. Missing Error Handling (Lines 67-70): - No try-catch for posting operation - - [Shows specific improvements] - -3. CodeCop Violations: - - Procedure names don't follow AL naming - - Missing XML documentation - - [Lists specific fixes needed] - -Would you like me to apply these fixes? -``` - -### Inline Edit (Cmd+K) - -**Select procedure, press Cmd+K**: -``` -"Add Application Insights telemetry to track execution time and parameters" -``` - -**Cursor Shows Diff**: -```al -- procedure ProcessOrder(OrderNo: Code[20]) -+ procedure ProcessOrder(OrderNo: Code[20]) - var - SalesHeader: Record "Sales Header"; -+ Telemetry: Codeunit Telemetry; -+ CustomDimensions: Dictionary of [Text, Text]; -+ StartTime: DateTime; - begin -+ StartTime := CurrentDateTime; -+ CustomDimensions.Add('OrderNo', OrderNo); -+ -+ Telemetry.LogMessage('ORDER-001', 'Processing order started', -+ Verbosity::Normal, DataClassification::SystemMetadata, -+ TelemetryScope::ExtensionPublisher, CustomDimensions); -+ - // Existing code... -+ -+ CustomDimensions.Add('Duration', Format(CurrentDateTime - StartTime)); -+ Telemetry.LogMessage('ORDER-002', 'Processing order completed', -+ Verbosity::Normal, DataClassification::SystemMetadata, -+ TelemetryScope::ExtensionPublisher, CustomDimensions); - end; -``` - -Accept with `Cmd+Enter`, reject with `Cmd+Backspace` - -### Codebase Chat - -**You**: -``` -@codebase How is customer validation currently implemented in this project? -``` - -**Cursor** (searches entire project): -``` -Found customer validation in 3 places: - -1. Codeunit 50100 "Customer Validator" (Lines 23-45): - - Main validation logic - - Checks blocked status - - Validates credit limit - -2. Table Extension 50100 "Customer Extension" (Lines 15-20): - - OnValidate trigger for custom field - -3. Event Subscriber in Codeunit 50105 (Lines 67-80): - - Subscribes to Customer OnAfterValidate - - Adds additional custom validation - -Recommendation: Consolidate validation logic into single codeunit -for better maintainability. - -[Shows code snippets from each location] -``` - -## How It Differs from Other Agents - -### vs. GitHub Copilot - -**Cursor**: -- ✓ Multiple AI models (including GPT-4) -- ✓ Composer mode (multi-file editing) -- ✓ Better codebase understanding -- ✓ More advanced features -- ✗ Higher cost ($20 vs $10) -- ✗ Different editor (learning curve) - -**Copilot**: -- ✓ Lower cost -- ✓ Standard VS Code -- ✓ Familiar workflow -- ✗ Single AI model -- ✗ No composer mode -- ✗ Less context awareness - -### vs. Claude - -**Cursor**: -- ✓ IDE integration -- ✓ Multi-file editing -- ✓ Direct code application -- ✓ Can use Claude as AI model! -- ✗ Smaller context per interaction - -**Claude**: -- ✓ Larger context window -- ✓ Better for analysis -- ✓ Web interface -- ✗ No IDE integration -- ✗ Manual copy-paste - -**Note**: Cursor can use Claude as its AI model, giving you best of both! - -### vs. VS Code + Extensions - -**Cursor**: -- ✓ Native AI integration -- ✓ Optimized for AI workflow -- ✓ Advanced features -- ✓ All VS Code extensions work -- ✗ Different app (not VS Code) -- ✗ Subscription required - -**VS Code + Copilot**: -- ✓ Standard VS Code -- ✓ Familiar environment -- ✓ Established workflow -- ✗ Less AI integration -- ✗ Fewer AI features - -## Setup & Configuration - -### Installation - -1. **Download Cursor** - - Visit [cursor.sh](https://cursor.sh) - - Download for your OS - - Install application - -2. **Sign Up** - - Create account - - Choose subscription plan - - Verify email - -3. **Configure AL Development** - - Install AL Language extension - - Import VS Code settings (optional) - - Download BC symbols - - Open your AL project - -### Migrating from VS Code - -**Import Settings**: -``` -Cursor > Settings > Import Settings from VS Code -``` - -**Your Extensions**: -- All VS Code extensions work -- Install AL Language -- Install AL Object Designer -- Install other AL tools - -**Keyboard Shortcuts**: -- Most VS Code shortcuts work -- Learn Cursor-specific shortcuts: - - `Cmd+K`: Inline edit - - `Cmd+L`: Chat - - `Cmd+I`: Composer - -### Optimizing for AL - -**Workspace Setup**: -- Keep `app.json` well-configured -- Download symbols first -- Organize files clearly -- Use descriptive naming - -**AI Model Selection**: -- GPT-4 for general coding -- Claude for analysis -- Experiment to find preference - -## Best Practices - -### Using Composer Mode - -**Clear Instructions**: -``` -Good: -"Create AL customer loyalty system with: -- Tier setup table (code, name, min points, discount %) -- Customer points table (customer no, points, tier code) -- List and card pages for both -- Codeunit to calculate and assign tiers -- Event subscriber to update on purchase -Follow AL naming conventions and add XML docs" - -Poor: -"Make a loyalty system" -``` - -**Iterative Development**: -1. Start with basic structure -2. Review generated files -3. Ask for refinements -4. Add features incrementally - -### Using Chat Effectively - -**Reference Files**: -``` -@filename.al What does this procedure do? -@codebase How is posting handled in this project? -``` - -**Specific Questions**: -``` -"Review CustomerProcessor.codeunit.al for: -- Performance issues -- AL best practices -- Missing error handling" -``` - -### Using Inline Edit - -**Targeted Changes**: -- Select specific code -- Request specific improvements -- Review diff carefully -- Accept or modify - -## Pricing - -**Pro Plan**: $20/month -- Unlimited AI completions -- Unlimited chat -- Composer mode -- All AI models -- Priority support - -**Business Plan**: Custom pricing -- Team features -- Organization management -- Usage analytics -- Enhanced security - -**Free Trial**: 14 days (typically) - -**Link**: [Cursor Pricing](https://cursor.sh/pricing) - -## Privacy & Security - -### What Gets Sent -- Code you're working on -- Files in your workspace -- Chat messages -- User interactions - -### Privacy Controls -- Can disable AI features -- Control what's indexed -- Configure model usage -- Review privacy settings - -### Best Practices -- Don't include sensitive data -- Review organization policies -- Use privacy mode when needed -- Understand data handling - -## When to Use Cursor - -### ✓ Ideal For - -- **New Projects**: Build from scratch with AI -- **Large Refactoring**: Multi-file changes -- **Learning**: Explore AL patterns -- **Rapid Development**: Build features quickly -- **Experimentation**: Try different approaches -- **Team Development**: Consistent patterns - -### ⚠ Consider Alternatives - -- **Quick edits** → GitHub Copilot faster -- **Just need VS Code** → Stick with Copilot -- **Budget constrained** → Copilot cheaper -- **Prefer standard tools** → VS Code + Copilot - -## Practical Workflows - -### Starting New Extension - -1. **Composer Mode**: Generate project structure -2. **Chat**: Refine and improve -3. **Inline Edit**: Add features -4. **Tab**: Complete code quickly - -### Refactoring Existing Code - -1. **Chat**: "Analyze this project for improvements" -2. **Review**: Understand suggestions -3. **Composer**: Apply multi-file changes -4. **Inline**: Fix specific issues - -### Learning AL Patterns - -1. **Generate example** with Composer -2. **Ask Chat** to explain -3. **Experiment** with variations -4. **Apply** to real project - -## Complementary Tools - -**Use With**: -- AL Language extension (required) -- AL analyzers for quality -- Git for version control -- BC symbols for context - -**Workflow**: -1. Cursor for development -2. AL analyzers for validation -3. Git for safety -4. Claude (via Cursor) for analysis - -## Tips for AL Development - -**Provide AL Context**: -``` -"Generate AL code for Business Central v22..." -"Follow AL naming conventions..." -"Use BC standard posting patterns..." -``` - -**Use Codebase Context**: -``` -@codebase Reference existing table structures -@CustomerTable.al Follow this naming pattern -``` - -**Leverage Models**: -- GPT-4 for code generation -- Claude for analysis -- Try both for comparison - -**Iterate**: -- Generate basic structure -- Review and refine -- Add complexity gradually -- Test thoroughly - -## Resources - -### Official -- [Cursor Website](https://cursor.sh) -- [Cursor Documentation](https://docs.cursor.sh) -- [Community Discord](https://discord.gg/cursor) - -### AL Guidelines -- [Effective Prompting](../../gettingstarted/effective-prompting) -- [Best Practices](../../gettingstarted/best-practices) -- [Getting More Examples](../../gettingmore) - -## Learning Cursor - -### Start Simple -1. Try tab completion (like Copilot) -2. Use chat for questions -3. Experiment with Cmd+K -4. Practice Composer on small tasks - -### Progress to Advanced -1. Multi-file projects with Composer -2. Codebase-wide refactoring -3. Multiple AI model usage -4. Advanced keyboard shortcuts - -### Master Features -1. Understand when to use each mode -2. Optimize prompts for better results -3. Integrate into daily workflow -4. Share patterns with team - ---- - -**Next Steps**: -- Download [Cursor](https://cursor.sh) -- Try free trial -- Compare with [other AI agents](./) -- Use alongside your existing tools - -**Questions?** Join [GitHub Discussions](https://github.com/microsoft/alguidelines/discussions) diff --git a/content/docs/agentic-coding/CommunityResources/Agents/github-copilot-agent.md b/content/docs/agentic-coding/CommunityResources/Agents/github-copilot-agent.md deleted file mode 100644 index d7f62dce..00000000 --- a/content/docs/agentic-coding/CommunityResources/Agents/github-copilot-agent.md +++ /dev/null @@ -1,552 +0,0 @@ ---- -title: "GitHub Copilot Agent" -linkTitle: "GitHub Copilot" -weight: 1 -description: > - Microsoft's AI pair programmer for real-time code completion and suggestions ---- - -## Overview - -GitHub Copilot is Microsoft's AI-powered coding assistant that provides real-time code suggestions directly in your editor. It's trained on billions of lines of public code and integrates seamlessly with Visual Studio Code. - -**Developer**: GitHub (Microsoft) -**Type**: AI Code Completion Agent -**Primary Use**: Inline code suggestions as you type -**Integration**: Native VS Code extension - -## What is GitHub Copilot? - -GitHub Copilot acts as an AI pair programmer that: -- Suggests code completions in real-time -- Generates entire functions from comments -- Provides multiple suggestion alternatives -- Understands context from your codebase -- Learns patterns specific to AL and Business Central - -### How It Works - -1. **You write**: A comment or start typing code -2. **Copilot analyzes**: Your code, open files, and context -3. **Copilot suggests**: Code completion in gray text -4. **You decide**: Accept (Tab), reject (Esc), or modify - -**Example**: -```al -// Type this comment: -/// Calculate total sales for customer including tax - -// Copilot suggests (press Tab to accept): -procedure CalculateTotalSalesWithTax(CustomerNo: Code[20]): Decimal -var - SalesLine: Record "Sales Line"; - TotalAmount: Decimal; -begin - SalesLine.SetRange("Sell-to Customer No.", CustomerNo); - SalesLine.SetRange(Type, SalesLine.Type::Item); - if SalesLine.FindSet() then - repeat - TotalAmount += SalesLine."Amount Including VAT"; - until SalesLine.Next() = 0; - exit(TotalAmount); -end; -``` - -## Key Capabilities - -### For AL Development - -**Understands AL Syntax**: -- Recognizes AL keywords and patterns -- Knows Business Central object types -- Suggests BC-appropriate code structures -- Follows AL naming conventions - -**Business Central Awareness**: -- Familiar with BC table structures -- Knows common BC APIs -- Suggests standard BC patterns -- Understands event subscriber patterns - -**Context-Aware**: -- Reads your open AL files -- Understands project structure -- References your existing code -- Adapts to your coding style - -### Code Generation Capabilities - -**From Comments**: -```al -/// Create a page extension for Customer Card that adds loyalty points field - -// Generates complete page extension structure -``` - -**From Partial Code**: -```al -procedure ValidateCustomer -// Continue typing... Copilot completes with parameters, logic -``` - -**From Patterns**: -```al -[EventSubscriber( -// Copilot suggests common event patterns -``` - -**Test Code**: -```al -[Test] -procedure TestCustomerValidation -// Generates test structure with Given-When-Then -``` - -## Strengths for AL Development - -### ✓ Excellent At - -**Boilerplate Code**: -- Table and field definitions -- Page layouts -- Standard procedures -- Variable declarations - -**Common Patterns**: -- CRUD operations -- Validation logic -- Event subscribers -- API pages - -**Code Structure**: -- Procedure signatures -- If-then-else logic -- Loop structures -- Error handling templates - -**Quick Edits**: -- Adding fields -- Extending objects -- Creating similar code -- Repetitive tasks - -### ⚠ Use With Care - -**Complex Business Logic**: -- May not understand specific requirements -- Review carefully for correctness -- Validate against business rules - -**Performance-Critical Code**: -- Check for efficient database queries -- Verify optimal AL patterns -- Profile if needed - -**Security-Sensitive Code**: -- Review authentication logic -- Validate input handling -- Check authorization patterns - -## How It Differs from Other Agents - -### vs. GitHub Copilot Chat -**Copilot (Inline)**: -- ✓ Better for code completion -- ✓ Faster for quick edits -- ✓ Non-intrusive workflow -- ✗ Limited explanation ability -- ✗ Can't answer questions - -**Copilot Chat**: -- ✓ Better for explanations -- ✓ Can answer questions -- ✓ Helps with debugging -- ✗ Slower for quick completions -- ✗ More context switching - -**Best Practice**: Use both together - -### vs. Claude -**GitHub Copilot**: -- ✓ Faster inline suggestions -- ✓ Better VS Code integration -- ✓ No context switching -- ✗ Smaller context window -- ✗ No deep analysis - -**Claude**: -- ✓ Larger context window -- ✓ Better for complex analysis -- ✓ Strong reasoning -- ✗ No inline completions -- ✗ Manual copy-paste workflow - -**Best Practice**: Copilot for coding, Claude for analysis - -### vs. Cursor -**GitHub Copilot**: -- ✓ Works in standard VS Code -- ✓ Familiar environment -- ✓ Just code completion -- ✗ Single AI model -- ✗ Limited multi-file edits - -**Cursor**: -- ✓ Multiple AI models -- ✓ Advanced features (Composer) -- ✓ Better multi-file editing -- ✗ Different editor -- ✗ Learning curve - -**Best Practice**: Copilot for standard workflow, Cursor for advanced needs - -## Practical AL Examples - -### Creating Tables - -**Input** (comment): -```al -/// Customer loyalty tier table with tier name, min points, and discount percentage -``` - -**Copilot Generates**: -```al -table 50100 "Customer Loyalty Tier" -{ - DataClassification = CustomerContent; - - fields - { - field(1; "Code"; Code[20]) - { - Caption = 'Code'; - } - field(2; "Tier Name"; Text[50]) - { - Caption = 'Tier Name'; - } - field(10; "Minimum Points"; Integer) - { - Caption = 'Minimum Points'; - MinValue = 0; - } - field(20; "Discount Percentage"; Decimal) - { - Caption = 'Discount Percentage'; - MinValue = 0; - MaxValue = 100; - DecimalPlaces = 0 : 2; - } - } - - keys - { - key(PK; "Code") - { - Clustered = true; - } - key(Points; "Minimum Points") - { - } - } -} -``` - -### Event Subscribers - -**Input** (partial code): -```al -[EventSubscriber(ObjectType::Table, Database::Customer, 'OnAfterValidateEvent', 'Credit Limit (LCY)', false, false)] -``` - -**Copilot Completes**: -```al -local procedure OnAfterValidateCreditLimit(var Rec: Record Customer; var xRec: Record Customer; CurrFieldNo: Integer) -begin - // Add your implementation - if Rec."Credit Limit (LCY)" > 100000 then - if not Confirm('Credit limit exceeds 100,000. Continue?') then - Error(''); -end; -``` - -### API Pages - -**Input**: -```al -/// API v2.0 page for items exposing no, description, unit price, inventory -``` - -**Copilot Generates**: -```al -page 50100 "Item API" -{ - APIVersion = 'v2.0'; - APIPublisher = 'yourcompany'; - APIGroup = 'inventory'; - EntityCaption = 'Item'; - EntitySetCaption = 'Items'; - PageType = API; - SourceTable = Item; - DelayedInsert = true; - ODataKeyFields = SystemId; - - layout - { - area(Content) - { - repeater(GroupName) - { - field(id; Rec.SystemId) - { - Caption = 'Id'; - Editable = false; - } - field(number; Rec."No.") - { - Caption = 'No.'; - } - field(description; Rec.Description) - { - Caption = 'Description'; - } - field(unitPrice; Rec."Unit Price") - { - Caption = 'Unit Price'; - } - field(inventory; Rec.Inventory) - { - Caption = 'Inventory'; - } - } - } - } -} -``` - -### Test Code - -**Input**: -```al -[Test] -procedure TestCustomerCreditLimitValidation -``` - -**Copilot Suggests**: -```al -[Test] -procedure TestCustomerCreditLimitValidation() -var - Customer: Record Customer; - LibrarySales: Codeunit "Library - Sales"; -begin - // [GIVEN] A customer with credit limit set - Customer.Init(); - Customer."No." := LibrarySales.CreateCustomerNo(); - Customer."Credit Limit (LCY)" := 50000; - Customer.Insert(); - - // [WHEN] Credit limit is exceeded - Customer.Validate("Credit Limit (LCY)", 150000); - - // [THEN] Appropriate validation occurs - // Add assertions here -end; -``` - -## Setup & Configuration - -### Installation - -See the detailed [GitHub Copilot Tool Page](../Tools/github-copilot) for complete installation instructions. - -**Quick Start**: -1. Install GitHub Copilot extension in VS Code -2. Sign in with GitHub account -3. Verify subscription is active -4. Start coding in AL files - -### Optimizing for AL - -**VS Code Settings**: -```json -{ - "github.copilot.enable": { - "*": true, - "al": true - }, - "editor.inlineSuggest.enabled": true, - "editor.quickSuggestions": { - "other": true, - "comments": true, - "strings": true - } -} -``` - -**Project Setup**: -- Keep `app.json` well-configured -- Download BC symbols -- Use descriptive file names -- Maintain good code organization - -## Best Practices - -### Getting Quality Suggestions - -**Write Clear Comments**: -```al -❌ // calc total -✅ /// Calculate the total sales amount for a customer including tax and discounts -``` - -**Use Meaningful Names**: -```al -❌ procedure Calc(x: Code[20]): Decimal -✅ procedure CalculateCustomerTotalSales(CustomerNo: Code[20]): Decimal -``` - -**Provide Context**: -- Keep related files open -- Use consistent naming -- Follow AL conventions -- Add XML documentation - -### Review Checklist - -Before accepting Copilot suggestions: - -- [ ] Does it match my requirements? -- [ ] Is the AL syntax correct? -- [ ] Are BC APIs used properly? -- [ ] Is it performant? -- [ ] Does it follow best practices? -- [ ] Is error handling appropriate? -- [ ] Are data types correct? - -### Workflow Integration - -**Effective Use**: -1. Write descriptive comment or start typing -2. Review Copilot's suggestion -3. Accept if good, modify if needed -4. Test the generated code -5. Refine as necessary - -**Don't**: -- Blindly accept every suggestion -- Skip testing generated code -- Ignore code analysis warnings -- Use without understanding - -## Pricing - -**Individual**: -- $10/month or $100/year -- Free for verified students -- Free for open source maintainers - -**Business**: -- $19/user/month -- Organization management -- Policy controls -- Usage insights - -**Free Trial**: Usually 30 days available - -**Link**: [GitHub Copilot Pricing](https://github.com/features/copilot) - -## Privacy & Security - -### What Gets Sent -- Code snippets from your editor -- File names and structure -- Code you're working on -- Acceptance/rejection of suggestions - -### What You Control -- Enable/disable globally -- Disable for specific files/repos -- Block suggestions from public code -- Telemetry settings - -### Best Practices -- Don't commit secrets to code -- Review organization policies -- Use Business plan for enterprise control -- Understand data retention policies - -## Troubleshooting - -### Common Issues - -**No Suggestions Appearing**: -- Check extension is enabled -- Verify subscription is active -- Ensure AL files are recognized -- Reload VS Code window - -**Poor Quality Suggestions**: -- Download BC symbols -- Add more context (comments, related files) -- Use descriptive names -- Open related AL files - -**Slow Performance**: -- Close unnecessary files -- Check internet connection -- Reduce workspace size -- Update VS Code - -## Learning Resources - -### Official Resources -- [GitHub Copilot Documentation](https://docs.github.com/copilot) -- [VS Code Extension Page](https://marketplace.visualstudio.com/items?itemName=GitHub.copilot) -- [Getting Started Guide](https://docs.github.com/copilot/gettingstarted-with-github-copilot) - -### AL Guidelines Resources -- [Setup Guide](../../gettingstarted/setup) -- [Effective Prompting](../../gettingstarted/effective-prompting) -- [Best Practices](../../gettingstarted/best-practices) -- [Code Review Examples](../../gettingmore/code-review) - -## When to Use GitHub Copilot - -### ✓ Ideal For - -- Daily AL development -- Quick code generation -- Learning AL patterns -- Reducing boilerplate -- Standard BC implementations -- Exploring APIs - -### ⚠ Consider Alternatives - -- **Complex analysis** → Use Claude or Copilot Chat -- **Multi-file refactoring** → Use Cursor -- **Learning deep concepts** → Use Copilot Chat or Claude -- **Architecture decisions** → Human expertise required - -## Complementary Tools - -**Use With**: -- [GitHub Copilot Chat](github-copilot-chat-agent) - For explanations and debugging -- [AL Language Extension](../Tools/al-language) - For AL support -- [AL Code Analyzers](../Tools/al-codecop) - For quality checks - -**Workflow**: -1. Copilot generates code -2. AL analyzers check quality -3. Copilot Chat explains complex parts -4. You review and test - ---- - -**Next Steps**: -- Install and try [GitHub Copilot](../Tools/github-copilot) -- Learn about [Copilot Chat](github-copilot-chat-agent) for complementary features -- Compare with other [AI Agents](./) - -**Questions?** Join [GitHub Discussions](https://github.com/microsoft/alguidelines/discussions) diff --git a/content/docs/agentic-coding/CommunityResources/Agents/github-copilot-chat-agent.md b/content/docs/agentic-coding/CommunityResources/Agents/github-copilot-chat-agent.md deleted file mode 100644 index 378bbd5f..00000000 --- a/content/docs/agentic-coding/CommunityResources/Agents/github-copilot-chat-agent.md +++ /dev/null @@ -1,627 +0,0 @@ ---- -title: "GitHub Copilot Chat Agent" -linkTitle: "Copilot Chat" -weight: 2 -description: > - Conversational AI assistant for code explanations, debugging, and interactive development ---- - -## Overview - -GitHub Copilot Chat is a conversational AI assistant integrated into Visual Studio Code that allows you to ask questions, get explanations, and receive guidance through natural language interaction. - -**Developer**: GitHub (Microsoft) -**Type**: Conversational AI Agent -**Primary Use**: Interactive code assistance, explanations, and debugging -**Integration**: Native VS Code extension - -## What is GitHub Copilot Chat? - -GitHub Copilot Chat provides an interactive AI assistant that: -- Answers questions about code and AL development -- Explains complex code segments -- Helps debug issues -- Suggests refactoring approaches -- Provides learning and guidance -- Generates code based on detailed requirements - -### How It Works - -1. **You ask**: Questions in natural language via chat panel or inline -2. **Chat analyzes**: Your code, context, and question -3. **Chat responds**: With explanations, code, or suggestions -4. **You interact**: Ask follow-ups, refine, or apply suggestions - -**Example Interaction**: -``` -You: Explain how this procedure validates customer credit limits - -Chat: This procedure validates customer credit limits by: -1. Retrieving the customer record -2. Calculating current outstanding amount -3. Comparing against credit limit -4. Raising an error if exceeded - -The validation ensures customers cannot exceed their credit limits -before creating new sales orders... -``` - -## Key Capabilities - -### For AL Development - -**Code Explanation**: -- Describes what AL code does -- Explains Business Central concepts -- Breaks down complex procedures -- Clarifies AL syntax and patterns - -**Interactive Learning**: -- Teaches AL patterns -- Explains BC APIs -- Provides examples -- Answers "how do I..." questions - -**Debugging Assistance**: -- Helps diagnose errors -- Suggests fixes -- Explains error messages -- Proposes debugging strategies - -**Code Generation**: -- Creates code from detailed descriptions -- Generates test cases -- Produces documentation -- Builds complex structures - -## How to Use Copilot Chat - -### Chat Panel - -**Open Chat**: -- Press `Ctrl+Shift+I` (Windows/Linux) -- Press `Cmd+Shift+I` (Mac) -- Click chat icon in sidebar - -**Chat Interface**: -``` -┌─────────────────────────────┐ -│ GitHub Copilot Chat │ -├─────────────────────────────┤ -│ Your conversation history │ -│ │ -│ You: How do I... │ -│ Copilot: You can... │ -│ │ -├─────────────────────────────┤ -│ Type your question... [>]│ -└─────────────────────────────┘ -``` - -### Inline Chat - -**Open Inline**: -- Press `Ctrl+I` in editor -- Chat appears next to your code -- Ask quick questions -- Get instant suggestions - -**Use Cases**: -- Quick refactoring -- Explain selected code -- Fix errors in place -- Generate code snippets - -### Slash Commands - -Quick access to common tasks: - -| Command | Purpose | Example | -|---------|---------|---------| -| `/explain` | Explain selected code | `/explain this validation logic` | -| `/fix` | Suggest fixes | `/fix the error in this procedure` | -| `/tests` | Generate tests | `/tests for this codeunit` | -| `/help` | Show commands | `/help` | -| `/clear` | Clear chat | `/clear` | - -## Strengths for AL Development - -### ✓ Excellent At - -**Learning & Understanding**: -- Explaining AL concepts -- Teaching BC patterns -- Clarifying syntax -- Answering "why" questions - -**Debugging Help**: -- Diagnosing errors -- Suggesting fixes -- Explaining error messages -- Proposing test cases - -**Code Exploration**: -- Understanding legacy code -- Identifying patterns -- Finding issues -- Suggesting improvements - -**Documentation**: -- Generating XML comments -- Creating README files -- Writing user guides -- Explaining features - -### ⚠ Use With Care - -**Generated Code**: -- Always review for correctness -- Verify BC compatibility -- Test thoroughly -- Adapt to your standards - -**Complex Business Logic**: -- May not understand specific requirements -- Review carefully -- Validate against rules - -**Performance Advice**: -- General suggestions may not fit your scenario -- Profile and test -- Verify with BC best practices - -## AL-Specific Examples - -### Code Explanation - -**You**: -``` -Explain this procedure and when I should use it - -[Select and paste this code]: -procedure CheckCreditLimit(var SalesHeader: Record "Sales Header") -var - Customer: Record Customer; - OutstandingAmount: Decimal; -begin - Customer.Get(SalesHeader."Sell-to Customer No."); - Customer.CalcFields("Balance (LCY)"); - OutstandingAmount := Customer."Balance (LCY)" + SalesHeader."Amount Including VAT"; - - if OutstandingAmount > Customer."Credit Limit (LCY)" then - Error('Customer %1 would exceed credit limit. Outstanding: %2, Limit: %3', - Customer."No.", OutstandingAmount, Customer."Credit Limit (LCY)"); -end; -``` - -**Chat Explains**: -``` -This procedure validates customer credit limits before allowing sales orders: - -Purpose: -- Prevents customers from exceeding their credit limits -- Calculates total outstanding amount (existing balance + new order) -- Raises an error if limit would be exceeded - -When to use: -- Before posting sales orders -- In OnValidate triggers for sales headers -- As part of order validation workflow - -Key points: -- Uses CalcFields to get current customer balance -- Adds new order amount to existing balance -- Compares total against credit limit -- Provides clear error message with details - -Best practice considerations: -- Consider making error message translatable -- Might want to allow override with permission -- Could log credit limit violations for reporting -``` - -### Generating Complex Code - -**You**: -``` -Create a codeunit that processes sales orders in batch. - -Requirements: -- Accept list of sales order numbers -- Validate each order (check credit, inventory) -- Post valid orders -- Collect and return errors for failed orders -- Log to telemetry -- Show progress dialog - -Use proper error handling and AL best practices. -``` - -**Chat Generates**: -```al -codeunit 50100 "Batch Sales Order Processor" -{ - /// - /// Processes multiple sales orders in batch with validation and error handling - /// - procedure ProcessOrders(OrderNumbers: List of [Code[20]]): List of [Text] - var - SalesHeader: Record "Sales Header"; - Errors: List of [Text]; - Dialog: Dialog; - Counter: Integer; - TotalCount: Integer; - begin - TotalCount := OrderNumbers.Count; - Dialog.Open('Processing orders...\Order #1######### of #2#########'); - - foreach OrderNo in OrderNumbers do begin - Counter += 1; - Dialog.Update(1, Counter); - Dialog.Update(2, TotalCount); - - if not ProcessSingleOrder(OrderNo, Errors) then - LogProcessingError(OrderNo, Errors); - end; - - Dialog.Close(); - LogBatchCompletion(TotalCount, Counter - Errors.Count, Errors.Count); - exit(Errors); - end; - - local procedure ProcessSingleOrder(OrderNo: Code[20]; var Errors: List of [Text]): Boolean - var - SalesHeader: Record "Sales Header"; - SalesPost: Codeunit "Sales-Post"; - begin - if not SalesHeader.Get(SalesHeader."Document Type"::Order, OrderNo) then begin - Errors.Add(StrSubstNo('Order %1 not found', OrderNo)); - exit(false); - end; - - if not ValidateOrder(SalesHeader, Errors) then - exit(false); - - if not SalesPost.Run(SalesHeader) then begin - Errors.Add(StrSubstNo('Order %1: %2', OrderNo, GetLastErrorText())); - exit(false); - end; - - exit(true); - end; - - // Additional helper procedures... -end; -``` - -### Debugging Assistance - -**You**: -``` -I'm getting error "The field Customer No. must have a value" -when trying to post a sales order. Here's my code: - -[Paste problematic code] - -What's wrong? -``` - -**Chat Analyzes**: -``` -The error occurs because the Customer No. field is empty when posting. - -Looking at your code, the issue is on line 15: - SalesHeader."Sell-to Customer No." := ''; - -This explicitly sets the customer number to empty before posting. - -Likely causes: -1. Customer lookup/validation failed earlier -2. Field being cleared unintentionally -3. Missing validation before assignment - -Recommended fixes: - -1. Remove the line setting it to empty -2. Add validation: - if Customer.Get(CustomerNo) then - SalesHeader.Validate("Sell-to Customer No.", CustomerNo) - else - Error('Customer %1 does not exist', CustomerNo); - -3. Add defensive check before posting: - if SalesHeader."Sell-to Customer No." = '' then - Error('Customer must be specified before posting'); - -Would you like me to show the corrected version of your code? -``` - -### Learning AL Patterns - -**You**: -``` -What's the standard AL pattern for implementing a document posting routine? -``` - -**Chat Explains**: -``` -Standard AL document posting routines follow this pattern: - -1. Structure: - - Main posting codeunit - - Optional posting preview codeunit - - Optional batch posting codeunit - -2. Main Posting Codeunit Pattern: - -codeunit 50100 "Document-Post" -{ - TableNo = "Your Document Header"; - - trigger OnRun() - begin - PostDocument(Rec); - end; - - procedure PostDocument(var DocHeader: Record "Your Document Header") - var - DocLine: Record "Your Document Line"; - begin - // 1. Pre-posting checks - CheckDocumentIsReadyToPost(DocHeader); - - // 2. Run events (OnBeforePost) - OnBeforePostDocument(DocHeader); - - // 3. Lock tables - DocHeader.LockTable(); - - // 4. Post with transaction - PostDocumentLines(DocHeader); - FinalizePosting(DocHeader); - - // 5. Run events (OnAfterPost) - OnAfterPostDocument(DocHeader); - end; - - // Additional procedures... - - // Events for extensibility - [IntegrationEvent(false, false)] - local procedure OnBeforePostDocument(var DocHeader: Record "Your Document Header") - begin - end; -} - -Key principles: -- Use transactions (implicit in OnRun trigger) -- Lock records being modified -- Provide integration events -- Validate before posting -- Handle errors gracefully -- Use Codeunit.Run for error handling - -Would you like examples of specific parts? -``` - -## How It Differs from Other Agents - -### vs. GitHub Copilot (Inline) - -**Copilot Chat**: -- ✓ Better for explanations -- ✓ Can answer questions -- ✓ Helps with debugging -- ✓ Teaches concepts -- ✗ Slower for quick edits - -**Copilot (Inline)**: -- ✓ Faster completions -- ✓ Less disruptive -- ✓ Great for quick coding -- ✗ No explanations -- ✗ Can't answer questions - -**Use Together**: Inline for coding, Chat for understanding - -### vs. Claude - -**Copilot Chat**: -- ✓ Native VS Code integration -- ✓ Automatic code context -- ✓ No copy-paste needed -- ✗ Smaller context window -- ✗ Less analytical depth - -**Claude**: -- ✓ Larger context window -- ✓ Deeper analysis -- ✓ Better reasoning -- ✗ Manual workflow -- ✗ No editor integration - -**Use Cases**: Chat for daily work, Claude for deep analysis - -### vs. Cursor - -**Copilot Chat**: -- ✓ Standard VS Code -- ✓ Familiar environment -- ✓ Single purpose -- ✗ One AI model -- ✗ Limited features - -**Cursor**: -- ✓ Multiple AI models -- ✓ Composer mode -- ✓ Advanced features -- ✗ Different editor -- ✗ Higher learning curve - -**Use Cases**: Chat for standard workflow, Cursor for advanced needs - -## Best Practices - -### Asking Good Questions - -**Be Specific**: -``` -❌ "Explain this" -✅ "Explain how this procedure handles inventory updates - and why it uses a transaction" -``` - -**Provide Context**: -``` -❌ "How do I validate?" -✅ "How do I validate customer credit limits in AL before - posting a sales order? Show me the BC standard pattern." -``` - -**Break Down Complex Questions**: -``` -Instead of: -"Build complete order management system" - -Try: -1. "Show me pattern for order validation" -2. "Now add posting logic" -3. "Add error handling" -4. "Add telemetry" -``` - -### Using Responses Effectively - -**Review Code**: -- Understand what it does -- Verify BC compatibility -- Check best practices -- Test thoroughly - -**Learn from Explanations**: -- Read thoroughly -- Try examples yourself -- Ask follow-up questions -- Apply to your own code - -**Iterate**: -- Start with basic request -- Refine based on response -- Add requirements gradually -- Build understanding - -## Setup & Configuration - -### Installation - -See [GitHub Copilot Chat Tool Page](../Tools/github-copilot-chat) for detailed setup. - -**Quick Start**: -1. Install GitHub Copilot Chat extension -2. Verify Copilot subscription -3. Open chat panel (`Ctrl+Shift+I`) -4. Start asking questions - -### Optimizing for AL - -**Provide Context**: -- Keep relevant AL files open -- Reference BC objects specifically -- Mention AL version when relevant -- Include app.json configuration - -## Pricing - -**Included with GitHub Copilot**: -- Individual: $10/month or $100/year -- Business: $19/user/month -- Free for students and OSS maintainers - -No separate charge - comes with Copilot subscription. - -## Privacy & Security - -### What Gets Sent -- Your chat messages -- Selected code snippets -- Context from open files -- Workspace information (limited) - -### Best Practices -- Don't paste sensitive data -- Avoid customer information -- Review organization policies -- Use business plan for enterprise controls - -## When to Use Copilot Chat - -### ✓ Ideal For - -- Learning AL and BC concepts -- Understanding existing code -- Debugging issues -- Getting explanations -- Generating complex code -- Exploring patterns -- Documentation creation - -### ⚠ Consider Alternatives - -- **Quick completions** → Use GitHub Copilot (inline) -- **Very large context** → Use Claude -- **Multi-file refactoring** → Use Cursor -- **Critical decisions** → Consult humans - -## Practical Workflow - -**Daily Development**: -1. Write code with Copilot inline suggestions -2. Use Chat to explain complex parts -3. Ask Chat for debugging help when stuck -4. Generate tests with Chat -5. Create documentation with Chat - -**Learning**: -1. Ask Chat about AL patterns -2. Request examples -3. Get explanations of BC concepts -4. Explore APIs and features - -**Code Review**: -1. Select code section -2. Ask Chat to review -3. Get suggestions for improvements -4. Learn better patterns - -## Resources - -### Official Documentation -- [Copilot Chat Docs](https://docs.github.com/copilot/github-copilot-chat) -- [VS Code Extension](https://marketplace.visualstudio.com/items?itemName=GitHub.copilot-chat) - -### AL Guidelines Resources -- [Effective Prompting](../../gettingstarted/effective-prompting) -- [Best Practices](../../gettingstarted/best-practices) -- [Getting More Examples](../../gettingmore) - -## Complementary Tools - -**Use With**: -- [GitHub Copilot](github-copilot-agent) - For inline completions -- [AL Language Extension](../Tools/al-language) - For AL support -- [AL Code Analyzers](../Tools/al-codecop) - For quality - -**Workflow**: -1. Copilot generates code quickly -2. Chat explains what it does -3. You understand and refine -4. Analyzers validate quality - ---- - -**Next Steps**: -- Install [GitHub Copilot Chat](../Tools/github-copilot-chat) -- Try with [GitHub Copilot](github-copilot-agent) -- Compare with other [AI Agents](./) - -**Questions?** Join [GitHub Discussions](https://github.com/microsoft/alguidelines/discussions) diff --git a/content/docs/agentic-coding/CommunityResources/Tools/_index.md b/content/docs/agentic-coding/CommunityResources/Tools/_index.md deleted file mode 100644 index 8bddcda3..00000000 --- a/content/docs/agentic-coding/CommunityResources/Tools/_index.md +++ /dev/null @@ -1,151 +0,0 @@ ---- -title: "MCP Servers" -linkTitle: "Tools" -weight: 3 -description: > - Model Context Protocol (MCP) servers that enhance AI-assisted AL development ---- - -## Overview - -This section catalogs Model Context Protocol (MCP) servers designed to enhance AI coding assistants for Business Central development. MCP is an open standard that enables AI assistants to connect to external tools, databases, and knowledge sources, making them significantly more powerful for AL development workflows. - -**What is MCP?** -- Standard protocol for connecting AI assistants to external capabilities -- STDIO transport for local execution -- Tool-based architecture for exposing features to AI assistants -- Supported by Claude Desktop, GitHub Copilot, Cursor, VS Code extensions, and more - -Each MCP server below has its own dedicated page with detailed information, setup instructions, and integration guidance. - ---- - -## AL & Business Central MCP Servers - -### [BC Code Intelligence MCP](bc-code-intelligence-mcp.md) - -**Key Features**: -- 14 specialist AI personas for BC development (Sam Coder, Dean Debug, Alex Architect, etc.) -- 20+ MCP tools for knowledge discovery, code analysis, and workflow orchestration -- Seamless specialist handoffs with context preservation -- Integration with GitHub Copilot, Claude Desktop, VS Code - ---- - -### [AL Dependency MCP Server](al-dependency-mcp-server.md) - -**Key Features**: -- Exposes AL workspace compiled symbols (.app files) to AI assistants -- 6 token-optimized tools for symbol search and dependency analysis -- O(1) lookups with optimized indexing for 50MB+ symbol files -- Auto-discovers .alpackages directories - ---- - -### [Serena MCP](serena-mcp.md) - -**Key Features**: -- Multi-language LSP-based coding assistant with AL support -- AL Language Server integration via VS Code AL extension -- Symbolic operations (find references, definitions, document symbols) -- Supports 20+ languages including AL, Python, TypeScript, Rust, Go - ---- - -### [AL Object ID Ninja MCP](al-objid-mcp-server.md) - -**Key Features**: -- AL object ID collision prevention and management -- Two modes: LITE (individual developers) and STANDARD (teams) -- Backend integration for shared ID pools and team collaboration -- Automatic app identification via Git integration - ---- - -## DevOps & Productivity MCP Servers - -### [Azure DevOps MCP](azure-devops-mcp.md) - -**Key Features**: -- Official Microsoft MCP server for Azure DevOps integration -- 50+ tools covering work items, repos, pipelines, wiki, and advanced security -- Domain filtering to manage tool count and focus -- Multiple authentication options (interactive, azcli, env) - ---- - -### [Clockify MCP](clockify-mcp.md) - -**Key Features**: -- Comprehensive Clockify time tracking integration -- 33 tools for workspace, client, project, task, tag, and time entry management -- Timer start/stop functionality and bulk operations -- Full CRUD operations for all Clockify entities - ---- - -### [BC Telemetry Buddy (Waldo)](waldo-bctelemetrybuddy.md) - -**Key Features**: -- Simple helpers for collecting Business Central telemetry -- Forward telemetry events to a custom endpoint for analysis -- Lightweight and easy to add to development workflows - ---- - -### [NAB AL Tools MCP](nab-al-tools-mcp.md) - -**Key Features**: -- XLIFF/XLF translation tooling exposed as MCP tools -- Rich validation and annotations (Zod schemas) -- Multiple invocation options (npx, global, local) - ---- - -## Contributing Tools - -**Created a tool for AI-assisted AL development?** - -Share it with the community! - -**Submission Guidelines**: -- Must be useful for AL/BC development -- Should enhance AI-assisted workflows -- Open source preferred -- Well documented - -**How to Submit**: See [Contributing](../../../contributing) section - ---- - -## Tool Safety & Privacy - -### Privacy Considerations - -**What Gets Shared**: -- Code in your workspace (with AI assistants) -- File names and structure -- Your prompts and questions - -**Best Practices**: -- Review extension permissions -- Understand data handling -- Use organization-approved tools -- Don't include sensitive data in code - -### Security - -**Verify Extensions**: -- Check publisher reputation -- Read reviews -- Review permissions requested -- Keep extensions updated - ---- - -## Related Resources - -- [Setup Guide](../../gettingstarted/setup) - Environment configuration -- [Blog Posts](../articles) - Tool reviews and comparisons -- [Videos](../videos) - Tool demonstrations -- [GitHub Discussions](https://github.com/microsoft/alguidelines/discussions) - Tool recommendations and support diff --git a/content/docs/agentic-coding/CommunityResources/Tools/al-dependency-mcp-server.md b/content/docs/agentic-coding/CommunityResources/Tools/al-dependency-mcp-server.md deleted file mode 100644 index 7f467316..00000000 --- a/content/docs/agentic-coding/CommunityResources/Tools/al-dependency-mcp-server.md +++ /dev/null @@ -1,45 +0,0 @@ ---- -title: "AL Dependency MCP Server" -linkTitle: "AL Dependency MCP" -weight: 2 -description: > - Expose AL workspace compiled symbols to AI assistants for intelligent code navigation and dependency analysis ---- - -## Overview - -The AL Dependency MCP Server exposes all compiled AL package symbols (.app files) to AI assistants, enabling them to understand dependencies, navigate code, and provide accurate suggestions based on Microsoft base app and other extension symbols. - -**Created by**: [Stefan Maron](https://github.com/StefanMaron) - ---- - -## Key Features - -**Symbol Database**: -- O(1) lookup performance via optimized indices -- Streaming JSON parser for large symbol files (handles 50MB+ Microsoft base app) -- Sub-100ms query responses -- <500MB memory usage even with massive symbol databases - -**6 Token-Optimized Tools**: -- `al_search_objects` - Search for AL objects across all packages -- `al_get_object_definition` - Get full object definition with all members -- `al_get_object_summary` - Get token-optimized summary (96% smaller) -- `al_get_object_members` - Get object members without full definition -- `al_packages` - List all available AL packages -- `al_get_stats` - Get database statistics and diagnostics - -**Auto-Discovery**: -- Automatically finds .alpackages directories in your workspace -- Detects package changes in real-time -- Supports multiple package sources - -**Requirements**: Node.js 18+, .NET SDK 8.0+ (for development) - ---- - -## Links - -- **GitHub**: https://github.com/StefanMaron/AL-Dependency-MCP-Server -- **npm Package**: https://www.npmjs.com/package/al-mcp-server diff --git a/content/docs/agentic-coding/CommunityResources/Tools/al-development-collection.md b/content/docs/agentic-coding/CommunityResources/Tools/al-development-collection.md deleted file mode 100644 index 7312b533..00000000 --- a/content/docs/agentic-coding/CommunityResources/Tools/al-development-collection.md +++ /dev/null @@ -1,49 +0,0 @@ ---- -title: "AL Development Collection" -linkTitle: "AL Development Collection" -weight: 1 -description: > - AI Native AL Development toolkit with 37 Agent Primitives for systematic Business Central development ---- - -## Overview - -The AL Development Collection provides a complete AI-native development toolkit for Microsoft Dynamics 365 Business Central. Instead of ad-hoc AI usage, you get systematic engineering through 37 Agent Primitives organized across 3 framework layers implementing the AI Native-Instructions Architecture. - -**Created by**: [Javier Armesto](https://github.com/javiarmesto) - ---- - -## Key Features - -**6 Role-Based Specialist Agents**: -- al-architect 🏗️ (solution design), al-developer 💻 (implementation), al-debugger 🐛 (troubleshooting) -- al-tester ✅ (TDD/quality), al-api 🌐 (API development), al-copilot 🤖 (AI features) - -**4 Orchestra System Agents** (Multi-agent TDD): -- al-conductor 🎭 (orchestration), al-planning-subagent 🔍 (research) -- al-implement-subagent 💻 (TDD implementation), al-review-subagent ✅ (validation) - -**9 Auto-Applied Instructions**: -- Code style, naming conventions, performance patterns -- Error handling, events, testing standards -- Context-aware loading via `applyTo` patterns - -**18 Agentic Workflows**: -- Environment setup (al-initialize, al-build) -- Development (al-events, al-pages, al-permissions) -- Analysis (al-diagnose, al-performance, al-migrate) -- Copilot features (al-copilot-capability, al-copilot-promptdialog, al-copilot-test) - -**Smart Complexity Routing (Experimental)**: -- 🟢 LOW → al-developer (direct implementation) -- 🟡 MEDIUM → al-conductor (TDD orchestration) -- 🔴 HIGH → al-architect → al-conductor (full design + TDD) - ---- - -## Links - -- **GitHub**: https://github.com/javiarmesto/AL-Development-Collection-for-GitHub-Copilot -- **Quick Start**: https://github.com/javiarmesto/AL-Development-Collection-for-GitHub-Copilot/blob/main/QUICK-START.md -- **Complete Development Flow**: https://github.com/javiarmesto/AL-Development-Collection-for-GitHub-Copilot/blob/main/docs/workflows/complete-development-flow.md diff --git a/content/docs/agentic-coding/CommunityResources/Tools/al-objid-mcp-server.md b/content/docs/agentic-coding/CommunityResources/Tools/al-objid-mcp-server.md deleted file mode 100644 index a520f6ed..00000000 --- a/content/docs/agentic-coding/CommunityResources/Tools/al-objid-mcp-server.md +++ /dev/null @@ -1,44 +0,0 @@ ---- -title: "AL Object ID Ninja MCP" -linkTitle: "AL Object ID Ninja" -weight: 5 -description: > - AL object ID collision prevention and management for Business Central development teams ---- - -## Overview - -The AL Object ID Ninja MCP Server manages AL object IDs to prevent collisions in Business Central development. It provides two modes: LITE for individual developers and STANDARD for teams with shared ID pools and backend integration. - -**Created by**: [SShadowS (Torben Leth)](https://github.com/SShadowS) - ---- - -## Key Features - -**Two Modes**: -- **LITE Mode** (4 tools) - Individual developers: authorization, config, allocate_id, analyze_workspace -- **STANDARD Mode** (8 tools) - Teams: adds pool management, consumption reports, backend sync, activity logs - -**Collision Prevention**: -- Automatic ID conflict detection -- Smart ID allocation with preview/reserve/reclaim -- Git integration for automatic app identification -- Real-time workspace analysis - -**Team Collaboration** (STANDARD): -- Shared ID pools across team members -- Backend integration with AL Object ID Ninja service -- Consumption tracking and reporting -- Audit trail with activity logs - -**Configuration**: -Set `MCP_MODE` environment variable to `lite` or `standard` (default: lite) - ---- - -## Links - -- **GitHub**: https://github.com/SShadowS/al-objid-mcp-server -- **npm Package**: https://www.npmjs.com/package/@sshadows/objid-mcp -- **Backend Service**: AL Object ID Ninja (vjekocom-alext-weu.azurewebsites.net) diff --git a/content/docs/agentic-coding/CommunityResources/Tools/azure-devops-mcp.md b/content/docs/agentic-coding/CommunityResources/Tools/azure-devops-mcp.md deleted file mode 100644 index 1ab55260..00000000 --- a/content/docs/agentic-coding/CommunityResources/Tools/azure-devops-mcp.md +++ /dev/null @@ -1,46 +0,0 @@ ---- -title: "Azure DevOps MCP" -linkTitle: "Azure DevOps MCP" -weight: 4 -description: > - Official Microsoft MCP server for comprehensive Azure DevOps integration with AI assistants ---- - -## Overview - -The Azure DevOps MCP Server is Microsoft's official Model Context Protocol implementation for Azure DevOps, providing AI assistants with comprehensive access to work items, repositories, pipelines, wiki, search, and advanced security features. - -**Publisher**: [Microsoft](https://github.com/microsoft) - ---- - -## Key Features - -**50+ Tools Across All Azure DevOps Services**: -- Core (projects, teams, identities) -- Work (iterations, capacity) -- Work Items (CRUD, queries, comments, linking) -- Repositories (repos, branches, PRs, commits, file contents) -- Pipelines (builds, logs, artifacts, triggering) -- Wiki (pages, content, CRUD operations) -- Search (code, wiki, work items) -- Advanced Security (alerts, findings) - -**Domain Filtering**: -- Enable only needed domains to reduce tool count -- Example: `-d work-items -d repositories` for focused workflows - -**Multiple Authentication Options**: -- Interactive (default) - Browser-based OAuth -- Azure CLI - Use existing `az login` session -- Environment variable - PAT via `AZURE_DEVOPS_PAT` - - ---- - -## Links - -- **GitHub**: https://github.com/microsoft/azure-devops-mcp -- **npm Package**: https://www.npmjs.com/package/@azure-devops/mcp -- **Getting Started**: https://github.com/microsoft/azure-devops-mcp/blob/main/docs/GETTINGSTARTED.md - diff --git a/content/docs/agentic-coding/CommunityResources/Tools/bc-code-intelligence-mcp.md b/content/docs/agentic-coding/CommunityResources/Tools/bc-code-intelligence-mcp.md deleted file mode 100644 index 9597cc61..00000000 --- a/content/docs/agentic-coding/CommunityResources/Tools/bc-code-intelligence-mcp.md +++ /dev/null @@ -1,43 +0,0 @@ ---- -title: "BC Code Intelligence MCP" -linkTitle: "BC Code Intelligence" -weight: 1 -description: > - Business Central knowledge base with 14 specialist AI personas for comprehensive development guidance ---- - -## Overview - -The BC Code Intelligence MCP Server provides atomic Business Central knowledge topics through an innovative specialist system. Instead of a single generic AI, you interact with 14 domain-expert personas who provide focused, expert guidance for specific BC development areas. - -**Created by**: [Jeremy Vyska](https://github.com/JeremyVyska) - ---- - -## Key Features - -**14 BC Domain Specialists**: -- Sam Coder (AL coding), Dean Debug (troubleshooting), Alex Architect (design) -- Casey Cloud (cloud/SaaS), Morgan Modern (DevOps), Taylor Test (testing) -- Quinn Quality (code quality), Riley Report (reporting), Parker Performance (optimization) -- Jordan Journey (learning), Blake Business (business logic), Skyler Security (security) -- Drew Data (data modeling), Finley Flow (workflow/UX) - -**Smart Routing**: -- Automatic routing to the right specialist based on your question -- Multi-specialist collaboration for complex questions -- Seamless context handoffs between specialists - -**20+ MCP Tools**: -- Knowledge discovery (search topics, get content) -- Specialist management (routing, handoffs) -- Code analysis (reviews, patterns, architecture) -- Workflow orchestration - ---- - -## Links - -- **GitHub**: https://github.com/JeremyVyska/bc-code-intelligence-mcp -- **Knowledge Base**: https://github.com/JeremyVyska/bc-code-intelligence -- **npm Package**: https://www.npmjs.com/package/bc-code-intelligence-mcp diff --git a/content/docs/agentic-coding/CommunityResources/Tools/clockify-mcp.md b/content/docs/agentic-coding/CommunityResources/Tools/clockify-mcp.md deleted file mode 100644 index dd805816..00000000 --- a/content/docs/agentic-coding/CommunityResources/Tools/clockify-mcp.md +++ /dev/null @@ -1,44 +0,0 @@ ---- -title: "Clockify MCP" -linkTitle: "Clockify MCP" -weight: 6 -description: > - Comprehensive Clockify time tracking integration for project management and invoicing ---- - -## Overview - -The Clockify MCP Server provides comprehensive integration with Clockify time tracking and project management. It exposes 33 tools for managing workspaces, clients, projects, tasks, tags, time entries, and timers through AI assistants. - -**Created by**: [Jeremy Vyska](https://github.com/JeremyVyska) - ---- - -## Key Features - -**33 Tools for Complete Clockify Management**: -- Workspace Management - list, get, update workspaces -- Client Management - full CRUD operations -- Project Management - full CRUD operations -- Task Management - full CRUD operations -- Tag Management - full CRUD operations -- Time Entry Management - CRUD plus bulk operations -- Timer Operations - start, stop, get active timer - -**Enhanced from Reference Implementation**: -- Full CRUD operations (create, read, update, delete) -- Bulk operations for time entries -- Comprehensive filtering and pagination -- Timer start/stop functionality - -**Requirements**: -- Node.js 20+ -- CLOCKIFY_API_KEY from Clockify profile settings - ---- - -## Links - -- **GitHub**: https://github.com/JeremyVyska/clockify-mcp -- **Clockify API**: https://clockify.me/developers-api -- **Get API Key**: https://app.clockify.me/user/settings (Profile Settings → API) diff --git a/content/docs/agentic-coding/CommunityResources/Tools/nab-al-tools-mcp.md b/content/docs/agentic-coding/CommunityResources/Tools/nab-al-tools-mcp.md deleted file mode 100644 index a80c09c6..00000000 --- a/content/docs/agentic-coding/CommunityResources/Tools/nab-al-tools-mcp.md +++ /dev/null @@ -1,79 +0,0 @@ ---- -title: "NAB AL Tools MCP" -linkTitle: "NAB AL Tools" -weight: 8 -description: > - XLIFF translation and localization tools exposed as an MCP server for AL projects. ---- - -## Overview - -The NAB AL Tools MCP Server exposes XLIFF translation management capabilities (the same core features used by the NAB AL Tools VS Code extension) as a standalone MCP server. It lets AI assistants and other MCP-compatible clients inspect, create, refresh and save XLF files used for AL localization workflows. - -**Publisher**: [nabsolutions / Johannes Wikman](https://github.com/jwikman) - ---- - -## Key Features - -- Comprehensive XLF/XLIFF tooling: refresh, create language XLFs, search by keyword, get untranslated texts, and more -- Proper MCP annotations and Zod input validation for robust error handling -- Configurable: works with npx, global install, or local project install -- Supports pagination, batch saves and performant parsing for large translation files -- Designed to work locally (openWorldHint: false) and respects workspace boundaries - ---- - -## Installation & Usage - -Recommended via npx (no install required): - -```bash -npx -y @nabsolutions/nab-al-tools-mcp -``` - -Or install globally: - -```bash -npm install -g @nabsolutions/nab-al-tools-mcp -``` - -MCP client example (npx): - -```json -{ - "mcpServers": { - "nab-al-tools": { - "command": "npx", - "args": ["-y", "@nabsolutions/nab-al-tools-mcp"] - } - } -} -``` - ---- - -## Notable Tools (summary) - -- `refreshXlf` — synchronize generated .g.xlf with target XLF -- `getTextsToTranslate` — list untranslated units with pagination -- `getTranslatedTextsMap` — fetch existing translations grouped by source -- `getTranslatedTextsByState` — filter translations by state (needs-review, translated, final) -- `saveTranslatedTexts` — batch save translated units (up to configured limits) -- `createLanguageXlf` — generate new language XLFs (can optionally match base app translations) -- `getTextsByKeyword` — search XLF content by keyword or regex -- `getGlossaryTerms` — return builtin BC glossary pairs for consistent terminology - ---- - -## Requirements - -- Node.js >= 20 -- npm (if installing) - ---- - -## Links - -- **GitHub**: https://github.com/jwikman/nab-al-tools -- **MCP readme (source)**: https://github.com/jwikman/nab-al-tools/blob/main/extension/MCP_SERVER.md diff --git a/content/docs/agentic-coding/CommunityResources/Tools/serena-mcp.md b/content/docs/agentic-coding/CommunityResources/Tools/serena-mcp.md deleted file mode 100644 index 2ead1ed0..00000000 --- a/content/docs/agentic-coding/CommunityResources/Tools/serena-mcp.md +++ /dev/null @@ -1,46 +0,0 @@ ---- -title: "Serena MCP" -linkTitle: "Serena MCP" -weight: 3 -description: > - Multi-language LSP-based coding assistant with AL Language Server support for Business Central ---- - -## Overview - -Serena is an AI-first code editor that provides MCP (Model Context Protocol) integration with Language Server Protocol (LSP) support for 20+ programming languages, including Business Central AL. It enables AI assistants to perform accurate code navigation, symbol lookup, and refactoring by leveraging native language servers. - -**Created by**: [oraios](https://github.com/oraios) - ---- - -## Key Features - -**AL Language Server Support**: -- Automatic setup using VS Code AL extension (`ms-dynamics-smb.al`) -- Native AL LSP integration with custom AL commands -- Platform detection for Windows/Linux/macOS -- Proper AL Language Server initialization - -**20+ Supported Languages**: -- AL, Python, TypeScript/JavaScript, Rust, Go, C/C++, C#, Java, Ruby, PHP, Kotlin, Swift, and more -- Each language uses native LSP implementation for maximum accuracy - -**Symbolic Operations**: -- Find References - Locate all usages across codebase -- Go to Definition - Jump to symbol definitions -- Document/Workspace Symbols - Search and list symbols -- Type & Call Hierarchy - Navigate relationships and call traces - -**Modes**: -- Lite Mode - Minimal tool set for focused tasks -- Standard Mode - Full tool suite with additional capabilities - ---- - -## Links - -- **GitHub**: https://github.com/oraios/serena -- **PyPI Package**: https://pypi.org/project/serena-mcp/ -- **Changelog**: https://github.com/oraios/serena/blob/main/CHANGELOG.md - diff --git a/content/docs/agentic-coding/CommunityResources/Tools/waldo-bctelemetrybuddy.md b/content/docs/agentic-coding/CommunityResources/Tools/waldo-bctelemetrybuddy.md deleted file mode 100644 index 56ae5d8c..00000000 --- a/content/docs/agentic-coding/CommunityResources/Tools/waldo-bctelemetrybuddy.md +++ /dev/null @@ -1,29 +0,0 @@ ---- -title: "BC Telemetry Buddy (waldo)" -linkTitle: "BC Telemetry Buddy" -weight: 7 -description: > - A small utility to help collect and forward Business Central telemetry for analysis and debugging. ---- - -## Overview - -BC Telemetry Buddy (by waldo) is a community tool that simplifies collecting telemetry from Business Central extensions and forwarding events to analysis endpoints. It's useful for teams who want lightweight telemetry during development or targeted diagnostics in production-like environments. - -**Publisher**: [waldo1001](https://github.com/waldo1001) - ---- - -## Key Features - -- Easy instrumentation helpers for common telemetry scenarios -- Lightweight forwarding to custom endpoints -- Works well in development environments and with local testing setups -- Simple configuration and minimal dependencies - ---- - -## Links - -- **GitHub**: https://github.com/waldo1001/waldo.BCTelemetryBuddy -- **Marketplace**: [BC Telemetry Buddy](https://marketplace.visualstudio.com/items?itemName=waldoBC.bc-telemetry-buddy) \ No newline at end of file diff --git a/content/docs/agentic-coding/CommunityResources/_index.md b/content/docs/agentic-coding/CommunityResources/_index.md deleted file mode 100644 index c98ceb4c..00000000 --- a/content/docs/agentic-coding/CommunityResources/_index.md +++ /dev/null @@ -1,131 +0,0 @@ ---- -title: "Community Resources" -linkTitle: "Community Resources" -weight: 100 -description: > - Curated resources from the AL and Business Central community for agentic coding ---- - -## Overview - -The AL and Business Central community has created excellent resources for learning about and using AI-powered coding assistants. This section curates and summarizes these valuable community contributions. - -## Resource Categories - -### AI Coding Agents -Learn about different AI assistants available for AL development, their capabilities, and when to use each one. - -- **[AI Coding Agents](Agents)** - Compare GitHub Copilot, Claude, Cursor, and other AI agents for AL development - -### Articles & Blog Posts -In-depth articles and blog posts from community members sharing their experiences and insights with AI-assisted AL development. - -### Video Tutorials -Video content demonstrating AI coding techniques, tips, and workflows for Business Central development. - -### Tools & Extensions -Community-created tools and VS Code extensions that enhance AI-assisted development. - -- **[Tools & Extensions](Tools)** - Detailed guides for GitHub Copilot, AL Language extension, and other essential tools - -### Discussions & Forums -Active community discussions about agentic coding practices, challenges, and solutions. - -## Featured Resources - -### Getting Started with AI for AL -Collection of introductory resources for developers new to AI-assisted AL development. - -### Advanced Techniques -Resources covering advanced AI prompting, workflow optimization, and specialized use cases. - -### Real-World Examples -Case studies and examples from community members showing AI assistance in production projects. - -## Contributing Resources - -Found a great resource about AI-assisted AL development? We welcome contributions! - -**To suggest a resource:** -1. Ensure it's relevant to AL/Business Central development -2. Verify the content is high-quality and accurate -3. Check it's not already listed -4. Submit via the [Contributing](../../contributing) process - -**Resource Guidelines:** -- Must be publicly accessible -- Should provide clear value to AL developers -- Content should be accurate and up-to-date -- Appropriate for a professional audience - -## Resource Pages - -Explore detailed summaries and links for specific types of resources: - -- **[Blog Posts & Articles](articles)** - Written content from the community -- **[Video Tutorials](videos)** - Visual learning resources -- **[Tools & Extensions](Tools)** - Utilities that enhance AI development -- **[Community Discussions](https://github.com/microsoft/alguidelines/discussions)** - Join the conversation on GitHub - -## Stay Connected - -### Community Platforms - -**Business Central Community** -- [Business Central Community Forum](https://community.dynamics.com/business/) -- Dedicated sections for development questions and AI tooling - -**GitHub** -- [microsoft/AL](https://github.com/microsoft/AL) - AL Language repository -- Various community extension repositories with AI-friendly code - -**Social Media** -- Follow #MSDyn365BC hashtag -- Follow #ALDevelopment hashtag -- Connect with AL developers sharing AI tips - -**Discord & Slack** -- Business Central developer communities -- Channels dedicated to development tools and automation - -## Regular Contributors - -The community is grateful to developers who regularly share their knowledge about AI-assisted development. Check out content from active contributors in each resource category. - -## Learning Paths - -### For Beginners -1. Start with [Getting Started](../gettingstarted) in this hub -2. Watch introductory videos about AI coding assistants -3. Read beginner-friendly blog posts -4. Try simple prompts with your own code - -### For Intermediate Developers -1. Review advanced prompting techniques -2. Study real-world examples -3. Experiment with specialized tools -4. Participate in community discussions - -### For Advanced Users -1. Explore cutting-edge use cases -2. Contribute your own insights -3. Develop tools for the community -4. Mentor others in AI-assisted development - -## Quality Standards - -Resources listed here are reviewed for: -- **Accuracy**: Content is technically correct -- **Relevance**: Directly applicable to AL/BC development -- **Value**: Provides genuine insights or learning -- **Accessibility**: Publicly available and clearly presented - -## Updates - -This section is regularly updated with new community resources. Check back often for the latest content! - -**Last Updated**: Check individual resource pages for update dates. - -## Feedback - -Have feedback about these resources or suggestions for improvement? Please share through our [Contributing](../../contributing) channels. diff --git a/content/docs/agentic-coding/CommunityResources/articles.md b/content/docs/agentic-coding/CommunityResources/articles.md deleted file mode 100644 index 44ec92a6..00000000 --- a/content/docs/agentic-coding/CommunityResources/articles.md +++ /dev/null @@ -1,112 +0,0 @@ ---- -title: "Blog Posts & Articles" -linkTitle: "Articles" -weight: 1 -description: > - Written content from the community about AI-assisted AL development ---- - -## Overview - -This page curates blog posts, articles, and written guides from the Business Central community about using AI coding assistants for AL development. - -## Featured Articles - -### Getting Started - -#### "AI for BC Development - The Knowledge Gap That Ships to Production" — Kine -**Author**: Kine (blog.kine.cz) -**Published**: 2025 -**Summary**: A critical self-assessment guide on whether you are ready to validate what AI produces for BC development. Covers common AI mistakes in AL code (wrong field assignment, missing keys, reinventing standard libraries) and what BC knowledge areas you need to review AI output responsibly. - -**Key Takeaways**: -- Common AI mistakes in AL code that ship to production -- Self-assessment framework for AI-assisted development readiness -- BC knowledge areas required to review AI output responsibly -- Practical examples of wrong field assignments, missing keys, and reinvented standard libraries - -**Link**: https://blog.kine.cz/posts/bcdevelopmentserie-02b/ - ---- - -#### "Vibe Coding — yes or no?" — Demiliani -**Author**: Demiliani -**Published**: 2025-08-21 -**Summary**: A thoughtful exploration of the Vibe Coding approach and whether its rules and conventions help or hinder real-world AL development. - -**Key Takeaways**: -- Pros and cons of strict Vibe Coding rules -- When to adopt vs. adapt guidance for your team -- Practical examples and trade-offs - -**Link**: https://demiliani.com/2025/08/21/vibe-coding-yes-or-no/ - ---- - -#### "Gestión de contexto y estados en servidores MCP" — TechSphere Dynamics -**Author**: TechSphere Dynamics -**Published**: 2025-08-15 -**Summary**: A Spanish-language deep dive into context handling and state management patterns for MCP servers, including practical patterns used in production systems. - -**Key Takeaways**: -- Context lifecycle and state management strategies -- Common pitfalls when preserving or discarding context -- Examples of robust MCP server patterns - -**Link**: https://techspheredynamics.com/2025/08/15/gestion-de-contexto-y-estados-en-servidores-mcp/ - ---- - -#### "Testing GitHub Copilot: knowledge engineering — what actually works and what doesn't" — Nubimancy -**Author**: Nubimancy -**Published**: 2025-09-09 -**Summary**: An empirical look at how well GitHub Copilot handles knowledge-engineering tasks, with experiments and practical recommendations for prompt authors. - -**Key Takeaways**: -- Which prompting patterns produce reliable results -- When Copilot is prone to hallucination or brittle outputs -- Strategies to validate and refine AI-suggested knowledge artifacts - -**Link**: https://nubimancy.com/2025/09/09/testing-github-copilot-knowledge-engineering-what-actually-works-and-what-doesnt/ - ---- - -Each of these are just great examples from those blogs, so make sure to explore around! - ---- - -## Contributing Articles - -Have you written about AI-assisted AL development? We'd love to include high-quality, publicly accessible content from the community. - -Submission checklist: -- Publicly accessible article or blog post -- Clearly focused on AL / Business Central development -- Accurate, well-written, and actionable -- Original work or properly attributed - -How to submit: -1. Review the [Contributing](../../../contributing) guidelines -2. Open a pull request adding your article to this list -3. Provide: Title, Author, Short Summary (1–2 lines), and Link - -## Article Quality Criteria - -Items listed on this page should meet these standards: -- Technically accurate -- Relevant to AL development -- Clear and well-written -- Provides actionable insights -- Publicly accessible - -## Updates - -This is a living document — new resources will be added as the community publishes them. To suggest an addition, open a PR against this repository and follow the submission checklist above. - ---- - -## Related Resources - -- [Video Tutorials](../videos) - Visual learning content -- [Tools & Extensions](../tools) - Development utilities -- [Discussions](../discussions) - Community conversations diff --git a/content/docs/agentic-coding/CommunityResources/tools.md b/content/docs/agentic-coding/CommunityResources/tools.md deleted file mode 100644 index 41e86422..00000000 --- a/content/docs/agentic-coding/CommunityResources/tools.md +++ /dev/null @@ -1,569 +0,0 @@ ---- -title: "Tools & Extensions" -linkTitle: "Tools" -weight: 3 -description: > - VS Code extensions and tools that enhance AI-assisted AL development ---- - -## Overview - -This page catalogs VS Code extensions, utilities, and tools that complement AI coding assistants for Business Central development. - -## AI Coding Assistants - -### GitHub Copilot -**Publisher**: GitHub -**Type**: AI Code Completion - -**Features**: -- Inline code suggestions -- Chat interface for questions -- Multi-file context awareness -- Code explanation and refactoring - -**AL-Specific Benefits**: -- Understands AL syntax and patterns -- Suggests Business Central APIs -- Generates AL-compliant code -- Helps with event subscribers and patterns - -**Installation**: -``` -Extension ID: GitHub.copilot -``` - -**Link**: [VS Code Marketplace](https://marketplace.visualstudio.com/items?itemName=GitHub.copilot) - -**Pricing**: Subscription required (free for students/open source maintainers) - ---- - -### GitHub Copilot Chat -**Publisher**: GitHub -**Type**: AI Chat Assistant - -**Features**: -- Interactive chat in VS Code -- Code explanations -- Debugging assistance -- Inline code chat - -**Best For**: -- Asking questions about AL code -- Getting explanations of Business Central patterns -- Debugging assistance -- Code refactoring discussions - -**Installation**: -``` -Extension ID: GitHub.copilot-chat -``` - -**Link**: [VS Code Marketplace](https://marketplace.visualstudio.com/items?itemName=GitHub.copilot-chat) - -**Requires**: GitHub Copilot subscription - ---- - -## AL Development Extensions - -### AL Language -**Publisher**: Microsoft -**Type**: Language Support - -**Why It's Essential**: -- Core AL language support -- Syntax highlighting -- IntelliSense -- Code analysis - -**AI Integration**: -- Provides context for AI suggestions -- Enables AL-aware completions -- Works with AL code analyzers - -**Installation**: -``` -Extension ID: ms-dynamics-smb.al -``` - -**Link**: [VS Code Marketplace](https://marketplace.visualstudio.com/items?itemName=ms-dynamics-smb.al) - -**Required**: Yes, for AL development - ---- - -### AL Code Outline -**Publisher**: Community -**Type**: Code Navigation - -**Features**: -- Visual code structure -- Quick navigation -- Object overview -- Procedure list - -**AI Enhancement**: -- Helps AI understand code structure -- Easier to reference specific procedures in prompts -- Better context for AI suggestions - -**Installation**: -``` -Extension ID: davidfeldhoff.al-code-outline -``` - -**Link**: [VS Code Marketplace](https://marketplace.visualstudio.com/items?itemName=davidfeldhoff.al-code-outline) - ---- - -### AL Object Designer -**Publisher**: Community -**Type**: Object Browser - -**Features**: -- Browse all AL objects -- Search functionality -- Quick navigation -- Object creation - -**AI Enhancement**: -- Quickly find objects to reference in prompts -- Better workspace navigation -- Context for AI when working with multiple objects - -**Installation**: -``` -Extension ID: martonsagi.al-object-designer -``` - -**Link**: [VS Code Marketplace](https://marketplace.visualstudio.com/items?itemName=martonsagi.al-object-designer) - ---- - -### AL Variable Helper -**Publisher**: Community -**Type**: Variable Management - -**Features**: -- Auto-declare variables -- Variable suggestions -- Type inference -- Quick fixes - -**AI Complement**: -- Use with AI-generated code to clean up variables -- Auto-complete variables from AI suggestions -- Streamline AI-generated code - -**Installation**: -``` -Extension ID: rasmus.al-var-helper -``` - -**Link**: [VS Code Marketplace](https://marketplace.visualstudio.com/items?itemName=rasmus.al-var-helper) - ---- - -### AL Test Runner -**Publisher**: Community -**Type**: Test Framework - -**Features**: -- Run AL tests from VS Code -- Test results visualization -- Code coverage -- Test debugging - -**AI Use Cases**: -- Test AI-generated code -- Verify AI refactoring -- Run tests for AI-written procedures - -**Installation**: -``` -Extension ID: jamespearson.al-test-runner -``` - -**Link**: [VS Code Marketplace](https://marketplace.visualstudio.com/items?itemName=jamespearson.al-test-runner) - ---- - -## Code Quality Tools - -### AL CodeCop -**Type**: Code Analyzer -**Included In**: AL Language Extension - -**What It Does**: -- Enforces AL coding standards -- Identifies best practice violations -- Provides code fixes - -**AI Integration**: -- Review AI-generated code -- Ensure AI follows standards -- Auto-fix AI code issues - -**Usage**: Enabled in `app.json`: -```json -"codeAnalyzers": ["${CodeCop}"] -``` - ---- - -### Business Central Performance Toolkit -**Publisher**: Microsoft -**Type**: Performance Testing - -**Features**: -- Performance scenario testing -- Load testing -- Performance metrics - -**AI Use Cases**: -- Test performance of AI-generated code -- Validate AI optimization suggestions -- Benchmark before/after AI refactoring - -**Link**: [GitHub Repository](https://github.com/microsoft/BusinessCentralPerfToolkit) - ---- - -## Documentation Tools - -### AL XML Documentation -**Publisher**: Community -**Type**: Documentation Generator - -**Features**: -- Generate XML documentation -- Documentation snippets -- Template support - -**AI Enhancement**: -- Complement AI-generated docs -- Standardize documentation format -- Quick doc generation - -**Installation**: -``` -Extension ID: andrzejzwierzchowski.al-xml-doc -``` - -**Link**: [VS Code Marketplace](https://marketplace.visualstudio.com/items?itemName=andrzejzwierzchowski.al-xml-doc) - ---- - -### Markdown All in One -**Publisher**: Community -**Type**: Markdown Editor - -**Features**: -- Markdown preview -- Keyboard shortcuts -- Auto-completion -- Table formatting - -**AI Use Cases**: -- Edit AI-generated README files -- Format AI-generated documentation -- Create documentation with AI assistance - -**Installation**: -``` -Extension ID: yzhang.markdown-all-in-one -``` - -**Link**: [VS Code Marketplace](https://marketplace.visualstudio.com/items?itemName=yzhang.markdown-all-in-one) - ---- - -## Productivity Enhancers - -### GitLens -**Publisher**: GitKraken -**Type**: Git Enhancement - -**Features**: -- Code authorship -- Commit history -- Blame annotations -- Git visualization - -**AI Context**: -- See who wrote code (human vs AI-assisted) -- Track AI-generated code changes -- Review AI code evolution - -**Installation**: -``` -Extension ID: eamodio.gitlens -``` - -**Link**: [VS Code Marketplace](https://marketplace.visualstudio.com/items?itemName=eamodio.gitlens) - ---- - -### Code Spell Checker -**Publisher**: Street Side Software -**Type**: Spell Checker - -**Features**: -- Spell checking in code -- Supports AL/BC terms -- Custom dictionaries - -**AI Complement**: -- Catch spelling errors in AI-generated code -- Improve AI-generated documentation -- Ensure consistent terminology - -**Installation**: -``` -Extension ID: streetsidesoftware.code-spell-checker -``` - -**Link**: [VS Code Marketplace](https://marketplace.visualstudio.com/items?itemName=streetsidesoftware.code-spell-checker) - ---- - -### Better Comments -**Publisher**: Community -**Type**: Comment Enhancement - -**Features**: -- Color-coded comments -- TODO highlighting -- Comment categories - -**AI Use Cases**: -- Mark AI-generated code sections -- Highlight AI code for review -- Track AI TODOs - -**Installation**: -``` -Extension ID: aaron-bond.better-comments -``` - -**Link**: [VS Code Marketplace](https://marketplace.visualstudio.com/items?itemName=aaron-bond.better-comments) - ---- - -## Specialized Tools - -### AL Toolbox -**Publisher**: Community -**Type**: Utility Collection - -**Features**: -- Code snippets -- Quick actions -- AL utilities -- Productivity commands - -**AI Enhancement**: -- Complements AI code generation -- Quick fixes for AI code -- Utilities for AI-generated projects - -**Installation**: -``` -Extension ID: BartPermentier.al-toolbox -``` - -**Link**: [VS Code Marketplace](https://marketplace.visualstudio.com/items?itemName=BartPermentier.al-toolbox) - ---- - -### AZ AL Dev Tools -**Publisher**: Community -**Type**: Development Tools - -**Features**: -- Code generators -- AL object wizards -- Development utilities -- Code analysis - -**AI Complement**: -- Generate boilerplate for AI to enhance -- Analyze AI-generated code structure -- Quick object creation - -**Installation**: -``` -Extension ID: andrzejzwierzchowski.az-al-dev-tools-vscode -``` - -**Link**: [VS Code Marketplace](https://marketplace.visualstudio.com/items?itemName=andrzejzwierzchowski.az-al-dev-tools-vscode) - ---- - -## Recommended Extension Packs - -### Essential AL + AI Setup -Minimal setup for AI-assisted AL development: - -1. **AL Language** (Microsoft) -2. **GitHub Copilot** (GitHub) -3. **GitHub Copilot Chat** (GitHub) -4. **AL Code Outline** (Community) -5. **AL Object Designer** (Community) - -### Complete AL Development Suite -Comprehensive setup: - -- All from Essential Setup -- AL Variable Helper -- AL Test Runner -- AL XML Documentation -- GitLens -- Code Spell Checker -- Better Comments - -### Team Development -Recommended for development teams: - -- Complete Suite extensions -- AL Toolbox -- AZ AL Dev Tools -- Business Central Performance Toolkit - -## Configuration Tips - -### Optimizing for AI -Add to your `settings.json`: - -```json -{ - // Enable GitHub Copilot for AL - "github.copilot.enable": { - "*": true, - "al": true - }, - - // Better inline suggestions - "editor.inlineSuggest.enabled": true, - "editor.suggest.showMethods": true, - "editor.suggest.showFunctions": true, - - // Code analysis - "al.enableCodeAnalysis": true, - "al.codeAnalyzers": ["${CodeCop}", "${UICop}", "${PerTenantExtensionCop}"], - - // Auto-save for better AI context - "files.autoSave": "afterDelay", - "files.autoSaveDelay": 1000 -} -``` - -### Keyboard Shortcuts -Useful shortcuts when working with AI: - -- **Trigger Suggestion**: `Ctrl+Space` (Windows/Linux) or `Cmd+Space` (Mac) -- **Accept Suggestion**: `Tab` -- **Next Suggestion**: `Alt+]` -- **Previous Suggestion**: `Alt+[` -- **Open Copilot Chat**: `Ctrl+Shift+I` - -## Tool Integrations - -### Combining Tools Effectively - -**AI + Code Analysis**: -1. Generate code with AI -2. Run CodeCop to check standards -3. Fix issues with AI assistance -4. Verify with test runner - -**AI + Documentation**: -1. Write code with AI -2. Generate XML docs with AI -3. Format with AL XML Documentation extension -4. Create README with Markdown All in One - -**AI + Version Control**: -1. Develop features with AI -2. Review changes with GitLens -3. Commit with clear AI-related messages -4. Track AI productivity over time - -## Community-Created Tools - -### AL Prompt Templates -**Type**: Snippet Collection -**What**: Pre-written prompts for common AL tasks - -**Repository**: [Example - To be created by community] - ---- - -### BC AI Helper Scripts -**Type**: PowerShell Scripts -**What**: Automation scripts for AI-assisted BC development - -**Repository**: [Example - To be created by community] - ---- - -## Contributing Tools - -**Created a tool for AI-assisted AL development?** - -Share it with the community! - -**Submission Guidelines**: -- Must be useful for AL/BC development -- Should enhance AI-assisted workflows -- Open source preferred -- Well documented - -**How to Submit**: See [Contributing](../../../contributing) section - -## Tool Safety & Privacy - -### Privacy Considerations - -**What Gets Shared**: -- Code in your workspace (with AI assistants) -- File names and structure -- Your prompts and questions - -**Best Practices**: -- Review extension permissions -- Understand data handling -- Use organization-approved tools -- Don't include sensitive data in code - -### Security - -**Verify Extensions**: -- Check publisher reputation -- Read reviews -- Review permissions requested -- Keep extensions updated - -## Updates - -Extensions are frequently updated. Check for updates regularly: - -- VS Code: `Ctrl+Shift+X` → Click update icon -- Auto-update: Enable in VS Code settings - ---- - -## Placeholder Notice - -> **Note**: Some tools listed may be examples for community-created utilities. As real tools are developed and published, they should be added here. The core Microsoft and established community extensions (GitHub Copilot, AL Language, etc.) are real and available now. - ---- - -## Related Resources - -- [Setup Guide](../../gettingstarted/setup) - Environment configuration -- [Blog Posts](../articles) - Tool reviews and comparisons -- [Videos](../videos) - Tool demonstrations -- [Discussions](../discussions) - Tool recommendations and support diff --git a/content/docs/agentic-coding/CommunityResources/videos.md b/content/docs/agentic-coding/CommunityResources/videos.md deleted file mode 100644 index dc114325..00000000 --- a/content/docs/agentic-coding/CommunityResources/videos.md +++ /dev/null @@ -1,66 +0,0 @@ ---- ---- -title: "Video Tutorials" -linkTitle: "Videos" -weight: 2 -description: > - Curated videos demonstrating AI-assisted AL development techniques ---- - -## Overview - -Short, curated videos that show AI coding assistants in practical AL/Business Central workflows. The list below is a starting point — contribute more quality content via the contributing process. - -## Curated Videos (starter list) - -1. "Vibe Coding: Yes or No?" — demiliani - - Link: https://www.youtube.com/watch?v=vY5WfipEK8M - - Quick: discussion and viewpoints on the "vibe coding" approach when using AI assistants - -2. "Gestion de Contexto y Estados en Servidores MCP" — techspheredynamics - - Link: https://www.youtube.com/watch?v=PzEbaPw-E1o - - Quick: context and state management patterns for MCP servers (Spanish) - -3. "Testing GitHub Copilot Knowledge Engineering" — nubimancy - - Link: https://www.youtube.com/watch?v=K8nFVw5M-Po - - Quick: experiments and lessons around prompting and knowledge engineering for Copilot - -4. "Live Feature Build with AI Assistance" — community live stream - - Link: https://www.youtube.com/watch?v=SP1UJNjTN7s - - Quick: live coding session demonstrating end-to-end feature development with AI help - -## How to Use These Videos - -- Start with the shorter discussion/intro videos to build context -- Watch demos and live coding to see practical workflows and gotchas -- Rewatch technical deep dives for specific patterns or tools - -## Contribute a Video - -If you've created a high-quality video about AI-assisted AL development, please contribute it. - -Submission checklist: -- Publicly accessible (YouTube, Vimeo, etc.) -- Focused on AL / Business Central development -- Provides clear, actionable content or valuable discussion - -How to submit: -1. Review the [Contributing](../../../contributing) guidelines -2. Open a pull request adding your video to this page -3. Provide: Title, Creator, Short Summary (1–2 lines), and Link - -## Video Quality Criteria - -Videos listed here should meet these standards: -- Clear audio and video -- Accurate and relevant to AL development -- Demonstrates techniques, patterns, or practical workflows -- Publicly accessible - ---- - -## Related Resources - -- [Blog Posts & Articles](../articles) -- [Tools & Extensions](../tools) -- [Discussions](../discussions) diff --git a/content/docs/agentic-coding/GettingMore/_index.md b/content/docs/agentic-coding/GettingMore/_index.md deleted file mode 100644 index 432eea27..00000000 --- a/content/docs/agentic-coding/GettingMore/_index.md +++ /dev/null @@ -1,47 +0,0 @@ ---- -title: "Getting More" -linkTitle: "Getting More" -weight: 20 -description: > - Practical examples and advanced techniques for AI-assisted AL development ---- - -This section provides hands-on examples of how to use AI assistants for common AL development tasks. Each guide walks through a realistic scenario with step-by-step instructions and prompts. - -## In This Section - -- **[AI-Assisted Code Review](code-review)** - Use AI to review code for quality, performance, and best practices -- **[Generating Documentation](documentation)** - Automatically create and maintain documentation for your AL code -- **[Adding Telemetry](telemetry)** - Instrument your extensions with Application Insights telemetry -- **[Refactoring Legacy Code](refactoring)** - Modernize and improve existing AL code -- **[Writing Tests](testing)** - Create comprehensive test coverage with AI assistance - -## How to Use These Guides - -Each guide follows a practical, scenario-based approach: - -1. **Scenario**: A realistic development task -2. **Goal**: What you're trying to achieve -3. **Step-by-Step**: Detailed instructions with actual prompts -4. **Review Points**: What to check in the AI-generated code -5. **Tips**: Additional insights and variations - -## Before You Start - -Make sure you've completed the [Getting Started](../gettingstarted) section to: -- Understand agentic coding concepts -- Set up your environment -- Learn effective prompting -- Know the best practices and limitations - -## Learning by Doing - -These examples are designed to be: -- **Practical**: Based on real AL development tasks -- **Detailed**: Step-by-step instructions you can follow -- **Educational**: Explains why, not just what -- **Adaptable**: Patterns you can apply to your own work - -## Contributing Examples - -Have a great example of AI-assisted AL development? Consider contributing! See the [Contributing](../../contributing) section for guidelines. diff --git a/content/docs/agentic-coding/GettingMore/code-review.md b/content/docs/agentic-coding/GettingMore/code-review.md deleted file mode 100644 index 0010d847..00000000 --- a/content/docs/agentic-coding/GettingMore/code-review.md +++ /dev/null @@ -1,368 +0,0 @@ ---- -title: "AI-Assisted Code Review" -linkTitle: "Code Review" -weight: 1 -description: > - Learn how to use AI assistants to review AL code for quality, performance, and best practices ---- - -## Scenario - -You've just finished implementing a new feature: a codeunit that processes sales orders and applies volume-based discounts. Before submitting for peer review, you want to use AI to catch potential issues. - -## Goal - -Use AI to identify: -- Potential bugs or logical errors -- Performance issues -- AL best practice violations -- Missing error handling -- Code quality improvements - -## The Code to Review - -Here's the codeunit we'll review: - -```al -codeunit 50100 "Sales Order Discount Processor" -{ - procedure ApplyVolumeDiscounts(var SalesHeader: Record "Sales Header") - var - SalesLine: Record "Sales Line"; - TotalQuantity: Decimal; - DiscountPct: Decimal; - begin - TotalQuantity := 0; - SalesLine.SetRange("Document Type", SalesHeader."Document Type"); - SalesLine.SetRange("Document No.", SalesHeader."No."); - if SalesLine.FindSet() then - repeat - TotalQuantity := TotalQuantity + SalesLine.Quantity; - until SalesLine.Next() = 0; - - if TotalQuantity > 100 then - DiscountPct := 15 - else if TotalQuantity > 50 then - DiscountPct := 10 - else if TotalQuantity > 10 then - DiscountPct := 5; - - SalesLine.Reset(); - if SalesLine.FindSet() then - repeat - SalesLine."Line Discount %" := DiscountPct; - SalesLine.Modify(); - until SalesLine.Next() = 0; - end; -} -``` - -## Step-by-Step Review Process - -### Step 1: Initial Quality Review - -**Prompt**: -``` -Review this AL codeunit for potential bugs, code quality issues, and best practice violations. - -[Paste the code above] - -Specifically check for: -- Logical errors -- Missing error handling -- Variable declarations -- Commit/transaction issues -- Performance concerns -``` - -**Expected AI Findings**: -The AI should identify issues like: -- Missing `FindSet(true, false)` parameters for modification -- No error handling -- Multiple FindSet operations (inefficient) -- Hard-coded discount percentages -- No validation of input parameters -- Missing ModifyAll opportunity -- No telemetry or logging - -### Step 2: Performance Analysis - -**Prompt**: -``` -Analyze the performance of this code, especially database operations. -What are potential performance issues? How can it be optimized? - -[Include the code or reference it if in context] -``` - -**Expected AI Suggestions**: -- Combine the two FindSet loops into one -- Use ModifyAll instead of looping for discount application -- Consider using SetLoadFields for better performance -- Cache discount percentages instead of hard-coding - -### Step 3: Best Practices Check - -**Prompt**: -``` -Check this code against AL coding best practices and Business Central development standards. -What improvements would you recommend? -``` - -**Expected AI Recommendations**: -- Add XML documentation -- Extract discount calculation to separate function -- Use constants for discount tiers -- Add error handling -- Implement proper transaction handling -- Add telemetry for monitoring - -### Step 4: Request Improved Version - -**Prompt**: -``` -Rewrite this codeunit implementing all the improvements you suggested. -Include: -- XML documentation -- Error handling -- Performance optimizations -- Best practice compliance -- Telemetry/logging -- Discount tiers from setup table instead of hard-coded values -``` - -**Review the Generated Code**: -The AI should generate something like: - -```al -/// -/// Processes sales orders and applies volume-based discounts -/// -codeunit 50100 "Sales Order Discount Processor" -{ - /// - /// Applies volume-based discounts to a sales order - /// - /// The sales header to process - procedure ApplyVolumeDiscounts(var SalesHeader: Record "Sales Header") - var - SalesLine: Record "Sales Line"; - TotalQuantity: Decimal; - DiscountPct: Decimal; - begin - if SalesHeader."No." = '' then - Error('Sales order number cannot be empty.'); - - TotalQuantity := CalculateTotalQuantity(SalesHeader); - DiscountPct := GetDiscountPercentage(TotalQuantity); - - if DiscountPct > 0 then - ApplyDiscountToLines(SalesHeader, DiscountPct); - - LogTelemetry(SalesHeader."No.", TotalQuantity, DiscountPct); - end; - - local procedure CalculateTotalQuantity(SalesHeader: Record "Sales Header"): Decimal - var - SalesLine: Record "Sales Line"; - TotalQty: Decimal; - begin - SalesLine.SetRange("Document Type", SalesHeader."Document Type"); - SalesLine.SetRange("Document No.", SalesHeader."No."); - SalesLine.SetLoadFields(Quantity); - - if SalesLine.FindSet() then - repeat - TotalQty += SalesLine.Quantity; - until SalesLine.Next() = 0; - - exit(TotalQty); - end; - - local procedure GetDiscountPercentage(TotalQuantity: Decimal): Decimal - var - VolumeDiscountSetup: Record "Volume Discount Setup"; // Assuming this table exists - begin - VolumeDiscountSetup.SetFilter("Minimum Quantity", '<=%1', TotalQuantity); - VolumeDiscountSetup.SetCurrentKey("Minimum Quantity"); - VolumeDiscountSetup.Ascending(false); - - if VolumeDiscountSetup.FindFirst() then - exit(VolumeDiscountSetup."Discount Percentage"); - - exit(0); - end; - - local procedure ApplyDiscountToLines(SalesHeader: Record "Sales Header"; DiscountPct: Decimal) - var - SalesLine: Record "Sales Line"; - begin - SalesLine.SetRange("Document Type", SalesHeader."Document Type"); - SalesLine.SetRange("Document No.", SalesHeader."No."); - SalesLine.ModifyAll("Line Discount %", DiscountPct, true); - end; - - local procedure LogTelemetry(DocumentNo: Code[20]; TotalQty: Decimal; DiscountPct: Decimal) - var - Telemetry: Codeunit Telemetry; - CustomDimensions: Dictionary of [Text, Text]; - begin - CustomDimensions.Add('DocumentNo', DocumentNo); - CustomDimensions.Add('TotalQuantity', Format(TotalQty)); - CustomDimensions.Add('DiscountPercentage', Format(DiscountPct)); - - Telemetry.LogMessage('0001', 'Volume discount applied', Verbosity::Normal, - DataClassification::SystemMetadata, TelemetryScope::All, CustomDimensions); - end; -} -``` - -## Review Points: What to Check - -After AI generates the improved code, verify: - -### ✅ Correctness -- [ ] Logic matches business requirements -- [ ] All edge cases handled -- [ ] No regression in functionality -- [ ] Error messages are clear - -### ✅ Performance -- [ ] Efficient database queries -- [ ] Proper use of SetLoadFields -- [ ] ModifyAll used where appropriate -- [ ] No unnecessary loops - -### ✅ Best Practices -- [ ] XML documentation complete -- [ ] Proper error handling -- [ ] Appropriate transaction handling -- [ ] Good function decomposition - -### ✅ AL Specifics -- [ ] Correct AL syntax -- [ ] Proper use of BC APIs -- [ ] No deprecated features -- [ ] Appropriate data types - -### ✅ Maintainability -- [ ] Clear, descriptive names -- [ ] Logical organization -- [ ] Easy to test -- [ ] Well documented - -## Additional Review Prompts - -### Security Review -``` -Review this code for potential security issues: -- Input validation -- Authorization checks -- Data access controls -- Potential injection vulnerabilities -``` - -### Testability Review -``` -Analyze this code for testability. -What makes it easy or hard to test? -How can it be restructured to be more testable? -``` - -### Documentation Review -``` -Review the XML documentation for this code. -Is it complete? Clear? Helpful? -What additional documentation would be valuable? -``` - -## Common Issues AI Might Miss - -Be aware AI might not catch: - -1. **Business Logic Errors** - - AI doesn't know your specific discount rules - - Verify the logic matches actual requirements - -2. **Integration Issues** - - AI doesn't know about other extensions - - Check for conflicts with existing code - -3. **BC Version Compatibility** - - AI might use features not in your BC version - - Verify all APIs are available - -4. **Organization Standards** - - AI doesn't know your specific standards - - Adapt to match your conventions - -## Best Practices for AI Code Review - -### 1. Use Specific Review Criteria -Instead of "review this code", specify what to look for: -``` -Review for: performance, error handling, AL best practices, testability -``` - -### 2. Review in Layers -Don't try to review everything at once: -- First: Correctness and logic -- Second: Performance -- Third: Best practices -- Fourth: Documentation - -### 3. Combine with Tools -Use AI review alongside: -- AL code analyzers -- Static analysis tools -- Peer review -- Testing - -### 4. Iterate -Review, improve, review again: -``` -Review the updated code. Are there any remaining issues? -``` - -### 5. Document Findings -Keep track of: -- Common issues AI finds -- Issues AI misses -- Effective review prompts - -## Practice Exercise - -Try reviewing this code with AI: - -```al -procedure CalculateShippingCost(SalesHeader: Record "Sales Header"): Decimal -var - SalesLine: Record "Sales Line"; - Weight: Decimal; -begin - Weight := 0; - SalesLine.SetRange("Document No.", SalesHeader."No."); - if SalesLine.Find('-') then - repeat - Weight := Weight + SalesLine.Quantity; - until SalesLine.Next() = 0; - - if Weight < 10 then - exit(5.00) - else - exit(10.00); -end; -``` - -**Your Tasks**: -1. Ask AI to review for issues -2. Request performance improvements -3. Ask for best practice compliance -4. Get an improved version -5. Review the improved version yourself - -## Next Steps - -- Learn how to use AI for [generating documentation](../documentation) -- See how AI can help with [adding telemetry](../telemetry) -- Explore [refactoring legacy code](../refactoring) with AI assistance diff --git a/content/docs/agentic-coding/GettingMore/documentation.md b/content/docs/agentic-coding/GettingMore/documentation.md deleted file mode 100644 index d853917b..00000000 --- a/content/docs/agentic-coding/GettingMore/documentation.md +++ /dev/null @@ -1,661 +0,0 @@ ---- -title: "Generating Documentation" -linkTitle: "Documentation" -weight: 2 -description: > - Use AI to create and maintain comprehensive documentation for your AL code ---- - -## Scenario - -You have a well-functioning AL extension but it lacks documentation. You need to create: -- XML documentation comments for all procedures -- A comprehensive README file -- API documentation for public interfaces -- User-facing feature documentation - -## Goal - -Use AI to quickly generate high-quality documentation that: -- Explains what the code does -- Documents parameters and return values -- Provides usage examples -- Helps developers and users understand the extension - -## Types of Documentation - -### 1. XML Documentation Comments -In-code documentation for procedures, triggers, and events. - -### 2. README Files -Project overview, setup instructions, and getting started guides. - -### 3. API Documentation -Documentation for public APIs and integration points. - -### 4. User Documentation -End-user guides and feature explanations. - -## Step-by-Step Guide - -### Step 1: XML Documentation for Procedures - -**Scenario**: You have a codeunit without documentation. - -**Starting Code**: -```al -codeunit 50100 "Customer Email Manager" -{ - procedure SendWelcomeEmail(CustomerNo: Code[20]): Boolean - var - Customer: Record Customer; - EmailMessage: Codeunit "Email Message"; - begin - if not Customer.Get(CustomerNo) then - exit(false); - - if Customer."E-Mail" = '' then - exit(false); - - EmailMessage.Create(Customer."E-Mail", 'Welcome!', GetWelcomeEmailBody(Customer)); - exit(EmailMessage.Send()); - end; - - local procedure GetWelcomeEmailBody(Customer: Record Customer): Text - begin - exit(StrSubstNo('Dear %1, welcome to our service!', Customer.Name)); - end; -} -``` - -**Prompt**: -``` -Add XML documentation comments to all procedures in this codeunit. -Include: -- Summary for each procedure -- Parameter descriptions -- Return value descriptions -- Example usage where helpful -- Any important notes or warnings - -[Paste the code] -``` - -**Expected Result**: -```al -/// -/// Manages customer email communications -/// -codeunit 50100 "Customer Email Manager" -{ - /// - /// Sends a welcome email to a customer - /// - /// The customer number to send the email to - /// True if the email was sent successfully, false otherwise - /// - /// The procedure will return false if: - /// - The customer does not exist - /// - The customer has no email address - /// - The email fails to send - /// - /// - /// if CustomerEmailManager.SendWelcomeEmail('C001') then - /// Message('Welcome email sent successfully'); - /// - procedure SendWelcomeEmail(CustomerNo: Code[20]): Boolean - var - Customer: Record Customer; - EmailMessage: Codeunit "Email Message"; - begin - if not Customer.Get(CustomerNo) then - exit(false); - - if Customer."E-Mail" = '' then - exit(false); - - EmailMessage.Create(Customer."E-Mail", 'Welcome!', GetWelcomeEmailBody(Customer)); - exit(EmailMessage.Send()); - end; - - /// - /// Generates the body text for the welcome email - /// - /// The customer record to generate the email for - /// The formatted email body text - local procedure GetWelcomeEmailBody(Customer: Record Customer): Text - begin - exit(StrSubstNo('Dear %1, welcome to our service!', Customer.Name)); - end; -} -``` - -### Step 2: Batch Documentation for Multiple Files - -**Prompt**: -``` -I have multiple AL files that need XML documentation. -For each public procedure, add: -- Summary -- Parameter descriptions -- Return value (if applicable) -- Example usage - -Process these files: -[List files or paste multiple code blocks] -``` - -**Tip**: Do this in batches of 3-5 files to maintain context and quality. - -### Step 3: Creating a README File - -**Prompt**: -``` -Create a comprehensive README.md for my AL extension with the following information: - -Extension Name: Customer Engagement Suite -Purpose: Enhance customer communication and engagement features -Key Features: -- Automated welcome emails -- Customer portal access -- Newsletter subscription management -- Customer satisfaction surveys - -Include sections for: -- Overview and purpose -- Features list -- Installation instructions -- Configuration steps -- Usage examples -- Dependencies -- Compatibility (BC version 21+) -- Contributing guidelines -- License (MIT) -``` - -**Expected Structure**: -```markdown -# Customer Engagement Suite - -> Enhance customer communication and engagement in Microsoft Dynamics 365 Business Central - -## Overview - -The Customer Engagement Suite is a Business Central extension that provides comprehensive -tools for managing customer communications and engagement activities. - -## Features - -### Automated Welcome Emails -Automatically send personalized welcome emails to new customers upon registration. - -### Customer Portal Access -Enable customers to access a self-service portal for order tracking and account management. - -### Newsletter Subscription Management -Allow customers to manage their newsletter preferences and subscriptions. - -### Customer Satisfaction Surveys -Collect and analyze customer feedback through integrated surveys. - -## Installation - -### Prerequisites -- Microsoft Dynamics 365 Business Central version 21.0 or higher -- Email account configured in Business Central -- System Application extension installed - -### Steps - -1. Download the extension file (.app) -2. Open Business Central Administration Shell -3. Run the installation command: - ```powershell - Publish-NAVApp -ServerInstance BC210 -Path "CustomerEngagementSuite.app" - Install-NAVApp -ServerInstance BC210 -Name "Customer Engagement Suite" - ``` - -## Configuration - -### Email Setup -1. Navigate to **Email Accounts** page -2. Configure your SMTP settings -3. Test the email connection - -### Welcome Email Template -1. Go to **Customer Engagement Setup** -2. Configure the welcome email template -3. Enable automatic sending - -## Usage - -### Sending Welcome Emails -```al -codeunit 50100 "Customer Email Manager" -{ - procedure SendWelcomeEmail(CustomerNo: Code[20]): Boolean -} -``` - -**Example:** -```al -CustomerEmailManager: Codeunit "Customer Email Manager"; -begin - if CustomerEmailManager.SendWelcomeEmail('C001') then - Message('Welcome email sent successfully'); -end; -``` - -## Dependencies - -- **System Application**: Core BC functionality -- **Base Application**: Standard BC tables and pages - -## Compatibility - -- **Business Central**: Version 21.0 and higher -- **Platform**: Compatible with both SaaS and On-Premises - -## Contributing - -We welcome contributions! Please see [CONTRIBUTING.md](CONTRIBUTING.md) for guidelines. - -## License - -This project is licensed under the MIT License - see [LICENSE](LICENSE) file for details. - -## Support - -For issues and questions: -- Create an issue in our [GitHub repository](https://github.com/yourorg/customer-engagement) -- Email: support@yourcompany.com - -## Changelog - -See [CHANGELOG.md](CHANGELOG.md) for version history. -``` - -### Step 4: API Documentation - -**Prompt**: -``` -Create API documentation for the public procedures in my Customer Email Manager codeunit. -Format it as markdown suitable for a developer portal. - -Include: -- Procedure signature -- Description -- Parameters with types and descriptions -- Return values -- Code examples -- Error conditions -- Notes and best practices - -[Include the codeunit code] -``` - -**Expected Output**: -```markdown -# Customer Email Manager API - -## Overview -The Customer Email Manager provides functionality for sending customer-related emails. - -## Procedures - -### SendWelcomeEmail - -Sends a welcome email to a specified customer. - -**Signature:** -```al -procedure SendWelcomeEmail(CustomerNo: Code[20]): Boolean -``` - -**Parameters:** - -| Name | Type | Description | -|------|------|-------------| -| CustomerNo | Code[20] | The unique identifier of the customer | - -**Returns:** - -| Type | Description | -|------|-------------| -| Boolean | `true` if email sent successfully, `false` otherwise | - -**Example:** -```al -var - CustomerEmailManager: Codeunit "Customer Email Manager"; - Success: Boolean; -begin - Success := CustomerEmailManager.SendWelcomeEmail('C00001'); - if Success then - Message('Email sent successfully') - else - Error('Failed to send email'); -end; -``` - -**Error Conditions:** - -The procedure returns `false` in the following cases: -- Customer does not exist -- Customer has no email address configured -- Email service is unavailable - -**Best Practices:** -- Always check the return value -- Ensure email account is configured before calling -- Consider calling asynchronously for better performance - -**See Also:** -- Email Account Setup -- Customer Configuration -``` - -### Step 5: User Documentation - -**Prompt**: -``` -Create user-facing documentation for the Welcome Email feature. -Target audience: Business users who are not developers. - -Explain: -- What the feature does -- How to enable/configure it -- How to use it -- Troubleshooting common issues - -Use simple language and include screenshots placeholders where helpful. -``` - -**Expected Output**: -```markdown -# Welcome Email Feature - -## What is the Welcome Email Feature? - -The Welcome Email feature automatically sends a personalized email to new customers -when they are added to your system. This helps create a positive first impression -and provides important information to your customers right away. - -## Enabling Welcome Emails - -1. Open Business Central -2. Search for "Customer Engagement Setup" -3. Check the box next to "Send Welcome Emails" -4. Click "OK" to save - -![Setup Screenshot] - -## Configuring the Email Template - -### Step 1: Access Email Templates -1. Search for "Email Templates" -2. Find "Customer Welcome Email" -3. Click "Edit" - -### Step 2: Customize the Message -1. Update the email subject line -2. Modify the email body -3. Add your company logo -4. Click "OK" to save - -### Step 3: Test the Email -1. Open a test customer -2. Click "Actions" > "Send Welcome Email" -3. Check that you receive the email - -## Using Welcome Emails - -### Automatic Sending -When enabled, welcome emails are sent automatically when: -- A new customer is created -- An existing customer is marked as active for the first time - -### Manual Sending -You can also send welcome emails manually: - -1. Open the customer card -2. Click "Actions" > "Email" > "Send Welcome Email" -3. Confirm the action - -## Troubleshooting - -### Email Not Sending - -**Problem:** Welcome emails are not being sent - -**Solutions:** -- Check that the feature is enabled in setup -- Verify the customer has an email address -- Confirm your email account is configured correctly -- Check that email service is running - -### Wrong Email Template - -**Problem:** Customers receive the wrong email content - -**Solutions:** -- Review the email template configuration -- Ensure you're editing the correct template -- Clear any cached templates - -### Customer Not Receiving Email - -**Problem:** Email shows as sent but customer didn't receive it - -**Solutions:** -- Ask customer to check spam folder -- Verify the email address is correct -- Check email delivery logs -- Test with a different email address - -## Tips and Best Practices - -- **Personalization**: Use customer name and company name in the email -- **Timing**: Send welcome emails within 24 hours of customer creation -- **Follow-up**: Consider a follow-up email series -- **Feedback**: Ask customers if they received and found the email helpful - -## Need Help? - -Contact your Business Central administrator or support team for assistance. -``` - -## Review Points - -After generating documentation, verify: - -### ✅ Accuracy -- [ ] Documentation matches actual code behavior -- [ ] Parameter descriptions are correct -- [ ] Return values accurately described -- [ ] Examples work as shown - -### ✅ Completeness -- [ ] All public procedures documented -- [ ] All parameters explained -- [ ] Edge cases mentioned -- [ ] Error conditions documented - -### ✅ Clarity -- [ ] Easy to understand -- [ ] Appropriate level of detail -- [ ] Good examples provided -- [ ] Proper formatting - -### ✅ Maintenance -- [ ] Easy to update -- [ ] Versioned appropriately -- [ ] Consistent style -- [ ] Well organized - -## Advanced Documentation Prompts - -### Generate CHANGELOG -``` -Create a CHANGELOG.md file documenting these changes from version 1.0 to 2.0: -- Added: Customer portal access -- Added: Newsletter preferences -- Changed: Welcome email now includes portal link -- Fixed: Email encoding issues with special characters -- Deprecated: Old email API (will be removed in 3.0) - -Follow Keep a Changelog format. -``` - -### Create Migration Guide -``` -Create a migration guide for users upgrading from version 1.x to 2.0. -Include: -- Breaking changes -- New features -- Configuration changes needed -- Data migration steps -- Deprecation warnings -``` - -### Generate Inline Code Comments -``` -Add helpful inline comments to this complex procedure explaining the logic flow. -Don't over-comment obvious code, but do explain: -- Complex algorithms -- Business rule implementations -- Non-obvious optimizations -- Workarounds - -[Paste code] -``` - -## Keeping Documentation Updated - -### When Code Changes -**Prompt**: -``` -I've updated this procedure to add a new parameter. -Update the XML documentation to reflect the change: - -Old procedure: -[paste old code] - -New procedure: -[paste new code] -``` - -### Regular Documentation Reviews -**Prompt**: -``` -Review the documentation for this codeunit. -Check for: -- Outdated information -- Missing documentation -- Incorrect examples -- Deprecated features - -[Paste codeunit] -``` - -## Best Practices - -### 1. Document As You Code -``` -I'm about to write a procedure to validate customer credit limits. -Create the XML documentation comment first, then we'll implement the procedure. -``` - -### 2. Use Consistent Style -Create a documentation template: -``` -Create an XML documentation template I can use for all my procedures. -Include sections for: summary, parameters, returns, exceptions, examples, and remarks. -``` - -### 3. Generate Documentation in Batches -Document related code together for consistency: -``` -Document all procedures in this codeunit that relate to email sending. -Use consistent terminology and structure. -``` - -### 4. Include Real Examples -``` -Add a realistic code example to this procedure's documentation showing: -- Typical usage -- Error handling -- Integration with other features -``` - -## Common Documentation Patterns - -### For Validation Procedures -``` -Document this validation procedure. Include: -- What is being validated -- Valid conditions -- Error messages that can be raised -- Example of valid and invalid inputs -``` - -### For Event Subscribers -``` -Document this event subscriber. Include: -- What event it subscribes to -- When it triggers -- What it does -- Side effects or implications -- Integration points -``` - -### For APIs -``` -Create REST API documentation for this AL API page. -Include: -- Endpoint URL -- HTTP methods supported -- Request/response examples -- Authentication requirements -- Error codes -``` - -## Practice Exercise - -Generate documentation for this code: - -```al -codeunit 50110 "Order Status Manager" -{ - procedure UpdateOrderStatus(OrderNo: Code[20]; NewStatus: Enum "Order Status"): Boolean - var - SalesHeader: Record "Sales Header"; - begin - if not SalesHeader.Get(SalesHeader."Document Type"::Order, OrderNo) then - exit(false); - - SalesHeader.Status := NewStatus; - SalesHeader.Modify(true); - SendStatusNotification(OrderNo, NewStatus); - exit(true); - end; - - local procedure SendStatusNotification(OrderNo: Code[20]; Status: Enum "Order Status") - begin - // Implementation - end; -} -``` - -**Your Tasks**: -1. Generate XML documentation -2. Create a README section explaining this feature -3. Write API documentation -4. Create user documentation -5. Review and improve the generated docs - -## Next Steps - -- Learn how to use AI for [adding telemetry](../telemetry) -- Explore [refactoring legacy code](../refactoring) while maintaining documentation -- See how to conduct [AI-assisted code reviews](../code-review) diff --git a/content/docs/agentic-coding/GettingMore/refactoring.md b/content/docs/agentic-coding/GettingMore/refactoring.md deleted file mode 100644 index 6aaac1c6..00000000 --- a/content/docs/agentic-coding/GettingMore/refactoring.md +++ /dev/null @@ -1,1041 +0,0 @@ ---- -title: "Refactoring Legacy Code" -linkTitle: "Refactoring" -weight: 4 -description: > - Use AI to modernize and improve existing AL code while maintaining functionality ---- - -## Scenario - -You've inherited a legacy AL codeunit from an older Business Central version. The code works, but it: - -- Uses deprecated patterns and APIs -- Has poor structure and naming -- Lacks documentation and error handling -- Contains duplicated logic -- Is difficult to test and maintain - -You need to modernize this code while ensuring it continues to work correctly. - -## Goal - -Use AI to help you: - -- Identify refactoring opportunities -- Modernize deprecated APIs -- Improve code structure -- Enhance readability and maintainability -- Add tests to ensure no regression - -## The Legacy Code - -Here's a typical legacy codeunit that needs refactoring: - -```al -codeunit 50100 "Sales Order Management" -{ - procedure ProcessSalesOrder(DocNo: Code[20]) - var - SH: Record "Sales Header"; - SL: Record "Sales Line"; - C: Record Customer; - I: Record Item; - qty: Decimal; - amt: Decimal; - begin - if DocNo = '' then - exit; - - SH.Get(SH."Document Type"::Order, DocNo); - C.Get(SH."Sell-to Customer No."); - - if C.Blocked <> C.Blocked::" " then begin - Message('Customer is blocked!'); - exit; - end; - - SL.SetRange("Document No.", DocNo); - if SL.Find('-') then - repeat - if SL.Type = SL.Type::Item then begin - I.Get(SL."No."); - if I.Inventory < SL.Quantity then - Message('Not enough inventory for item ' + I."No."); - end; - qty := qty + SL.Quantity; - amt := amt + SL."Line Amount"; - until SL.Next() = 0; - - if amt > 10000 then begin - SL.SetRange("Document No.", DocNo); - if SL.Find('-') then - repeat - SL."Line Discount %" := 5; - SL.Modify(); - until SL.Next() = 0; - end; - - Message('Processed order ' + DocNo + ' with total amount ' + Format(amt)); - end; -} -``` - -## Step-by-Step Refactoring Process - -### Step 1: Write Tests First - -{{% alert title="Critical: Test First!" color="warning" %}} -Before refactoring any code, write tests that verify its current behavior. These tests act as a safety net, ensuring you don't accidentally break functionality during refactoring. -{{% /alert %}} - -**Prompt**: - -``` -I need to refactor this legacy code, but first I need comprehensive tests for its current behavior. - -Create tests that verify: -- All current functionality (even if imperfect) -- Expected outputs for given inputs -- Error conditions and edge cases -- Current behavior (not ideal behavior) - -These tests should pass with the current code and catch any regressions during refactoring. - -[Paste the legacy code] -``` - -**Expected Test Code**: - -```al -codeunit 50101 "Sales Order Mgmt. Tests" -{ - Subtype = Test; - - var - Assert: Codeunit "Library Assert"; - LibrarySales: Codeunit "Library - Sales"; - - [Test] - procedure TestCurrentBehaviorWithValidOrder() - var - SalesHeader: Record "Sales Header"; - SalesOrderMgmt: Codeunit "Sales Order Management"; - begin - // [GIVEN] A valid sales order (testing CURRENT behavior) - CreateTestSalesOrder(SalesHeader, 'CUST001', 5000); - - // [WHEN] Processing the order - SalesOrderMgmt.ProcessSalesOrder(SalesHeader."No."); - - // [THEN] No error occurs and discount is applied - // (These tests lock in current behavior before refactoring) - Assert.IsTrue(true, 'Process completed without error'); - end; - - [Test] - procedure TestCurrentBlockedCustomerBehavior() - var - SalesHeader: Record "Sales Header"; - Customer: Record Customer; - SalesOrderMgmt: Codeunit "Sales Order Management"; - begin - // [GIVEN] Order with blocked customer - CreateBlockedCustomerOrder(SalesHeader, Customer); - - // [WHEN] Processing (currently just shows Message, doesn't error) - SalesOrderMgmt.ProcessSalesOrder(SalesHeader."No."); - - // [THEN] Process completes (testing CURRENT behavior, even if not ideal) - // Note: After refactoring, this might throw an error instead - end; - - // Additional tests for current behavior... -} -``` - -**Why This Matters**: - -- Tests catch regressions immediately -- You can refactor confidently -- Tests document current behavior -- You can run tests after each small refactoring step - -### Step 2: Initial Assessment - -**Prompt**: - -``` -Analyze this legacy AL code and identify refactoring opportunities. - -Categorize issues by: -- Critical: Deprecated APIs, security issues, bugs -- Major: Poor structure, missing error handling, performance issues -- Minor: Naming, formatting, documentation - -[Paste the legacy code] - -For each issue, explain: -- What the problem is -- Why it's problematic -- How to fix it -``` - -**Expected AI Findings**: - -**Critical Issues:** - -- Using `Message()` for errors instead of `Error()` -- No proper error handling (could cause partial updates) -- Using deprecated `Find('-')` syntax -- Modifying records in a loop without proper handling - -**Major Issues:** - -- Poor variable naming (SH, SL, C, I, qty, amt) -- Single large procedure doing multiple things -- Hard-coded business rules (10000 threshold, 5% discount) -- Inefficient database operations (multiple FindSet loops) -- No validation of input parameters -- Missing XML documentation -- No telemetry or logging - -**Minor Issues:** - -- No separation of concerns -- String concatenation for messages -- Magic numbers without explanation -- Inconsistent code style - -### Step 3: Create a Refactoring Plan - -**Prompt**: - -``` -Create a detailed refactoring plan for this code. - -I already have tests in place that verify current behavior. - -Break refactoring into phases: -1. Safe refactorings (no behavior change) -2. Structural improvements -3. API modernization -4. Behavioral improvements (may change behavior) -5. Final enhancements - -For each phase, list specific changes and estimated risk level. -After each phase, I'll run tests to ensure nothing broke. -``` - -**Expected Plan**: - -**Phase 0: Tests (COMPLETED)** - -- ✅ Written tests for current behavior -- ✅ Tests pass with current code -- ✅ Ready to refactor safely - -**Phase 1: Safe Refactorings (Low Risk)** - -- Rename variables to be descriptive -- Add XML documentation -- Extract magic numbers to constants -- Format code consistently -- Add code comments for complex logic - -**Phase 2: Structural Improvements (Medium Risk)** - -- Extract methods for each responsibility -- Separate validation, calculation, and update logic -- Remove code duplication -- Improve error handling - -**Phase 3: API Modernization (Medium Risk)** - -- Replace deprecated Find('-') with FindSet() -- Use proper Error() instead of Message() -- Add SetLoadFields for performance -- Use ModifyAll where appropriate - -**Phase 4: Behavioral Improvements (Medium Risk)** - -- Change Message() to Error() for validation failures -- This CHANGES behavior, so update tests accordingly -- Add new tests for improved error handling - -**Phase 5: Additional Testing (Low Risk)** - -- Add more edge case tests -- Add tests for each extracted method -- Expand test coverage - -**Phase 6: Enhancements (Low Risk)** - -- Add telemetry -- Implement proper transaction handling -- Add configuration instead of hard-coded values - -### Step 4: Run Your Tests - -**Before refactoring anything**, verify your tests pass: - -```powershell -# Run the test codeunit -Invoke-NAVCodeunit -CodeunitId 50101 -``` - -**All tests should pass.** If they don't, fix the tests first. - -### Step 5: Phase 1 - Safe Refactorings - -**Prompt**: - -``` -Perform Phase 1 refactorings on this code: -- Rename all variables to be descriptive -- Add XML documentation to the procedure -- Extract magic numbers (10000, 5) to constants -- Add inline comments for complex logic -- Improve code formatting - -[Paste the legacy code] -``` - -**Expected Result**: - -```al -/// -/// Manages sales order processing including validation, inventory checking, and discount application -/// -codeunit 50100 "Sales Order Management" -{ - var - LargeOrderThreshold: Decimal; - LargeOrderDiscountPct: Decimal; - - /// - /// Processes a sales order by validating customer, checking inventory, and applying discounts - /// - /// The sales order number to process - procedure ProcessSalesOrder(DocumentNo: Code[20]) - var - SalesHeader: Record "Sales Header"; - SalesLine: Record "Sales Line"; - Customer: Record Customer; - Item: Record Item; - TotalQuantity: Decimal; - TotalAmount: Decimal; - begin - // Initialize thresholds - LargeOrderThreshold := 10000; - LargeOrderDiscountPct := 5; - - // Validate input - if DocumentNo = '' then - exit; - - // Get sales order and customer - SalesHeader.Get(SalesHeader."Document Type"::Order, DocumentNo); - Customer.Get(SalesHeader."Sell-to Customer No."); - - // Check if customer is blocked - if Customer.Blocked <> Customer.Blocked::" " then begin - Message('Customer is blocked!'); - exit; - end; - - // Check inventory and calculate totals - SalesLine.SetRange("Document No.", DocumentNo); - if SalesLine.Find('-') then - repeat - // Verify inventory availability for items - if SalesLine.Type = SalesLine.Type::Item then begin - Item.Get(SalesLine."No."); - if Item.Inventory < SalesLine.Quantity then - Message('Not enough inventory for item ' + Item."No."); - end; - - // Accumulate totals - TotalQuantity := TotalQuantity + SalesLine.Quantity; - TotalAmount := TotalAmount + SalesLine."Line Amount"; - until SalesLine.Next() = 0; - - // Apply discount for large orders - if TotalAmount > LargeOrderThreshold then begin - SalesLine.SetRange("Document No.", DocumentNo); - if SalesLine.Find('-') then - repeat - SalesLine."Line Discount %" := LargeOrderDiscountPct; - SalesLine.Modify(); - until SalesLine.Next() = 0; - end; - - // Notify user of completion - Message('Processed order ' + DocumentNo + ' with total amount ' + Format(TotalAmount)); - end; -} -``` - -### Step 6: Run Tests After Phase 1 - -**Critical**: After completing Phase 1 refactorings, run your tests: - -```powershell -Invoke-NAVCodeunit -CodeunitId 50101 -``` - -**All tests should still pass.** If any fail, the refactoring changed behavior - fix it before proceeding. - -### Step 7: Phase 2 - Structural Improvements - -**Prompt**: - -``` -Refactor this code to improve structure: - -1. Extract separate methods for: - - Customer validation - - Inventory checking - - Total calculation - - Discount application - -2. Replace Message() with proper Error() for validation failures - -3. Add proper error handling with try-catch where appropriate - -4. Use StrSubstNo for string formatting - -[Include the Phase 1 code] -``` - -**Expected Result**: - -```al -/// -/// Manages sales order processing including validation, inventory checking, and discount application -/// -codeunit 50100 "Sales Order Management" -{ - var - LargeOrderThreshold: Decimal; - LargeOrderDiscountPct: Decimal; - InsufficientInventoryErr: Label 'Insufficient inventory for item %1. Available: %2, Required: %3'; - CustomerBlockedErr: Label 'Customer %1 is blocked and cannot place orders.'; - OrderProcessedMsg: Label 'Successfully processed order %1 with total amount %2.'; - - /// - /// Processes a sales order by validating customer, checking inventory, and applying discounts - /// - /// The sales order number to process - procedure ProcessSalesOrder(DocumentNo: Code[20]) - var - SalesHeader: Record "Sales Header"; - TotalAmount: Decimal; - begin - LargeOrderThreshold := 10000; - LargeOrderDiscountPct := 5; - - if DocumentNo = '' then - Error('Document number cannot be empty.'); - - if not SalesHeader.Get(SalesHeader."Document Type"::Order, DocumentNo) then - Error('Sales order %1 not found.', DocumentNo); - - ValidateCustomer(SalesHeader); - CheckInventoryAvailability(SalesHeader); - TotalAmount := CalculateOrderTotal(SalesHeader); - - if TotalAmount > LargeOrderThreshold then - ApplyLargeOrderDiscount(SalesHeader); - - Message(OrderProcessedMsg, DocumentNo, Format(TotalAmount, 0, '')); - end; - - /// - /// Validates that the customer is not blocked - /// - /// The sales header to validate - local procedure ValidateCustomer(SalesHeader: Record "Sales Header") - var - Customer: Record Customer; - begin - if not Customer.Get(SalesHeader."Sell-to Customer No.") then - Error('Customer %1 not found.', SalesHeader."Sell-to Customer No."); - - if Customer.Blocked <> Customer.Blocked::" " then - Error(CustomerBlockedErr, Customer."No."); - end; - - /// - /// Checks that sufficient inventory is available for all items on the order - /// - /// The sales header to check - local procedure CheckInventoryAvailability(SalesHeader: Record "Sales Header") - var - SalesLine: Record "Sales Line"; - Item: Record Item; - begin - SalesLine.SetRange("Document Type", SalesHeader."Document Type"); - SalesLine.SetRange("Document No.", SalesHeader."No."); - SalesLine.SetRange(Type, SalesLine.Type::Item); - - if SalesLine.FindSet() then - repeat - if Item.Get(SalesLine."No.") then - if Item.Inventory < SalesLine.Quantity then - Error(InsufficientInventoryErr, Item."No.", Item.Inventory, SalesLine.Quantity); - until SalesLine.Next() = 0; - end; - - /// - /// Calculates the total amount for the sales order - /// - /// The sales header to calculate totals for - /// The total amount of the order - local procedure CalculateOrderTotal(SalesHeader: Record "Sales Header"): Decimal - var - SalesLine: Record "Sales Line"; - TotalAmount: Decimal; - begin - SalesLine.SetRange("Document Type", SalesHeader."Document Type"); - SalesLine.SetRange("Document No.", SalesHeader."No."); - SalesLine.SetLoadFields("Line Amount"); - - if SalesLine.FindSet() then - repeat - TotalAmount += SalesLine."Line Amount"; - until SalesLine.Next() = 0; - - exit(TotalAmount); - end; - - /// - /// Applies a discount to all lines on large orders - /// - /// The sales header to apply discounts to - local procedure ApplyLargeOrderDiscount(SalesHeader: Record "Sales Header") - var - SalesLine: Record "Sales Line"; - begin - SalesLine.SetRange("Document Type", SalesHeader."Document Type"); - SalesLine.SetRange("Document No.", SalesHeader."No."); - SalesLine.ModifyAll("Line Discount %", LargeOrderDiscountPct, true); - end; -} -``` - -### Step 8: Run Tests After Phase 2 - -Run your tests again after structural improvements: - -```powershell -Invoke-NAVCodeunit -CodeunitId 50101 -``` - -Tests should pass. If not, review the structural changes. - -### Step 9: Phase 3 - API Modernization - -**Prompt**: - -``` -Modernize this code to use current AL best practices: - -1. Replace deprecated Find('-') with FindSet() -2. Add proper SetLoadFields for performance -3. Use better error handling patterns -4. Implement proper transaction handling -5. Replace hard-coded values with setup table - -[Include the Phase 2 code] -``` - -**Expected Result**: - -```al -/// -/// Manages sales order processing including validation, inventory checking, and discount application -/// -codeunit 50100 "Sales Order Management" -{ - var - InsufficientInventoryErr: Label 'Insufficient inventory for item %1. Available: %2, Required: %3'; - CustomerBlockedErr: Label 'Customer %1 is blocked and cannot place orders.'; - OrderProcessedMsg: Label 'Successfully processed order %1 with total amount %2.'; - - /// - /// Processes a sales order by validating customer, checking inventory, and applying discounts - /// - /// The sales order number to process - procedure ProcessSalesOrder(DocumentNo: Code[20]) - var - SalesHeader: Record "Sales Header"; - TotalAmount: Decimal; - begin - if DocumentNo = '' then - Error('Document number cannot be empty.'); - - if not SalesHeader.Get(SalesHeader."Document Type"::Order, DocumentNo) then - Error('Sales order %1 not found.', DocumentNo); - - ValidateCustomer(SalesHeader); - CheckInventoryAvailability(SalesHeader); - TotalAmount := CalculateOrderTotal(SalesHeader); - - if ShouldApplyLargeOrderDiscount(TotalAmount) then - ApplyLargeOrderDiscount(SalesHeader); - - LogOrderProcessing(DocumentNo, TotalAmount); - Message(OrderProcessedMsg, DocumentNo, Format(TotalAmount, 0, '')); - end; - - local procedure ValidateCustomer(SalesHeader: Record "Sales Header") - var - Customer: Record Customer; - begin - Customer.SetLoadFields(Blocked); - - if not Customer.Get(SalesHeader."Sell-to Customer No.") then - Error('Customer %1 not found.', SalesHeader."Sell-to Customer No."); - - if Customer.Blocked <> Customer.Blocked::" " then - Error(CustomerBlockedErr, Customer."No."); - end; - - local procedure CheckInventoryAvailability(SalesHeader: Record "Sales Header") - var - SalesLine: Record "Sales Line"; - Item: Record Item; - begin - SalesLine.SetRange("Document Type", SalesHeader."Document Type"); - SalesLine.SetRange("Document No.", SalesHeader."No."); - SalesLine.SetRange(Type, SalesLine.Type::Item); - SalesLine.SetLoadFields("No.", Quantity); - - if SalesLine.FindSet() then begin - Item.SetLoadFields(Inventory); - repeat - if Item.Get(SalesLine."No.") then - if Item.Inventory < SalesLine.Quantity then - Error(InsufficientInventoryErr, Item."No.", Item.Inventory, SalesLine.Quantity); - until SalesLine.Next() = 0; - end; - end; - - local procedure CalculateOrderTotal(SalesHeader: Record "Sales Header"): Decimal - var - SalesLine: Record "Sales Line"; - TotalAmount: Decimal; - begin - SalesLine.SetRange("Document Type", SalesHeader."Document Type"); - SalesLine.SetRange("Document No.", SalesHeader."No."); - SalesLine.SetLoadFields("Line Amount"); - - if SalesLine.FindSet() then - repeat - TotalAmount += SalesLine."Line Amount"; - until SalesLine.Next() = 0; - - exit(TotalAmount); - end; - - local procedure ShouldApplyLargeOrderDiscount(TotalAmount: Decimal): Boolean - var - SalesSetup: Record "Sales & Receivables Setup"; - begin - SalesSetup.Get(); - exit(TotalAmount > SalesSetup."Large Order Threshold"); - end; - - local procedure ApplyLargeOrderDiscount(SalesHeader: Record "Sales Header") - var - SalesLine: Record "Sales Line"; - SalesSetup: Record "Sales & Receivables Setup"; - begin - SalesSetup.Get(); - - SalesLine.SetRange("Document Type", SalesHeader."Document Type"); - SalesLine.SetRange("Document No.", SalesHeader."No."); - SalesLine.ModifyAll("Line Discount %", SalesSetup."Large Order Discount %", true); - end; - - local procedure LogOrderProcessing(DocumentNo: Code[20]; TotalAmount: Decimal) - var - Telemetry: Codeunit Telemetry; - CustomDimensions: Dictionary of [Text, Text]; - begin - CustomDimensions.Add('DocumentNo', DocumentNo); - CustomDimensions.Add('TotalAmount', Format(TotalAmount)); - - Telemetry.LogMessage('SALESORD-001', 'Sales order processed successfully', - Verbosity::Normal, DataClassification::SystemMetadata, - TelemetryScope::ExtensionPublisher, CustomDimensions); - end; -} -``` - -### Step 10: Update Tests for Behavioral Changes - -Now that the code is cleaner, you may want to improve behavior (e.g., Error() instead of Message()): - -**Prompt**: - -``` -I want to change the behavior to use Error() instead of Message() for validation failures. - -First, update the tests to expect these errors: -- TestBlockedCustomerError should expect an error -- Update assertions to use asserterror and Assert.ExpectedError - -Then show the code changes needed. -``` - -### Step 11: Expand Test Coverage - -**Prompt**: - -``` -Now that refactoring is complete, add more comprehensive tests for edge cases. - -Include tests for: -- Happy path: successful processing -- Customer validation errors -- Inventory availability checks -- Large order discount application -- Edge cases: empty document number, non-existent order - -Use the AL test framework with proper setup and teardown. -``` - -**Expected Test Codeunit**: - -```al -codeunit 50101 "Sales Order Management Tests" -{ - Subtype = Test; - - var - Assert: Codeunit "Library Assert"; - LibrarySales: Codeunit "Library - Sales"; - LibraryInventory: Codeunit "Library - Inventory"; - - [Test] - procedure TestSuccessfulOrderProcessing() - var - SalesHeader: Record "Sales Header"; - SalesOrderMgmt: Codeunit "Sales Order Management"; - begin - // [GIVEN] A valid sales order with sufficient inventory - CreateSalesOrderWithInventory(SalesHeader); - - // [WHEN] Processing the order - SalesOrderMgmt.ProcessSalesOrder(SalesHeader."No."); - - // [THEN] No error is raised - // Verified by test not failing - end; - - [Test] - procedure TestBlockedCustomerError() - var - SalesHeader: Record "Sales Header"; - Customer: Record Customer; - SalesOrderMgmt: Codeunit "Sales Order Management"; - begin - // [GIVEN] A sales order for a blocked customer - CreateSalesOrderWithBlockedCustomer(SalesHeader, Customer); - - // [WHEN] Processing the order - asserterror SalesOrderMgmt.ProcessSalesOrder(SalesHeader."No."); - - // [THEN] Error is raised about blocked customer - Assert.ExpectedError(StrSubstNo('Customer %1 is blocked', Customer."No.")); - end; - - [Test] - procedure TestInsufficientInventoryError() - var - SalesHeader: Record "Sales Header"; - Item: Record Item; - SalesOrderMgmt: Codeunit "Sales Order Management"; - begin - // [GIVEN] A sales order with more quantity than available inventory - CreateSalesOrderWithInsufficientInventory(SalesHeader, Item); - - // [WHEN] Processing the order - asserterror SalesOrderMgmt.ProcessSalesOrder(SalesHeader."No."); - - // [THEN] Error is raised about insufficient inventory - Assert.ExpectedError('Insufficient inventory'); - end; - - [Test] - procedure TestLargeOrderDiscountApplied() - var - SalesHeader: Record "Sales Header"; - SalesLine: Record "Sales Line"; - SalesSetup: Record "Sales & Receivables Setup"; - SalesOrderMgmt: Codeunit "Sales Order Management"; - ExpectedDiscount: Decimal; - begin - // [GIVEN] A large order that qualifies for discount - SalesSetup.Get(); - ExpectedDiscount := SalesSetup."Large Order Discount %"; - CreateLargeSalesOrder(SalesHeader); - - // [WHEN] Processing the order - SalesOrderMgmt.ProcessSalesOrder(SalesHeader."No."); - - // [THEN] Discount is applied to all lines - SalesLine.SetRange("Document Type", SalesHeader."Document Type"); - SalesLine.SetRange("Document No.", SalesHeader."No."); - SalesLine.FindSet(); - repeat - Assert.AreEqual(ExpectedDiscount, SalesLine."Line Discount %", 'Discount not applied correctly'); - until SalesLine.Next() = 0; - end; - - local procedure CreateSalesOrderWithInventory(var SalesHeader: Record "Sales Header") - var - Item: Record Item; - SalesLine: Record "Sales Line"; - begin - LibraryInventory.CreateItem(Item); - Item.Inventory := 100; - Item.Modify(); - - LibrarySales.CreateSalesHeader(SalesHeader, SalesHeader."Document Type"::Order, LibrarySales.CreateCustomerNo()); - LibrarySales.CreateSalesLine(SalesLine, SalesHeader, SalesLine.Type::Item, Item."No.", 10); - end; - - // Additional helper procedures... -} -``` - -## Review Points: What to Check - -After each refactoring phase, verify: - -### ✅ Functionality Preserved - -- [ ] All original functionality still works -- [ ] No regressions introduced -- [ ] Tests pass (or create tests first!) -- [ ] Edge cases still handled - -### ✅ Code Quality Improved - -- [ ] More readable and maintainable -- [ ] Better structure and organization -- [ ] Clearer naming and documentation -- [ ] Proper error handling - -### ✅ Performance Not Degraded - -- [ ] Database operations optimized -- [ ] No unnecessary loops or queries -- [ ] Proper use of SetLoadFields -- [ ] Efficient algorithms - -### ✅ Modern Practices Applied - -- [ ] Current AL syntax and APIs -- [ ] No deprecated features -- [ ] Proper telemetry -- [ ] Good separation of concerns - -## Advanced Refactoring Patterns - -### Extract Interface for Testability - -**Prompt**: - -``` -Refactor this codeunit to use interfaces for dependencies, making it more testable. - -Extract interfaces for: -- Customer validation -- Inventory checking -- Discount calculation - -This will allow us to mock these dependencies in tests. -``` - -### Convert to Event-Driven Architecture - -**Prompt**: - -``` -Refactor this code to use Business Central events: - -1. Publish events for: - - Before order processing - - After order validation - - Before discount application - - After order processing - -2. This allows other extensions to hook into the process -``` - -### Add Batch Processing Support - -**Prompt**: - -``` -Extend this codeunit to support batch processing of multiple orders. - -Include: -- Progress tracking -- Error handling per order (don't fail entire batch) -- Summary reporting -- Performance optimization for bulk operations -``` - -## Common Refactoring Challenges - -### Challenge 1: Unknown Business Logic - -**Problem**: Code has complex logic without documentation - -**Solution**: - -``` -Analyze this code and explain what business logic it implements: -[paste complex code] - -Then suggest how to make the logic clearer through refactoring. -``` - -### Challenge 2: Tightly Coupled Code - -**Problem**: Code has many dependencies that are hard to untangle - -**Solution**: - -``` -This code is tightly coupled. Create a refactoring plan to: -1. Identify dependencies -2. Extract interfaces -3. Use dependency injection -4. Make code more modular -``` - -### Challenge 3: Large Procedures - -**Problem**: Single procedure doing too many things - -**Solution**: - -``` -This procedure is too large and complex. -Apply the Single Responsibility Principle to break it into smaller procedures. -Each procedure should have one clear purpose. -``` - -## Best Practices for Refactoring with AI - -### 1. Always Write Tests First - -{{% alert title="Golden Rule" color="primary" %}} -**Never refactor without tests.** Tests are your safety net. Write them first, run them, then refactor. -{{% /alert %}} - -``` -Before we start refactoring, let's write tests that lock in the current behavior. -Even if the current behavior isn't perfect, we need to know if we change it. -``` - -### 2. Refactor in Small Steps - -Don't try to refactor everything at once. Use incremental changes: - -``` -Let's refactor this code in three phases: -Phase 1: Just improve naming and documentation -[Run tests - should pass] -Phase 2: Extract methods -[Run tests - should pass] -Phase 3: Modernize APIs -[Run tests - should pass] -``` - -### 3. Run Tests After Every Change - -``` -I've completed the naming refactoring. -Let me run the tests to make sure nothing broke. - -[Run tests] - -Great, tests pass. Now let's proceed to Phase 2. -``` - -### 3. Use Git Commits for Each Phase - -``` -We've completed Phase 1: Safe Refactorings. -Before moving to Phase 2, I'll commit these changes. -Suggest a good commit message for these refactorings. -``` - -### 4. Document Why, Not Just What - -``` -For each major refactoring, add a comment explaining WHY the change was made: -- Why was the old approach problematic? -- What does the new approach solve? -- Are there trade-offs? -``` - -### 5. Keep Performance in Mind - -``` -As we refactor this code, let's ensure we don't hurt performance. -For each database operation change, explain the performance implications. -``` - -## Practice Exercise - -Refactor this legacy code: - -```al -codeunit 50200 "Item Price Calculator" -{ - procedure CalcPrice(IN: Code[20]; CU: Code[10]; QT: Decimal): Decimal - var - I: Record Item; - C: Record Customer; - P: Decimal; - begin - I.Get(IN); - P := I."Unit Price"; - - if QT > 100 then - P := P * 0.9; - - if CU <> '' then begin - C.Get(CU); - if C."Customer Price Group" = 'VIP' then - P := P * 0.95; - end; - - exit(P); - end; -} -``` - -**Your Tasks**: - -1. Assess the code and list issues -2. Create a refactoring plan -3. Apply naming improvements -4. Extract methods for each responsibility -5. Add proper documentation and error handling -6. Create tests -7. Add telemetry - -## Next Steps - -- Learn about [writing tests](../testing) for your refactored code -- See how [code review](../code-review) catches refactoring issues -- Explore [adding telemetry](../telemetry) to monitor refactored code diff --git a/content/docs/agentic-coding/GettingMore/telemetry.md b/content/docs/agentic-coding/GettingMore/telemetry.md deleted file mode 100644 index c4d0c696..00000000 --- a/content/docs/agentic-coding/GettingMore/telemetry.md +++ /dev/null @@ -1,599 +0,0 @@ ---- -title: "Adding Telemetry" -linkTitle: "Telemetry" -weight: 3 -description: > - Use AI to instrument your AL extensions with Application Insights telemetry ---- - -## Scenario - -You have a Business Central extension that's running in production, but you have limited visibility into: -- How often features are used -- Where errors occur -- Performance bottlenecks -- User behavior patterns - -You want to add comprehensive telemetry using Application Insights to monitor your extension in production. - -## Goal - -Use AI to help you: -- Add telemetry calls throughout your code -- Log important events and errors -- Track feature usage -- Monitor performance -- Create custom dimensions for better analysis - -## Prerequisites - -Understanding of: -- Business Central telemetry concepts -- Application Insights basics -- When to log (without over-logging) -- Data classification requirements - -## Step-by-Step Guide - -### Step 1: Understanding Your Code - -**Starting Code** (without telemetry): -```al -codeunit 50100 "Sales Order Processor" -{ - procedure ProcessOrder(var SalesHeader: Record "Sales Header"): Boolean - var - SalesLine: Record "Sales Line"; - begin - if not ValidateOrder(SalesHeader) then - exit(false); - - CalculateTotals(SalesHeader); - ApplyDiscounts(SalesHeader); - - if not PostOrder(SalesHeader) then - exit(false); - - exit(true); - end; - - local procedure ValidateOrder(var SalesHeader: Record "Sales Header"): Boolean - var - Customer: Record Customer; - begin - if not Customer.Get(SalesHeader."Sell-to Customer No.") then begin - Error('Customer %1 does not exist.', SalesHeader."Sell-to Customer No."); - end; - - if Customer.Blocked <> Customer.Blocked::" " then begin - Error('Customer %1 is blocked.', Customer."No."); - end; - - exit(true); - end; - - local procedure PostOrder(var SalesHeader: Record "Sales Header"): Boolean - var - SalesPost: Codeunit "Sales-Post"; - begin - exit(SalesPost.Run(SalesHeader)); - end; -} -``` - -### Step 2: Request Telemetry Strategy - -**Prompt**: -``` -I want to add Application Insights telemetry to this Business Central codeunit. - -Help me plan what to log: -- What events should be logged? -- What custom dimensions should be included? -- What data classification is appropriate? -- Where should we log errors vs. information? - -[Paste the code above] -``` - -**Expected AI Response**: -The AI should suggest logging: - -**Events to Log:** -1. Order processing started (Informational) -2. Order validation completed (Informational) -3. Order posted successfully (Informational) -4. Validation failures (Warning) -5. Posting failures (Error) -6. Performance metrics (Informational) - -**Custom Dimensions:** -- Order Number -- Customer Number -- Order Amount -- Number of Lines -- Processing Duration -- Error Details (when applicable) - -**Data Classification:** -- Order Number: CustomerContent -- Customer Number: CustomerContent -- Amounts: CustomerContent -- Counts: SystemMetadata -- Duration: SystemMetadata - -### Step 3: Add Telemetry to Main Procedure - -**Prompt**: -``` -Add Application Insights telemetry to the ProcessOrder procedure. - -Include: -- Start event when processing begins -- Success event when complete -- Error event on failure -- Custom dimensions: OrderNo, CustomerNo, TotalAmount, LineCount, Duration -- Use appropriate data classification -- Use the Telemetry codeunit from System Application - -Update this code: -[Paste ProcessOrder procedure] -``` - -**Expected Result**: -```al -procedure ProcessOrder(var SalesHeader: Record "Sales Header"): Boolean -var - Telemetry: Codeunit Telemetry; - CustomDimensions: Dictionary of [Text, Text]; - StartTime: DateTime; - Duration: Duration; - LineCount: Integer; -begin - StartTime := CurrentDateTime; - - // Log start of processing - AddOrderDimensions(CustomDimensions, SalesHeader); - Telemetry.LogMessage('SALESORD-001', 'Order processing started', - Verbosity::Normal, DataClassification::SystemMetadata, - TelemetryScope::ExtensionPublisher, CustomDimensions); - - if not ValidateOrder(SalesHeader) then begin - LogProcessingError(SalesHeader, 'Validation failed'); - exit(false); - end; - - CalculateTotals(SalesHeader); - ApplyDiscounts(SalesHeader); - - if not PostOrder(SalesHeader) then begin - LogProcessingError(SalesHeader, 'Posting failed'); - exit(false); - end; - - // Log successful completion - Duration := CurrentDateTime - StartTime; - Clear(CustomDimensions); - AddOrderDimensions(CustomDimensions, SalesHeader); - CustomDimensions.Add('Duration', Format(Duration)); - CustomDimensions.Add('LineCount', Format(CountLines(SalesHeader))); - - Telemetry.LogMessage('SALESORD-002', 'Order processing completed successfully', - Verbosity::Normal, DataClassification::SystemMetadata, - TelemetryScope::ExtensionPublisher, CustomDimensions); - - exit(true); -end; - -local procedure AddOrderDimensions(var CustomDimensions: Dictionary of [Text, Text]; SalesHeader: Record "Sales Header") -begin - CustomDimensions.Add('OrderNo', SalesHeader."No."); - CustomDimensions.Add('CustomerNo', SalesHeader."Sell-to Customer No."); - CustomDimensions.Add('OrderType', Format(SalesHeader."Document Type")); -end; - -local procedure LogProcessingError(SalesHeader: Record "Sales Header"; ErrorMessage: Text) -var - Telemetry: Codeunit Telemetry; - CustomDimensions: Dictionary of [Text, Text]; -begin - AddOrderDimensions(CustomDimensions, SalesHeader); - CustomDimensions.Add('ErrorMessage', ErrorMessage); - CustomDimensions.Add('ErrorCallStack', GetLastErrorCallStack()); - - Telemetry.LogMessage('SALESORD-E001', StrSubstNo('Order processing failed: %1', ErrorMessage), - Verbosity::Error, DataClassification::SystemMetadata, - TelemetryScope::ExtensionPublisher, CustomDimensions); -end; - -local procedure CountLines(SalesHeader: Record "Sales Header"): Integer -var - SalesLine: Record "Sales Line"; -begin - SalesLine.SetRange("Document Type", SalesHeader."Document Type"); - SalesLine.SetRange("Document No.", SalesHeader."No."); - exit(SalesLine.Count); -end; -``` - -### Step 4: Add Error Tracking - -**Prompt**: -``` -Update the ValidateOrder procedure to log validation failures with telemetry. - -For each validation check that fails: -- Log a warning message -- Include relevant details (customer number, block reason, etc.) -- Use appropriate custom dimensions -- Use proper data classification - -[Paste ValidateOrder procedure] -``` - -**Expected Result**: -```al -local procedure ValidateOrder(var SalesHeader: Record "Sales Header"): Boolean -var - Customer: Record Customer; - Telemetry: Codeunit Telemetry; - CustomDimensions: Dictionary of [Text, Text]; -begin - if not Customer.Get(SalesHeader."Sell-to Customer No.") then begin - AddValidationDimensions(CustomDimensions, SalesHeader, 'Customer not found'); - Telemetry.LogMessage('SALESORD-W001', 'Order validation failed: Customer not found', - Verbosity::Warning, DataClassification::SystemMetadata, - TelemetryScope::ExtensionPublisher, CustomDimensions); - - Error('Customer %1 does not exist.', SalesHeader."Sell-to Customer No."); - end; - - if Customer.Blocked <> Customer.Blocked::" " then begin - Clear(CustomDimensions); - AddValidationDimensions(CustomDimensions, SalesHeader, 'Customer blocked'); - CustomDimensions.Add('BlockedReason', Format(Customer.Blocked)); - - Telemetry.LogMessage('SALESORD-W002', 'Order validation failed: Customer blocked', - Verbosity::Warning, DataClassification::SystemMetadata, - TelemetryScope::ExtensionPublisher, CustomDimensions); - - Error('Customer %1 is blocked.', Customer."No."); - end; - - exit(true); -end; - -local procedure AddValidationDimensions(var CustomDimensions: Dictionary of [Text, Text]; SalesHeader: Record "Sales Header"; Reason: Text) -begin - CustomDimensions.Add('OrderNo', SalesHeader."No."); - CustomDimensions.Add('CustomerNo', SalesHeader."Sell-to Customer No."); - CustomDimensions.Add('ValidationFailure', Reason); -end; -``` - -### Step 5: Add Performance Tracking - -**Prompt**: -``` -Add performance telemetry to track how long different operations take. - -Track duration for: -- Overall processing -- Validation -- Calculation -- Posting - -Log if any operation takes longer than expected thresholds: -- Validation > 1 second -- Calculation > 2 seconds -- Posting > 5 seconds - -[Include the codeunit] -``` - -**Expected Result**: -```al -local procedure ValidateOrder(var SalesHeader: Record "Sales Header"): Boolean -var - Customer: Record Customer; - Telemetry: Codeunit Telemetry; - CustomDimensions: Dictionary of [Text, Text]; - StartTime: DateTime; - Duration: Duration; -begin - StartTime := CurrentDateTime; - - // Validation logic here... - - Duration := CurrentDateTime - StartTime; - - // Log if validation took too long - if Duration > 1000 then begin - AddOrderDimensions(CustomDimensions, SalesHeader); - CustomDimensions.Add('Duration', Format(Duration)); - CustomDimensions.Add('Operation', 'Validation'); - - Telemetry.LogMessage('SALESORD-P001', 'Slow validation detected', - Verbosity::Warning, DataClassification::SystemMetadata, - TelemetryScope::ExtensionPublisher, CustomDimensions); - end; - - exit(true); -end; -``` - -### Step 6: Add Feature Usage Tracking - -**Prompt**: -``` -Add telemetry to track feature usage for business analytics. - -Track: -- How often each discount type is applied -- Which payment methods are used -- Popular shipping methods -- Order size distribution - -Create a FeatureUsageTelemetry codeunit for this purpose. -``` - -**Expected Result**: -```al -codeunit 50101 "Feature Usage Telemetry" -{ - var - Telemetry: Codeunit Telemetry; - - procedure LogDiscountApplied(DiscountType: Text; DiscountPct: Decimal; OrderNo: Code[20]) - var - CustomDimensions: Dictionary of [Text, Text]; - begin - CustomDimensions.Add('FeatureArea', 'Discounts'); - CustomDimensions.Add('DiscountType', DiscountType); - CustomDimensions.Add('DiscountPercentage', Format(DiscountPct)); - CustomDimensions.Add('OrderNo', OrderNo); - - Telemetry.LogMessage('FEATURE-DISCOUNT', 'Discount applied', - Verbosity::Normal, DataClassification::SystemMetadata, - TelemetryScope::ExtensionPublisher, CustomDimensions); - end; - - procedure LogPaymentMethod(PaymentMethodCode: Code[10]; Amount: Decimal) - var - CustomDimensions: Dictionary of [Text, Text]; - begin - CustomDimensions.Add('FeatureArea', 'Payment'); - CustomDimensions.Add('PaymentMethod', PaymentMethodCode); - CustomDimensions.Add('AmountRange', GetAmountRange(Amount)); - - Telemetry.LogMessage('FEATURE-PAYMENT', 'Payment method used', - Verbosity::Normal, DataClassification::SystemMetadata, - TelemetryScope::ExtensionPublisher, CustomDimensions); - end; - - local procedure GetAmountRange(Amount: Decimal): Text - begin - case true of - Amount < 100: - exit('0-100'); - Amount < 1000: - exit('100-1000'); - Amount < 10000: - exit('1000-10000'); - else - exit('10000+'); - end; - end; -} -``` - -## Telemetry Best Practices - -### ✅ Do's - -**Use Consistent Event IDs** -``` -SALESORD-001: Processing started -SALESORD-002: Processing completed -SALESORD-E001: Processing error -SALESORD-W001: Validation warning -SALESORD-P001: Performance warning -``` - -**Use Meaningful Messages** -```al -// Good -Telemetry.LogMessage('SALESORD-001', 'Sales order processing started for large order', ...); - -// Bad -Telemetry.LogMessage('001', 'Started', ...); -``` - -**Include Helpful Custom Dimensions** -```al -CustomDimensions.Add('OrderNo', OrderNo); -CustomDimensions.Add('CustomerNo', CustomerNo); -CustomDimensions.Add('LineCount', Format(LineCount)); -CustomDimensions.Add('TotalAmount', Format(TotalAmount)); -CustomDimensions.Add('ProcessingDuration', Format(Duration)); -``` - -**Use Appropriate Data Classification** -```al -// Customer data -DataClassification::CustomerContent - -// System metrics -DataClassification::SystemMetadata - -// Organizational data -DataClassification::OrganizationIdentifiableInformation -``` - -### ❌ Don'ts - -**Don't Over-Log** -```al -// Too much logging -Telemetry.LogMessage('001', 'Line 1', ...); -Telemetry.LogMessage('002', 'Line 2', ...); -// Don't log every single step -``` - -**Don't Log Sensitive Data** -```al -// Bad - logging password -CustomDimensions.Add('Password', Password); - -// Bad - logging full credit card -CustomDimensions.Add('CreditCard', CreditCardNo); - -// Bad - logging personal data unnecessarily -CustomDimensions.Add('SSN', SSN); -``` - -**Don't Use Generic Messages** -```al -// Bad -Telemetry.LogMessage('001', 'Error', ...); - -// Good -Telemetry.LogMessage('SALESORD-E001', 'Failed to post sales order due to credit limit exceeded', ...); -``` - -## Advanced Telemetry Patterns - -### Pattern 1: Telemetry Wrapper -**Prompt**: -``` -Create a telemetry wrapper codeunit that simplifies logging for my extension. -Include helper methods for: -- LogStart/LogEnd (with automatic duration calculation) -- LogError (with automatic call stack) -- LogFeatureUsage -- LogPerformanceWarning - -Prefix all event IDs with my extension prefix "SALESEXT-" -``` - -### Pattern 2: Telemetry Constants -**Prompt**: -``` -Create a codeunit with constants for all my telemetry event IDs and messages. -This will ensure consistency and make it easier to update messages. -``` - -### Pattern 3: Contextual Telemetry -**Prompt**: -``` -Create a system that automatically adds common context to all telemetry: -- User ID (anonymized) -- Company name (anonymized) -- Extension version -- BC version -- Environment type (Production/Sandbox) - -Make this context added automatically to all telemetry calls. -``` - -## Monitoring and Analysis - -### Creating Useful Queries - -**Prompt**: -``` -Create KQL (Kusto Query Language) queries for Application Insights to analyze: - -1. Most common errors in the last 24 hours -2. Average processing duration by order size -3. Feature usage statistics -4. Performance trends over time -5. Error rate percentage - -Base the queries on the telemetry we added to the Sales Order Processor codeunit. -``` - -**Expected Result**: -```kql -// Most common errors in last 24 hours -traces -| where timestamp > ago(24h) -| where message startswith "SALESORD-E" -| summarize ErrorCount = count() by message, customDimensions.ErrorMessage -| order by ErrorCount desc -| take 10 - -// Average processing duration by line count -traces -| where message == "SALESORD-002" -| extend LineCount = toint(customDimensions.LineCount) -| extend Duration = toint(customDimensions.Duration) -| summarize AvgDuration = avg(Duration) by LineCountBucket = bin(LineCount, 5) -| order by LineCountBucket asc - -// Feature usage - discount types -traces -| where message == "FEATURE-DISCOUNT" -| summarize UsageCount = count() by DiscountType = tostring(customDimensions.DiscountType) -| order by UsageCount desc - -// Performance warnings -traces -| where message startswith "SALESORD-P" -| summarize WarningCount = count() by Operation = tostring(customDimensions.Operation), bin(timestamp, 1h) -| render timechart - -// Error rate percentage -let totalOrders = toscalar( - traces - | where message == "SALESORD-001" - | where timestamp > ago(24h) - | count -); -let failedOrders = toscalar( - traces - | where message startswith "SALESORD-E" - | where timestamp > ago(24h) - | count -); -print ErrorRate = (todouble(failedOrders) / todouble(totalOrders)) * 100 -``` - -## Practice Exercise - -Add comprehensive telemetry to this code: - -```al -codeunit 50200 "Inventory Adjustment Handler" -{ - procedure AdjustInventory(ItemNo: Code[20]; Quantity: Decimal; ReasonCode: Code[10]) - var - Item: Record Item; - ItemJnlLine: Record "Item Journal Line"; - begin - Item.Get(ItemNo); - - ItemJnlLine.Init(); - ItemJnlLine."Item No." := ItemNo; - ItemJnlLine.Quantity := Quantity; - ItemJnlLine."Reason Code" := ReasonCode; - ItemJnlLine.Insert(true); - - CODEUNIT.Run(CODEUNIT::"Item Jnl.-Post Line", ItemJnlLine); - end; -} -``` - -**Your Tasks**: -1. Add start/end telemetry -2. Add error handling and logging -3. Track performance -4. Log feature usage -5. Add appropriate custom dimensions -6. Create KQL queries for analysis - -## Next Steps - -- Learn about [refactoring legacy code](../refactoring) while adding telemetry -- See how [code review](../code-review) can catch telemetry issues -- Explore [testing strategies](../testing) for telemetry code diff --git a/content/docs/agentic-coding/GettingMore/testing.md b/content/docs/agentic-coding/GettingMore/testing.md deleted file mode 100644 index 3c9cfae7..00000000 --- a/content/docs/agentic-coding/GettingMore/testing.md +++ /dev/null @@ -1,902 +0,0 @@ ---- -title: "Writing Tests" -linkTitle: "Testing" -weight: 5 -description: > - Use AI to create comprehensive test coverage for your AL code ---- - -## Scenario - -You've developed new features for your Business Central extension, but you need comprehensive test coverage to: - -- Ensure code works as expected -- Prevent regressions when making changes -- Document expected behavior -- Enable confident refactoring -- Meet quality standards - -Writing tests manually is time-consuming, and you want to use AI to accelerate the process while maintaining test quality. - -## Goal - -Use AI to help you: - -- Generate unit tests for individual procedures -- Create integration tests for complex workflows -- Design test data and scenarios -- Write test helpers and fixtures -- Create mock objects for dependencies -- Implement data-driven tests - -## The Code to Test - -Here's a codeunit that needs test coverage: - -```al -codeunit 50100 "Order Discount Manager" -{ - procedure CalculateDiscount(var SalesHeader: Record "Sales Header"): Decimal - var - Customer: Record Customer; - DiscountPct: Decimal; - begin - if not Customer.Get(SalesHeader."Sell-to Customer No.") then - Error('Customer %1 not found', SalesHeader."Sell-to Customer No."); - - DiscountPct := GetCustomerDiscount(Customer); - DiscountPct += GetVolumeDiscount(SalesHeader); - DiscountPct += GetSeasonalDiscount(); - - if DiscountPct > 50 then - DiscountPct := 50; - - exit(DiscountPct); - end; - - local procedure GetCustomerDiscount(Customer: Record Customer): Decimal - begin - case Customer."Customer Discount Group" of - 'VIP': - exit(10); - 'PREMIUM': - exit(5); - else - exit(0); - end; - end; - - local procedure GetVolumeDiscount(SalesHeader: Record "Sales Header"): Decimal - var - SalesLine: Record "Sales Line"; - TotalAmount: Decimal; - begin - SalesLine.SetRange("Document Type", SalesHeader."Document Type"); - SalesLine.SetRange("Document No.", SalesHeader."No."); - SalesLine.CalcSums("Line Amount"); - TotalAmount := SalesLine."Line Amount"; - - case true of - TotalAmount >= 10000: - exit(15); - TotalAmount >= 5000: - exit(10); - TotalAmount >= 1000: - exit(5); - else - exit(0); - end; - end; - - local procedure GetSeasonalDiscount(): Decimal - begin - if Date2DMY(Today, 2) in [11, 12] then - exit(5); - exit(0); - end; -} -``` - -## Step-by-Step Testing Process - -### Step 1: Generate Basic Test Structure - -**Prompt**: - -``` -Create a test codeunit for the Order Discount Manager. - -Include: -- Proper test codeunit structure with Subtype = Test -- Setup and teardown if needed -- Test procedure stubs for all public procedures -- Use AL test framework conventions -- Include Assert codeunit -- Add test isolation attributes where needed - -[Include the codeunit code] -``` - -**Expected Result**: - -```al -codeunit 50101 "Order Discount Mgr. Tests" -{ - Subtype = Test; - - var - Assert: Codeunit "Library Assert"; - LibrarySales: Codeunit "Library - Sales"; - LibraryUtility: Codeunit "Library - Utility"; - IsInitialized: Boolean; - - [Test] - procedure TestCalculateDiscountBasic() - begin - // Test basic discount calculation - end; - - [Test] - procedure TestVIPCustomerDiscount() - begin - // Test VIP customer gets 10% discount - end; - - [Test] - procedure TestVolumeDiscountTiers() - begin - // Test volume discount calculation at different amounts - end; - - [Test] - procedure TestSeasonalDiscount() - begin - // Test seasonal discount in November/December - end; - - [Test] - procedure TestMaximumDiscountCap() - begin - // Test discount cannot exceed 50% - end; - - [Test] - procedure TestCustomerNotFoundError() - begin - // Test error when customer doesn't exist - end; - - local procedure Initialize() - begin - if IsInitialized then - exit; - - // Setup code here - IsInitialized := true; - end; -} -``` - -### Step 2: Implement Happy Path Test - -**Prompt**: - -``` -Implement the TestCalculateDiscountBasic test using the Given-When-Then pattern. - -This test should verify: -- A standard customer with a medium-size order -- Gets the expected discount percentage -- No errors are raised - -Use the Library codeunits for test data creation. -Include clear comments for each section. -``` - -**Expected Result**: - -```al -[Test] -procedure TestCalculateDiscountBasic() -var - Customer: Record Customer; - SalesHeader: Record "Sales Header"; - OrderDiscountMgr: Codeunit "Order Discount Manager"; - ActualDiscount: Decimal; - ExpectedDiscount: Decimal; -begin - // [SCENARIO] Calculate discount for a standard order - Initialize(); - - // [GIVEN] A standard customer with no special discount group - Customer.Get(LibrarySales.CreateCustomerNo()); - Customer."Customer Discount Group" := ''; - Customer.Modify(); - - // [GIVEN] A sales order with total amount of 2000 (5% volume discount) - SalesHeader := CreateSalesOrderWithAmount(Customer."No.", 2000); - - // [GIVEN] Expected discount is 5% (volume only, not in seasonal months) - ExpectedDiscount := 5; - - // [WHEN] Calculating the discount - ActualDiscount := OrderDiscountMgr.CalculateDiscount(SalesHeader); - - // [THEN] The discount matches expected value - Assert.AreEqual(ExpectedDiscount, ActualDiscount, 'Discount calculation incorrect'); -end; - -local procedure CreateSalesOrderWithAmount(CustomerNo: Code[20]; TotalAmount: Decimal): Record "Sales Header" -var - SalesHeader: Record "Sales Header"; - SalesLine: Record "Sales Line"; - Item: Record Item; -begin - LibrarySales.CreateSalesHeader(SalesHeader, SalesHeader."Document Type"::Order, CustomerNo); - - Item."No." := LibraryUtility.GenerateRandomCode(Item.FieldNo("No."), Database::Item); - Item."Unit Price" := TotalAmount; - Item.Insert(); - - LibrarySales.CreateSalesLine(SalesLine, SalesHeader, SalesLine.Type::Item, Item."No.", 1); - SalesLine.Validate("Unit Price", TotalAmount); - SalesLine.Modify(true); - - exit(SalesHeader); -end; -``` - -### Step 3: Implement Error Case Tests - -**Prompt**: - -``` -Implement the TestCustomerNotFoundError test. - -This should verify: -- When an invalid customer number is used -- An appropriate error is raised -- The error message contains the customer number - -Use asserterror and Assert.ExpectedError. -``` - -**Expected Result**: - -```al -[Test] -procedure TestCustomerNotFoundError() -var - SalesHeader: Record "Sales Header"; - OrderDiscountMgr: Codeunit "Order Discount Manager"; - InvalidCustomerNo: Code[20]; -begin - // [SCENARIO] Error is raised when customer doesn't exist - Initialize(); - - // [GIVEN] A sales header with non-existent customer - InvalidCustomerNo := 'INVALID123'; - LibrarySales.CreateSalesHeader(SalesHeader, SalesHeader."Document Type"::Order, ''); - SalesHeader."Sell-to Customer No." := InvalidCustomerNo; - SalesHeader.Modify(); - - // [WHEN] Calculating discount for non-existent customer - asserterror OrderDiscountMgr.CalculateDiscount(SalesHeader); - - // [THEN] Appropriate error is raised with customer number - Assert.ExpectedError(StrSubstNo('Customer %1 not found', InvalidCustomerNo)); -end; -``` - -### Step 4: Implement Data-Driven Tests - -**Prompt**: - -``` -Create a comprehensive test for all customer discount tiers (VIP, PREMIUM, standard). - -Use a data-driven approach where one test function tests all scenarios. -Create a local helper that takes customer group and expected discount as parameters. - -Test these scenarios: -- VIP customer: 10% discount -- PREMIUM customer: 5% discount -- Standard customer: 0% discount -- Empty group: 0% discount -``` - -**Expected Result**: - -```al -[Test] -procedure TestCustomerDiscountTiers() -begin - // [SCENARIO] Different customer types receive appropriate discounts - Initialize(); - - // [GIVEN] [WHEN] [THEN] VIP customers get 10% discount - VerifyCustomerTypeDiscount('VIP', 10); - - // [GIVEN] [WHEN] [THEN] Premium customers get 5% discount - VerifyCustomerTypeDiscount('PREMIUM', 5); - - // [GIVEN] [WHEN] [THEN] Standard customers get 0% discount - VerifyCustomerTypeDiscount('STANDARD', 0); - - // [GIVEN] [WHEN] [THEN] Customers with no group get 0% discount - VerifyCustomerTypeDiscount('', 0); -end; - -local procedure VerifyCustomerTypeDiscount(CustomerGroup: Code[10]; ExpectedDiscount: Decimal) -var - Customer: Record Customer; - SalesHeader: Record "Sales Header"; - OrderDiscountMgr: Codeunit "Order Discount Manager"; - ActualDiscount: Decimal; -begin - // Create customer with specific group - Customer.Get(LibrarySales.CreateCustomerNo()); - Customer."Customer Discount Group" := CustomerGroup; - Customer.Modify(); - - // Create order with amount below volume discount threshold (< 1000) - SalesHeader := CreateSalesOrderWithAmount(Customer."No.", 500); - - // Calculate discount - ActualDiscount := OrderDiscountMgr.CalculateDiscount(SalesHeader); - - // Verify expected discount - Assert.AreEqual(ExpectedDiscount, ActualDiscount, - StrSubstNo('Incorrect discount for customer group %1', CustomerGroup)); -end; -``` - -### Step 5: Test Edge Cases and Boundaries - -**Prompt**: - -``` -Create tests for volume discount boundary conditions. - -Test these specific amounts: -- 999: Just below 1000 threshold (expect 0% volume discount) -- 1000: Exactly at threshold (expect 5% volume discount) -- 4999: Just below 5000 threshold (expect 5% volume discount) -- 5000: Exactly at threshold (expect 10% volume discount) -- 9999: Just below 10000 threshold (expect 10% volume discount) -- 10000: Exactly at threshold (expect 15% volume discount) - -Use descriptive test names and clear assertions. -``` - -**Expected Result**: - -```al -[Test] -procedure TestVolumeDiscountBoundaries() -begin - // [SCENARIO] Volume discounts applied at exact thresholds - Initialize(); - - // Just below first threshold - VerifyVolumeDiscount(999, 0, 'Below 1000 threshold'); - - // Exactly at first threshold - VerifyVolumeDiscount(1000, 5, 'At 1000 threshold'); - - // Between first and second threshold - VerifyVolumeDiscount(4999, 5, 'Below 5000 threshold'); - - // Exactly at second threshold - VerifyVolumeDiscount(5000, 10, 'At 5000 threshold'); - - // Between second and third threshold - VerifyVolumeDiscount(9999, 10, 'Below 10000 threshold'); - - // Exactly at third threshold - VerifyVolumeDiscount(10000, 15, 'At 10000 threshold'); -end; - -local procedure VerifyVolumeDiscount(OrderAmount: Decimal; ExpectedVolumeDiscount: Decimal; Scenario: Text) -var - Customer: Record Customer; - SalesHeader: Record "Sales Header"; - OrderDiscountMgr: Codeunit "Order Discount Manager"; - ActualDiscount: Decimal; -begin - // Create standard customer (no customer discount) - Customer.Get(LibrarySales.CreateCustomerNo()); - Customer."Customer Discount Group" := ''; - Customer.Modify(); - - // Create order with specific amount - SalesHeader := CreateSalesOrderWithAmount(Customer."No.", OrderAmount); - - // Calculate discount (assuming not in seasonal months) - ActualDiscount := OrderDiscountMgr.CalculateDiscount(SalesHeader); - - // Verify volume discount - Assert.AreEqual(ExpectedVolumeDiscount, ActualDiscount, - StrSubstNo('Incorrect volume discount for %1 (amount: %2)', Scenario, OrderAmount)); -end; -``` - -### Step 6: Test Maximum Discount Cap - -**Prompt**: - -``` -Create a test that verifies the 50% maximum discount cap. - -Test scenario: -- VIP customer (10% discount) -- Large order (15% volume discount) -- Seasonal period (5% discount) -- Total would be 30%, which is under cap - verify this works -- Then test a scenario that would exceed 50% and verify it's capped - -Consider: What combination would exceed 50%? We may need to modify setup or mock GetSeasonalDiscount. -``` - -**Expected Result**: - -```al -[Test] -procedure TestMaximumDiscountCap() -var - Customer: Record Customer; - SalesHeader: Record "Sales Header"; - OrderDiscountMgr: Codeunit "Order Discount Manager"; - ActualDiscount: Decimal; -begin - // [SCENARIO] Discount is capped at 50% even when combined discounts exceed it - Initialize(); - - // [GIVEN] VIP customer (10% discount) - Customer.Get(LibrarySales.CreateCustomerNo()); - Customer."Customer Discount Group" := 'VIP'; - Customer.Modify(); - - // [GIVEN] Large order (15% volume discount) - SalesHeader := CreateSalesOrderWithAmount(Customer."No.", 10000); - - // [GIVEN] If testing in Nov/Dec, total would be 10+15+5=30% (under cap) - // [GIVEN] To test cap, we need a scenario that would exceed 50% - // Note: With current business logic, max is 30%, so cap isn't reached - - // [WHEN] Calculating discount - ActualDiscount := OrderDiscountMgr.CalculateDiscount(SalesHeader); - - // [THEN] Discount doesn't exceed 50% - Assert.IsTrue(ActualDiscount <= 50, 'Discount exceeds maximum allowed 50%'); -end; - -[Test] -procedure TestDiscountCapWithMultipleDiscounts() -var - Customer: Record Customer; - SalesHeader: Record "Sales Header"; - OrderDiscountMgr: Codeunit "Order Discount Manager"; - ActualDiscount: Decimal; -begin - // [SCENARIO] Combined discounts that would exceed 50% are capped - // [NOTE] This test may need the business logic modified to make it relevant - // Current max possible: 10% (VIP) + 15% (volume) + 5% (seasonal) = 30% - - Initialize(); - - // Create scenario with maximum possible discounts - Customer.Get(LibrarySales.CreateCustomerNo()); - Customer."Customer Discount Group" := 'VIP'; - Customer.Modify(); - - SalesHeader := CreateSalesOrderWithAmount(Customer."No.", 10000); - - // Calculate discount - ActualDiscount := OrderDiscountMgr.CalculateDiscount(SalesHeader); - - // Verify cap is applied if needed - Assert.IsTrue(ActualDiscount <= 50, 'Discount cap not enforced'); -end; -``` - -### Step 7: Test Time-Dependent Logic - -**Prompt**: - -``` -Create tests for the seasonal discount that depends on the current date. - -Since GetSeasonalDiscount uses Today, we need to: -1. Test behavior for November (month 11) -2. Test behavior for December (month 12) -3. Test behavior for other months - -Explain how to handle date-dependent tests in AL. -Options: -- Make the date a parameter (refactor code) -- Use test isolation / mocking -- Create tests that work year-round -``` - -**Expected Result**: - -```al -[Test] -procedure TestSeasonalDiscountInNovember() -var - Customer: Record Customer; - SalesHeader: Record "Sales Header"; - OrderDiscountMgr: Codeunit "Order Discount Manager"; - ActualDiscount: Decimal; - CurrentMonth: Integer; -begin - // [SCENARIO] Seasonal discount is applied in November - // [NOTE] This test only passes when run in November - - Initialize(); - - CurrentMonth := Date2DMY(Today, 2); - if CurrentMonth <> 11 then - exit; // Skip test if not November - - // [GIVEN] Standard customer with small order (no other discounts) - Customer.Get(LibrarySales.CreateCustomerNo()); - Customer."Customer Discount Group" := ''; - Customer.Modify(); - - SalesHeader := CreateSalesOrderWithAmount(Customer."No.", 500); - - // [WHEN] Calculating discount in November - ActualDiscount := OrderDiscountMgr.CalculateDiscount(SalesHeader); - - // [THEN] 5% seasonal discount is applied - Assert.AreEqual(5, ActualDiscount, 'Seasonal discount not applied in November'); -end; - -[Test] -procedure TestNoSeasonalDiscountInJanuary() -var - Customer: Record Customer; - SalesHeader: Record "Sales Header"; - OrderDiscountMgr: Codeunit "Order Discount Manager"; - ActualDiscount: Decimal; - CurrentMonth: Integer; -begin - // [SCENARIO] No seasonal discount in non-holiday months - - Initialize(); - - CurrentMonth := Date2DMY(Today, 2); - if CurrentMonth in [11, 12] then - exit; // Skip test if in seasonal period - - // [GIVEN] Standard customer with small order - Customer.Get(LibrarySales.CreateCustomerNo()); - Customer."Customer Discount Group" := ''; - Customer.Modify(); - - SalesHeader := CreateSalesOrderWithAmount(Customer."No.", 500); - - // [WHEN] Calculating discount outside seasonal period - ActualDiscount := OrderDiscountMgr.CalculateDiscount(SalesHeader); - - // [THEN] No discount is applied - Assert.AreEqual(0, ActualDiscount, 'Unexpected discount outside seasonal period'); -end; - -// [RECOMMENDATION] Refactor GetSeasonalDiscount to accept date parameter -// This would make testing much easier and more reliable: -// local procedure GetSeasonalDiscount(CheckDate: Date): Decimal -``` - -### Step 8: Integration Test - -**Prompt**: - -``` -Create an integration test that tests the complete workflow: - -1. Create a new customer with VIP status -2. Create a sales order for that customer -3. Add multiple lines totaling over 10,000 -4. Calculate the discount -5. Verify all discount types are correctly combined -6. Verify the discount is applied to the sales header - -This test should verify the codeunit works correctly with real BC data and tables. -``` - -**Expected Result**: - -```al -[Test] -procedure TestCompleteDiscountWorkflow() -var - Customer: Record Customer; - SalesHeader: Record "Sales Header"; - SalesLine: Record "Sales Line"; - Item: Record Item; - OrderDiscountMgr: Codeunit "Order Discount Manager"; - ExpectedDiscount: Decimal; - ActualDiscount: Decimal; - CurrentMonth: Integer; -begin - // [SCENARIO] Complete workflow of discount calculation and application - Initialize(); - - // [GIVEN] A VIP customer - Customer.Get(LibrarySales.CreateCustomerNo()); - Customer."Customer Discount Group" := 'VIP'; - Customer.Modify(); - - // [GIVEN] A sales order with multiple lines totaling 12,000 - LibrarySales.CreateSalesHeader(SalesHeader, SalesHeader."Document Type"::Order, Customer."No."); - - // Add three items - CreateItemAndSalesLine(SalesHeader, 5000); - CreateItemAndSalesLine(SalesHeader, 4000); - CreateItemAndSalesLine(SalesHeader, 3000); - - // [GIVEN] Expected discount calculation: - // - VIP customer: 10% - // - Volume (>10000): 15% - // - Seasonal (if Nov/Dec): 5% - CurrentMonth := Date2DMY(Today, 2); - if CurrentMonth in [11, 12] then - ExpectedDiscount := 30 // 10 + 15 + 5 - else - ExpectedDiscount := 25; // 10 + 15 - - // [WHEN] Calculating the discount - ActualDiscount := OrderDiscountMgr.CalculateDiscount(SalesHeader); - - // [THEN] The correct combined discount is calculated - Assert.AreEqual(ExpectedDiscount, ActualDiscount, - 'Complete discount calculation incorrect'); - - // [THEN] Verify order total is correct - SalesLine.SetRange("Document Type", SalesHeader."Document Type"); - SalesLine.SetRange("Document No.", SalesHeader."No."); - SalesLine.CalcSums("Line Amount"); - Assert.AreEqual(12000, SalesLine."Line Amount", 'Order total incorrect'); -end; - -local procedure CreateItemAndSalesLine(SalesHeader: Record "Sales Header"; Amount: Decimal) -var - Item: Record Item; - SalesLine: Record "Sales Line"; -begin - Item."No." := LibraryUtility.GenerateRandomCode(Item.FieldNo("No."), Database::Item); - Item."Unit Price" := Amount; - Item.Insert(); - - LibrarySales.CreateSalesLine(SalesLine, SalesHeader, SalesLine.Type::Item, Item."No.", 1); - SalesLine.Validate("Unit Price", Amount); - SalesLine.Modify(true); -end; -``` - -## Review Points: What to Check in Tests - -### ✅ Test Quality - -- [ ] Tests are independent (can run in any order) -- [ ] Tests are repeatable (same result every time) -- [ ] Tests have clear Given-When-Then structure -- [ ] Test names clearly describe what is being tested -- [ ] Assertions have descriptive error messages - -### ✅ Test Coverage - -- [ ] Happy path scenarios covered -- [ ] Error cases tested -- [ ] Edge cases and boundaries tested -- [ ] All public procedures have tests -- [ ] Integration scenarios tested - -### ✅ Test Maintainability - -- [ ] Helper methods reduce duplication -- [ ] Test data creation is centralized -- [ ] Tests are easy to understand -- [ ] Tests don't depend on specific data -- [ ] Setup and teardown properly implemented - -### ✅ Test Performance - -- [ ] Tests run quickly -- [ ] Minimal database operations -- [ ] Proper use of test isolation -- [ ] No unnecessary waits or delays - -## Advanced Testing Patterns - -### Pattern 1: Test Fixtures - -**Prompt**: - -``` -Create a test fixture class for sales orders that provides: -- Standard order (customer with no discounts, small amount) -- VIP order (VIP customer, medium amount) -- Large order (standard customer, large amount) -- Complex order (VIP customer, large amount, multiple lines) - -These fixtures should be reusable across all tests. -``` - -### Pattern 2: Mock Objects - -**Prompt**: - -``` -The GetSeasonalDiscount procedure is hard to test because it depends on Today. - -Refactor the code to use dependency injection: -1. Create an interface for date provider -2. Inject the date provider into the codeunit -3. Create a mock date provider for testing -4. Show how to test with different dates -``` - -### Pattern 3: Test Data Builders - -**Prompt**: - -``` -Create a fluent test data builder for sales orders: - -SalesOrderBuilder - .WithCustomer(CustomerNo) - .WithVIPStatus() - .WithLine(ItemNo, Quantity, Price) - .WithTotalAmount(Amount) - .Build() - -This makes test data creation more readable and flexible. -``` - -## Best Practices for AI-Assisted Testing - -### 1. Start with Test Cases, Then Generate - -``` -Before generating test code, help me identify all test cases for this procedure: -- What scenarios should be tested? -- What are the edge cases? -- What error conditions exist? -- What are the boundary conditions? - -[Include procedure code] -``` - -### 2. Generate Tests in Batches - -``` -Generate tests for these three related procedures together so they share test helpers: -- CalculateDiscount -- ApplyDiscount -- ValidateDiscount -``` - -### 3. Request Explanatory Comments - -``` -Generate the test with detailed comments explaining: -- Why this test case is important -- What could go wrong if this test didn't exist -- Any gotchas or special considerations -``` - -### 4. Ask for Test Improvements - -``` -Review this test I wrote. Suggest improvements for: -- Clarity and readability -- Better assertions -- Edge cases I might have missed -- Ways to make it more maintainable -``` - -### 5. Generate Test Documentation - -``` -Create documentation for this test suite explaining: -- What is being tested -- Test coverage summary -- How to run the tests -- How to add new tests -- Known limitations -``` - -## Common Testing Challenges - -### Challenge 1: Testing Private Methods - -**Problem**: Local procedures can't be tested directly - -**Solution**: - -``` -I need to test this local procedure. Options: -1. Make it public (if appropriate) -2. Test it indirectly through public procedures -3. Extract to a separate testable codeunit - -Which approach is best for this scenario? [Include code] -``` - -### Challenge 2: Testing Database Operations - -**Problem**: Tests that modify database are slow and fragile - -**Solution**: - -``` -This codeunit performs database operations. Help me: -1. Identify which operations need real database -2. Which can be mocked or isolated -3. Create a testing strategy that balances coverage and speed -``` - -### Challenge 3: Testing External Dependencies - -**Problem**: Code calls external services or APIs - -**Solution**: - -``` -This code calls an external API. Create: -1. An interface for the API -2. A mock implementation for testing -3. Tests using the mock -4. Integration tests for the real API (marked for manual runs) -``` - -## Practice Exercise - -Write comprehensive tests for this codeunit: - -```al -codeunit 50200 "Credit Limit Checker" -{ - procedure CheckCreditLimit(CustomerNo: Code[20]; NewOrderAmount: Decimal): Boolean - var - Customer: Record Customer; - CustLedgerEntry: Record "Cust. Ledger Entry"; - TotalOutstanding: Decimal; - begin - if not Customer.Get(CustomerNo) then - Error('Customer not found'); - - Customer.CalcFields("Balance (LCY)"); - TotalOutstanding := Customer."Balance (LCY)" + NewOrderAmount; - - if Customer."Credit Limit (LCY)" = 0 then - exit(true); - - exit(TotalOutstanding <= Customer."Credit Limit (LCY)"); - end; -} -``` - -**Your Tasks**: - -1. List all test scenarios -2. Create test codeunit structure -3. Implement tests for: - - Customer not found - - No credit limit (unlimited) - - Within credit limit - - Exactly at credit limit - - Over credit limit - - Edge cases -4. Add integration test -5. Review and improve tests - -## Next Steps - -- Learn how [code review](../code-review) can verify test quality -- See how [refactoring](../refactoring) benefits from good tests -- Explore [documentation](../documentation) for test procedures diff --git a/content/docs/agentic-coding/GettingStarted/_index.md b/content/docs/agentic-coding/GettingStarted/_index.md deleted file mode 100644 index 2d092a76..00000000 --- a/content/docs/agentic-coding/GettingStarted/_index.md +++ /dev/null @@ -1,20 +0,0 @@ ---- -title: "Getting Started" -linkTitle: "Getting Started" -weight: 10 -description: > - Essential concepts and practices for working with AI coding assistants ---- - -This section covers the fundamentals you need to start working effectively with AI-powered coding assistants in your AL development workflow. - -## In This Section - -- **[What is Agentic Coding](what-is-agentic-coding)** - Understanding the core concepts and benefits -- **[Glossary](glossary)** - Common terms and concepts explained -- **[Setting Up Your Environment](setup)** - Configure your development environment for AI assistance -- **[Effective Prompting](effective-prompting)** - Learn how to communicate clearly with AI assistants -- **[Best Practices](best-practices)** - Guidelines for successful AI-assisted development -- **[Understanding Limitations](limitations)** - Know when to use (and not use) AI assistance - -Start with understanding the concepts, then move through the practical setup and techniques to get the most value from your AI coding assistant. diff --git a/content/docs/agentic-coding/GettingStarted/best-practices.md b/content/docs/agentic-coding/GettingStarted/best-practices.md deleted file mode 100644 index 28698b30..00000000 --- a/content/docs/agentic-coding/GettingStarted/best-practices.md +++ /dev/null @@ -1,415 +0,0 @@ ---- -title: "Best Practices" -linkTitle: "Best Practices" -weight: 4 -description: > - Guidelines for successful AI-assisted AL development ---- - -## Overview - -AI coding assistants are powerful tools, but they work best when used thoughtfully. This guide provides best practices for integrating AI assistance into your AL development workflow. - -## General Principles - -### 1. AI Augments, Not Replaces -**You are still the developer.** The AI is a tool to enhance your productivity, not a replacement for your expertise. - -✅ **Good Approach**: -- Use AI to generate boilerplate code -- Review and understand all generated code -- Make architectural decisions yourself -- Validate business logic - -❌ **Poor Approach**: -- Blindly accept all AI suggestions -- Skip code review for AI-generated code -- Let AI make design decisions -- Assume AI understands your business requirements - -### 2. Trust, but Verify -Always review AI-generated code: - -```al -// AI might generate this: -procedure CalculateDiscount(Amount: Decimal): Decimal -begin - exit(Amount * 0.1); // Always 10% discount -end; - -// But you need to verify it matches requirements: -// - Is 10% correct for all scenarios? -// - Should it vary by customer type? -// - Are there discount limits? -// - Should it read from setup? -``` - -### 3. Provide Good Context -Better context = better results: - -✅ **Provide**: -- Clear file and folder names -- XML documentation comments -- Descriptive variable names -- Project README with conventions -- Open related files - -❌ **Avoid**: -- Generic names like `Temp1`, `DoStuff` -- Undocumented complex logic -- Mixing unrelated code in one file - -## Code Generation Best Practices - -### Start with Structure -Generate scaffolding first, then refine: - -1. **First**: Generate basic structure -``` -Create a codeunit skeleton for "Sales Order Processor" with procedures for: -- ValidateOrder -- CalculateTotals -- ProcessPayment -- PostOrder -``` - -2. **Then**: Implement each procedure -``` -Implement the ValidateOrder procedure with these checks: -- Customer exists -- All lines have positive quantities -- Credit limit not exceeded -``` - -### Review Generated Code -Check AI-generated code for: - -**Correctness** -- Does it do what you asked? -- Are there edge cases not handled? -- Is the logic sound? - -**AL Best Practices** -- Proper error handling -- Appropriate use of transactions -- Correct field validations -- No unnecessary database calls - -**Business Central Standards** -- Correct use of BC APIs -- Proper event patterns -- Standard naming conventions -- Application area settings - -**Performance** -- Efficient database queries -- Appropriate filtering -- Minimal record iterations -- Proper use of FindSet vs FindFirst - -### Iterate and Refine -Don't expect perfection on first try: - -``` -// Initial prompt -Create a procedure to import customers from CSV - -// After reviewing generated code -Add validation for required fields: Name and Email - -// After further review -Add error logging and return a list of failed imports - -// Final refinement -Add telemetry tracking for import metrics -``` - -## Code Review with AI - -### Use AI for Initial Review -AI can catch common issues: - -``` -Review this code for: -- Potential bugs -- Performance issues -- AL best practice violations -- Missing error handling -``` - -### Don't Skip Human Review -AI review is a supplement, not a replacement: - -- **AI catches**: Syntax issues, common patterns, style violations -- **You catch**: Business logic errors, architectural concerns, context-specific issues - -### Review AI's Review -The AI might miss context: - -```al -// AI might flag this as inefficient: -Customer.SetRange("No.", CustNo); -if Customer.FindFirst() then - Customer.Name := NewName; - -// But might miss that in your context, you're in a loop -// processing thousands of customers, which is inefficient -``` - -## Documentation with AI - -### Generate Drafts, Then Personalize -Use AI for documentation drafts: - -``` -Generate XML documentation for this codeunit -``` - -Then review and enhance: -- Add business context -- Include usage examples -- Document assumptions -- Note dependencies - -### Keep Documentation Updated -When AI generates code changes: - -``` -Update this procedure and its XML documentation to include the new parameter -``` - -### Create User-Facing Documentation -AI can help with user docs too: - -``` -Create user documentation explaining how to set up customer discount categories. -Target audience: Business users, not developers. -``` - -## Testing with AI - -### Generate Test Scaffolding -``` -Create a test codeunit structure for testing the Sales Order Processor -Include test methods for each public procedure -``` - -### Create Test Data Setup -``` -Create a helper procedure that sets up test data: -- One customer with normal credit limit -- One customer with exceeded credit -- Sample items with prices -- Sales header with lines -``` - -### Don't Rely Only on AI Tests -AI-generated tests might miss: -- Edge cases specific to your business -- Integration scenarios -- Performance testing needs -- User acceptance criteria - -## Refactoring with AI - -### Safe Refactoring Steps - -1. **Ensure Tests Exist** -``` -Create tests for this procedure before we refactor it -``` - -2. **Refactor with AI** -``` -Refactor this procedure to extract the discount calculation into a separate function -``` - -3. **Verify Tests Still Pass** -Run your test suite to confirm behavior unchanged - -4. **Review Changes** -Understand what changed and why - -### When to Refactor with AI -✅ **Good for**: -- Extracting methods -- Renaming variables -- Applying consistent formatting -- Adding error handling -- Modernizing deprecated APIs - -❌ **Be Careful with**: -- Complex business logic changes -- Architectural changes -- Database schema modifications -- Integration point changes - -## Learning from AI - -### Use AI as a Learning Tool - -**Ask for Explanations**: -``` -Explain why this code uses Commit instead of direct posting -``` - -**Request Alternatives**: -``` -Show me three different ways to implement this validation, -with pros and cons of each -``` - -**Learn Patterns**: -``` -Show me the standard AL pattern for implementing a document posting routine -``` - -### Build Your Knowledge -Don't become dependent: -- Understand the code, don't just use it -- Learn the patterns being used -- Research unfamiliar APIs or techniques -- Practice writing code without AI assistance - -## Performance Considerations - -### AI and Code Performance -AI doesn't automatically write optimal code: - -```al -// AI might generate this: -for i := 1 to Customer.Count do begin - Customer.Get(i); - ProcessCustomer(Customer); -end; - -// You should refactor to: -if Customer.FindSet() then - repeat - ProcessCustomer(Customer); - until Customer.Next() = 0; -``` - -### Review for Performance -Always check AI-generated code for: -- Database query efficiency -- Unnecessary loops -- Proper use of filters -- Appropriate use of temporary tables - -## Security Considerations - -### Don't Share Sensitive Data -Be careful what's in your workspace: -- Production connection strings -- Customer data -- API keys or secrets -- Proprietary algorithms - -### Review Security Aspects -AI might not catch security issues: - -```al -// AI might generate this: -procedure ExecuteSQL(SQLStatement: Text) -begin - // Direct SQL execution - potential SQL injection! -end; - -// You need to catch security concerns -``` - -## Collaboration Best Practices - -### Team Standards -Establish team guidelines: -- When to use AI assistance -- Required review process for AI code -- Documentation requirements -- Testing standards - -### Code Review Process -For AI-generated code: -1. Mark commits that include AI-generated code -2. Extra scrutiny during review -3. Explain AI usage in PR descriptions -4. Share learnings with the team - -### Knowledge Sharing -Help your team: -- Share effective prompts -- Document successful patterns -- Discuss AI limitations found -- Teach AI-assisted techniques - -## When NOT to Use AI - -### AI is Not Ideal For: - -**Critical Security Code** -- Authentication and authorization -- Encryption implementations -- Security-sensitive validations - -**Highly Specialized Logic** -- Unique business rules requiring deep domain knowledge -- Complex calculations with many edge cases -- Industry-specific compliance requirements - -**Exploration and Learning** -- When you're trying to learn a new concept -- When you need to deeply understand the solution -- When the journey is as important as the destination - -**Quick, Simple Tasks** -- You can type it faster than explaining it -- It's simpler to do it yourself -- The prompt would be longer than the code - -## Measuring Success - -### Track Your Productivity -Monitor how AI affects your work: -- Time saved on boilerplate code -- Reduction in syntax errors -- Faster documentation creation -- More time for design and testing - -### Quality Metrics -Ensure quality isn't suffering: -- Bug rates in AI-assisted code -- Code review findings -- Test coverage -- Performance benchmarks - -### Continuous Improvement -- Refine your prompting skills -- Learn from unsuccessful attempts -- Share successes with your team -- Update your practices as AI tools evolve - -## Quick Reference: Do's and Don'ts - -### ✅ Do -- Review all AI-generated code -- Provide clear, specific prompts -- Use AI for boilerplate and repetitive tasks -- Learn from AI-generated examples -- Test AI-generated code thoroughly -- Keep documentation updated -- Share knowledge with your team - -### ❌ Don't -- Blindly accept AI suggestions -- Skip code review for AI code -- Include sensitive data in prompts -- Rely on AI for architectural decisions -- Use AI-generated code you don't understand -- Assume AI knows your business requirements -- Let AI replace your expertise - -## Next Steps - -- Understand [AI limitations](../limitations) to know when caution is needed -- Try the [practical examples](../../gettingmore) to apply these best practices -- Explore [community resources](../../community-resources) for more tips and techniques diff --git a/content/docs/agentic-coding/GettingStarted/effective-prompting.md b/content/docs/agentic-coding/GettingStarted/effective-prompting.md deleted file mode 100644 index d91ba1ab..00000000 --- a/content/docs/agentic-coding/GettingStarted/effective-prompting.md +++ /dev/null @@ -1,358 +0,0 @@ ---- -title: "Effective Prompting" -linkTitle: "Effective Prompting" -weight: 3 -description: > - Learn how to communicate clearly with AI assistants to get the best results ---- - -## Overview - -The quality of AI-generated code depends heavily on how you communicate your needs. This guide teaches you how to write effective prompts that lead to better results. - -## The Basics of Good Prompts - -### Be Specific -Vague prompts lead to generic results. Provide clear, specific instructions. - -❌ **Vague**: "Create a page" -``` -Create a page -``` - -✅ **Specific**: "Create a card page for Customer with fields No, Name, Address, and Phone Number" -``` -Create a card page for the Customer table that displays these fields: -- No. -- Name -- Address -- Phone No. -Include FactBoxes for Sales Statistics and Contact Information. -``` - -### Provide Context -Help the AI understand what you're working on. - -❌ **No Context**: "Add a field" -``` -Add a field to store email -``` - -✅ **With Context**: "Add an email field to the Customer table extension for newsletter subscriptions" -``` -I'm extending the Customer table. Add a new field called "Newsletter Email" to store -the email address customers want to use for newsletters. This is separate from their -primary email. Make it a Text field with length 80. -``` - -### Include Examples -Show the AI what you want by providing examples. - -✅ **With Example**: -``` -Create an event subscriber for OnAfterValidate on Sales Header's "Sell-to Customer No." -field, similar to this pattern: - -[EventSubscriber(ObjectType::Table, Database::"Sales Header", 'OnAfterValidateEvent', 'Sell-to Customer No.', false, false)] -local procedure OnAfterValidateSellToCustomerNo(var Rec: Record "Sales Header") -begin - // Your implementation here -end; - -The subscriber should copy the Newsletter Email from the Customer to the Sales Header. -``` - -## Prompting Patterns for AL Development - -### 1. Code Generation - -**Pattern**: `Create a [object type] that [does what] with [specific requirements]` - -**Example**: -``` -Create a codeunit named "Sales Order Validator" that validates sales orders before posting. -It should: -- Check that all lines have quantities > 0 -- Verify customer credit limit is not exceeded -- Ensure all required fields are filled -- Return a list of validation errors -Use AL coding best practices. -``` - -### 2. Code Explanation - -**Pattern**: `Explain [what] in [level of detail]` - -**Examples**: -``` -// Simple explanation -Explain what this function does - -// Detailed explanation -Explain this procedure in detail, including the purpose of each parameter -and the business logic flow - -// For learning -Explain this code as if I'm new to AL development -``` - -### 3. Code Improvement - -**Pattern**: `Improve this code by [what to improve]` - -**Examples**: -``` -Improve this code to follow AL best practices - -Refactor this procedure to be more performant - -Add error handling to this code - -Make this code more testable by reducing dependencies -``` - -### 4. Code Review - -**Pattern**: `Review this code for [specific concerns]` - -**Examples**: -``` -Review this code for potential bugs and performance issues - -Check this code against AL coding guidelines - -Identify security concerns in this procedure - -Find opportunities to reduce database calls in this code -``` - -### 5. Documentation - -**Pattern**: `Generate [documentation type] for [what]` - -**Examples**: -``` -Generate XML documentation comments for all procedures in this file - -Create a README explaining what this extension does and how to install it - -Write user documentation for this new feature -``` - -### 6. Testing - -**Pattern**: `Create tests for [what] that [test scenarios]` - -**Example**: -``` -Create test codeunit for the Sales Order Validator that tests: -- Valid orders pass validation -- Orders with zero quantities fail -- Orders exceeding credit limit fail -- All validation error messages are correct -Use the AL Test framework with Given-When-Then pattern. -``` - -## Advanced Prompting Techniques - -### Chain of Thought -Break complex requests into steps: - -``` -I need to create a new feature for automatic discount calculation. Let's approach this step by step: - -1. First, create a table extension for Sales Line to store discount category -2. Then, create a discount setup table with categories and percentages -3. Next, create a codeunit to calculate discounts based on category -4. Finally, add an event subscriber to apply discounts automatically - -Let's start with step 1... -``` - -### Constraints and Requirements -Be explicit about what you do and don't want: - -``` -Create a procedure to import customer data from CSV. -Requirements: -- Use streams for large file handling -- Validate email format before importing -- Skip duplicate records (based on external ID) -- Log errors but continue processing -- Return summary of imported, skipped, and failed records -Do NOT: -- Use temporary files -- Import if any record fails validation -- Modify existing customer records -``` - -### Reference Standards -Point to specific coding standards or patterns: - -``` -Create a page extension following the AL coding standards in this repository. -Use the same XML documentation pattern as in CustomerProcessor.codeunit.al. -Follow the naming conventions in our README.md. -``` - -### Iterative Refinement -Start broad, then refine: - -``` -// First prompt -Create a codeunit to process sales orders - -// After seeing initial result, refine -Add error handling using try-catch pattern - -// Further refinement -Add logging using AL telemetry - -// Final touch -Add XML documentation comments -``` - -## AL-Specific Prompting Tips - -### Specify AL Version -``` -Create an AL procedure compatible with Business Central version 21 -``` - -### Mention Dependencies -``` -Create a page that uses the "Temp Blob" codeunit from the System Application -``` - -### Include Object Numbers (if applicable) -``` -Create table 50100 "Custom Discount Setup" with fields... -``` - -### Specify Application Area -``` -Create a page with ApplicationArea set to #Basic,#Suite -``` - -### Reference Standard BC Objects -``` -Create a table extension for table 18 "Customer" that adds... -``` - -## Common Mistakes to Avoid - -### ❌ Too Vague -``` -Make it better -Fix this -Create something for customers -``` - -### ❌ Asking Multiple Unrelated Things -``` -Create a customer page, fix the sales order bug, and document the project -``` -*Better*: Break into separate prompts - -### ❌ Assuming Too Much Context -``` -Add the field we discussed -``` -*Better*: Restate what you need - -### ❌ No Validation Criteria -``` -Create a validation function -``` -*Better*: Specify what to validate and how - -## Examples of Great Prompts - -### Example 1: Table Extension -``` -Create a table extension for Table 36 "Sales Header" that adds these fields: -- "Requested Delivery Date" (Date) -- "Special Instructions" (Text[250]) -- "Requires Approval" (Boolean) - -Add triggers: -- Set "Requires Approval" to true when amount exceeds $10,000 -- Validate "Requested Delivery Date" is not in the past - -Include XML documentation for all fields. -``` - -### Example 2: API Page -``` -Create an API page for the Item table that exposes: -- No. -- Description -- Unit Price -- Inventory - -Follow AL API best practices: -- Use API versioning (v1.0) -- Include OData annotations -- Handle GET, POST, PATCH methods -- Validate required fields on POST -``` - -### Example 3: Test Code -``` -Create a test codeunit for the "Sales Order Validator" codeunit. -Tests needed: -1. TestValidOrderPassesValidation - Create valid order, verify no errors -2. TestZeroQuantityFails - Create order with 0 quantity, verify error -3. TestCreditLimitExceeded - Create order exceeding limit, verify error -4. TestMissingRequiredField - Skip required field, verify error - -Use: -- [Test] attribute -- Given-When-Then pattern -- LibrarySales for test data -- Assert for verification -``` - -## Practice Exercise - -Try improving this vague prompt: - -❌ **Vague**: -``` -Create code for discounts -``` - -✅ **Improved Version** (your attempt): -``` -[Think about: What type of code? What discounts? What should it do? -What are the requirements? What standards should it follow?] -``` - -
-See Suggested Answer - -``` -Create a codeunit "Customer Discount Manager" that calculates volume-based discounts. - -Requirements: -- Accept parameters: Customer No., Item No., Quantity -- Return: Discount percentage (Decimal) -- Business logic: - * 0-10 units: No discount - * 11-50 units: 5% discount - * 51-100 units: 10% discount - * 100+ units: 15% discount -- Read discount tiers from a setup table -- Log calculation to telemetry -- Include error handling for invalid inputs -- Add XML documentation -- Follow AL best practices for procedure naming and structure -``` -
- -## Next Steps - -Now that you know how to write effective prompts: -- Review the [best practices](../best-practices) for AI-assisted development -- Try the [practical examples](../../gettingmore) with your new prompting skills -- Understand the [limitations](../limitations) of AI assistants diff --git a/content/docs/agentic-coding/GettingStarted/glossary.md b/content/docs/agentic-coding/GettingStarted/glossary.md deleted file mode 100644 index ebd356af..00000000 --- a/content/docs/agentic-coding/GettingStarted/glossary.md +++ /dev/null @@ -1,213 +0,0 @@ ---- -title: "Glossary" -linkTitle: "Glossary" -weight: 6 -description: > - Common terms and concepts in AI-assisted development ---- - -## AI & Coding Assistant Terms - -### Agent / Agentic AI -An AI system that can take actions autonomously, make decisions, and use tools to accomplish tasks. In coding, an agentic AI can read files, write code, run commands, and iterate on solutions without constant human intervention. - -### AI Assistant / Coding Assistant -Software that uses artificial intelligence to help you write code. Examples include GitHub Copilot, Claude, ChatGPT, Cursor, and Windsurf. - -### Context -Information the AI has access to when responding to your request. This can include: -- Your current file and selection -- Open files in your workspace -- Previous conversation messages -- Project structure and files -- Tools and external data sources - -**Why it matters**: The more relevant context the AI has, the better its responses. Limited context can lead to generic or incorrect suggestions. - -### Context Window -The maximum amount of text (measured in tokens) an AI can process at once. Think of it as the AI's "working memory." - -**Example**: A 200K token context window can hold roughly 150,000 words of text—about 300 pages. - -### Hallucination -When an AI generates information that sounds plausible but is incorrect or fabricated. This can include: -- Non-existent AL objects or methods -- Made-up API endpoints -- Incorrect syntax or patterns - -**How to avoid**: Always verify AI suggestions, especially for critical code or unfamiliar APIs. - -### Inference -The process of an AI model generating a response to your input. Each time you send a prompt and get a response, that's one inference. - -### Large Language Model (LLM) -The AI technology powering coding assistants. LLMs are trained on vast amounts of text (including code) to understand and generate human-like responses. - -**Examples**: GPT-4, Claude 3.5 Sonnet, Llama, Gemini - -### Model -The specific AI system you're interacting with. Different models have different capabilities, strengths, and context windows. - -**Examples**: -- Claude 3.5 Sonnet (good at code and reasoning) -- GPT-4o (fast, multimodal) -- o1 (optimized for complex reasoning) - ---- - -## Prompting & Communication - -### Prompt -Your input or question to the AI. A prompt can be: -- A question: "How do I post a sales invoice in AL?" -- A command: "Add error handling to this function" -- A request: "Refactor this code to use modern AL patterns" - -**Tip**: Clear, specific prompts get better results than vague ones. - -### System Prompt / Instructions -Background instructions that guide the AI's behavior and personality. You typically don't see these, but they tell the AI how to respond (e.g., "You are a Business Central expert," "Be concise," "Provide code examples"). - -### Few-Shot / One-Shot Prompting -Providing examples in your prompt to guide the AI's response format. - -**Example**: -``` -Create getter methods like this example: -procedure GetCustomerName(): Text[100] -begin - exit("Customer Name"); -end - -Now create a getter for "Customer Email" -``` - -### Chain of Thought -Asking the AI to explain its reasoning step-by-step before providing an answer. This often improves accuracy for complex problems. - -**Example**: "Let's think through how to design this posting routine step by step..." - ---- - -## Technical Terms - -### Token -The basic unit of text that AI models process. Roughly: -- 1 token ≈ 4 characters in English -- 1 token ≈ ¾ of a word -- 100 tokens ≈ 75 words - -**Why it matters**: Context windows, pricing, and API limits are measured in tokens. - -### Tool / Tool Calling / Function Calling -External capabilities the AI can use to perform actions beyond text generation: -- Read and write files -- Run terminal commands -- Search the web -- Query databases -- Execute MCP server tools - -**Example**: When you ask "What's in my app.json?", the AI uses a "read file" tool rather than guessing. - -### MCP (Model Context Protocol) -An open standard for connecting AI assistants to external tools and data sources. MCP servers expose capabilities (like AL symbol databases, Azure DevOps, time tracking) that AI assistants can use. - -**Example**: The AL Dependency MCP Server lets your AI assistant search compiled AL packages. - -### RAG (Retrieval-Augmented Generation) -A technique where the AI retrieves relevant information from external sources before generating a response. This helps provide accurate, up-to-date information beyond the AI's training data. - -**Example**: BC Code Intelligence MCP uses RAG to fetch specific Business Central knowledge topics. - -### Temperature -A setting that controls how creative or deterministic the AI's responses are: -- **Low temperature (0.0-0.3)**: Focused, consistent, predictable—good for code generation -- **High temperature (0.7-1.0)**: Creative, varied, exploratory—good for brainstorming - -### Embeddings -Mathematical representations of text that capture semantic meaning. Used to find relevant information by similarity rather than exact keyword matches. - -**Example**: Searching for "customer posting" would find content about "posting customer transactions" even without exact word matches. - ---- - -## AL & Business Central Specific - -### AL Language Server -A background service that provides intelligent code features for AL: -- Code completion -- Go to definition -- Find references -- Syntax checking - -**Note**: Some MCP servers (like Serena) integrate with the AL Language Server to give AI assistants these capabilities. - -### Symbol -In AL, a symbol is any named code element: -- Objects (tables, pages, codeunits) -- Fields -- Procedures -- Variables - -### .app Package -A compiled AL extension package containing symbols and metadata. AI assistants can't read these directly, which is why tools like AL Dependency MCP Server exist. - -### Object ID -The numeric identifier for AL objects (tables, pages, codeunits, etc.). Managing these IDs across teams requires coordination to avoid conflicts—that's where AL Object ID Ninja MCP helps. - ---- - -## Development Workflow - -### Pair Programming -A development practice where two people work together on the same code. With AI assistants, you're essentially pair programming with an AI partner. - -### Code Review -Examining code to find issues, ensure quality, and share knowledge. AI assistants can help with code review by analyzing patterns, suggesting improvements, and catching common mistakes. - -### Refactoring -Improving code structure and readability without changing its behavior. AI assistants excel at refactoring tasks like renaming, extracting methods, and modernizing patterns. - -### Test-Driven Development (TDD) -Writing tests before writing the code that satisfies them. AI assistants can help generate test cases and implementations. - ---- - -## Common Acronyms - -| Term | Meaning | -|------|---------| -| **AI** | Artificial Intelligence | -| **LLM** | Large Language Model | -| **MCP** | Model Context Protocol | -| **NLP** | Natural Language Processing | -| **RAG** | Retrieval-Augmented Generation | -| **TDD** | Test-Driven Development | -| **LSP** | Language Server Protocol | -| **IDE** | Integrated Development Environment | -| **API** | Application Programming Interface | -| **CRUD** | Create, Read, Update, Delete | -| **CLI** | Command Line Interface | -| **PAT** | Personal Access Token | - ---- - -## Tips for Learning the Language - -**Don't worry about knowing everything!** Start with these core concepts: -- **Prompt**: What you say to the AI -- **Context**: What information the AI can see -- **Token**: How AI text is measured -- **Hallucination**: When AI makes things up -- **Tool**: Actions the AI can take (like reading files) - -As you work with AI assistants, you'll naturally pick up more terminology. The important thing is understanding how to communicate effectively and knowing when to verify AI suggestions. - ---- - -## Related Resources - -- **[What is Agentic Coding](../what-is-agentic-coding)** - Core concepts explained -- **[Effective Prompting](../effective-prompting)** - How to communicate with AI -- **[Understanding Limitations](../limitations)** - What AI can and can't do -- **[Tools & MCP Servers](../../communityresources/tools)** - Extending AI capabilities diff --git a/content/docs/agentic-coding/GettingStarted/limitations.md b/content/docs/agentic-coding/GettingStarted/limitations.md deleted file mode 100644 index 3d78b238..00000000 --- a/content/docs/agentic-coding/GettingStarted/limitations.md +++ /dev/null @@ -1,473 +0,0 @@ ---- -title: "Understanding Limitations" -linkTitle: "Limitations" -weight: 5 -description: > - Know when to use (and not use) AI assistance in AL development ---- - -## Overview - -AI coding assistants are powerful tools, but they have limitations. Understanding these limitations helps you use AI effectively and avoid common pitfalls. - -## Knowledge Limitations - -### Training Data Cutoff -AI models are trained on data up to a specific date: - -**Implication**: -- May not know about the latest AL features -- Might suggest deprecated APIs -- Could miss recent Business Central updates -- May not be aware of new best practices - -**What to Do**: -- Verify suggestions against current documentation -- Check for deprecated features -- Stay updated on BC releases yourself -- Supplement AI with official Microsoft docs - -### Lack of Real-Time Information -AI doesn't know: -- Your specific BC version and configuration -- Your organization's custom extensions -- Your specific business requirements -- Current state of your codebase - -**What to Do**: -- Provide context in your prompts -- Specify BC version when relevant -- Describe dependencies and extensions -- Share organizational standards - -### Incomplete AL Knowledge -AI might not fully understand: -- Complex AL compiler behavior -- Subtle differences between AL versions -- Specific BC platform limitations -- Performance characteristics of certain operations - -**What to Do**: -- Test generated code thoroughly -- Verify with official documentation -- Profile performance-critical code -- Consult AL experts for complex scenarios - -## Code Quality Limitations - -### May Generate Suboptimal Code - -**Example 1: Inefficient Database Access** -```al -// AI might generate: -procedure CountCustomersInCity(CityName: Text): Integer -var - Customer: Record Customer; - Counter: Integer; -begin - Counter := 0; - if Customer.FindSet() then - repeat - if Customer.City = CityName then - Counter += 1; - until Customer.Next() = 0; - exit(Counter); -end; - -// Better approach: -procedure CountCustomersInCity(CityName: Text): Integer -var - Customer: Record Customer; -begin - Customer.SetRange(City, CityName); - exit(Customer.Count); -end; -``` - -**Example 2: Missing Error Handling** -```al -// AI might generate: -procedure GetCustomerEmail(CustomerNo: Code[20]): Text -var - Customer: Record Customer; -begin - Customer.Get(CustomerNo); - exit(Customer."E-Mail"); -end; - -// Should include error handling: -procedure GetCustomerEmail(CustomerNo: Code[20]): Text -var - Customer: Record Customer; -begin - if not Customer.Get(CustomerNo) then - Error('Customer %1 does not exist.', CustomerNo); - - if Customer."E-Mail" = '' then - Error('Customer %1 has no email address.', CustomerNo); - - exit(Customer."E-Mail"); -end; -``` - -### May Not Follow Your Standards -AI doesn't automatically know: -- Your naming conventions -- Your code organization preferences -- Your error handling patterns -- Your logging standards - -**What to Do**: -- Include standards in prompts -- Create prompt templates -- Maintain coding guidelines document -- Review and adapt generated code - -### May Create Inconsistent Code -AI might: -- Use different patterns across files -- Mix coding styles -- Apply inconsistent naming -- Vary error handling approaches - -**What to Do**: -- Establish clear patterns early -- Refactor for consistency -- Use linters and code analyzers -- Conduct thorough code reviews - -## Business Logic Limitations - -### No Domain Knowledge -AI doesn't understand: -- Your specific business processes -- Industry regulations you must follow -- Your customers' needs -- Your company's policies - -**Example**: -``` -You ask: "Create discount calculation logic" - -AI generates: 10% flat discount - -But you need: -- Tiered discounts by volume -- Special rates for preferred customers -- Regional pricing variations -- Promotional discounts -- Loyalty program integration -``` - -**What to Do**: -- Provide detailed business requirements -- Include business rules in prompts -- Review logic for business correctness -- Validate with business stakeholders - -### Can't Make Business Decisions -AI shouldn't decide: -- Which features to implement -- How to prioritize requirements -- What trade-offs to make -- Which approach best fits your needs - -**You must decide**: -- Architecture and design -- Feature scope -- Performance vs. complexity trade-offs -- User experience choices - -## Technical Limitations - -### Context Window Limitations -AI can only see: -- A limited amount of code at once -- Recently opened files -- Content you explicitly share - -**Implications**: -- Might miss dependencies in other files -- May not see full context of large codebases -- Could suggest code that conflicts with other parts - -**What to Do**: -- Keep related files open -- Provide context in prompts -- Reference specific files and procedures -- Review for integration issues - -### Can't Execute or Test Code -AI can't: -- Run your code -- Execute tests -- Connect to your database -- Verify actual behavior - -**Implications**: -- Might generate syntactically correct but broken code -- Can't verify business logic works -- Won't catch runtime errors -- Can't validate performance - -**What to Do**: -- Always test generated code -- Run your test suite -- Verify in actual BC environment -- Profile performance-critical code - -### Can't Access External Systems -AI doesn't know about: -- Your database state -- External APIs you integrate with -- Third-party extensions installed -- Network or security constraints - -**What to Do**: -- Document external dependencies -- Test integrations thoroughly -- Verify API compatibility -- Check security implications - -## Safety and Security Limitations - -### Limited Security Awareness -AI might not catch: -- SQL injection vulnerabilities -- Authorization bypass issues -- Data leakage risks -- Insecure data handling - -**Example**: -```al -// AI might generate: -procedure RunDynamicQuery(FilterText: Text) -begin - // Could be SQL injection risk if FilterText comes from user - Customer.SetFilter(City, FilterText); -end; - -// Need to add validation: -procedure RunDynamicQuery(FilterText: Text) -begin - ValidateFilterInput(FilterText); // Add validation - Customer.SetFilter(City, FilterText); -end; -``` - -**What to Do**: -- Security review all generated code -- Validate inputs from users -- Follow security best practices -- Consult security experts - -### Privacy Concerns -Be careful not to share: -- Customer data -- Production database content -- API keys or credentials -- Proprietary business logic - -**What to Do**: -- Use sample data in prompts -- Sanitize code before sharing -- Review organizational policies -- Use private AI instances if available - -## Reliability Limitations - -### Inconsistent Results -AI might: -- Give different answers to same question -- Vary quality across generations -- Make occasional "hallucinations" -- Provide confident but wrong information - -**What to Do**: -- Verify all suggestions -- Don't assume correctness -- Cross-check with documentation -- Regenerate if quality is poor - -### Can Make Mistakes -AI can: -- Misunderstand requirements -- Make logical errors -- Suggest deprecated features -- Create subtle bugs - -**Real Examples**: -```al -// AI might confuse similar concepts: -// You ask for "customer balance" -// It generates code for "customer credit limit" - -// AI might mix AL versions: -// Suggest AL syntax not available in your BC version - -// AI might misapply patterns: -// Use patterns from C# instead of AL conventions -``` - -**What to Do**: -- Treat AI as a junior developer -- Review everything carefully -- Test thoroughly -- Validate assumptions - -## Workflow Limitations - -### Can't Handle Complex Refactoring -AI struggles with: -- Large-scale architecture changes -- Multi-file refactoring -- Complex dependency updates -- Breaking changes across modules - -**What to Do**: -- Break into smaller steps -- Do complex refactoring manually -- Use AI for individual pieces -- Plan architecture yourself - -### Limited Long-Term Memory -AI doesn't remember: -- Previous conversations (in some tools) -- Decisions made earlier in project -- Your preferences over time -- Context from last week - -**What to Do**: -- Restate context when needed -- Document decisions -- Include relevant background in prompts -- Don't assume AI remembers - -### Can't Collaborate Directly -AI can't: -- Participate in code reviews -- Attend planning meetings -- Discuss with stakeholders -- Make consensus decisions - -**What to Do**: -- Use AI for preparation -- Review AI suggestions with team -- Make collaborative decisions yourself -- Document team agreements - -## When to Be Extra Careful - -### High-Risk Scenarios - -**Financial Calculations** -``` -Extra vigilance needed for: -- Payment processing -- Tax calculations -- Currency conversions -- Pricing logic -``` - -**Compliance and Audit** -``` -Careful review for: -- Regulatory compliance code -- Audit trail functionality -- Data retention policies -- Access control -``` - -**Data Integrity** -``` -Thorough testing for: -- Database modifications -- Data migrations -- Batch processing -- Transaction handling -``` - -**Integration Points** -``` -Extensive validation for: -- API integrations -- Web service calls -- External system connections -- Data synchronization -``` - -## Recognizing AI Limitations - -### Warning Signs - -**The AI:** -- Gives very generic solutions -- Doesn't ask clarifying questions -- Suggests deprecated features -- Provides inconsistent answers -- Seems overly confident about uncertain things -- Generates syntactically correct but illogical code - -**What to Do:** -- Seek second opinion -- Consult documentation -- Ask a colleague -- Test more thoroughly -- Provide more context -- Try rephrasing prompt - -## Complementing AI with Other Resources - -### Use Multiple Sources - -**For Learning:** -- Official Microsoft Learn -- BC documentation -- Community blogs -- Training courses - -**For Problem Solving:** -- Microsoft Docs -- Community forums -- Stack Overflow -- Colleague expertise - -**For Best Practices:** -- AL Guidelines (this site!) -- Microsoft patterns -- Community standards -- Team conventions - -**For Validation:** -- Code analyzers -- Test frameworks -- Peer review -- Static analysis tools - -## The Bottom Line - -### AI is a Tool, Not a Solution -- Use it to augment your skills -- Don't rely on it exclusively -- Maintain your expertise -- Stay critical and thoughtful - -### Your Responsibilities Remain -- Understand the code -- Ensure correctness -- Maintain quality -- Make decisions -- Own the results - -### Continuous Learning -- AI tools will improve -- Your skills must keep pace -- Learn from AI's mistakes -- Evolve your practices - -## Next Steps - -Now that you understand AI limitations: -- Apply this knowledge in the [practical examples](../../gettingmore) -- See how to work within these limitations in [best practices](../best-practices) -- Explore [community resources](../../community-resources) for more insights diff --git a/content/docs/agentic-coding/GettingStarted/setup.md b/content/docs/agentic-coding/GettingStarted/setup.md deleted file mode 100644 index cd5b007f..00000000 --- a/content/docs/agentic-coding/GettingStarted/setup.md +++ /dev/null @@ -1,226 +0,0 @@ ---- -title: "Setting Up Your Environment" -linkTitle: "Setup" -weight: 2 -description: > - Configure your development environment for optimal AI-assisted AL development ---- - -## Overview - -To get the most out of AI-powered coding assistance for AL development, you'll need to set up your environment properly. This guide covers the essential tools and configurations. - -## Prerequisites - -### Required -- **Visual Studio Code**: The primary IDE for AL development -- **AL Language Extension**: Microsoft's official AL extension for VS Code -- **AI Coding Assistant**: One of the following: - - GitHub Copilot - - GitHub Copilot Chat - - Other compatible AI assistants - -### Recommended -- **Git**: For version control and change tracking -- **AL Test Runner**: For running and managing tests -- **Business Central Docker Container**: For local development and testing - -## Installing GitHub Copilot - -GitHub Copilot is one of the most popular AI assistants for coding: - -1. **Sign up for GitHub Copilot** - - Visit [GitHub Copilot](https://github.com/features/copilot) - - Choose a subscription plan (free trial available) - -2. **Install the VS Code Extension** - - Open VS Code - - Go to Extensions (Ctrl+Shift+X) - - Search for "GitHub Copilot" - - Install both: - - GitHub Copilot - - GitHub Copilot Chat - -3. **Sign In** - - Click "Sign in to GitHub" when prompted - - Authorize the extension - -## Configuring VS Code for AL + AI - -### Workspace Settings - -Add these settings to your workspace `.vscode/settings.json`: - -```json -{ - // AL Language settings - "al.enableCodeAnalysis": true, - "al.codeAnalyzers": ["${CodeCop}", "${PerTenantExtensionCop}", "${UICop}"], - - // GitHub Copilot settings - "github.copilot.enable": { - "*": true, - "al": true - }, - - // Editor settings for better AI integration - "editor.inlineSuggest.enabled": true, - "editor.quickSuggestions": { - "other": true, - "comments": true, - "strings": true - } -} -``` - -### AL Project Structure - -Organize your AL project for better AI context: - -``` -MyExtension/ -├── .vscode/ -│ ├── settings.json -│ └── launch.json -├── src/ -│ ├── Tables/ -│ ├── Pages/ -│ ├── Codeunits/ -│ ├── Reports/ -│ └── ... -├── test/ -│ └── ... -├── app.json -└── README.md -``` - -Clear folder organization helps AI assistants understand your project structure and provide more relevant suggestions. - -## Optimizing Context for AI - -AI assistants work better when they have good context. Here's how to provide it: - -### 1. Use Descriptive File Names -``` -❌ Page1.al -✅ CustomerListPage.al - -❌ Cod50100.al -✅ SalesOrderProcessor.codeunit.al -``` - -### 2. Maintain a Good README -Create a `README.md` in your project root with: -- Project purpose and overview -- Key features and functionality -- Naming conventions -- Architecture decisions - -### 3. Use XML Documentation -Document your procedures and functions: -```al -/// -/// Calculates the total amount for a sales order including tax -/// -/// The sales header record -/// The total amount including tax -procedure CalculateTotalWithTax(var SalesHeader: Record "Sales Header"): Decimal -``` - -### 4. Keep Related Code Together -Place related functionality in the same files or nearby files. AI assistants can see open files and nearby code. - -## Testing Your Setup - -To verify everything is working: - -1. **Open an AL file** in your project -2. **Start typing** a procedure declaration -3. **Check for suggestions** - You should see inline suggestions appear -4. **Open Copilot Chat** (if using GitHub Copilot) - - Press Ctrl+Shift+I (or Cmd+Shift+I on Mac) - - Try asking: "Explain this AL code" - -## Recommended Extensions - -Install these VS Code extensions to complement your AI assistant: - -- **AL Language**: Microsoft's official AL extension (required) -- **AL Object Designer**: Navigate AL objects easily -- **AL Code Outline**: View code structure -- **AL Test Runner**: Run and manage AL tests -- **AL Variable Helper**: Manage variable declarations -- **GitLens**: Enhanced git integration - -## Workspace Best Practices - -### Open Relevant Files -- Keep related files open in tabs -- AI assistants can use open files for context - -### Use Multi-Root Workspaces (When Appropriate) -If you have dependencies or multiple related projects: -```json -{ - "folders": [ - { "path": "./MyMainExtension" }, - { "path": "./MyDependencyExtension" } - ] -} -``` - -### Organize by Feature -Consider organizing code by business feature rather than object type for complex projects: -``` -src/ -├── SalesOrderProcessing/ -│ ├── SalesOrder.table.al -│ ├── SalesOrderPage.page.al -│ ├── SalesOrderProcessor.codeunit.al -├── CustomerManagement/ -│ └── ... -``` - -## Security and Privacy Considerations - -### What Gets Sent to AI Services -- Code snippets from your workspace -- Currently open files -- Your prompts and questions - -### What You Should NOT Include -- Sensitive credentials or passwords -- Customer data -- Proprietary business logic (if restricted) - -### Best Practices -- Review your organization's AI usage policy -- Use `.gitignore` and `.copilotignore` files appropriately -- Be mindful of what code is in your workspace -- Consider using GitHub Copilot for Business for enterprise controls - -## Troubleshooting - -### AI Suggestions Not Appearing -- Verify the AI extension is installed and enabled -- Check you're signed in to your AI service -- Ensure `editor.inlineSuggest.enabled` is true -- Restart VS Code - -### Poor Quality Suggestions -- Improve code context (better file names, comments) -- Open related files for more context -- Use more descriptive variable and function names -- Add XML documentation comments - -### Performance Issues -- Close unnecessary tabs/files -- Disable AI for specific file types if needed -- Check your system resources - -## Next Steps - -Now that your environment is set up: -- Learn [effective prompting techniques](../effective-prompting) -- Review [best practices](../best-practices) for AI-assisted development -- Try the [practical examples](../../gettingmore) to see AI assistance in action diff --git a/content/docs/agentic-coding/GettingStarted/what-is-agentic-coding.md b/content/docs/agentic-coding/GettingStarted/what-is-agentic-coding.md deleted file mode 100644 index aecf4e19..00000000 --- a/content/docs/agentic-coding/GettingStarted/what-is-agentic-coding.md +++ /dev/null @@ -1,128 +0,0 @@ ---- -title: "What is Agentic Coding?" -linkTitle: "What is Agentic Coding" -weight: 1 -description: > - Understanding AI-powered coding assistance and how it transforms development ---- - -## Overview - -**Agentic coding** is a development approach where you work collaboratively with AI-powered assistants (agents) that can understand context, generate code, provide suggestions, and help maintain your codebase. Unlike simple code completion tools, these agents can: - -- Understand natural language instructions -- Analyze existing code and context -- Generate complete implementations -- Refactor and improve code -- Explain complex code segments -- Assist with debugging and problem-solving - -## How It Works - -AI coding assistants work by: - -1. **Understanding Context**: The agent analyzes your workspace, open files, and the surrounding code to understand what you're working on -2. **Processing Instructions**: You provide instructions in natural language (or through inline comments) -3. **Generating Solutions**: The agent creates code, documentation, or suggestions based on your needs -4. **Iterative Refinement**: You review, provide feedback, and the agent adjusts the output - -## Key Capabilities for AL Development - -### Code Generation -Generate AL code from natural language descriptions: -- Complete procedures and functions -- Table extensions and page extensions -- API pages and queries -- Event subscribers -- Test code - -### Code Understanding -Get help understanding existing code: -- Explanations of complex logic -- Documentation of dependencies -- Impact analysis of changes - -### Code Improvement -Enhance existing code: -- Refactoring for better performance -- Applying AL best practices -- Modernizing legacy code -- Adding error handling - -### Documentation -Automatically create and maintain: -- XML documentation comments -- README files -- API documentation -- Code comments - -## Benefits for AL Developers - -### Faster Development -- Quickly scaffold new objects and extensions -- Implement common patterns without repetitive typing -- Generate boilerplate code instantly - -### Higher Quality -- Consistent application of best practices -- Fewer common mistakes -- Better code organization - -### Learning Accelerator -- Learn AL patterns through examples -- Understand Business Central APIs -- Discover best practices in context - -### Reduced Cognitive Load -- Focus on business logic, not syntax -- Less context switching for documentation lookups -- Automated handling of repetitive tasks - -## The Human-AI Partnership - -It's important to understand that agentic coding is a **collaborative** approach: - -### You Bring: -- **Domain Knowledge**: Understanding of business requirements and Business Central functionality -- **Decision Making**: Architectural choices and business logic decisions -- **Quality Control**: Review and validation of generated code -- **Context**: Specific requirements, constraints, and organizational standards - -### The AI Brings: -- **Speed**: Rapid code generation and transformation -- **Consistency**: Adherence to patterns and best practices -- **Breadth**: Knowledge of many AL patterns and APIs -- **Assistance**: Help with routine tasks and documentation - -## Common Use Cases - -### Daily Development -- Creating new tables, pages, and codeunits -- Implementing event subscribers -- Writing test code -- Adding XML documentation - -### Code Maintenance -- Refactoring existing code -- Adding telemetry to extensions -- Improving error handling -- Updating deprecated APIs - -### Code Review -- Identifying potential issues -- Suggesting improvements -- Checking adherence to standards -- Finding security concerns - -### Documentation -- Generating README files -- Creating API documentation -- Writing user guides -- Documenting complex algorithms - -## Next Steps - -Now that you understand what agentic coding is, learn how to: -- [Set up your environment](../setup) for AI assistance -- [Write effective prompts](../effective-prompting) to get better results -- Follow [best practices](../best-practices) for AI-assisted development diff --git a/content/docs/agentic-coding/_index.md b/content/docs/agentic-coding/_index.md deleted file mode 100644 index 44002af2..00000000 --- a/content/docs/agentic-coding/_index.md +++ /dev/null @@ -1,65 +0,0 @@ ---- -title: "Agentic Coding" -linkTitle: "Agentic Coding" -weight: 50 -description: > - Learn how to effectively leverage AI-powered coding assistants for AL development in Business Central ---- - -{{% pageinfo %}} -This section provides guidance on working with AI-powered coding assistants (agents) to enhance your AL development workflow for Microsoft Dynamics 365 Business Central. -{{% /pageinfo %}} - -## What is Agentic Coding? - -Agentic coding refers to the collaborative approach of working with AI-powered assistants that can understand context, generate code, review implementations, and help maintain codebases. These AI agents act as intelligent partners in your development process, offering suggestions, automating repetitive tasks, and helping you follow best practices. - -## Why Use Agentic Assistance for AL Development? - -AI coding assistants can significantly enhance your AL development workflow by: - -- **Accelerating Development**: Generate boilerplate code, implement common patterns, and scaffold new features quickly -- **Improving Code Quality**: Get real-time suggestions for code improvements and adherence to AL best practices -- **Knowledge Augmentation**: Access contextual help about AL syntax, Business Central APIs, and development patterns -- **Documentation**: Automatically generate and maintain code documentation -- **Code Review**: Get automated reviews highlighting potential issues, performance concerns, and style violations -- **Learning Tool**: Learn AL best practices and patterns through interactive assistance - -## What You'll Find Here - -This hub is organized into three main sections: - -### Getting Started -Learn the fundamentals of working with AI coding assistants: -- Understanding agentic coding concepts -- Setting up your environment -- Effective prompting techniques -- Best practices for collaboration with AI - -### Getting More -Practical examples and advanced techniques: -- Conducting AI-assisted code reviews -- Generating and maintaining documentation -- Adding telemetry to your extensions -- Refactoring legacy code -- Testing strategies - -### Vibe Coding Rules -Comprehensive AI-specific coding guidelines and instructions: -- AL code style and formatting rules -- Error handling patterns -- Event-driven architecture guidelines -- Performance optimization techniques -- Testing and upgrade code best practices -- Naming conventions and standards - -### Community Resources -Curated resources from the AL and Business Central community: -- Articles and blog posts -- Video tutorials -- Tools and extensions -- Community discussions - -## Getting Help - -As you explore agentic coding, remember that AI assistants are tools to augment your capabilities, not replace your expertise. Always review generated code, understand what it does, and ensure it meets your specific requirements and follows your organization's standards. diff --git a/content/docs/agentic-coding/vibe-coding-rules/README.md b/content/docs/agentic-coding/vibe-coding-rules/README.md deleted file mode 100644 index 48ce0477..00000000 --- a/content/docs/agentic-coding/vibe-coding-rules/README.md +++ /dev/null @@ -1,102 +0,0 @@ -# Vibe Coding Rules - Contribution Guide - -This directory contains AI-optimized coding rules for AL development. Each rule set is organized into markdown files that can be easily consumed by AI coding assistants. - -## Directory Structure - -``` -vibe-coding/ -├── _index.md # Main landing page -├── README.md # This file - contribution guide -├── al-guidelines-rules.md # Complete rules file with references to all categories -├── al-code-style.md # Code style, formatting, and documentation rules -├── al-naming-conventions.md # File naming, object naming, and variable naming -├── al-performance.md # Performance optimization and query guidelines -├── al-error-handling.md # Error handling patterns and troubleshooting -├── al-events.md # Event-driven development and extensibility -├── al-testing.md # AL-Go workspace structure and testing guidelines -``` - -## How to Add New Rules - -### 1. Choose the Right Category -Select the appropriate category for your rules, or create a new category if needed. - -### 2. Follow the Standard Format -Each rule file should follow this structure: - -```markdown ---- -title: "AL [Category Name] Rules" -description: > - Brief description of the rule category for AL development -globs: ["*.al", "*.json"] # File types this rule applies to -alwaysApply: true|false # Whether to always apply these rules ---- - -# AL [Category Name] Rules - -Brief introduction to the category and its importance in AL development. - -## Rule 1: [Descriptive Title] - -### Intent -What this rule aims to achieve, including implementation details and guidance for AI assistants. - -### Examples - -```al -// Good example -[code example] -``` - -You can also add a bad example to show what to avoid, but this is optional. -```al -// Bad example (avoid) -[code example] -``` - -## Rule 2: [Next Rule] -[Continue with same format...] -``` - -### 3. Update the Index -After adding new rule files: -- Update `al-guidelines-rules.md` to include `@your-new-file.md` reference -- Update `_index.md` to include links to new categories -- Ensure all cross-references are updated - -### 4. Test Your Rules -Before submitting, test your rules with AI assistants to ensure they: -- Are clearly understood by AI agents -- Produce the expected code patterns -- Don't conflict with existing rules - -## Contribution Workflow - -1. **Fork** the alguidelines repository -2. **Create** your rule files in the appropriate category -3. **Test** the rules with your preferred AI assistant -4. **Submit** a pull request with: - - Clear description of the rules added - - Examples of how the rules improve code quality - - Any testing results with AI assistants (optional) - -## Best Practices for Rule Writing - -### Make Rules AI-Friendly -- Use clear, unambiguous language specific to AL development -- Provide specific AL code examples with proper syntax -- Include both positive and negative examples, when applicable -- Structure content consistently - -## Questions? - -If you have questions about contributing rules, please: -- Open a discussion in the GitHub repository -- Join the Business Central community Discord -- Contact the initiative maintainers - ---- - -*This README is part of the Vibe Coding for AL initiative - enhancing AL development through AI-optimized guidelines.* \ No newline at end of file diff --git a/content/docs/agentic-coding/vibe-coding-rules/_index.md b/content/docs/agentic-coding/vibe-coding-rules/_index.md deleted file mode 100644 index 4e4060e4..00000000 --- a/content/docs/agentic-coding/vibe-coding-rules/_index.md +++ /dev/null @@ -1,101 +0,0 @@ ---- -title: "Vibe Coding Rules for AL" -weight: 90 -tags: ["AL", "Vibe Coding"] -categories: ["Vibe Coding"] -description: > - AI-optimized coding rules and guidelines for AL development ---- - -_Created by the Business Central Community, Enhanced for AI-powered AL Development_ - -# Vibe Coding Rules for AL - -Welcome to the **Vibe Coding Rules for AL** initiative! This section contains AI-optimized coding rules and guidelines specifically designed to enhance the AL developer experience in modern AI-powered IDEs like VS Code and Cursor. - -## What is Vibe Coding? - -Vibe Coding represents a new approach to coding guidelines that are specifically formatted and structured to work seamlessly with AI coding assistants. These rules are designed to: - -- **Enhance AI Understanding**: Provide clear, structured guidelines that AI agents can easily parse and apply -- **Improve Code Quality**: Maintain high standards while leveraging AI assistance -- **Standardize Practices**: Create consistent coding patterns across the AL development community -- **Boost Productivity**: Help developers write better code faster with AI assistance - -## Key Features - -### 📋 **Structured Rule Format** -All rules are provided in markdown format with clear structure that AI agents can easily understand and apply during development. - -### 🔄 **Community-Driven** -Built and maintained by the AL community, including MVPs and the Microsoft product team. - -### 🛠️ **IDE Integration** -Future AL extension support will allow generating local rules files directly in VS Code and Cursor. - -### 🤖 **AI-Ready** -Designed as a foundation for AL-focused AI tools and Model Context Protocols (MCPs). - -## How to Use - -1. **Browse the Rules**: Explore the various rule categories below -2. **Copy for Your Project**: Use these rules as templates for your own coding standards -3. **Contribute**: Submit your own rule variations via pull requests -4. **Stay Updated**: Watch for AL extension integration coming soon - -## Rule Categories - -The Vibe Coding rules are organized into logical categories to make them easy to find and implement: - -- **[Complete AL Guidelines Rules](al-guidelines-rules/)** - Comprehensive rules file with references to other rules -- **[AL Code Style & Formatting](al-code-style/)** - Indentation, folder organization, and code documentation -- **[AL Naming Conventions](al-naming-conventions/)** - File naming, object naming, and variable naming patterns -- **[AL Performance Optimization](al-performance/)** - Query optimization, temporary tables, and performance analysis -- **[AL Error Handling & Troubleshooting](al-error-handling/)** - Try/catch patterns, debugging, and telemetry integration -- **[AL Event-Driven Development](al-events/)** - Event subscribers, integration events, and extensibility patterns -- **[AL Testing & Project Structure](al-testing/)** - AL-Go workspace structure, test generation, and project organization - -## Getting Started - -To get started with Vibe Coding for AL: - -1. Review the rule categories that apply to your development needs -2. Adapt the rules to your specific project requirements -3. Configure your AI assistant to use these guidelines -4. Share your experiences and contribute improvements back to the community - -## Future Roadmap - -### Phase 1: Foundation ✅ -- Host rules in AL Guidelines repository -- Community contribution process -- Initial rule sets from key contributors - -### Phase 2: Integration 🔄 -- AL extension support for local rules generation -- Enhanced AI agent compatibility -- MCP server integration - -### Phase 3: Expansion 🚀 -- Convert legacy C/AL patterns where applicable -- Generate new AL-specific patterns -- Establish as central trust source for AL AI agents - -## Contributing - -This initiative thrives on community contributions! Here's how you can help: - -- **Submit Rule Sets**: Share your proven coding rules via pull requests -- **Improve Existing Rules**: Suggest enhancements to current guidelines -- **Test & Validate**: Try the rules in your projects and provide feedback -- **Share Examples**: Contribute real-world examples of rule applications - -## Community & Support - -- **GitHub Repository**: [Microsoft AL Guidelines](https://github.com/microsoft/alguidelines) -- **Discussions**: Join conversations about Vibe Coding rules -- **Issues**: Report problems or suggest new features - ---- - -*The Vibe Coding for AL initiative is a collaborative effort between the Business Central community and Microsoft, aimed at revolutionizing how we write AL code in the age of AI.* \ No newline at end of file diff --git a/content/docs/agentic-coding/vibe-coding-rules/al-code-style.md b/content/docs/agentic-coding/vibe-coding-rules/al-code-style.md deleted file mode 100644 index a8f429ef..00000000 --- a/content/docs/agentic-coding/vibe-coding-rules/al-code-style.md +++ /dev/null @@ -1,167 +0,0 @@ ---- -title: "AL Code Style & Formatting Rules" -description: > - AL Code structure, formatting, and folder organization guidelines for AL development -globs: ["*.al"] -alwaysApply: true ---- - -# AL Code Style & Formatting Rules - -These rules ensure consistent code structure and organization across AL projects, making code more maintainable and AI-assistant friendly. - -## Style guidelines for AL code - - Always use PascalCase for variable and function names. - - Use PascalCase for object names (e.g., tables, pages, reports). - - Maintain a consistent indentation style (2 spaces preferred). - -## Commonly used methods and patterns - - Temporary tables for performance optimization - - Use of events for extensibility - -## Rule 1: Consistent Indentation and Formatting - -### Intent -Maintain consistent code formatting to improve readability and enable better AI understanding of code structure. Use indentation with two spaces consistently throughout your project and maintain consistent formatting within functions and procedures. - -### Examples - -```al -// Good example -procedure CalculateDiscount(Amount: Decimal; DiscountPct: Decimal): Decimal -begin - if DiscountPct > 0 then - exit(Amount * DiscountPct / 100); - - exit(0); -end; -``` - -## Rule 2: Feature-Based Folder Organization - -### Intent -Organize code by business features rather than object types to improve maintainability and logical grouping. Use feature-based organization with `src/feature/subfeature/` structure and place shared components in `Common` or `Shared` folders. - -### Examples - -``` -// Good example - Feature-based organization -src/ -├── NoSeries/ -│ ├── NoSeries.Table.al -│ ├── NoSeries.Page.al -│ └── NoSeriesSetup.Codeunit.al -├── Sales/ -│ ├── Invoice/ -│ │ ├── SalesInvoice.Page.al -│ │ └── SalesInvoicePosting.Codeunit.al -│ └── Order/ -│ └── SalesOrder.Page.al -└── Common/ - ├── Helpers/ - │ └── DateHelper.Codeunit.al - └── Interfaces/ - └── IPostable.Interface.al -``` - -``` -// Bad example (avoid object-type segregation) -src/ -├── Tables/ -│ ├── NoSeries.Table.al -│ └── SalesHeader.Table.al -├── Pages/ -│ ├── NoSeries.Page.al -│ └── SalesInvoice.Page.al -└── Codeunits/ - ├── NoSeriesSetup.Codeunit.al - └── SalesInvoicePosting.Codeunit.al -``` - -## Rule 3: Code Documentation and Comments - -### Intent -Provide clear documentation for global functions using XML documentation comments. Code should be self-documenting through clear naming, but global functions in codeunits require proper documentation for API clarity. - -### Examples - -```al -// Good example - XML documentation for global functions -codeunit 50100 "Base64 Convert" -{ - /// - /// Converts the value of the input string to its equivalent string representation that is encoded with base-64 digits. - /// - /// The string to convert. - /// The string representation, in base-64, of the input string. - procedure ToBase64(String: Text): Text - begin - exit(Base64ConvertImpl.ToBase64(String)); - end; - - /// - /// Validates discount percentage against business rules. - /// - /// The discount percentage to validate. - procedure ValidateDiscountPercentage(DiscountPct: Decimal) - begin - if DiscountPct > 50 then - Error('Discount cannot exceed 50% due to company policy'); - - if DiscountPct < 0 then - Error('Discount percentage cannot be negative'); - end; -} -``` - -```al -// Bad example (avoid inline comments for obvious operations) -procedure ValidateDiscountPercentage(DiscountPct: Decimal) -begin - // Check if discount is greater than 50 - if DiscountPct > 50 then - Error('Discount cannot exceed 50%'); - - // Check if discount is less than 0 - if DiscountPct < 0 then - Error('Discount percentage cannot be negative'); -end; -``` - -## Rule 4: Modular and Reusable Code Structure - -### Intent -Keep code modular and reusable to enhance maintainability and reduce duplication. Write small, focused procedures that do one thing well and use interfaces and patterns where appropriate. - -### Examples - -```al -// Good example - Modular approach -procedure PostDocument(var DocumentHeader: Record "Sales Header") -begin - ValidateDocument(DocumentHeader); - CalculateTotals(DocumentHeader); - CreateLedgerEntries(DocumentHeader); - UpdateStatus(DocumentHeader); -end; - -local procedure ValidateDocument(var DocumentHeader: Record "Sales Header") -begin - if DocumentHeader."No." = '' then - Error('Document number cannot be empty'); -end; - -local procedure CalculateTotals(var DocumentHeader: Record "Sales Header") -begin - DocumentHeader.CalcFields(Amount); -end; -``` - -```al -// Bad example (avoid monolithic procedures) -procedure PostDocument(var DocumentHeader: Record "Sales Header") -begin - // All validation, calculation, and posting logic in one procedure - // ... 200+ lines of mixed concerns -end; -``` \ No newline at end of file diff --git a/content/docs/agentic-coding/vibe-coding-rules/al-error-handling.md b/content/docs/agentic-coding/vibe-coding-rules/al-error-handling.md deleted file mode 100644 index dcc84411..00000000 --- a/content/docs/agentic-coding/vibe-coding-rules/al-error-handling.md +++ /dev/null @@ -1,167 +0,0 @@ ---- -title: "AL Error Handling & Troubleshooting Rules" -description: > - AL Error handling patterns, debugging techniques, and troubleshooting guidelines for AL development -globs: ["*.al"] -alwaysApply: false ---- - -# AL Error Handling & Troubleshooting Rules - -Robust error handling and effective troubleshooting practices are essential for maintaining reliable Business Central applications. - -## Rule 1: Use TryFunctions for Error Handling - -### Intent -Implement proper error handling using TryFunctions to manage exceptions gracefully and provide meaningful user feedback. Use TryFunctions for error handling in scenarios where rollback is required, implement proper exception handling for external service calls, provide meaningful error messages to users, and log errors appropriately for debugging purposes. When generating code that might fail (external calls, data operations, calculations), implement appropriate TryFunction error handling and provide clear error messages. - -### Examples - -```al -// Good example - TryFunction with proper error handling and error labels -procedure ProcessPayment(Amount: Decimal): Boolean -var - PaymentService: Codeunit "Payment Service"; - ErrorText: Text; - PaymentProcessingFailedLbl: Label 'Payment processing failed: %1', Comment = '%1 = Error message'; - PaymentProcessingFailedTelemetryLbl: Label 'Payment processing failed', Locked = true; -begin - if not TryProcessPaymentInternal(Amount) then begin - ErrorText := GetLastErrorText(); - LogError(PaymentProcessingFailedTelemetryLbl, ErrorText); - Message(PaymentProcessingFailedLbl, ErrorText); - exit(false); - end; - - exit(true); -end; - -[TryFunction] -local procedure TryProcessPaymentInternal(Amount: Decimal) -var - PaymentService: Codeunit "Payment Service"; -begin - PaymentService.ProcessPayment(Amount); -end; -``` - -```al -// Bad example (avoid hardcoded error messages and unhandled errors) -procedure ProcessPayment(Amount: Decimal) -var - PaymentService: Codeunit "Payment Service"; -begin - // No error handling - will cause unhandled exceptions - // Also avoid hardcoded messages like this: - // Message('Payment could not be processed'); - PaymentService.ProcessPayment(Amount); -end; -``` - -## Rule 2: Use Error Labels for All Messages - -### Intent -All error messages, warnings, and user messages must use label variables instead of hardcoded text. This ensures proper localization support and maintainability. Define labels with appropriate comments for translators and use Locked = true for technical messages that should not be translated. - -### Examples - -```al -// Good example - Using error labels -procedure ValidateBusinessLogic(SalesHeader: Record "Sales Header") -var - Customer: Record Customer; - CustomerNotFoundErr: Label 'Customer %1 does not exist for sales document %2.', Comment = '%1 = Customer No., %2 = Sales Header No.'; - CustomerBlockedErr: Label 'Customer %1 is blocked (%2). Cannot process sales document %3.', Comment = '%1 = Customer No., %2 = Blocked reason, %3 = Sales Header No.'; - EmptyHeaderNoErr: Label 'Sales header number cannot be empty.'; -begin - if SalesHeader."No." = '' then - Error(EmptyHeaderNoErr); - - if not Customer.Get(SalesHeader."Sell-to Customer No.") then - Error(CustomerNotFoundErr, SalesHeader."Sell-to Customer No.", SalesHeader."No."); - - if Customer.Blocked <> Customer.Blocked::" " then - Error(CustomerBlockedErr, Customer."No.", Customer.Blocked, SalesHeader."No."); -end; -``` - -```al -// Bad example (avoid hardcoded error messages) -procedure ValidateBusinessLogic(SalesHeader: Record "Sales Header") -var - Customer: Record Customer; -begin - if not Customer.Get(SalesHeader."Sell-to Customer No.") then - Error('Customer not found'); // Hardcoded - avoid this - - if Customer.Blocked <> Customer.Blocked::" " then - Error('Customer blocked'); // Hardcoded - avoid this -end; -``` - -## Rule 3: Code Compilation and Correctness Priority - -### Intent -Generated AL code should prioritize correctness over immediate compilation. Code can fail to compile if AI suggests base functions or events that don't exist, or if variables in event subscriptions are incorrect. When this happens, leave space for manual fixes rather than changing the intended behavior. If you're confident the logic should work as suggested but there are naming or parameter issues, leave it for user correction rather than altering the business logic. - -### Examples - -```al -// Good example - Correct logic even if function names need verification -procedure HandleCustomerModification(var Customer: Record Customer) -var - CustomerValidation: Codeunit "Customer Validation"; // May need verification -begin - // Correct business logic - even if codeunit name needs adjustment - if not CustomerValidation.ValidateCustomerData(Customer) then - Error(ValidationFailedErr); - - Customer.Modify(true); -end; -``` - -```al -// Good example - Event subscription with correct intent -[EventSubscriber(ObjectType::Table, Database::Customer, OnAfterModifyEvent, '', false, false)] -local procedure OnAfterCustomerModify(var Rec: Record Customer; var xRec: Record Customer; RunTrigger: Boolean) -var - CustomerChangeLog: Codeunit "Customer Change Log"; // Function may need verification -begin - // Correct logic - even if codeunit or method names need adjustment - CustomerChangeLog.LogCustomerChange(Rec, xRec); -end; -``` - -## Rule 4: Custom Telemetry Implementation - -### Intent -Add custom telemetry for tracking business-critical operations, but only when explicitly requested by the user. Use Session.LogMessage for custom telemetry with appropriate verbosity levels and data classification. Include relevant custom dimensions for context and use proper telemetry scope for extension publishers. - -### Examples - -```al -// Good example - Custom telemetry (only when user explicitly requests it) -procedure PostSalesDocument(var SalesHeader: Record "Sales Header") -var - TelemetryCustomDimensions: Dictionary of [Text, Text]; - SalesDocPostedMsg: Label 'Sales document posted successfully', Locked = true; - SalesDocPostFailedMsg: Label 'Sales document posting failed', Locked = true; -begin - // Add context for telemetry - TelemetryCustomDimensions.Add('DocumentType', Format(SalesHeader."Document Type")); - TelemetryCustomDimensions.Add('CustomerNo', SalesHeader."Sell-to Customer No."); - - if TryPostSalesDocument(SalesHeader) then begin - // Log successful operation - Session.LogMessage('SAL001', SalesDocPostedMsg, - Verbosity::Normal, DataClassification::SystemMetadata, - TelemetryScope::ExtensionPublisher, TelemetryCustomDimensions); - end else begin - // Log failed operation with error details - TelemetryCustomDimensions.Add('ErrorText', GetLastErrorText()); - Session.LogMessage('SAL002', SalesDocPostFailedMsg, - Verbosity::Error, DataClassification::SystemMetadata, - TelemetryScope::ExtensionPublisher, TelemetryCustomDimensions); - end; -end; -``` \ No newline at end of file diff --git a/content/docs/agentic-coding/vibe-coding-rules/al-events.md b/content/docs/agentic-coding/vibe-coding-rules/al-events.md deleted file mode 100644 index 8eab527c..00000000 --- a/content/docs/agentic-coding/vibe-coding-rules/al-events.md +++ /dev/null @@ -1,120 +0,0 @@ ---- -title: "Event-Driven Development Rules" -description: > - Guidelines for implementing event-driven patterns and extensibility in AL development -globs: ["*.al"] -alwaysApply: false ---- - -# Event-Driven Development Rules - -Event-driven development is fundamental to creating extensible and maintainable Business Central applications that follow the platform's architecture principles. - -## Rule 1: Use Events for Extensibility - -### Intent -Implement proper event patterns to enable extensibility without modifying base application code. Subscribe to relevant Business Central events (OnBeforeInsert, OnAfterModify, etc.), create integration events in your code for future extensibility, use extension objects or events for all changes to standard application objects. When implementing business logic, prioritize event subscribers and suggest appropriate event subscription patterns and integration event creation. - -### Examples - -```al -// Good example - Event subscriber implementation with Handler suffix -codeunit 50100 "Sales Document Events Handler" -{ - [EventSubscriber(ObjectType::Table, Database::"Sales Header", OnBeforeInsert, '', false, false)] - local procedure OnBeforeInsertSalesHeader(var SalesHeader: Record "Sales Header"; RunTrigger: Boolean) - begin - // Custom validation logic - ValidateCustomFields(SalesHeader); - end; -} -``` - -## Rule 2: Add Integration Events for Extensibility - -### Intent -Use integration events to provide better extensibility points and clearer API contracts for other developers. Create integration events at logical business process points, document integration event parameters and expected behavior, provide meaningful event names that describe the business context, and implement handled patterns to allow subscribers to control execution flow. When designing extensible code, suggest integration events at appropriate business logic points with clear documentation and meaningful names. - -### Examples - -```al -// Good example - Integration events with handled pattern -codeunit 50101 "Customer Management" -{ - procedure CreateCustomer(var Customer: Record Customer): Boolean - var - IsHandled: Boolean; - begin - OnBeforeCreateCustomer(Customer, IsHandled); - if IsHandled then - exit(true); - - if not Customer.Insert(true) then - exit(false); - - OnAfterCreateCustomer(Customer); - exit(true); - end; - - [IntegrationEvent(false, false)] - procedure OnBeforeCreateCustomer(var Customer: Record Customer; var IsHandled: Boolean) - begin - // Allow extensions to modify customer data before creation - // Set IsHandled to true to skip default processing - end; - - [IntegrationEvent(false, false)] - procedure OnAfterCreateCustomer(var Customer: Record Customer) - begin - // Allow extensions to perform additional actions after customer creation - end; -} -``` - -```al -// Extension subscribing to integration events with Handler suffix -codeunit 50102 "Customer Validation Handler" -{ - [EventSubscriber(ObjectType::Codeunit, Codeunit::"Customer Management", OnBeforeCreateCustomer, '', false, false)] - local procedure ValidateCustomerOnBeforeCreate(var Customer: Record Customer; var IsHandled: Boolean) - begin - // Custom validation logic - ValidateCustomerCreditLimit(Customer); - - // Optionally handle the event to skip default processing - if ShouldSkipDefaultProcessing(Customer) then - IsHandled := true; - end; -} -``` - -## Rule 3: Event Parameter Best Practices - -### Intent -Design event parameters that provide sufficient context while maintaining performance and usability. Pass record variables by reference when possible, include relevant context parameters, use meaningful parameter names, consider performance implications of parameter passing, and implement handled patterns where appropriate. When creating events, ensure parameters provide sufficient context for subscribers while maintaining good performance and use descriptive parameter names that clearly indicate their purpose. - -### Examples - -```al -// Good example - Well-designed event parameters with handled pattern -codeunit 50103 "Document Posting Events" -{ - [IntegrationEvent(false, false)] - procedure OnBeforePostDocument(var DocumentHeader: Record "Sales Header"; var DocumentLines: Record "Sales Line"; PostingDate: Date; var IsHandled: Boolean) - begin - // Comprehensive context for document posting - // - Document header and lines for full context - // - Posting date for temporal context - // - IsHandled flag for control flow - end; - - [IntegrationEvent(false, false)] - procedure OnAfterPostDocument(DocumentHeader: Record "Sales Header"; PostedDocumentNo: Code[20]; PostingResult: Boolean) - begin - // Results context after posting - // - Original document for reference - // - Posted document number for tracking - // - Success/failure indication - end; -} -``` \ No newline at end of file diff --git a/content/docs/agentic-coding/vibe-coding-rules/al-guidelines-rules.md b/content/docs/agentic-coding/vibe-coding-rules/al-guidelines-rules.md deleted file mode 100644 index f0caf625..00000000 --- a/content/docs/agentic-coding/vibe-coding-rules/al-guidelines-rules.md +++ /dev/null @@ -1,58 +0,0 @@ ---- -description: AL Guidelines - Comprehensive AI-optimized coding rules for Microsoft Dynamics 365 Business Central development -globs: ["*.al", "*.json", "app.json", "launch.json"] -alwaysApply: true ---- - -# AL Guidelines - Vibe Coding Rules - -You are an AI assistant designed to aid in AL development, particularly for Microsoft Dynamics 365 Business Central. Your role is to assist developers in writing efficient, maintainable code following established patterns and best practices. - -## Core Principles - -- Follow event-driven programming model; never modify standard application objects -- Use clear, meaningful names and maintain consistent code structure -- Prioritize performance optimization and proper error handling -- Focus on main application implementation by default -- Only generate test code when explicitly requested -- Maintain proper AL-Go workspace structure separation - -## Rule Categories - -The following rule sets provide comprehensive guidance for AL development: - -@al-code-style.md - -@al-naming-conventions.md - -@al-performance.md - -@al-error-handling.md - -@al-events.md - -@al-testing.md - -## Key Guidelines Summary - -- **File Naming**: Use `..al` pattern consistently -- **Code Style**: Use two space indentation and PascalCase for variables, PascalCase for objects -- **Folder Structure**: Organize by feature (`src/feature/subfeature/`) not by object type -- **Performance**: Filter data early, use temporary tables, avoid unnecessary loops -- **Events**: Prefer integration events over direct modifications for extensibility -- **Testing**: Separate App and Test projects, generate tests only when requested -- **Error Handling**: Use TryFunctions, provide meaningful error messages, implement telemetry - -## AL-Go Workspace Structure - -When working in AL-Go environments: -- **App project**: Contains all application logic (tables, pages, codeunits, reports) -- **Test project**: Contains all test code and references App project as dependency -- **Never mix**: Application code stays in App, test code stays in Test project - -## AI Response Behavior - -- Provide concise, actionable advice with specific AL method references -- Always explain the reasoning behind recommendations -- Reference Business Central architecture patterns and established best practices -- Focus on practical implementation guidance that can be immediately applied \ No newline at end of file diff --git a/content/docs/agentic-coding/vibe-coding-rules/al-naming-conventions.md b/content/docs/agentic-coding/vibe-coding-rules/al-naming-conventions.md deleted file mode 100644 index f5bed480..00000000 --- a/content/docs/agentic-coding/vibe-coding-rules/al-naming-conventions.md +++ /dev/null @@ -1,133 +0,0 @@ ---- -title: "Naming Conventions Rules" -description: > - Comprehensive naming conventions for AL files, objects, variables, and functions -globs: ["*.al"] -alwaysApply: true ---- - -# Naming Conventions Rules - -Consistent naming conventions improve code readability, maintainability, and help AI assistants understand code structure and intent. - -## Rule 1: Object Naming Conventions - -### Intent -Use consistent naming patterns for all AL objects to improve discoverability and maintain professional standards. Use PascalCase for object names (tables, pages, reports, codeunits) and meaningful, descriptive names that clearly indicate the object's purpose. Object names must not exceed 30 characters total, with a maximum of 26 characters for the name itself to reserve space for prefixes/affixes (3 characters + 1 space). - -### Examples - -```al -// Good examples (within 26 character limit) -table 50100 "Customer Ledger Entry" // 20 chars -page 50101 "Sales Invoice" // 13 chars -codeunit 50102 "Sales Invoice Posting" // 21 chars -report 50103 "Customer Statement" // 18 chars -``` - -```al -// Bad examples (avoid abbreviations, unclear names, or length violations) -table 50100 "CustLE" // Too abbreviated -page 50101 "SalesInv" // Too abbreviated -table 50104 "Very Long Customer Ledger Entry" // 32 chars - exceeds limit -codeunit 50102 "SIPoster" // Unclear abbreviation -``` - -## Rule 2: File Naming Conventions - -### Intent -Establish consistent file naming patterns that clearly identify object types and facilitate organized development. Use pattern `..al` and maintain consistency across all file names. Ensure file names are descriptive and match the AL object name within the files. - -### Examples - -```al -// Good examples -NoSeries.Page.al -NoSeries.Table.al -NoSeriesErrorsImpl.Codeunit.al -NoSeriesSetup.Codeunit.al -CustomerCard.Page.al -SalesHeader.Table.al -PostSalesInvoice.Codeunit.al -ItemLedgerEntry.Report.al -InventorySetup.PageExt.al -SalesHeader.TableExt.al - -// For implementations and interfaces -INoSeries.Interface.al -NoSeriesImpl.Codeunit.al - -// For test files -NoSeriesTests.Codeunit.al -SalesPostingTests.Codeunit.al -``` - -## Rule 3: Variable and Function Naming - -### Intent -Use consistent naming conventions for variables and functions to improve code readability. Use PascalCase for variable and function names, descriptive names that clearly indicate purpose, and avoid abbreviations unless they are well-known business terms. Use consistent parameter naming in procedures. - -### Examples - -```al -// Good examples - Variables -var - CustomerLedgerEntry: Record "Cust. Ledger Entry"; - TotalAmount: Decimal; - DiscountPercentage: Decimal; - IsValidTransaction: Boolean; -``` - -```al -// Good examples - Functions -procedure CalculateCustomerBalance(CustomerNo: Code[20]): Decimal -procedure ValidateSalesDocument(var SalesHeader: Record "Sales Header") -procedure UpdateInventoryQuantity(ItemNo: Code[20]; Quantity: Decimal) -``` - -## Rule 4: Parameter Naming in Event Subscribers - -### Intent -Use meaningful parameter names in event subscribers to improve code clarity and maintainability. Use descriptive parameter names that clearly indicate their purpose, follow Business Central conventions for common parameter types, and maintain consistency across similar event subscribers. Avoid unclear generic names like "Rec" - use specific descriptive names. - -### Examples - -```al -// Good example - Descriptive parameter names -[EventSubscriber(ObjectType::Table, Database::"Sales Header", OnBeforeInsert, '', false, false)] -local procedure AddDefaultValuesOnBeforeInsertSalesHeader(var SalesHeader: Record "Sales Header"; RunTrigger: Boolean) -begin - // Event handling logic -end; - -[EventSubscriber(ObjectType::Table, Database::Customer, OnBeforeModify, '', false, false)] -local procedure CheckBalanceOnBeforeModifyCustomer(var Customer: Record Customer; var xCustomer: Record Customer) -begin - // Event handling logic -end; -``` - -## Rule 5: Interface and Implementation Naming - -### Intent -Clearly distinguish between interfaces and their implementations using consistent naming patterns. Prefix interfaces with "I" (e.g., `INoSeries`), use "Impl" suffix for implementation codeunits, and keep interface and implementation names closely related. Ensure names stay within the 26-character limit. - -### Examples - -```al -// Good examples (within character limits) -// Interface file: ICustomerService.Interface.al -interface ICustomerService -{ - procedure GetCustomerBalance(CustomerNo: Code[20]): Decimal; -} - -// Implementation file: CustomerServiceImpl.Codeunit.al -codeunit 50100 "Customer Service Impl" implements ICustomerService -{ - procedure GetCustomerBalance(CustomerNo: Code[20]): Decimal - begin - // Implementation logic - end; -} -``` \ No newline at end of file diff --git a/content/docs/agentic-coding/vibe-coding-rules/al-performance.md b/content/docs/agentic-coding/vibe-coding-rules/al-performance.md deleted file mode 100644 index 9e4aa7aa..00000000 --- a/content/docs/agentic-coding/vibe-coding-rules/al-performance.md +++ /dev/null @@ -1,225 +0,0 @@ ---- -title: "AL Performance Optimization Rules" -description: > - Performance optimization guidelines and best practices for AL development -globs: ["*.al"] -alwaysApply: true ---- - -# AL Performance Optimization Rules - -These rules focus on writing performant AL code that scales well and provides optimal user experience in Business Central environments. - -## AL Performance Guidelines Summary -- Always analyze performance impact when adding new features -- Optimize queries by filtering data as early as possible -- Avoid unnecessary loops; use set-based operations when possible -- Use SetLoadFields to minimize data retrieval -- Use temporary tables, dictionaries, or lists for temporary data storage - -## Rule 1: Early Data Filtering and Query Optimization - -### Intent -Optimize queries by filtering data as early as possible to reduce data transfer and processing overhead. Apply filters before processing records, use appropriate table keys and sorting, minimize the amount of data retrieved from the database, and use SetRange and SetFilter methods effectively. - -### Examples - -```al -// Good example - Early filtering -procedure GetNumberOfCustomersByCity(CityFilter: Text): Integer -var - Customer: Record Customer; -begin - Customer.SetRange(City, CityFilter); - Customer.SetRange(Blocked, Customer.Blocked::" "); - if Customer.FindSet() then - repeat - // Process only filtered customers - until Customer.Next() = 0; - - exit(Customer.Count); -end; -``` - -```al -// Bad example (avoid processing all records) -procedure GetNumberOfCustomersByCity(CityFilter: Text): Integer -var - Customer: Record Customer; - Count: Integer; -begin - if Customer.FindSet() then - repeat - // Processing all customers then filtering - if (Customer.City = CityFilter) and (Customer.Blocked = Customer.Blocked::" ") then - Count += 1; - until Customer.Next() = 0; - - exit(Count); -end; -``` - -## Rule 2: Use SetLoadFields for Optimal Data Retrieval - -### Intent -Use SetLoadFields to minimize data retrieval from the database by loading only the fields you need. Place SetLoadFields before the Get or Find operation, and include only the fields that will be used in your code. - -### Examples - -```al -// Good example - SetLoadFields with filtering -Item.SetRange("Third Party Item Exists", false); -Item.SetLoadFields("Item Category Code"); -Item.FindFirst(); -``` - -```al -// Bad example (avoid SetLoadFields after filtering) -Item.SetLoadFields("Item Category Code"); -Item.SetRange("Third Party Item Exists", false); -Item.FindFirst(); -``` - -## Rule 3: Use Temporary Tables, Dictionaries, and Lists for Performance - -### Intent -Leverage temporary tables, dictionaries, and lists to improve performance in read-heavy scenarios and complex data processing. Use temporary tables for structured record data, dictionaries for key-value pairs, and lists for simple collections that are only temporarily needed. - -### Examples - -```al -// Good example - Using temporary tables for structured data -procedure ProcessSalesData(var TempSalesLine: Record "Sales Line" temporary) -var - SalesLine: Record "Sales Line"; -begin - // Load data into temporary table once - if SalesLine.FindSet() then - repeat - TempSalesLine := SalesLine; - TempSalesLine.Insert(); - until SalesLine.Next() = 0; - - // Process temporary data multiple times without database hits - ProcessDiscounts(TempSalesLine); - CalculateTotals(TempSalesLine); - ValidateInventory(TempSalesLine); -end; -``` - -```al -// Good example - Using dictionaries for key-value temporary data -procedure CacheCustomerData() -var - Customer: Record Customer; - CustomerCache: Dictionary of [Code[20], Text]; -begin - if Customer.FindSet() then - repeat - CustomerCache.Add(Customer."No.", Customer.Name); - until Customer.Next() = 0; - - // Use cached data for lookups - ProcessOrdersWithCache(CustomerCache); -end; -``` - -```al -// Good example - Using lists for simple collections -procedure GetBlockedCustomers(): List of [Code[20]] -var - Customer: Record Customer; - BlockedCustomers: List of [Code[20]]; -begin - Customer.SetRange(Blocked, Customer.Blocked::All); - if Customer.FindSet() then - repeat - BlockedCustomers.Add(Customer."No."); - until Customer.Next() = 0; - - exit(BlockedCustomers); -end; -``` - -## Rule 4: Avoid Unnecessary Loops - Use Set-Based Operations - -### Intent -Minimize looping operations and favor set-based approaches when possible to improve performance. Use built-in aggregation methods (CalcSums, CalcFields), leverage SQL-based operations through AL, avoid nested loops when possible, and use batch operations for multiple record updates. - -### Examples - -```al -// Good example - Set-based operation -procedure GetTotalSalesAmount(CustomerNo: Code[20]): Decimal -var - CustLedgerEntry: Record "Cust. Ledger Entry"; -begin - CustLedgerEntry.SetRange("Customer No.", CustomerNo); - CustLedgerEntry.CalcSums(Amount); - exit(CustLedgerEntry.Amount); -end; -``` - -```al -// Bad example (avoid manual loops for aggregation) -procedure GetTotalSalesAmount(CustomerNo: Code[20]): Decimal -var - CustLedgerEntry: Record "Cust. Ledger Entry"; - TotalAmount: Decimal; -begin - CustLedgerEntry.SetRange("Customer No.", CustomerNo); - if CustLedgerEntry.FindSet() then - repeat - TotalAmount += CustLedgerEntry.Amount; - until CustLedgerEntry.Next() = 0; - - exit(TotalAmount); -end; -``` - -## Rule 5: Performance Impact Analysis - -### Intent -Always analyze and consider performance impact when adding new features or modifying existing code. While the AL compiler does not have direct access to performance profilers, you should implement performance-optimal code patterns from the start and consider scalability implications of code changes. - -### Examples - -```al -// Good example - Performance-conscious implementation -procedure UpdatePricesForItems(var Item: Record Item) -var - ItemCount: Integer; -begin - // Check data volume before processing - ItemCount := Item.Count(); - - if ItemCount > 1000 then begin - // Use batch processing for large datasets - UpdatePricesInBatches(Item); - end else begin - // Direct processing for smaller datasets - UpdatePricesDirectly(Item); - end; -end; -``` - -```al -// Good example - Batched modifications to minimize database writes -procedure UpdateCustomerStatistics(CustomerNo: Code[20]) -var - Customer: Record Customer; - TotalBalance: Decimal; - LastPaymentDate: Date; -begin - // Calculate all values first - CalculateCustomerTotals(CustomerNo, TotalBalance, LastPaymentDate); - - // Single database write with all changes - Customer.SetLoadFields("Balance (LCY)", "Last Payment Date"); - if Customer.Get(CustomerNo) then begin - Customer."Balance (LCY)" := TotalBalance; - Customer."Last Payment Date" := LastPaymentDate; - Customer.Modify(true); - end; -end; -``` \ No newline at end of file diff --git a/content/docs/agentic-coding/vibe-coding-rules/al-testing.md b/content/docs/agentic-coding/vibe-coding-rules/al-testing.md deleted file mode 100644 index 2c09aff0..00000000 --- a/content/docs/agentic-coding/vibe-coding-rules/al-testing.md +++ /dev/null @@ -1,194 +0,0 @@ ---- -title: "AL Testing & Project Structure Rules" -description: > - AL-Go workspace structure, test generation guidelines, and project organization rules -globs: ["*.al", "app.json"] -alwaysApply: false ---- - -# AL Testing & Project Structure Rules - -These rules ensure proper project organization, test implementation, and workspace structure in AL-Go based development environments. - -## Rule 1: AL-Go Workspace Structure Guidelines - -### Intent -Establish clear separation between application code and test code in AL-Go workspace environments. - -- App project contains Tables, Pages, Codeunits, Reports, APIs, Enums, etc. -- Test project contains Test Codeunits, Test Pages, Mock objects, Test data. -- Each project has its own app.json with appropriate dependencies. -- Test project references the App project as a dependency. -- Use App project ONLY for main application implementation, use Test project ONLY for test implementation, never include test files in the main App folder, and never include application logic in the Test folder. -- When working in AL-Go workspace, always place files in the correct project based on their purpose. - -### Examples - -``` -// Good example - Proper AL-Go workspace structure -Repository/ -├── .AL-Go/ -├── .github/ -├── App/ -│ ├── src/ -│ │ ├── Setup/ -│ │ ├── Feature1/ -│ │ ├── Feature2/ -│ │ ├── APIs/ -│ ├── app.json -│ └── launch.json -├── Test/ -│ ├── src/ -│ │ ├── SetupTests/ -│ │ ├── Feature1Tests/ -│ │ ├── Feature2Tests/ -│ │ ├── IntegrationTests/ -│ ├── app.json -│ └── launch.json -└── al.code-workspace -``` - -## Rule 2: Test Generation Guidelines - -### Intent -Control when and how test code is generated to maintain focus on main application implementation. - -- DO NOT automatically generate test code unless explicitly requested -- Focus on main application implementation by default -- When user asks for implementation create only the main application objects -- Only generate test files when user specifically requests "Create tests for...", "Generate unit tests...", "Add test coverage...", or "Write tests..." -- If tests are requested, place them in the Test project following the folder structure where test files should mirror the App project structure but in the Test project -- Unless the user explicitly requests tests, focus only on main application implementation - -## Rule 3: Project Dependencies Configuration - -### Intent -Establish correct dependency relationships between App and Test projects. - -- App project app.json should NOT reference Test project -- Test project app.json MUST reference App project as dependency -- Test project should include testing frameworks (e.g., "Any", "Library Assert"), and each project maintains its own dependencies. -- When configuring project dependencies, ensure Test project references App project but never the reverse and include appropriate testing frameworks in Test project. - -### Examples - -```json -// Good example - Test project app.json -{ - "dependencies": [ - { - "id": "your-app-id", - "name": "Your App Name", - "publisher": "Your Publisher", - "version": "1.0.0.0" - }, - { - "id": "dd0be2ea-f733-4d65-bb34-a28f4624fb14", - "name": "Library Assert", - "publisher": "Microsoft", - "version": "20.0.0.0" - } - ] -} -``` - -## Rule 4: Unit Testing Best Practices - -### Intent -Write comprehensive unit tests that ensure reliability of business logic. - -- Write unit tests for all business logic -- Follow given/when/then structure for test naming -- Use Assert statements for validating critical conditions -- Create test data factories for consistent test setup -- Always try to use standard library codeunits to create data and post documents -- When creating tests, use descriptive names that follow given/when/then pattern and include comprehensive assertions to validate expected behavior. - -### Examples - -```al -// Good example - Well-structured unit test with standard library codeunits -codeunit 50200 "Customer Management Tests" -{ - Subtype = Test; - - var - Assert: Codeunit Assert; - LibrarySales: Codeunit "Library - Sales"; - LibraryInventory: Codeunit "Library - Inventory"; - LibraryRandom: Codeunit "Library - Random"; - LibraryERM: Codeunit "Library - ERM"; - - [Test] - procedure GivenValidCustomer_WhenCreatingCustomer_ThenCustomerIsCreated() - var - Customer: Record Customer; - CustomerManagement: Codeunit "Customer Management"; - CustomerNo: Code[20]; - begin - // Given - Valid customer data using library - LibrarySales.CreateCustomer(Customer); - Customer."Credit Limit (LCY)" := LibraryRandom.RandDec(10000, 2); - - // When - Creating customer - CustomerNo := CustomerManagement.CreateCustomer(Customer); - - // Then - Customer is created successfully - Assert.IsTrue(Customer.Get(CustomerNo), 'Customer should be created'); - Assert.AreEqual(Customer.Name, Customer.Name, 'Customer name should match'); - end; - - [Test] - procedure GivenSalesOrder_WhenPostingOrder_ThenInvoiceIsCreated() - var - SalesHeader: Record "Sales Header"; - SalesLine: Record "Sales Line"; - Item: Record Item; - PostedInvoiceNo: Code[20]; - begin - // Given - Sales order with library-created data - LibraryInventory.CreateItem(Item); - LibrarySales.CreateSalesHeader(SalesHeader, SalesHeader."Document Type"::Order, ''); - LibrarySales.CreateSalesLine(SalesLine, SalesHeader, SalesLine.Type::Item, Item."No.", LibraryRandom.RandInt(10)); - - // When - Posting sales order - PostedInvoiceNo := LibrarySales.PostSalesDocument(SalesHeader, true, true); - - // Then - Posted invoice exists - Assert.AreNotEqual('', PostedInvoiceNo, 'Posted invoice should be created'); - end; -} -``` - -## Rule 5: Feature-Based Test Organization - -### Intent -Organize test files to mirror the application structure while maintaining clear separation. - -- Test files should mirror App project structure in Test project -- Use same feature-based organization for tests -- Place shared test utilities in Common folder -- Maintain consistent naming patterns - -### Examples - -``` -// Good example - Mirrored test structure -App/src/ -├── NoSeries/ -│ ├── NoSeries.Table.al -│ └── NoSeries.Page.al -└── Sales/ - └── Invoice/ - └── SalesInvoice.Page.al - -Test/src/ -├── NoSeries/ -│ └── NoSeriesTests.Codeunit.al -├── Sales/ -│ └── Invoice/ -│ └── SalesInvoiceTests.Codeunit.al -└── Common/ - └── TestHelpers/ - └── TestDataFactory.Codeunit.al -``` \ No newline at end of file diff --git a/content/docs/agentic-coding/vibe-coding-rules/al-upgrade.md b/content/docs/agentic-coding/vibe-coding-rules/al-upgrade.md deleted file mode 100644 index 3113ca8b..00000000 --- a/content/docs/agentic-coding/vibe-coding-rules/al-upgrade.md +++ /dev/null @@ -1,457 +0,0 @@ ---- -title: "AL Upgrade Instructions" -description: > - Guidelines for writing and handling upgrade code -globs: ["*.al"] -alwaysApply: false ---- - -## Overview -These instructions cover how to write and review Business Central AL upgrade code following best practices for performance, reliability, and maintainability. - -## 1. Upgrade Codeunit Structure - -### Basic Structure -All upgrade codeunits must follow this exact structure: - -```al -codeunit [ID] [CodeunitName] -{ - Subtype = Upgrade; - - trigger OnCheckPreconditionsPerCompany() - begin - // Your code here - end; - - trigger OnCheckPreconditionsPerDatabase() - begin - // Your code here - end; - - trigger OnUpgradePerCompany() - begin - // Your code here - end; - - trigger OnUpgradePerDatabase() - begin - // Your code here - end; - - trigger OnValidateUpgradePerCompany() - begin - // Your code here - end; - - trigger OnValidateUpgradePerDatabase() - begin - // Your code here - end; -} -``` - -### Critical Rule: Avoid OnValidate and OnCheckPreconditions Triggers -- **DO NOT USE** `OnValidateUpgradePerCompany()` and `OnValidateUpgradePerDatabase()` triggers -- **DO NOT USE** `OnCheckPreconditionsPerCompany()` and `OnCheckPreconditionsPerDatabase()` triggers -- These triggers have performance impact and run on every upgrade -- If developer insists on using them: - - They MUST provide written justification - - Code MUST include checks to skip execution when upgrade is completed - - Should use upgrade tags for these checks - -## 2. OnUpgrade Trigger Implementation - -### Rule: No Direct Code in Triggers -OnUpgrade triggers should only contain method calls, never direct implementation: - -**INCORRECT Example:** -```al -trigger OnUpgradePerCompany() -begin - // Direct implementation code here - WRONG! - Customer.ModifyAll("Some Field", true); -end; -``` - -**CORRECT Example:** -```al -codeunit 4123 UpgradeMyFeature -{ - Subtype = Upgrade; - - trigger OnUpgradePerCompany() - begin - UpgradeMyFeature(); - UpgradeSecondFeature(); - end; - - local procedure UpgradeMyFeature() - begin - Customer.ModifyAll("Some Field", true); - // Other upgrade code here - end; - - local procedure UpgradeSecondFeature() - begin - // Your upgrade implementation here - end; -} -``` - -## 3. Error Handling Philosophy - -### Rule: Minimize Upgrade Blocking -- **Throw errors ONLY** when absolutely necessary to abort upgrade -- Handle unexpected scenarios gracefully without blocking. All read operations (Get, Find, FindSet, FindFirst, FindLast) should have if [OPERATION] then to make it safe. -**BAD EXAMPLE** -```al -Item.Get(); -Customer.FindSet(); -Vendor.FindLast(); -``` -**GOOD EXAMPLE** -```al -if Item.Get() then - // CustomCode; -if Customer.FindSet() then; -if not Vendor.FindLast() then - exit; -``` - -- Use telemetry for logging issues instead of throwing errors -- Customers should not be blocked from upgrading due to data inconsistencies - -**Example:** -```al -// GOOD - Handle gracefully -if not Customer.Get(CustomerNo) then begin - // Log telemetry about missing customer - Session.LogMessage('0000ABC', 'Customer not found during upgrade', Verbosity::Warning, DataClassification::SystemMetadata); - exit; // Continue with upgrade -end; - -// BAD - Blocks upgrade -Customer.Get(CustomerNo); // Will throw error if not found -``` - -## 4. Database Operations Safety - -### Rule: All Read Operations Must Be Protected -Every GET, FIND, FINDSET, FINDLAST operation MUST be within IF-THEN structure: - -**CORRECT Examples:** -```al -if MyTable.Get(CustomerNo) then - MyTable.Modify(); - -if MyTable.FindSet() then - repeat - // Process records - until MyTable.Next() = 0; - -if MyTable.FindLast() then - // Process record -``` - -**INCORRECT Examples:** -```al -MyTable.Get(CustomerNo); // WRONG - not protected -MyTable.FindLast(); // WRONG - not protected -``` - -## 5. Execution Control - Use Upgrade Tags (Not Version Checks) - -### AVOID Version Checks -**BAD Examples (Do Not Use):** -```al -// WRONG - Version check approach -if MyApplication.DataVersion().Major > 14 then - exit; - -// WRONG - Complex version structure -if MyApplication.DataVersion().Major < 14 then - UpgradeFeatureA() -else if MyApplicationDataVersion().Major < 17 then - UpgradeFeatureB() -else - exit; -``` - -### Valid Version Check Usage -**ONLY acceptable use** - checking for first installation: -```al -trigger OnInstallAppPerCompany() -var - AppInfo: ModuleInfo; -begin - NavApp.GetCurrentModuleInfo(AppInfo); - if (AppInfo.DataVersion() <> Version.Create('0.0.0.0')) then - exit; - // Insert installation code here -end; - -// Alternative approach -trigger OnInstallAppPerCompany() -var - AppInfo: ModuleInfo; -begin - if AppInfo.DataVersion().Major() = 0 then - SetAllUpgradeTags(); - - CompanyInitialize(); -end; -``` - -### USE Upgrade Tags (Preferred Method) -**CORRECT Implementation:** -```al -local procedure UpgradeMyFeature() -var - UpgradeTag: Codeunit "Upgrade Tag"; -begin - if UpgradeTag.HasUpgradeTag(MyUpgradeTag()) then - exit; - - // Your upgrade code here - - UpgradeTag.SetUpgradeTag(MyUpgradeTag()); -end; - -// Register PerCompany tags -[EventSubscriber(ObjectType::Codeunit, Codeunit::"Upgrade Tag", 'OnGetPerCompanyUpgradeTags', '', false, false)] -local procedure RegisterPerCompanyTags(var PerCompanyUpgradeTags: List of [Code[250]]) -begin - PerCompanyUpgradeTags.Add(MyUpgradeTag()); -end; - -// Register PerDatabase tags -[EventSubscriber(ObjectType::Codeunit, Codeunit::"Upgrade Tag", 'OnGetPerDatabaseUpgradeTags', '', false, false)] -local procedure RegisterPerDatabaseTags(var PerDatabaseUpgradeTags: List of [Code[250]]) -begin - PerDatabaseUpgradeTags.Add(MyUpgradeTag()); -end; -``` - -### Upgrade Tag Rules -- Maximum 2 levels of nesting in upgrade tag logic -- No complex if-then structures -- **IMPORTANT** When adding new lines to the register upgrade tags subscribers you must check from where the upgrade method is called. If it is called from OnUpgradePerCompany then it must be registered from OnGetPerCompanyUpgradeTags method. If it is called from OnUpgradePerDatabase it must be registered under OnGetPerDatabaseUpgradeTags. It **MUST** not be called from both, we need to use a different tags in this case. -- Reuse existing event subscribers when possible - only add new lines. -- Use upgrade tags ONLY in upgrade code -- Every new upgrade tag added **MUST** be referenced within an OnGetPerDatabaseUpgradeTags or OnGetPerCompanyUpgradeTags event subscriber -## 6. No Outside Calls During Upgrade - -### Rule: No Outside Calls During Upgrade -**FORBIDDEN during upgrade:** -- HttpClient or web service requests -- DotNet interop method calls -- Any external system communication - -These operations can fail and block the upgrade process. If they succeed and the upgrade fails, it may not be possible to roll changes back. - -## 7. Execution Context Awareness - -### Rule: Use Execution Context to Skip Code -It's acceptable to skip code execution during upgrade using ExecutionContext: - -**CORRECT Example:** -```al -// Don't add report selection entries during upgrade -if GetExecutionContext() = ExecutionContext::Upgrade then - exit; -``` - -**Requirements:** -- MUST include comment explaining why code is skipped -- Should be used sparingly and with clear justification - -## 8. DataTransfer Usage for Performance - -### When to Use DataTransfer -**MUST use DataTransfer when:** -- Table can contain more than 300,000 records -- Adding new fields to existing tables -- Adding new tables that need data initialization - -**MUST use ONLY for:** -- New fields and tables added in the same PR -- Initializing newly added data structures - -**IMPORTANT** -- If there is no new fields and tables, comment should be added that the validation triggers and event subscribers will not be raised, potentially breaking the business logic. -- If a new field is added, especially with InitValue, datatransfer is strongly recommended to be used to have a fast upgrade. - -### DataTransfer vs Loop/Modify Comparison - -**BAD Example (Loop/Modify - Avoid for Large Data):** -```al -local procedure UpdatePriceSourceGroupInPriceListLines() -var - PriceListLine: Record "Price List Line"; - UpgradeTag: Codeunit "Upgrade Tag"; - UpgradeTagDefinitions: Codeunit "Upgrade Tag Definitions"; -begin - if UpgradeTag.HasUpgradeTag(UpgradeTagDefinitions.GetPriceSourceGroupUpgradeTag()) then - exit; - - PriceListLine.SetRange("Source Group", "Price Source Group"::All); - if PriceListLine.FindSet(true) then - repeat - if PriceListLine."Source Type" in - ["Price Source Type"::"All Jobs", - "Price Source Type"::Job, - "Price Source Type"::"Job Task"] - then - PriceListLine."Source Group" := "Price Source Group"::Job - else - case PriceListLine."Price Type" of - "Price Type"::Purchase: - PriceListLine."Source Group" := "Price Source Group"::Vendor; - "Price Type"::Sale: - PriceListLine."Source Group" := "Price Source Group"::Customer; - end; - if PriceListLine."Source Group" <> "Price Source Group"::All then - PriceListLine.Modify(); - until PriceListLine.Next() = 0; - - UpgradeTag.SetUpgradeTag(UpgradeTagDefinitions.GetPriceSourceGroupFixedUpgradeTag()); -end; -``` - -**GOOD Example (DataTransfer - Use for Large Data):** -```al -local procedure UpdatePriceSourceGroupInPriceListLines() -var - PriceListLine: Record "Price List Line"; - UpgradeTag: Codeunit "Upgrade Tag"; - UpgradeTagDefinitions: Codeunit "Upgrade Tag Definitions"; - PriceListLineDataTransfer: DataTransfer; -begin - if UpgradeTag.HasUpgradeTag(UpgradeTagDefinitions.GetPriceSourceGroupUpgradeTag()) then - exit; - - // Update Job-related records - PriceListLineDataTransfer.SetTables(Database::"Price List Line", Database::"Price List Line"); - PriceListLineDataTransfer.AddSourceFilter(PriceListLine.FieldNo("Source Group"), '=%1', "Price Source Group"::All); - PriceListLineDataTransfer.AddSourceFilter(PriceListLine.FieldNo("Source Type"), '%1|%2|%3', - "Price Source Type"::"All Jobs", "Price Source Type"::Job, "Price Source Type"::"Job Task"); - PriceListLineDataTransfer.AddConstantValue("Price Source Group"::Job, PriceListLine.FieldNo("Source Group")); - PriceListLineDataTransfer.CopyFields(); - Clear(PriceListLineDataTransfer); - - // Update Vendor-related records - PriceListLineDataTransfer.SetTables(Database::"Price List Line", Database::"Price List Line"); - PriceListLineDataTransfer.AddSourceFilter(PriceListLine.FieldNo("Source Group"), '=%1', "Price Source Group"::All); - PriceListLineDataTransfer.AddSourceFilter(PriceListLine.FieldNo("Source Type"), '<>%1&<>%2&<>%3', - "Price Source Type"::"All Jobs", "Price Source Type"::Job, "Price Source Type"::"Job Task"); - PriceListLineDataTransfer.AddSourceFilter(PriceListLine.FieldNo("Price Type"), '=%1', "Price Type"::Purchase); - PriceListLineDataTransfer.AddConstantValue("Price Source Group"::Vendor, PriceListLine.FieldNo("Source Group")); - PriceListLineDataTransfer.CopyFields(); - Clear(PriceListLineDataTransfer); - - // Update Customer-related records - PriceListLineDataTransfer.SetTables(Database::"Price List Line", Database::"Price List Line"); - PriceListLineDataTransfer.AddSourceFilter(PriceListLine.FieldNo("Source Group"), '=%1', "Price Source Group"::All); - PriceListLineDataTransfer.AddSourceFilter(PriceListLine.FieldNo("Source Type"), '<>%1&<>%2&<>%3', - "Price Source Type"::"All Jobs", "Price Source Type"::Job, "Price Source Type"::"Job Task"); - PriceListLineDataTransfer.AddSourceFilter(PriceListLine.FieldNo("Price Type"), '=%1', "Price Type"::Sale); - PriceListLineDataTransfer.AddConstantValue("Price Source Group"::Customer, PriceListLine.FieldNo("Source Group")); - PriceListLineDataTransfer.CopyFields(); - - UpgradeTag.SetUpgradeTag(UpgradeTagDefinitions.GetPriceSourceGroupFixedUpgradeTag()); -end; -``` - -**BAD Example (Loop/Modify - Avoid for Large Data):** -```al - ItemJournalLine.SetLoadFields("Cross-Reference No.", "Item Reference No."); - ItemJournalLine.SetFilter("Cross-Reference No.", '<>%1', ''); - if ItemJournalLine.FindSet() then - repeat - ItemJournalLine."Item Reference No." := ItemJournalLine."Cross-Reference No."; - ItemJournalLine.Modify(); - until ItemJournalLine.Next() = 0; -``` - -**GOOD Example (DataTransfer - Use for Large Data):** -```al - ItemJournalLine.SetFilter("Item Reference No.", '<>%1', ''); - if ItemJournalLine.IsEmpty() then begin - ItemJournalLineDataTransfer.SetTables(Database::"Item Journal Line", Database::"Item Journal Line"); - ItemJournalLineDataTransfer.AddSourceFilter(ItemJournalLine.FieldNo("Cross-Reference No."), '<>%1', ''); - ItemJournalLineDataTransfer.AddFieldValue(ItemJournalLine.FieldNo("Cross-Reference No."), ItemJournalLine.FieldNo("Item Reference No.")); - ItemJournalLineDataTransfer.CopyFields(); - end; -``` - -## 9. InitValue and Upgrade Code Connection - -### Rule: New Fields with InitValue Need Upgrade Code -When a field is added with InitValue: -- InitValue applies ONLY to new records -- Existing records get datatype default (0 for numbers, false for Boolean) -- Code reviewer MUST ask if upgrade code is needed for each of the fields. - -**Example Field Addition:** -```al -field(100; "New Field"; Boolean) -{ - DataClassification = CustomerContent; - Caption = 'New Field'; - InitValue = true; -} - -field(101; "New Field 2"; Integer) -{ - DataClassification = CustomerContent; - Caption = 'New Field 2'; - InitValue = 5; -} -``` - -**Required Upgrade Code:** -```al -local procedure UpgradeMyTables() -var - BlankMyTable: Record "My Table"; - UpgradeTag: Codeunit "Upgrade Tag"; - UpgradeTagDefinitions: Codeunit "Upgrade Tag Definitions"; - MyTableDataTransfer: DataTransfer; -begin - if UpgradeTag.HasUpgradeTag(UpgradeTagDefinitions.GetUpgradeMyTablesTag()) then - exit; - - MyTableDataTransfer.SetTables(Database::"My Table", Database::"My Table"); - MyTableDataTransfer.AddConstantValue(true, BlankMyTable.FieldNo("New Field")); - MyTableDataTransfer.AddConstantValue(5, BlankMyTable.FieldNo("New Field 2")); - MyTableDataTransfer.CopyFields(); - - UpgradeTag.SetUpgradeTag(UpgradeTagDefinitions.GetUpgradeMyTablesTag()); -end; -``` - -## Review Checklist - -When reviewing upgrade code, verify: - -1. ✅ No direct code in OnUpgrade triggers (only method calls) -2. ✅ No OnValidate or OnCheckPreconditions triggers without justification -3. ✅ All database read operations are protected with IF-THEN -4. ✅ Upgrade tags used instead of version checks -5. ✅ No external calls (HTTP, DotNet interop) -6. ✅ DataTransfer used for tables > 300k records -7. ✅ DataTransfer only used for new fields/tables -8. ✅ InitValue fields have corresponding upgrade code. Each new file **MUST** be verified. -9. ✅ Proper error handling (minimal blocking) -10. ✅ Upgrade tags properly registered with event subscribers - -## Common Anti-Patterns to Flag - -- Version checking instead of upgrade tags -- Direct database operations without IF protection -- Loop/Modify pattern on large datasets -- Missing upgrade code for InitValue fields -- External service calls during upgrade -- Complex nested upgrade tag logic -- Direct implementation in OnUpgrade triggers \ No newline at end of file diff --git a/content/docs/patterns/_index.md b/content/docs/patterns/_index.md index 61275700..b7813e2b 100644 --- a/content/docs/patterns/_index.md +++ b/content/docs/patterns/_index.md @@ -1,18 +1,13 @@ --- -title: "Design Patterns" +title: "Patterns" weight: 2 +no_list: true description: > AL Code Design Patterns --- # Business Central Design Patterns -This section will cover patterns that solve certain design challenges in Business Central. +## Why are Design Patterns important? -From wikipedia: - -_In Computer science, a Design pattern is an abstract solution to a certain problem. Design patterns are used in object oriented programming. They give a possible solution to a problem of designing software ... They also simplify the language between computer scientists. Ideally, a design pattern should be reusable many times. It is like a brick of a house, it can be used for many different problems. One can also build bridges with bricks, not only houses._ - -## Discussion - -All discussion related to Best Practice are to be found on the Github Repo's Discussion pages, found [here](https://github.com/microsoft/alguidelines/discussions/categories/bc-patterns) +Blah Blah Blah \ No newline at end of file diff --git a/content/docs/patterns/api-delegate-operation/index.md b/content/docs/patterns/api-delegate-operation/index.md deleted file mode 100644 index 5b3f2a92..00000000 --- a/content/docs/patterns/api-delegate-operation/index.md +++ /dev/null @@ -1,73 +0,0 @@ ---- -title: "Delegate API Operation" -tags: ["AL","API"] -categories: ["Pattern"] ---- - -_Created by Arend-Jan Kauffmann, Described by Arend-Jan Kauffmann_ - -## Abstract -The goal of this pattern is to delegate data operations from the API page to a codeunit. The codeunit can implement its own logic for inserting, modifying or deleting data. - -## Context -API pages implement a specific pattern for insert, modify and delete operations. An API page uses delayed insert behavior, which means that all fields will be validated before the record is inserted or modified. -This is different from the standard behavior on a UI card page, where a record is first inserted, followed by a modify when field values are updated. - -## Problem -The standard behavior can result in a number of challenges: - -* Code behind the OnValidate triggers and its event subscribers may expect that a record already exists when a field is being validated. -* Usage of temporary buffer tables can become really complex when fully handled from the API page. -* Applying default data to records before they are created does not work well with delayed inserts. - -## Description -To mitigate these problems, we can delegate the data operation to a codeunit while canceling the data operation inside the API page. - -```al -codeunit 50000 "Item API Operations" -{ - internal procedure InsertItem(var Item: Record Item) - begin - end; - - internal procedure ModifyItem(var Item: Record Item) - begin - end; - - internal procedure DeleteItem(var Item: Record Item) - begin - end; -} -``` - -It is important that the record parameter is updated with the final result. This allows the API page to return the result of the API operation to the caller. - -The API page implements this in the page triggers as follows: - -```al - var - ItemAPIOperations: Codeunit "Item API Operations"; - - trigger OnInsertRecord(BelowxRec: Boolean): Boolean - begin - ItemAPIOperations.InsertItem(Rec); - exit(false); - end; - - trigger OnModifyRecord(): Boolean - begin - ItemAPIOperations.ModifyItem(Rec); - exit(false); - end; - - trigger OnDeleteRecord(): Boolean - begin - ItemAPIOperations.DeleteItem(Rec); - exit(false); - end; -``` - -The triggers must return false in order to cancel the operation in the page. - -## Benefits -Inside the functions in the codeunit you have full control over the steps that are performed for the specific operation. diff --git a/content/docs/patterns/api-register-fieldset/index.md b/content/docs/patterns/api-register-fieldset/index.md deleted file mode 100644 index b091e808..00000000 --- a/content/docs/patterns/api-register-fieldset/index.md +++ /dev/null @@ -1,108 +0,0 @@ ---- -title: "API Register Fieldset" -tags: ["AL","API"] -categories: ["Pattern"] ---- - -_Created by Arend-Jan Kauffmann, Described by Arend-Jan Kauffmann_ - -## Abstract -The goal of this pattern is to register the fields that are part of the request body of an API call. - -## Context -A request to an API page to insert or modify a record requires a JSON body with the fields and values. It is not required to specify all fields that are exposed by the API page. Only those fields that are specified in the JSON body will be validated with a value. A call to insert a record (http POST) will leave the unspecified fields at their default value. A call to modify a record (http PATCH) will only update the specified fields and leave the other fields to their current value. - -## Problem -The standard behavior can result in a number of challenges: - -* It's not possible to implement mandatory fields. Especially for fields with an initial value like integers, decimals and booleans the code doesn't know if they are their initial value or were included in the API request. -* In the OnValidate trigger of a field it's not possible to verify if the API request is an insert or modify operation. Fields can't be protected to be modified during a specific operation, e.g. do not allow to modify during an insert. -* When implementing the Delegated API Operation pattern, for example to implement a template with default values, the template values should not overwrite the provided values in the API request while non-specified fields should get a value from the template. - -## Description -To mitigate these problems, we can register the fields during the OnValidate trigger in a temporary table. During the delayed insert or modify operation the code knows which fields were part of the API request. - -```al - field(displayName; Rec.Description) - { - trigger OnValidate() - begin - RegisterFieldSet(Rec.FieldNo(Description)); - end; - } - - .... - - var - TempFieldSet: Record Field temporary; - - local procedure RegisterFieldSet(FieldNumber: Integer) - begin - if TempFieldSet.Get(Database::Item, FieldNumber) then - exit; - - TempFieldSet.Init(); - TempFieldSet.TableNo := Database::Item; - TempFieldSet."No." := FieldNumber; - TempFieldSet.Insert(); - end; -``` - -Now that we have the list of fields that are part of the request, they can be checked during the insert or modify operation. Or they can be handed over to a delegated operation. - -Some examples to work with the list of fields: - -```al - trigger OnInsertRecord(BelowxRec: Boolean): Boolean - begin - if TempFieldSet.Get(Database::Item, Rec.FieldNo(Inventory)) then - Error(InventoryCannotBeChangedInAPostRequestErr); - - ItemAPIOperations.InsertItem(Rec, TempFieldSet) - exit(false); - end; - - trigger OnModifyRecord(): Boolean - begin - ItemAPIOperations.ModifyItem(Rec, TempFieldSet); - exit(false); - end; - - trigger OnDeleteRecord(): Boolean - begin - ItemAPIOperations.DeleteItem(Rec); - exit(false); - end; -``` - -The example code combines this with the Delegated API Operation pattern. The codeunit for the delegated operation can use the fieldset to apply a template while keeping the original values from the request. - -```al -codeunit 50000 "Item API Operations" -{ - internal procedure InsertItem(var Item: Record Item; var TempFieldSet: Record "Field"; ModifiedDateTime: DateTime) - var - ConfigTemplateHeader: Record "Config. Template Header"; - ConfigTemplateManagement: Codeunit "Config. Template Management"; - RecRef: RecordRef; - begin - if not FindConfigTemplateHeader(Item, ConfigTemplateHeader) then - exit; - RecRef.GetTable(Item); - - if ConfigTemplateManagement.ApplyTemplate(RecRef, TempFieldSet, RecRef, ConfigTemplateHeader) then - RecRef.SetTable(Item); - end; - - internal procedure ModifyItem(var Item: Record Item) - begin - end; - - internal procedure DeleteItem(var Item: Record Item) - begin - end; -} -``` - -## Benefits -Having a list of fields that are part of the API request provides more information during to the insert or modify operation. This helps to implement specific behavior, based on which fields were specified in the API request. diff --git a/content/docs/patterns/command-queue/index.md b/content/docs/patterns/command-queue/index.md deleted file mode 100644 index a414765f..00000000 --- a/content/docs/patterns/command-queue/index.md +++ /dev/null @@ -1,220 +0,0 @@ ---- -title: "Command Queue" -tags: ["AL"] -categories: ["Pattern"] ---- - -_Created by Patrick Schiefer, Described by Patrick Schiefer_ - -## Abstract -The goal of this pattern is to control the flow of multiple processes - - -## Problem -Sometimes its neccassary to perform multiple processes in Business Central, for example you want to post more than one order or before you post an Order you also have to post a purchase order this often leads to spaghetti code with big if else structures, which is not easy to read - -## Description -The pattern is ideal for executing several independent processes in succession. Since the processes are independent, each process must take care of error handling itself. -The command queue should not be used to control a single process. Also it is important to know that the queue is just in the memory so if the service gets restarted the queue is gone and has to be rebuilt. - -## The Pattern -To structure this problem we can use the "Command Queue" pattern. The pattern consist of two main parts the queue and the command interface - -![image](queue.png) - -First the command interface, it only has one procedure to execute the command -```al -interface ICommand -{ - procedure Execute() -} -``` - - -And then the Queue which consist of two codeunits, the Queue itself and a Queue Entry - -```al -codeunit 50100 "Queue" -{ - procedure Push(var value: Interface ICommand) - var - Entry: Codeunit QueueEntry; - begin - Entry.SetValue(value); - if count = 0 then begin - first := Entry; - last := Entry; - end - else begin - last.SetNextEntry(Entry); - last := Entry; - end; - count += 1; - end; - - procedure Pop() value: Interface ICommand - begin - if count > 0 then begin - value := first.GetValue(); - first := first.GetNextEntry(); - count -= 1; - end - else - Error('The Queue is empty!'); - end; - - procedure GetSize(): Integer - begin - exit(count); - end; - - var - first: Codeunit QueueEntry; - last: Codeunit QueueEntry; - count: Integer; -} - - - -codeunit 50102 "QueueEntry" -{ - procedure SetValue(var v: Interface ICommand) - begin - value := v; - end; - - procedure GetValue(): Interface ICommand - begin - exit(value); - end; - - procedure GetNextEntry(): Codeunit QueueEntry - begin - exit(NextEntry); - end; - - procedure SetNextEntry(var Entry: Codeunit QueueEntry) - begin - NextEntry := Entry; - end; - - var - value: Interface ICommand; - NextEntry: Codeunit QueueEntry; -} -``` - -As we see the queue entry stores a command, since the command is an interface we can hide each business logic behind. - -## Benefits -The logical flow is very easy to adopt, it is even possible to add entries to the queue while it is processed. - -## Example - -In this short example I show you how to post multiple sales orders and display message after finishing the last post. - - -We have two commands in this example, the "SalesOrderPostCommander" is used to post a sales order and the "MessageCommander" displays a message. -```al -codeunit 50104 "SalesOrderPostCommander" implements ICommand -{ - procedure SetSalesOrderNumber(value : Code[20]) - begin - No := value; - end; - - procedure Execute() - begin - // TODO Post Sales Header - end; - - - var - No : Code[20]; -} - - -codeunit 50103 "MessageCommander" implements ICommand -{ - procedure SetText(value: Text) - begin - t := value; - end; - - procedure Execute() - begin - Message(t); - end; - - var - t: Text; -} -``` - -Using this two codeunits we can now implement a patch posting -```al - -codeunit 50105 PatchPostQueue -{ - procedure PatchPost() - begin - FilterSalesOrdersToPost(); - if not SalesOrders.Findset(false) then - exit(); // Nothing to post - - repeat - AddSalesOrderToQueue(SalesOrder."No."); - until SalesOrders.Next() = 0; - - AddMessageToQueue('Posting Complete'); - ExecuteQueue(); - end; - - local procedure ExecuteQueue() - var - object : interface "ICommand"; - begin - repeat - object := queue.Pop(); - object.Execute(); - until queue.GetSize() = 0; - end; - - local procedure FilterSalesOrdersToPost() - begin - // Filter Sales Orders here - end; - - local procedure AddMessageToQueue(message : Text) - var - t: Codeunit MessageCommander; - object: Interface ICommand; - begin - t.SetText(message); - object := t; - queue.Push(object); - end; - - local procedure AddSalesOrderToQueue(No : Text) - var - SaleOrderCommander: Codeunit SalesOrderPostCommander; - object: Interface ICommand; - begin - SaleOrderCommander.SetSalesOrderNumber(No); - object := SaleOrderCommander; - queue.Push(object); - end; - - - var - SalesOrders : Record "Sales Header"; - queue: Codeunit Queue; -} - -``` - - - -## References -[Detailed Explanation of the pattern](https://patrickschiefer.wordpress.com/2022/02/24/part-2-how-to-implement-a-command-queue-in-pure-al/) - diff --git a/content/docs/patterns/command-queue/queue.png b/content/docs/patterns/command-queue/queue.png deleted file mode 100644 index a7411ca6..00000000 Binary files a/content/docs/patterns/command-queue/queue.png and /dev/null differ diff --git a/content/docs/patterns/error-handling/index.md b/content/docs/patterns/error-handling/index.md deleted file mode 100644 index ecd3635a..00000000 --- a/content/docs/patterns/error-handling/index.md +++ /dev/null @@ -1,24 +0,0 @@ -+++ -title = "Error Handling" -tags = ["AL"] -categories = ["Pattern"] -+++ - -_Created by Microsoft, Described by Luuk Busschers (Dysel)_ - -## Abstract - -The "Error Handling" system is used extensively to provide information to users about missing information in the system or other issues because of which the started process cannot be completed through Microsoft Dynamics 365 Business Central. - -## Description -Because of there is already a lot written about how to use the error handling the best in several scenario's in this page you will find a link to documentation on learn.microsoft.com and a link to a video about this subject on youtube. These links can be found in the list of references. - -The Microsoft Learn part is about collecting errors which means that the process you did start will not be interupted when one error is given, it will collect the errors in the process an you are able to show the user afterwards which errors where given in the process. - -The youtube video shows more about errors presented in such way that the user will be informed about how to solve the error. - -## List of references - -For error handling, there is more information available on: -- [Microsoft Learn: Error collections](https://learn.microsoft.com/en-us/dynamics365/business-central/dev-itpro/developer/devenv-error-collection/) -- [Youtube: Microsoft Presents: User friendly error handling in AL](https://www.youtube.com/watch?v=D8233xMjVog&list=PLI1l3dMI8xlDM9onioMWUyMSCiFs_mMWw&index=27) \ No newline at end of file diff --git a/content/docs/patterns/event-bridge-pattern/index.md b/content/docs/patterns/event-bridge-pattern/index.md index e05deadc..ca46e5b2 100644 --- a/content/docs/patterns/event-bridge-pattern/index.md +++ b/content/docs/patterns/event-bridge-pattern/index.md @@ -1,6 +1,6 @@ --- title: "Event Bridge" -tags: ["AL","Interface","Extendability"] +tags: ["Interface"] categories: ["Pattern"] --- @@ -20,8 +20,8 @@ Like in this example, we have an interface, to implement different ways for gett ```AL interface "IScale" { - procedure GetWeight(): Decimal - procedure Tare() + procedure GetWeight(): Decimal; + procedure Tare(); } ``` @@ -35,13 +35,13 @@ So, if we would implement it like this, it isn't really extensible, as a differe ```AL codeunit 50407 "Scale Wrong" implements IScale { - procedure GetWeight() Result: Decimal + procedure GetWeight() Result: Decimal; begin //TODO: Implement Bar GetWeight OnAfterGetWeight(Result); end; - procedure Tare() + procedure Tare(); begin //TODO: Implement Bar Tare OnAfterTare(); @@ -86,13 +86,13 @@ codeunit 50405 "Scale Bar" implements IScale var IScaleTriggers: Codeunit "IScale Triggers"; - procedure GetWeight() Result: Decimal + procedure GetWeight() Result: Decimal; begin //TODO: Implement Bar GetWeight IScaleTriggers.OnAfterGetWeight(Result); end; - procedure Tare() + procedure Tare(); begin //TODO: Implement Bar Tare IScaleTriggers.OnAfterTare(); @@ -102,10 +102,14 @@ codeunit 50405 "Scale Bar" implements IScale ## Benefits -This new codeunit, with public events, makes the events accessible from all places, including new apps that are dependent from this app. +This new codeunit, with public events, makes the events accessible from all places, including new apps that are dependent from this app, and wants to extend the The naming convention (both starting with "IScale") also makes it very easy to find that corresponding events for the interface. ## When not to use Obviously, the events should be carefully considered: only the events that make sense to "share" over all implementations, need this approach. + +## Discussions + +You can discuss this pattern [here](https://github.com/microsoft/alguidelines/discussions/66) \ No newline at end of file diff --git a/content/docs/patterns/facade-pattern/index.md b/content/docs/patterns/facade-pattern/index.md index 965a4adb..ef138707 100644 --- a/content/docs/patterns/facade-pattern/index.md +++ b/content/docs/patterns/facade-pattern/index.md @@ -1,6 +1,6 @@ --- title: "Façade" -tags: ["AL","Decoupling","Readability","Testability","Extendability"] +tags: [""] categories: ["Pattern"] --- @@ -9,7 +9,6 @@ _Created by Erich Gamma, Richard Helm, Ralph Johnson, John Vlissides (Gang of Fo ## Abstract The intent of this pattern is to provide a unified API to a single or a collection of potentially complex subsystems. If you apply this pattern as a general pattern, you will ensure improved: - - Decoupling - Encapsulation - Readability @@ -23,7 +22,6 @@ Whenever you want to write an isolated piece of business logic, from now on refe ## Problem The facade pattern addresses two main problems: - - Over time as systems grow, they tend to become complex and harder to comprehend. By adding a facade on top of the subsystem, that complexity is hidden, and a clear API is defined. - Any object or method which is publicly accessible, may not receive breaking changes in future releases without announced deprecation. This complicates maintainability of the system. By adding a facade, you ensure that the subsystem is inaccessible to the outside systems, enabling you to change the implementation details of the subsystem at will. @@ -61,12 +59,9 @@ This is arguably one of easiest patterns to understand and implement. Loosely sp To achieve this, we are using [access modifiers](https://docs.microsoft.com/bs-cyrl-ba/dynamics365/business-central/dev-itpro/developer/devenv-using-access-modifiers). Let's try to take a look at an example, taken from the system application: [the Image module](https://github.com/microsoft/ALAppExtensions/tree/main/Modules/System/Image). I'm using this very simplified example for illustration purposes. Notice, that even the full subsystem at time of writing isn't complex - it merely has a single codeunit containing the implementation details. However, as it is expected that the complexity will increase over time or that the implementation details can change, the subsystem is already equipped with a facade from the beginning. -_The Facade_ +*The Facade* ```AL -/// -/// Codeunit to extract image information. -/// codeunit 3971 Image { Access = Public; @@ -95,7 +90,6 @@ codeunit 3971 Image ``` The facade codeunit above has some characteristics: - - Access is explicitly set to Public, to underline that this is a facade. - All methods are public. - All methods are documented. @@ -108,7 +102,7 @@ Anyone who wants to access the subsystem, will only have to relate to this one p Test of the subsystem can be limited to testing the facade - it is strictly speaking the only thing that needs verification, that it functions as designed. It is the contract of the subsystem. -_The Subsystem_ +*The Subsystem* ```AL codeunit 3970 "Image Impl." @@ -141,7 +135,6 @@ codeunit 3970 "Image Impl." There are no rules for the subsystem, except that access needs to be **internal**. How you implement, how much you document, how you test, is entirely up to you and not the business of the outside caller. Of course, you should apply all of the best practices and patterns anyway, as you and possibly other developers will have to understand, extend and maintain the subsystem too. But from the view of this pattern, the complexity of the subsystem is irrelevant - just as long as it's not accessible. ## Usage - The facade pattern is one of the most prominent patterns in the [system application](https://github.com/microsoft/ALAppExtensions/tree/main/Modules/System). You will find plenty of examples here. ## Benefits @@ -149,23 +142,18 @@ The facade pattern is one of the most prominent patterns in the [system applicat The benefits of this rather simple pattern should be abundantly clear by now. But let's go over them once more, structured by the advantages this patterns brings: ### Decoupling - As the entire subsystem is inaccessible to outside systems, no dependencies can be taken. Hence this patterns strongly promotes the decoupling of objects. ### Encapsulation - The entire purpose of this very pattern is to encapsulate complexity; you hide away the implementation details behind an easy to understand facade. ### Readability - If done right, the developer doesn't need to be able to understand the details of the subsystem. Everything relevant to using the subsystem is described in the facade. ### Testability - Ensuring the correct behaviour of the subsystem can be done by testing the facade. The facade defines the contract of your subsystem - what does it expose and how should it behave. That contract should be covered with adequate tests, which will ensure that it is upheld, even if you decide to change the implementation of the subsystem. ### Maintainability - The one thing you may not change freely, is the facade and the test of the facade. It can be extended, but you should not break any existing APIs. But that leaves the entire subsystem to be completely rewritten, if you desire to do so. As no external dependencies can exist, there is no risk of introducing any syntactical breaking changes to the outside world. And as the tests of the public facade remain, there is no risk of introducing semantical breaking changes either - the contract is upheld, as long as your tests pass. ## When not to use @@ -185,3 +173,6 @@ This is one of the most commonly used and discussed, initially described here: It is also a key pattern in the design of our system application modules, which is described here: [Module Architecture](https://docs.microsoft.com/en-us/dynamics365/business-central/dev-itpro/developer/devenv-blueprint) + +## Discussions +You can discuss this pattern [here](https://github.com/microsoft/alguidelines/discussions/42) \ No newline at end of file diff --git a/content/docs/patterns/generic-method-pattern/index.md b/content/docs/patterns/generic-method-pattern/index.md index a16e2b64..13083941 100644 --- a/content/docs/patterns/generic-method-pattern/index.md +++ b/content/docs/patterns/generic-method-pattern/index.md @@ -1,6 +1,6 @@ --- title: "Generic Method" -tags: ["AL","Decoupling","Readability","Testability","Extendability"] +tags: [""] categories: ["Pattern"] --- @@ -9,7 +9,6 @@ _Created by Gary Winter (Cloud Ready Software), Described by waldo (iFacto Busin ## Abstract The goal of this pattern is to facilitate a lot of things in one single awesome way of writing code. If you apply this pattern as a general pattern, you'll implement: - - Extensibility - Decoupling - Readability @@ -26,11 +25,11 @@ Usually, when you ask people where to place code, they all have their own opinio ## Description -What if we have some kind of "standard way" to always write our code. The _Generic Method Pattern_ is kind of like what it says: a generic way to implement a method. +What if we have some kind of "standard way" to always write our code. The *Generic Method Pattern* is kind of like what it says: a generic way to implement a method. ### What is a method? -Well, a method is _a significant piece of business logic_ - maybe best explained by some examples: +Well, a method is *a significant piece of business logic* - maybe best explained by some examples: - Posting a document - Any button on a page that executes business logic @@ -42,7 +41,7 @@ In a way, except "data validation", most of the things we write in our daily lif ### The Pattern **One method, one codeunit** -The idea is to put the code in one _encapsulated_ codeunit with the purpose to have all the code in that one codeunit function for that one method. This way, the codeunit will stay relatively small and readable. +The idea is to put the code in one *encapsulated* codeunit with the purpose to have all the code in that one codeunit function for that one method. This way, the codeunit will stay relatively small and readable. Let me start by showing an example, so you can refer to this complete example during the rest of the article: @@ -53,15 +52,14 @@ codeunit 53100 "WLD BlockCustomer Meth" var IsHandled: Boolean; begin - if not ConfirmBlockCustomer(HideDialog) then - exit; + if not ConfirmBlockCustomer(HideDialog) then exit; OnBeforeBlockCustomer(Cust, IsHandled); DoBlockCustomer(Cust, IsHandled); OnAfterBlockCustomer(Cust); AcknowledgeBlockCustomer(HideDialog) end; - local procedure DoBlockCustomer(var Cust: Record Customer; IsHandled: Boolean) + local procedure DoBlockCustomer(var Cust: Record Customer; IsHandled: Boolean); begin if IsHandled then exit; @@ -78,8 +76,7 @@ codeunit 53100 "WLD BlockCustomer Meth" begin DefaultAnswer := true; - if HideDialog then - exit(DefaultAnswer); + if HideDialog then exit(DefaultAnswer); exit(ConfirmManagement.GetResponseOrDefault(ConfirmQst, DefaultAnswer)); end; @@ -87,37 +84,34 @@ codeunit 53100 "WLD BlockCustomer Meth" var AcknowledgeMsg: Label 'You successfully executed "BlockCustomer"'; begin - if not GuiAllowed or HideDialog then - exit; + if not GuiAllowed or HideDialog then exit; Message(AcknowledgeMsg); end; [IntegrationEvent(false, false)] - local procedure OnBeforeBlockCustomer(var Cust: Record Customer; var IsHandled: Boolean) + local procedure OnBeforeBlockCustomer(var Cust: Record Customer; var IsHandled: Boolean); begin end; [IntegrationEvent(false, false)] - local procedure OnAfterBlockCustomer(var Cust: Record Customer) + local procedure OnAfterBlockCustomer(var Cust: Record Customer); begin end; } ``` Within that codeunit, the pattern is always the same: - - One public (internal) procedure - The rest is always local So, from outside the codeunit, there is only one clear entrypoint: that one (public) internal function with its parameters. The **pattern** within the codeunit exists of a few layers: - - The UI layer - The Event layer - The method layer -_The UI layer_ +*The UI layer* The UI layer takes care of the UI, obviously. What is important in this case, is that you always make sure that there is a "HideDialog" parameter that the business logic can use to still decide whether to use the dialog or not. These are the UI Layer parts, where you see the public function gets the HideDialog, and passes it to the UI-related procedures, where the business logic for showing the UI takes place. Also, the default answer of the confirmation is handled there as well (what if the business logic calls this method with HideDialog to "true"). @@ -129,8 +123,7 @@ codeunit 53100 "WLD BlockCustomer Meth" var IsHandled: Boolean; begin - if not ConfirmBlockCustomer(HideDialog) then - exit; + if not ConfirmBlockCustomer(HideDialog) then exit; ... AcknowledgeBlockCustomer(HideDialog) end; @@ -144,8 +137,7 @@ codeunit 53100 "WLD BlockCustomer Meth" begin DefaultAnswer := true; - if HideDialog then - exit(DefaultAnswer); + if HideDialog then exit(DefaultAnswer); exit(ConfirmManagement.GetResponseOrDefault(ConfirmQst, DefaultAnswer)); end; @@ -153,15 +145,14 @@ codeunit 53100 "WLD BlockCustomer Meth" var AcknowledgeMsg: Label 'You successfully executed "BlockCustomer"'; begin - if not GuiAllowed or HideDialog then - exit; + if not GuiAllowed or HideDialog then exit; Message(AcknowledgeMsg); end; ... } ``` -_The Event layer_ +*The Event layer* This layer is going to add flexibility to any app that has a dependency on this app. By default, the pattern always foresees an `OnBefore` and an `OnAfter` event. This is the relevant code for the event layer: @@ -181,18 +172,18 @@ codeunit 53100 "WLD BlockCustomer Meth" end; ... [IntegrationEvent(false, false)] - local procedure OnBeforeBlockCustomer(var Cust: Record Customer; var IsHandled: Boolean) + local procedure OnBeforeBlockCustomer(var Cust: Record Customer; var IsHandled: Boolean); begin end; [IntegrationEvent(false, false)] - local procedure OnAfterBlockCustomer(var Cust: Record Customer) + local procedure OnAfterBlockCustomer(var Cust: Record Customer); begin end; } ``` -_The method layer_ +*The method layer* The last layer is obviously where the business logic will be written. The relevant part is: @@ -200,7 +191,7 @@ The relevant part is: ```AL codeunit 53100 "WLD BlockCustomer Meth" { - internal procedure BlockCustomer(var Cust: Record Customer; HideDialog: Boolean) + internal procedure BlockCustomer(var Cust: Record Customer; HideDialog: Boolean); var IsHandled: Boolean; begin @@ -209,7 +200,7 @@ codeunit 53100 "WLD BlockCustomer Meth" ... end; - local procedure DoBlockCustomer(var Cust: Record Customer; IsHandled: Boolean) + local procedure DoBlockCustomer(var Cust: Record Customer; IsHandled: Boolean); begin if IsHandled then exit; @@ -220,9 +211,7 @@ codeunit 53100 "WLD BlockCustomer Meth" ... } ``` - Usually indicated with a "do"-function, the business logic takes place in that procedure. Obviously, when you have a decent amount of code, it's recommended that you make it readable by applying all the Best Practices in terms of readability in the codeunit. Though, a few pointers here: - - keep the [cyclomatic complexity](https://en.wikipedia.org/wiki/Cyclomatic_complexity) low - one line (function call) after an IF-clause - one line (function call) after a repeat @@ -254,12 +243,11 @@ tableextension 53100 "Customer Ext BASE" extends Customer This practice improves readability. In fact, by doing this, you just extended the suggestions-list in VSCode (IntelliSense) indicating a new method that your class can do. This is very convenient for the developer that might need your new method. -_Note - it could very well be that there simply isn't any table that can act as a class for our method. In that case, you could use a codeunit as well._ +*Note - it could very well be that there simply isn't any table that can act as a class for our method. In that case, you could use a codeunit as well.* **Naming Conventions** You might have noticed that the naming of our method is quite strict: - - codeunit name: `WLD BlockCustomer Meth` - internal proc: `BlockCustomer` - do-procedure: `DoBlockCustomer` @@ -267,7 +255,6 @@ You might have noticed that the naming of our method is quite strict: It is important to align these namings. It indicates that the codeunit only does one thing (remember: encapsulation), and it improves searchability from outside the codeunit (for example when you're searching symbols or something). ## Usage - Currently, there is no usage of this pattern in the BaseApp. The pattern has a main advantage in an ISV product, just because of the decoupling and extensibility. Although, I have seen many occasions where parts of the pattern was useful on PTE's as well. You simply never know if ever at the customer site, there is going to be another partner that needs to create its own PTE, and has to depend on yours. So I'd say, this pattern is everywhere applicable, no matter the type of the app. @@ -277,11 +264,9 @@ The pattern has a main advantage in an ISV product, just because of the decoupli As I said, it will facilitate a lot of advantages. Let's explain a bit more in depth: ### Extensibility - -Thanks to the _event layer_, by applying this pattern for all methods, we will automatically have the bare minimum of events that we need to hook into a method: the `OnBefore-` and the `OnAfter`. Of course it would make sense to even add more events to the method when appropriate (eg, when you're inserting a record in a table, it might be interesting to also raise an event just before you call the insert). +Thanks to the *event layer*, by applying this pattern for all methods, we will automatically have the bare minimum of events that we need to hook into a method: the `OnBefore-` and the `OnAfter`. Of course it would make sense to even add more events to the method when appropriate (eg, when you're inserting a record in a table, it might be interesting to also raise an event just before you call the insert). ### Decoupling - Thanks to these same events, and the fact the pattern foresees a handler as well, we are able to "decouple" our method as well. What do I mean with that? Well, we can simply subscribe to the `OnBefore`event, and set `IsHandled` to `true`. This means it will never execute the do-procedure, which means, the original procedure/method/business logic is "decoupled". We can use this obviously for implementing our own method (a new way to accomplish this method), or to disable the method by simply subscribing to it, and only providing the `IsHandled := true` in our subscriber. However, there are many more usages where we can use this for. @@ -292,41 +277,34 @@ if you would apply this pattern to your product, at the customer, you'll be able This gives a lot of flexibility. ### Readability - When we talk about readability, we actually talk about the part where we expose our method on the class. The rule is: never call the codeunit, but only from one place: from its "class" - or in BC terms: its table (or codeunit). In terms of readability, that means that intellisense comes into play. In stead of: - ```AL Codeunit.Run(Codeunit::"Sales-Post", SalesHeader); ``` - you simply get - ```AL SalesHeader.Post(); ``` - THAT is readable. The previous is not! That is just something we got used to. ### Testability - There are two things in terms of testability where this pattern helps a lot. -_Unit testing_ +*Unit testing* You can interpret "unit testing" very broadly. But just imagine: when you're building your software entirely out of "methods" - which means: when you'd build your software entirely with this "Generic Method Pattern". Now, the list of methods, are all the units that you need to test: if you test all your methods, you kind of like test the majority of your software, right? So you could simply set up rules in your company like: EVERY method needs a test-codeunit. And even more: since every method only has one global function - it's pretty easy to know the context, and all the flavors to test your method. The pattern describes the tests that needs to be written. -_Disabling methods_ +*Disabling methods* Coming back to the "decoupling" part - in tests, you actually might need it more than you realize. Just imagine: you want to test method 1, but method 2 comes in the way by interfering with configurations that you need to do, or UI that is popping up, while it could be completely pointless. Solution: simply - within your test-codeunit - subscribe (with a manual subscriber) to method 2, set `IsHandled` to `false` - done! ### Encapsulation - -Don't underestimate the power of the encapsulation part of this pattern. One of the first questions that people ask themselves when reading into this pattern is: "_isn't it going to consume all my codeunit-id's_" or "_so many codeunits, that can't be readable, right?_". +Don't underestimate the power of the encapsulation part of this pattern. One of the first questions that people ask themselves when reading into this pattern is: "*isn't it going to consume all my codeunit-id's*" or "*so many codeunits, that can't be readable, right?*". The fact that the functionality of one method is encapsulated in one codeunit is very powerful. You'll avoid [Boat anchors](https://sourcemaking.com/antipatterns/boat-anchor) simply because because, thanks to the encapsulation, there is a limited amount of code in the codeunit, of course. And because of that, it so much more maintainable, upgradable, readable, .. . Only advantages. @@ -338,14 +316,13 @@ So all I can say is: use your common sense. One example: set the bar at "validation code": any code that is solely there to facilitate data integrity doesn't belong in method codeunits. -Another tip might be: don't let the amount of codelines trick you in deciding to _not_ use this pattern: when it's a method, it's a method. When it makes sense to be able to extend, decouple, .. then this pattern can help. +Another tip might be: don't let the amount of codelines trick you in deciding to *not* use this pattern: when it's a method, it's a method. When it makes sense to be able to extend, decouple, .. then this pattern can help. ## Snippets [waldo's CRS AL Language Extension](https://marketplace.visualstudio.com/items?itemName=waldo.crs-al-language-extension) contains snippets that help you in setting up the boiler plate code in a matter of seconds. The snippets are: - - `tcodeunitMethodWithoutUIwaldo` - `tcodeunitMethodWithUIwaldo` @@ -354,3 +331,7 @@ The snippets are: There have been a number of occasions where people have been sharing this pattern. Here is one: {{< youtube id="CWpaD9RUa6U" yt_start="1516" >}} + + +## Discussions +You can discuss this pattern [here](https://github.com/microsoft/alguidelines/discussions/41) \ No newline at end of file diff --git a/content/docs/patterns/no-series/index.md b/content/docs/patterns/no-series/index.md deleted file mode 100644 index 538a7eb9..00000000 --- a/content/docs/patterns/no-series/index.md +++ /dev/null @@ -1,348 +0,0 @@ -+++ -title = "No. Series" -tags = ["AL"] -categories = ["Pattern"] -+++ - -_Created by Microsoft, Described by Jeremy Vyska (Spare Brained Ideas)_ - -## Abstract - -The "Number Series" system is used extensively to provide numbers to master records, documents, and other transactions through Microsoft Dynamics 365 Business Central. - -## Important: BC v24+ Modern Pattern (Updated 2024) - -**As of Business Central version 24.0 and later**, Microsoft deprecated the `NoSeriesManagement` codeunit in favor of the new `codeunit "No. Series"` with simplified methods. - -### Modern Implementation (BC v24+) - -**Variable declaration:** -```al -var - NoSeries: Codeunit "No. Series"; -``` - -**OnInsert pattern (simplified):** -```al -trigger OnInsert() -begin - if "No." = '' then begin - MySetup.Get(); - MySetup.TestField("Document Nos."); - "No. Series" := MySetup."Document Nos."; - if NoSeries.AreRelated(MySetup."Document Nos.", xRec."No. Series") then - "No. Series" := xRec."No. Series"; - "No." := NoSeries.GetNextNo("No. Series"); - end; -end; -``` - -**OnValidate pattern (same as before):** -```al -trigger OnValidate() -begin - if "No." <> xRec."No." then begin - MySetup.Get(); - NoSeries.TestManual(MySetup."Document Nos."); - "No. Series" := ''; - end; -end; -``` - -### Key Differences from Legacy Pattern - -| Legacy (NoSeriesManagement) | Modern (No. Series) | -|----------------------------|---------------------| -| `NoSeriesMgt.InitSeries(...)` - 5 parameters | `NoSeries.GetNextNo(...)` - 1-2 parameters | -| `NoSeriesMgt.TestManual(...)` | `NoSeries.TestManual(...)` - Same method name | -| `NoSeriesMgt.TryGetNextNo(...)` | `NoSeries.PeekNextNo(...)` - New name | -| `NoSeriesMgt.SelectSeries(...)` | `NoSeries.AreRelated(...)` - Simplified API | -| Complex parameter passing | Simplified, intuitive API | - -### Migration Strategy - -For **backward compatibility** (supporting both BC v23 and v24+): -1. Use conditional compilation with `#if` directives based on platform version -2. Check runtime platform version and branch logic accordingly -3. Implement both patterns in separate procedures with version detection - -{{% alert title="Note" color="warning" %}} -**The examples below reflect the LEGACY pattern** (NoSeriesManagement codeunit) for reference and historical context. For new development on BC v24+, use the modern `codeunit "No. Series"` pattern shown above. -{{% /alert %}} - ---- - -## Description - -At the heart of things, the Number Series engine allows users to define structure for a sequential numeric or alphanumeric string (collectively referred to as a 'number series'), then assign that structure to different parts of the system. - -Typically, one creates a single number series for each _type_ of data entity. For example, Customers or Sales Orders each could have a series defined so that all new Customers or Sales Orders get a new number automatically. - -The Number Series system serves a few ancillary roles: - -- maintains the usage information to know when the last number was generated and on which date -- allows for date driven structures, so that different periods may have different structures -- allows control of if manual entries are or are not permitted -- allow for incrementing in different steps (+1 each time or +1000 each time) -- warn users as a series is running out of numbers -- control if any gaps in a series are permitted (as some regional laws do not allow skipping) - -This is many roles, features, and controls for generation of a single field so the implementation of this can seem difficult at first. - -{{% alert title="Note" color="info" %}} -One additional (and somewhat optional) feature in the Number Series engine allows multiple sequences per type, called **Relationships**. For example, different numbers for Items that are finished goods versus raw materials. This requires additional hooks on the Page. -{{% /alert %}} - -## Usage in Data Entities - -To understand an example use in the Base App, the Customer data entity is a good choice. - -Implementation to connect the Customer **`No.`** field to the Number Series engine is done at the table level. The Customer table contains: - -A field to contain the number (typically the primary key), which will be of type **`Code`**, length of **20**: - -```AL -field(1; "No."; Code[20]) -{ - Caption = 'No.'; - - trigger OnValidate() - begin - [...] - end; -} -``` - -A field to contain the unique ID of the Number Series, typically called "No. Series" - -```AL -field(107; "No. Series"; Code[20]) -{ - Caption = 'No. Series'; - Editable = false; - TableRelation = "No. Series"; -} -``` -{{% alert title="Note" color="warning" %}} -The **`TableRelation`** is important, and the **`Editable`** being false is advised. -{{% /alert %}} - -And on the **`OnInsert`** trigger, code populates the **`No. Series`** and **`No.`** field. - -```AL -trigger OnInsert() -var - IsHandled: Boolean; -begin - IsHandled := false; - OnBeforeInsert(Rec, IsHandled); - if IsHandled then - exit; - - if "No." = '' then begin - SalesSetup.Get(); - SalesSetup.TestField("Customer Nos."); - NoSeriesMgt.InitSeries(SalesSetup."Customer Nos.", xRec."No. Series", 0D, "No.", "No. Series"); - end; - [...] - - OnAfterOnInsert(Rec, xRec); - end; -``` - -In the case of Customer, this is a Data Entity within the Sales module of the system. The Sales module has a **Sales Setup** table where the user can specify a **No. Series** to use for Customers by default. - -`SalesSetup.Get();` fetches the sole setup table record. - -`SalesSetup.TestField("Customer Nos.");` is the basic validation that the **Sales Setup** table has a non-empty **Customer Nos.** field. If the setup field isn't populated, when the user attempts to create a new Customer, they will receive an error message. - -`NoSeriesMgt.InitSeries(SalesSetup."Customer Nos.", xRec."No. Series", 0D, "No.", "No. Series");` is more parameters to a function than most expect. - -The function call takes the following parameters: - -```AL -procedure InitSeries( - DefaultNoSeriesCode: Code[20]; - OldNoSeriesCode: Code[20]; - NewDate: Date; - var NewNo: Code[20]; - var NewNoSeriesCode: Code[20]) -``` - -The **DefaultNoSeriesCode** parameter is typically from a setup table. In the Customer example, this comes from the **Sales Setup** **Customer Nos.** setting. - -The **OldNoSeriesCode** is used to verify when changing from one No Series to another that they are related. - -The **NewDate** parameter is used to drive numbering based on Dates. This is typically used on Documents. For master entities, like Customer, an empty date `0D` can be passed in. - -{{% alert title="Note" color="info" %}} -Many parts of the NoSeriesManagement codeunit predate method overloading, so if the system was created today, some parameters like NewDate would likely be optional. -{{% /alert %}} - -The **NewNo** is a `var` parameter, and is how the new value comes back from the engine. This also serves two other purposes: - - if passed in blank, the Number Series used must be configured to have **Default Nos.** enabled - - if passed in with a value, the Number Series used must be configured to have **Manual Nos** enabled. - -The **NewNoSeriesCode** is more often used to switch between related number series, but is a required parameter, and is also passed back from the engine, so it is also a `var`. - -Additionally, it is a good idea to have `OnValidate` functionality on the **`No.`** field. The complete code for the Customer **`No.`** field: - -```AL -field(1; "No."; Code[20]) -{ - Caption = 'No.'; - - trigger OnValidate() - begin - if "No." <> xRec."No." then begin - SalesSetup.Get(); - NoSeriesMgt.TestManual(SalesSetup."Customer Nos."); - "No. Series" := ''; - end; - if "Invoice Disc. Code" = '' then - "Invoice Disc. Code" := "No."; - end; -} -``` - -If the user has changed the **`No.`** field (`"No." <> xRec."No."`), then: -- the Number Series is checked if manually setting a new value is allowed via the `TestManual` function -- The `No. Series` is cleared on the record, as it has no longer been given a value from that Series. - - -Since the Customer data entity supports the **No. Series Relationship** functionality, there are additional components. On the table, there is a function called `AssistEdit`: - -```AL -procedure AssistEdit(OldCust: Record Customer): Boolean -var - Cust: Record Customer; -begin - with Cust do begin - Cust := Rec; - SalesSetup.Get(); - SalesSetup.TestField("Customer Nos."); - if NoSeriesMgt.SelectSeries(SalesSetup."Customer Nos.", OldCust."No. Series", "No. Series") then begin - NoSeriesMgt.SetSeries("No."); - Rec := Cust; - OnAssistEditOnBeforeExit(Cust); - exit(true); - end; - end; -end; -``` - -{{% alert title="Note" color="warning" %}} -The use of **`WITH`** is deprecated. While this code block represents the current state of the Base App, the use of **`WTIH`** should not be copied. -{{% /alert %}} - -Similar to the **`OnInsert`** trigger, some setup fields are checked. - -Then, the `SelectSeries` function is called. This will present a List to the user of available and relevant **No. Series** that are connected to the `SalesSetup."Customer Nos."` by a Number Series Relationship. - -From the **`Customer Page`** (a Card type page), the **No.** field has an **`AssistEdit`** trigger: - -```AL -trigger OnAssistEdit() -begin - if AssistEdit(xRec) then - CurrPage.Update(); -end; -``` - -## Usage in Journals - -Journals utilize a **`Document No.`** as a non-primary key field and use a different strategy for use of the Number Series engine. For each Journal Batch, a different **`No. Series`** can be set. - -For example, on the **`General Journal`** Page, in the **`OnNewRecord`**, the **`SetUpNewLine`** function on the **`Gen. Journal Line`** Table is called: - -```AL -procedure SetUpNewLine(LastGenJnlLine: Record "Gen. Journal Line"; Balance: Decimal; BottomLine: Boolean) -var - IsHandled: Boolean; -begin - IsHandled := false; - OnBeforeSetUpNewLine(GenJnlTemplate, GenJnlBatch, GenJnlLine, LastGenJnlLine, GLSetupRead, Balance, BottomLine, IsHandled); - if IsHandled then - exit; - - GenJnlTemplate.Get("Journal Template Name"); - GenJnlBatch.Get("Journal Template Name", "Journal Batch Name"); - GenJnlLine.SetRange("Journal Template Name", "Journal Template Name"); - GenJnlLine.SetRange("Journal Batch Name", "Journal Batch Name"); - if GenJnlLine.FindFirst then begin - "Posting Date" := LastGenJnlLine."Posting Date"; - "Document Date" := LastGenJnlLine."Posting Date"; - "Document No." := LastGenJnlLine."Document No."; - OnSetUpNewLineOnBeforeIncrDocNo(GenJnlLine, LastGenJnlLine, Balance, BottomLine); - if BottomLine and - (Balance - LastGenJnlLine."Balance (LCY)" = 0) and - not LastGenJnlLine.EmptyLine - then - IncrementDocumentNo(GenJnlBatch, "Document No."); - end else begin - "Posting Date" := WorkDate; - "Document Date" := WorkDate; - if GenJnlBatch."No. Series" <> '' then begin - Clear(NoSeriesMgt); - "Document No." := NoSeriesMgt.TryGetNextNo(GenJnlBatch."No. Series", "Posting Date"); - end; - end; - [...] -``` - -If the Batch is empty, and if the **`Gen. Journal Batch`** has a **`No. Series`** set, the **`Document No.`** is set from the number series via the **`NoSeriesManagement`** codeunit's **`TryGetNextNo`** function. This takes two parameters: -- Which **`No. Series`** to get the next number from -- Which date to fetch for - -This function does *not* update the **`Last No. Used`** and **`Last Date Used`** fields on the number series. Those will be updated during the Posting process. - - -If the Batch is not empty *and* the sum of the existing lines totals to zero (in balance), the General Journal assumes the user wants to start a new set of lines under a new **`Document No.`**. The table level procedure **`IncrementDocumentNo`** function is called: - -```AL -procedure IncrementDocumentNo(GenJnlBatch: Record "Gen. Journal Batch"; var LastDocNumber: Code[20]) -var - NoSeriesLine: Record "No. Series Line"; -begin - if GenJnlBatch."No. Series" <> '' then begin - NoSeriesMgt.SetNoSeriesLineFilter(NoSeriesLine, GenJnlBatch."No. Series", "Posting Date"); - if NoSeriesLine."Increment-by No." > 1 then - NoSeriesMgt.IncrementNoText(LastDocNumber, NoSeriesLine."Increment-by No.") - else - LastDocNumber := IncStr(LastDocNumber); - end else - LastDocNumber := IncStr(LastDocNumber); -end; -``` - -If the batch's **`No. Series`** is set, it is checked if the **`Increment-By No.`** setting is anything besides `1`. If so, use the special **`IncrementNoText`** function. - -If neither of those cases is true, then the line's **`Document No.`** is updated with the language function **`IncStr`**. - - -## Objects to Inspect - -Business Central objects in the Base App to review to find out more: - -| Object Type | Object ID | Object Name | -|-------------|-----------|--------------------------| -| Table | 308 | No. Series | -| Table | 309 | No. Series Line | -| Table | 310 | No. Series Relationship | -| Page | 456 | No. Series | -| Page | 457 | No. Series Lines | -| Page | 458 | No. Series Relationships | -| Page | 571 | No. Series List | -| Codeunit | 396 | NoSeriesManagement | - -## When not to use - -Typically, this pattern is used for unique Data Entities. It is not recommended for use in parts of the system where entries are created permanently (such as an **`Entry No.`** for ledgers) or highly mutable / working line data (such as **`Line No.`** for journals or document lines). - -## List of references - -For usage of number series, there is more information available on: -- [Microsoft Docs: Create Number Series](https://docs.microsoft.com/en-us/dynamics365/business-central/ui-create-number-series) -- [Microsoft Learn: Set up number series and trail codes](https://docs.microsoft.com/en-us/learn/modules/number-series-trail-codes-dynamics-365-business-central/) - -For more programming details, there is more information on [Microsoft Docs: Number Sequences in Business Central](https://docs.microsoft.com/en-us/dynamics365/business-central/dev-itpro/developer/devenv-number-sequences). diff --git a/content/docs/patterns/template-method-pattern/index.md b/content/docs/patterns/template-method-pattern/index.md deleted file mode 100644 index 95570ecd..00000000 --- a/content/docs/patterns/template-method-pattern/index.md +++ /dev/null @@ -1,153 +0,0 @@ ---- -title: "Template Method Pattern" -tags: ["AL", "Interface", "Readability"] -categories: ["Pattern"] ---- - -_Created by Patrick Schiefer, Described by Patrick Schiefer_ - -## Abstract -The goal of this pattern is to simplify the solution of similar problems and make your code more readable. - -## Problem -In nearly every app you sometimes have to solve similar problems for different cases. Mostly not the same developer will solve every case. This results in different solutions. - -## Description -The pattern is used when you have problems which are independent but require the same logical flow. Examples which occur very often are: _Posting Documents_, _Printing Reports_ or _Exporting Data_. - -## Bad Code Example -```al -codeunit 50010 ExportSalesLines -{ - procedure ExportData(SalesHeader: Record "Sales Header", SalesLine : Record "Sales Line") - begin - if not SalesHeader.CheckData() then - exit; - repeat - case SalesHeader.ExportType of - Enum::ExportType::A: - GenerateLineTypeA(SalesLine); - Enum::ExportType::B: - GenerateLineTypeB(SalesLine); - end; - until SalesLine.Next() = 0; - - case SalesHeader.ExportType of - Enum::ExportType::A: - WriteToFile(); - Enum::ExportType::B: - SendToWebService(); - end; - end; - - //TODO Implementation of procedures used in example -} -``` -As you can see in this example the readability gets worse with every new case. - -## The Pattern -To implement the Pattern you need at least 3 objects: -- A template codeunit -- An Interface which provides the needed procedures -- A codeunit which Implements the interrface - -In my example I show how to implement a data export with templating. - -We start with the template -```al -codeunit 50000 ExportTemplate -{ - procedure ExportData(Export: Interface IDataExport) - begin - if not Export.CheckData() then - exit; - if Export.GetLinesToExport() then - repeat - Export.ExportLine(); - until not Export.NextLine(); - Export.Finish(); - end; -} -``` -As you can see the template just calls procedures via an interface and just defines the flow of the export without really implementing it. - -As the second part we need an interface for the export functions -```al -interface IDataExport -{ - procedure CheckData(): Boolean - procedure GetLinesToExport(): Boolean - procedure ExportLine() - procedure NextLine(): Boolean - procedure Finish() -} -``` - -And now we need an implementation. For my example I wrote a export Codeunit for Sales Headers -```al -codeunit 50001 SalesHeaderExport implements IDataExport -{ - procedure SetSalesHeader(DocType: Enum "Sales Document Type"; No: Code[10]) - begin - SalesHeader.Get(DocType, No); - end; - - procedure CheckData(): Boolean - begin - SalesHeader.TestField(Status, Enum::"Sales Document Status"::Released); - end; - - procedure GetLinesToExport(): Boolean - begin - SalesLines.SetRange("Document Type", SalesHeader."Document Type"); - SalesLines.SetRange("Document No.", SalesHeader."No."); - exit(SalesLines.FindSet()); - end; - - procedure ExportLine() - begin - //Generate Exportdata here - end; - - procedure NextLine(Steps : integer): Boolean - begin - exit(SalesLines.Next(Steps) <> 0); - end; - - procedure Finish() - begin - // Send or Save data here - end; - - var - SalesHeader: Record "Sales Header"; - SalesLines: Record "Sales Line"; -} -``` - -Now lets have a look how to use the pattern -```al -codeunit 50002 ExportOrders -{ - procedure ExportOrder(DocType: Enum "Sales Document Type"; No: Code[10]) - var - Export: Codeunit ExportTemplate; - ExportImpl: Codeunit SalesHeaderExport; - ExportInt: Interface IDataExport; - begin - ExportImpl.SetSalesHeader(DocType, No); - ExportInt := exportImpl; - Export.ExportData(exportInt); - end; -} -``` - -## Benefits -Your code gains readability and it is very easy to add new cases for the template. You don't always have to think about the whole logic. You just have to implement the details. - -## When not to use -The Pattern should not be used for problems which differ too much. For example, if you have two data exports in your app, one is exporting header and lines and the second one only export header. In this case I would suggest to not use the pattern or to make two templates out of it. - -## References -[Detailed Explanation of the pattern](https://patrickschiefer.com/2022/04/08/template-method-pattern/) - diff --git a/go.mod b/go.mod deleted file mode 100644 index 318448e4..00000000 --- a/go.mod +++ /dev/null @@ -1,5 +0,0 @@ -module github.com/microsoft/alguidelines - -go 1.23.0 - -require github.com/google/docsy v0.12.0 // indirect diff --git a/go.sum b/go.sum deleted file mode 100644 index 963e3a5b..00000000 --- a/go.sum +++ /dev/null @@ -1,8 +0,0 @@ -github.com/FortAwesome/Font-Awesome v0.0.0-20240402185447-c0f460dca7f7/go.mod h1:IUgezN/MFpCDIlFezw3L8j83oeiIuYoj28Miwr/KUYo= -github.com/FortAwesome/Font-Awesome v0.0.0-20241216213156-af620534bfc3/go.mod h1:IUgezN/MFpCDIlFezw3L8j83oeiIuYoj28Miwr/KUYo= -github.com/google/docsy v0.10.0 h1:6tMDacPwAyRWNCfvsn/9qGOZDQ8b0aRzjRZvnZPY5dg= -github.com/google/docsy v0.10.0/go.mod h1:c0nIAqmRTOuJ01F85U/wJPQtc3Zj9N58Kea9bOT2AJc= -github.com/google/docsy v0.12.0 h1:CddZKL39YyJzawr8GTVaakvcUTCJRAAYdz7W0qfZ2P4= -github.com/google/docsy v0.12.0/go.mod h1:1bioDqA493neyFesaTvQ9reV0V2vYy+xUAnlnz7+miM= -github.com/twbs/bootstrap v5.3.3+incompatible/go.mod h1:fZTSrkpSf0/HkL0IIJzvVspTt1r9zuf7XlZau8kpcY0= -github.com/twbs/bootstrap v5.3.6+incompatible/go.mod h1:fZTSrkpSf0/HkL0IIJzvVspTt1r9zuf7XlZau8kpcY0= diff --git a/layouts/partials/page-meta-lastmod.html b/layouts/partials/page-meta-lastmod.html deleted file mode 100644 index 28b7aead..00000000 --- a/layouts/partials/page-meta-lastmod.html +++ /dev/null @@ -1,11 +0,0 @@ -{{ if and (.GitInfo) (.Site.Params.github_repo) -}} -
- {{ T "post_last_mod" }} {{ .Lastmod.Format .Site.Params.time_format_default -}} - {{ with .GitInfo }}: {{/* Trim WS */ -}} - - {{- .Subject }} ({{ .AbbreviatedHash }}) {{- /* Trim WS */ -}} - - by {{ .AuthorName }} - {{- end }} -
-{{ end -}} \ No newline at end of file diff --git a/package-lock.json b/package-lock.json index 87e17e5a..3afc9d20 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,7 +1,7 @@ { "name": "tech-doc-hugo", "version": "0.0.1", - "lockfileVersion": 3, + "lockfileVersion": 2, "requires": true, "packages": { "": { @@ -10,7 +10,7 @@ "license": "ISC", "devDependencies": { "autoprefixer": "^10.4.2", - "postcss": "^8.4.31", + "postcss": "^8.4.6", "postcss-cli": "^9.1.0" } }, @@ -135,12 +135,12 @@ } }, "node_modules/braces": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.3.tgz", - "integrity": "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==", + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz", + "integrity": "sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==", "dev": true, "dependencies": { - "fill-range": "^7.1.1" + "fill-range": "^7.0.1" }, "engines": { "node": ">=8" @@ -303,9 +303,9 @@ } }, "node_modules/fill-range": { - "version": "7.1.1", - "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.1.1.tgz", - "integrity": "sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==", + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz", + "integrity": "sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==", "dev": true, "dependencies": { "to-regex-range": "^5.0.1" @@ -342,9 +342,9 @@ } }, "node_modules/fsevents": { - "version": "2.3.3", - "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", - "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==", + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.2.tgz", + "integrity": "sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==", "dev": true, "hasInstallScript": true, "optional": true, @@ -518,16 +518,10 @@ } }, "node_modules/nanoid": { - "version": "3.3.6", - "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.6.tgz", - "integrity": "sha512-BGcqMMJuToF7i1rt+2PWSNVnWIkGCU78jBG3RxO/bZlnZPK2Cmi2QaffxGO/2RvWi9sL+FAiRiXMgsyxQ1DIDA==", + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.1.tgz", + "integrity": "sha512-n6Vs/3KGyxPQd6uO0eH4Bv0ojGSUvuLlIHtC3Y0kEO23YRge8H9x1GCzLn28YX0H66pMkxuaeESFq4tKISKwdw==", "dev": true, - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/ai" - } - ], "bin": { "nanoid": "bin/nanoid.cjs" }, @@ -596,31 +590,21 @@ } }, "node_modules/postcss": { - "version": "8.4.31", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.31.tgz", - "integrity": "sha512-PS08Iboia9mts/2ygV3eLpY5ghnUcfLV/EXTOW1E2qYxJKGGBUtNjN76FYHnMs36RmARn41bC0AZmn+rR0OVpQ==", + "version": "8.4.6", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.6.tgz", + "integrity": "sha512-OovjwIzs9Te46vlEx7+uXB0PLijpwjXGKXjVGGPIGubGpq7uh5Xgf6D6FiJ/SzJMBosHDp6a2hiXOS97iBXcaA==", "dev": true, - "funding": [ - { - "type": "opencollective", - "url": "https://opencollective.com/postcss/" - }, - { - "type": "tidelift", - "url": "https://tidelift.com/funding/github/npm/postcss" - }, - { - "type": "github", - "url": "https://github.com/sponsors/ai" - } - ], "dependencies": { - "nanoid": "^3.3.6", + "nanoid": "^3.2.0", "picocolors": "^1.0.0", "source-map-js": "^1.0.2" }, "engines": { "node": "^10 || ^12 || >=14" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" } }, "node_modules/postcss-cli": { @@ -931,5 +915,610 @@ "node": ">=12" } } + }, + "dependencies": { + "@nodelib/fs.scandir": { + "version": "2.1.5", + "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz", + "integrity": "sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==", + "dev": true, + "requires": { + "@nodelib/fs.stat": "2.0.5", + "run-parallel": "^1.1.9" + } + }, + "@nodelib/fs.stat": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz", + "integrity": "sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==", + "dev": true + }, + "@nodelib/fs.walk": { + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz", + "integrity": "sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==", + "dev": true, + "requires": { + "@nodelib/fs.scandir": "2.1.5", + "fastq": "^1.6.0" + } + }, + "ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "dev": true + }, + "ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "requires": { + "color-convert": "^2.0.1" + } + }, + "anymatch": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.2.tgz", + "integrity": "sha512-P43ePfOAIupkguHUycrc4qJ9kz8ZiuOUijaETwX7THt0Y/GNK7v0aa8rY816xWjZ7rJdA5XdMcpVFTKMq+RvWg==", + "dev": true, + "requires": { + "normalize-path": "^3.0.0", + "picomatch": "^2.0.4" + } + }, + "array-union": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/array-union/-/array-union-3.0.1.tgz", + "integrity": "sha512-1OvF9IbWwaeiM9VhzYXVQacMibxpXOMYVNIvMtKRyX9SImBXpKcFr8XvFDeEslCyuH/t6KRt7HEO94AlP8Iatw==", + "dev": true + }, + "autoprefixer": { + "version": "10.4.2", + "resolved": "https://registry.npmjs.org/autoprefixer/-/autoprefixer-10.4.2.tgz", + "integrity": "sha512-9fOPpHKuDW1w/0EKfRmVnxTDt8166MAnLI3mgZ1JCnhNtYWxcJ6Ud5CO/AVOZi/AvFa8DY9RTy3h3+tFBlrrdQ==", + "dev": true, + "requires": { + "browserslist": "^4.19.1", + "caniuse-lite": "^1.0.30001297", + "fraction.js": "^4.1.2", + "normalize-range": "^0.1.2", + "picocolors": "^1.0.0", + "postcss-value-parser": "^4.2.0" + } + }, + "binary-extensions": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.2.0.tgz", + "integrity": "sha512-jDctJ/IVQbZoJykoeHbhXpOlNBqGNcwXJKJog42E5HDPUwQTSdjCHdihjj0DlnheQ7blbT6dHOafNAiS8ooQKA==", + "dev": true + }, + "braces": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz", + "integrity": "sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==", + "dev": true, + "requires": { + "fill-range": "^7.0.1" + } + }, + "browserslist": { + "version": "4.19.3", + "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.19.3.tgz", + "integrity": "sha512-XK3X4xtKJ+Txj8G5c30B4gsm71s69lqXlkYui4s6EkKxuv49qjYlY6oVd+IFJ73d4YymtM3+djvvt/R/iJwwDg==", + "dev": true, + "requires": { + "caniuse-lite": "^1.0.30001312", + "electron-to-chromium": "^1.4.71", + "escalade": "^3.1.1", + "node-releases": "^2.0.2", + "picocolors": "^1.0.0" + } + }, + "caniuse-lite": { + "version": "1.0.30001312", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001312.tgz", + "integrity": "sha512-Wiz1Psk2MEK0pX3rUzWaunLTZzqS2JYZFzNKqAiJGiuxIjRPLgV6+VDPOg6lQOUxmDwhTlh198JsTTi8Hzw6aQ==", + "dev": true + }, + "chokidar": { + "version": "3.5.3", + "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.5.3.tgz", + "integrity": "sha512-Dr3sfKRP6oTcjf2JmUmFJfeVMvXBdegxB0iVQ5eb2V10uFJUCAS8OByZdVAyVb8xXNz3GjjTgj9kLWsZTqE6kw==", + "dev": true, + "requires": { + "anymatch": "~3.1.2", + "braces": "~3.0.2", + "fsevents": "~2.3.2", + "glob-parent": "~5.1.2", + "is-binary-path": "~2.1.0", + "is-glob": "~4.0.1", + "normalize-path": "~3.0.0", + "readdirp": "~3.6.0" + } + }, + "cliui": { + "version": "7.0.4", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-7.0.4.tgz", + "integrity": "sha512-OcRE68cOsVMXp1Yvonl/fzkQOyjLSu/8bhPDfQt0e0/Eb283TKP20Fs2MqoPsr9SwA595rRCA+QMzYc9nBP+JQ==", + "dev": true, + "requires": { + "string-width": "^4.2.0", + "strip-ansi": "^6.0.0", + "wrap-ansi": "^7.0.0" + } + }, + "color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "requires": { + "color-name": "~1.1.4" + } + }, + "color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true + }, + "dependency-graph": { + "version": "0.11.0", + "resolved": "https://registry.npmjs.org/dependency-graph/-/dependency-graph-0.11.0.tgz", + "integrity": "sha512-JeMq7fEshyepOWDfcfHK06N3MhyPhz++vtqWhMT5O9A3K42rdsEDpfdVqjaqaAhsw6a+ZqeDvQVtD0hFHQWrzg==", + "dev": true + }, + "dir-glob": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/dir-glob/-/dir-glob-3.0.1.tgz", + "integrity": "sha512-WkrWp9GR4KXfKGYzOLmTuGVi1UWFfws377n9cc55/tb6DuqyF6pcQ5AbiHEshaDpY9v6oaSr2XCDidGmMwdzIA==", + "dev": true, + "requires": { + "path-type": "^4.0.0" + } + }, + "electron-to-chromium": { + "version": "1.4.71", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.71.tgz", + "integrity": "sha512-Hk61vXXKRb2cd3znPE9F+2pLWdIOmP7GjiTj45y6L3W/lO+hSnUSUhq+6lEaERWBdZOHbk2s3YV5c9xVl3boVw==", + "dev": true + }, + "emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", + "dev": true + }, + "escalade": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.1.1.tgz", + "integrity": "sha512-k0er2gUkLf8O0zKJiAhmkTnJlTvINGv7ygDNPbeIsX/TJjGJZHuh9B2UxbsaEkmlEo9MfhrSzmhIlhRlI2GXnw==", + "dev": true + }, + "fast-glob": { + "version": "3.2.11", + "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.2.11.tgz", + "integrity": "sha512-xrO3+1bxSo3ZVHAnqzyuewYT6aMFHRAd4Kcs92MAonjwQZLsK9d0SF1IyQ3k5PoirxTW0Oe/RqFgMQ6TcNE5Ew==", + "dev": true, + "requires": { + "@nodelib/fs.stat": "^2.0.2", + "@nodelib/fs.walk": "^1.2.3", + "glob-parent": "^5.1.2", + "merge2": "^1.3.0", + "micromatch": "^4.0.4" + } + }, + "fastq": { + "version": "1.13.0", + "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.13.0.tgz", + "integrity": "sha512-YpkpUnK8od0o1hmeSc7UUs/eB/vIPWJYjKck2QKIzAf71Vm1AAQ3EbuZB3g2JIy+pg+ERD0vqI79KyZiB2e2Nw==", + "dev": true, + "requires": { + "reusify": "^1.0.4" + } + }, + "fill-range": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz", + "integrity": "sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==", + "dev": true, + "requires": { + "to-regex-range": "^5.0.1" + } + }, + "fraction.js": { + "version": "4.1.3", + "resolved": "https://registry.npmjs.org/fraction.js/-/fraction.js-4.1.3.tgz", + "integrity": "sha512-pUHWWt6vHzZZiQJcM6S/0PXfS+g6FM4BF5rj9wZyreivhQPdsh5PpE25VtSNxq80wHS5RfY51Ii+8Z0Zl/pmzg==", + "dev": true + }, + "fs-extra": { + "version": "10.0.0", + "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-10.0.0.tgz", + "integrity": "sha512-C5owb14u9eJwizKGdchcDUQeFtlSHHthBk8pbX9Vc1PFZrLombudjDnNns88aYslCyF6IY5SUw3Roz6xShcEIQ==", + "dev": true, + "requires": { + "graceful-fs": "^4.2.0", + "jsonfile": "^6.0.1", + "universalify": "^2.0.0" + } + }, + "fsevents": { + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.2.tgz", + "integrity": "sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==", + "dev": true, + "optional": true + }, + "get-caller-file": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz", + "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==", + "dev": true + }, + "get-stdin": { + "version": "9.0.0", + "resolved": "https://registry.npmjs.org/get-stdin/-/get-stdin-9.0.0.tgz", + "integrity": "sha512-dVKBjfWisLAicarI2Sf+JuBE/DghV4UzNAVe9yhEJuzeREd3JhOTE9cUaJTeSa77fsbQUK3pcOpJfM59+VKZaA==", + "dev": true + }, + "glob-parent": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", + "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", + "dev": true, + "requires": { + "is-glob": "^4.0.1" + } + }, + "globby": { + "version": "12.2.0", + "resolved": "https://registry.npmjs.org/globby/-/globby-12.2.0.tgz", + "integrity": "sha512-wiSuFQLZ+urS9x2gGPl1H5drc5twabmm4m2gTR27XDFyjUHJUNsS8o/2aKyIF6IoBaR630atdher0XJ5g6OMmA==", + "dev": true, + "requires": { + "array-union": "^3.0.1", + "dir-glob": "^3.0.1", + "fast-glob": "^3.2.7", + "ignore": "^5.1.9", + "merge2": "^1.4.1", + "slash": "^4.0.0" + } + }, + "graceful-fs": { + "version": "4.2.9", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.9.tgz", + "integrity": "sha512-NtNxqUcXgpW2iMrfqSfR73Glt39K+BLwWsPs94yR63v45T0Wbej7eRmL5cWfwEgqXnmjQp3zaJTshdRW/qC2ZQ==", + "dev": true + }, + "ignore": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.2.0.tgz", + "integrity": "sha512-CmxgYGiEPCLhfLnpPp1MoRmifwEIOgjcHXxOBjv7mY96c+eWScsOP9c112ZyLdWHi0FxHjI+4uVhKYp/gcdRmQ==", + "dev": true + }, + "is-binary-path": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz", + "integrity": "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==", + "dev": true, + "requires": { + "binary-extensions": "^2.0.0" + } + }, + "is-extglob": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", + "integrity": "sha1-qIwCU1eR8C7TfHahueqXc8gz+MI=", + "dev": true + }, + "is-fullwidth-code-point": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", + "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", + "dev": true + }, + "is-glob": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", + "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", + "dev": true, + "requires": { + "is-extglob": "^2.1.1" + } + }, + "is-number": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", + "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", + "dev": true + }, + "jsonfile": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-6.1.0.tgz", + "integrity": "sha512-5dgndWOriYSm5cnYaJNhalLNDKOqFwyDB/rr1E9ZsGciGvKPs8R2xYGCacuf3z6K1YKDz182fd+fY3cn3pMqXQ==", + "dev": true, + "requires": { + "graceful-fs": "^4.1.6", + "universalify": "^2.0.0" + } + }, + "lilconfig": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/lilconfig/-/lilconfig-2.0.4.tgz", + "integrity": "sha512-bfTIN7lEsiooCocSISTWXkiWJkRqtL9wYtYy+8EK3Y41qh3mpwPU0ycTOgjdY9ErwXCc8QyrQp82bdL0Xkm9yA==", + "dev": true + }, + "merge2": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz", + "integrity": "sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==", + "dev": true + }, + "micromatch": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.4.tgz", + "integrity": "sha512-pRmzw/XUcwXGpD9aI9q/0XOwLNygjETJ8y0ao0wdqprrzDa4YnxLcz7fQRZr8voh8V10kGhABbNcHVk5wHgWwg==", + "dev": true, + "requires": { + "braces": "^3.0.1", + "picomatch": "^2.2.3" + } + }, + "nanoid": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.1.tgz", + "integrity": "sha512-n6Vs/3KGyxPQd6uO0eH4Bv0ojGSUvuLlIHtC3Y0kEO23YRge8H9x1GCzLn28YX0H66pMkxuaeESFq4tKISKwdw==", + "dev": true + }, + "node-releases": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.2.tgz", + "integrity": "sha512-XxYDdcQ6eKqp/YjI+tb2C5WM2LgjnZrfYg4vgQt49EK268b6gYCHsBLrK2qvJo4FmCtqmKezb0WZFK4fkrZNsg==", + "dev": true + }, + "normalize-path": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", + "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==", + "dev": true + }, + "normalize-range": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/normalize-range/-/normalize-range-0.1.2.tgz", + "integrity": "sha1-LRDAa9/TEuqXd2laTShDlFa3WUI=", + "dev": true + }, + "path-type": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/path-type/-/path-type-4.0.0.tgz", + "integrity": "sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==", + "dev": true + }, + "picocolors": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.0.0.tgz", + "integrity": "sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ==", + "dev": true + }, + "picomatch": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", + "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", + "dev": true + }, + "pify": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz", + "integrity": "sha1-7RQaasBDqEnqWISY59yosVMw6Qw=", + "dev": true + }, + "postcss": { + "version": "8.4.6", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.6.tgz", + "integrity": "sha512-OovjwIzs9Te46vlEx7+uXB0PLijpwjXGKXjVGGPIGubGpq7uh5Xgf6D6FiJ/SzJMBosHDp6a2hiXOS97iBXcaA==", + "dev": true, + "requires": { + "nanoid": "^3.2.0", + "picocolors": "^1.0.0", + "source-map-js": "^1.0.2" + } + }, + "postcss-cli": { + "version": "9.1.0", + "resolved": "https://registry.npmjs.org/postcss-cli/-/postcss-cli-9.1.0.tgz", + "integrity": "sha512-zvDN2ADbWfza42sAnj+O2uUWyL0eRL1V+6giM2vi4SqTR3gTYy8XzcpfwccayF2szcUif0HMmXiEaDv9iEhcpw==", + "dev": true, + "requires": { + "chokidar": "^3.3.0", + "dependency-graph": "^0.11.0", + "fs-extra": "^10.0.0", + "get-stdin": "^9.0.0", + "globby": "^12.0.0", + "picocolors": "^1.0.0", + "postcss-load-config": "^3.0.0", + "postcss-reporter": "^7.0.0", + "pretty-hrtime": "^1.0.3", + "read-cache": "^1.0.0", + "slash": "^4.0.0", + "yargs": "^17.0.0" + } + }, + "postcss-load-config": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/postcss-load-config/-/postcss-load-config-3.1.3.tgz", + "integrity": "sha512-5EYgaM9auHGtO//ljHH+v/aC/TQ5LHXtL7bQajNAUBKUVKiYE8rYpFms7+V26D9FncaGe2zwCoPQsFKb5zF/Hw==", + "dev": true, + "requires": { + "lilconfig": "^2.0.4", + "yaml": "^1.10.2" + } + }, + "postcss-reporter": { + "version": "7.0.5", + "resolved": "https://registry.npmjs.org/postcss-reporter/-/postcss-reporter-7.0.5.tgz", + "integrity": "sha512-glWg7VZBilooZGOFPhN9msJ3FQs19Hie7l5a/eE6WglzYqVeH3ong3ShFcp9kDWJT1g2Y/wd59cocf9XxBtkWA==", + "dev": true, + "requires": { + "picocolors": "^1.0.0", + "thenby": "^1.3.4" + } + }, + "postcss-value-parser": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-4.2.0.tgz", + "integrity": "sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ==", + "dev": true + }, + "pretty-hrtime": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/pretty-hrtime/-/pretty-hrtime-1.0.3.tgz", + "integrity": "sha1-t+PqQkNaTJsnWdmeDyAesZWALuE=", + "dev": true + }, + "queue-microtask": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz", + "integrity": "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==", + "dev": true + }, + "read-cache": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/read-cache/-/read-cache-1.0.0.tgz", + "integrity": "sha1-5mTvMRYRZsl1HNvo28+GtftY93Q=", + "dev": true, + "requires": { + "pify": "^2.3.0" + } + }, + "readdirp": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz", + "integrity": "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==", + "dev": true, + "requires": { + "picomatch": "^2.2.1" + } + }, + "require-directory": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", + "integrity": "sha1-jGStX9MNqxyXbiNE/+f3kqam30I=", + "dev": true + }, + "reusify": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/reusify/-/reusify-1.0.4.tgz", + "integrity": "sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw==", + "dev": true + }, + "run-parallel": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz", + "integrity": "sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==", + "dev": true, + "requires": { + "queue-microtask": "^1.2.2" + } + }, + "slash": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/slash/-/slash-4.0.0.tgz", + "integrity": "sha512-3dOsAHXXUkQTpOYcoAxLIorMTp4gIQr5IW3iVb7A7lFIp0VHhnynm9izx6TssdrIcVIESAlVjtnO2K8bg+Coew==", + "dev": true + }, + "source-map-js": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.0.2.tgz", + "integrity": "sha512-R0XvVJ9WusLiqTCEiGCmICCMplcCkIwwR11mOSD9CR5u+IXYdiseeEuXCVAjS54zqwkLcPNnmU4OeJ6tUrWhDw==", + "dev": true + }, + "string-width": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "dev": true, + "requires": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + } + }, + "strip-ansi": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "dev": true, + "requires": { + "ansi-regex": "^5.0.1" + } + }, + "thenby": { + "version": "1.3.4", + "resolved": "https://registry.npmjs.org/thenby/-/thenby-1.3.4.tgz", + "integrity": "sha512-89Gi5raiWA3QZ4b2ePcEwswC3me9JIg+ToSgtE0JWeCynLnLxNr/f9G+xfo9K+Oj4AFdom8YNJjibIARTJmapQ==", + "dev": true + }, + "to-regex-range": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", + "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", + "dev": true, + "requires": { + "is-number": "^7.0.0" + } + }, + "universalify": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/universalify/-/universalify-2.0.0.tgz", + "integrity": "sha512-hAZsKq7Yy11Zu1DE0OzWjw7nnLZmJZYTDZZyEFHZdUhV8FkH5MCfoU1XMaxXovpyW5nq5scPqq0ZDP9Zyl04oQ==", + "dev": true + }, + "wrap-ansi": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", + "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", + "dev": true, + "requires": { + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" + } + }, + "y18n": { + "version": "5.0.8", + "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz", + "integrity": "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==", + "dev": true + }, + "yaml": { + "version": "1.10.2", + "resolved": "https://registry.npmjs.org/yaml/-/yaml-1.10.2.tgz", + "integrity": "sha512-r3vXyErRCYJ7wg28yvBY5VSoAF8ZvlcW9/BwUzEtUsjvX/DKs24dIkuwjtuprwJJHsbyUbLApepYTR1BN4uHrg==", + "dev": true + }, + "yargs": { + "version": "17.3.1", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-17.3.1.tgz", + "integrity": "sha512-WUANQeVgjLbNsEmGk20f+nlHgOqzRFpiGWVaBrYGYIGANIIu3lWjoyi0fNlFmJkvfhCZ6BXINe7/W2O2bV4iaA==", + "dev": true, + "requires": { + "cliui": "^7.0.2", + "escalade": "^3.1.1", + "get-caller-file": "^2.0.5", + "require-directory": "^2.1.1", + "string-width": "^4.2.3", + "y18n": "^5.0.5", + "yargs-parser": "^21.0.0" + } + }, + "yargs-parser": { + "version": "21.0.0", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-21.0.0.tgz", + "integrity": "sha512-z9kApYUOCwoeZ78rfRYYWdiU/iNL6mwwYlkkZfJoyMR1xps+NEBX5X7XmRpxkZHhXJ6+Ey00IwKxBBSW9FIjyA==", + "dev": true + } } } diff --git a/package.json b/package.json index 382dd766..1f75ad3a 100644 --- a/package.json +++ b/package.json @@ -18,7 +18,7 @@ "homepage": "https://github.com/google/docsy-example#readme", "devDependencies": { "autoprefixer": "^10.4.2", - "postcss": "^8.4.31", + "postcss": "^8.4.6", "postcss-cli": "^9.1.0" } } diff --git a/static/files/CAL-Coding-Guidelines-at-Microsoft-Development-Center-Copenhagen.pdf b/static/files/CAL-Coding-Guidelines-at-Microsoft-Development-Center-Copenhagen.pdf deleted file mode 100644 index 59ff8266..00000000 Binary files a/static/files/CAL-Coding-Guidelines-at-Microsoft-Development-Center-Copenhagen.pdf and /dev/null differ diff --git a/themes/docsy b/themes/docsy new file mode 160000 index 00000000..66818eb0 --- /dev/null +++ b/themes/docsy @@ -0,0 +1 @@ +Subproject commit 66818eb0905c014f342432c5fc918fc4b1c04b57