+ Guidelines and Patterns
+ for Development for
+ Microsoft Dynamics 365 Business Central
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
Design Patterns?
+
+ A "Design Pattern" is a software design template to solve common development needs.
+
+
+ This site is community run and Microsoft endorsed.
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
Benefits
+
+
+
+
+
+
Repeatability
+
+ By using consistent patterns and best practices, code is far more reusable across diverse projects.
+
+
+
+
+
+
+
Teamwork
+
+ If all code uses design patterns and best practices, it's simpler for new team members to ensure
+ that code is correct, as well as making it easier to jump into a project.
+
+
+
+
+
+
+
Performance
+
+ Many design patterns and best practices are also heavily focused on ensuring compliance with
+ the latest performance recommendations to get the most out of every system.
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
What's New
+
+
+
+ NEW
+
+ Getting Started With Agentic Coding
+
+
+
+ Essential concepts and practices for working with AI coding assistants in your AL development workflow.
+
\ No newline at end of file
diff --git a/content/blog/_index.md b/content/blog/_index.md
new file mode 100644
index 00000000..a4d08959
--- /dev/null
+++ b/content/blog/_index.md
@@ -0,0 +1,5 @@
+---
+title: "AL Guidelines Blog"
+linkTitle: "Blog"
+type: "blog"
+---
\ No newline at end of file
diff --git a/content/docs/BestPractices/CustomTelemetry/index.md b/content/docs/BestPractices/CustomTelemetry/index.md
new file mode 100644
index 00000000..c2f1eb20
--- /dev/null
+++ b/content/docs/BestPractices/CustomTelemetry/index.md
@@ -0,0 +1,96 @@
+---
+title: "Custom Telemetry"
+tags: ["AL","Telemetry"]
+categories: ["Best Practice"]
+---
+
+_Created by Microsoft, Described by Arend-Jan Kauffmann_
+
+## Description
+With AL it is possible to emit custom telemetry signals to Azure Application Insights. There are a number of considerations that you should keep in mind when designing custom telemetry signals.
+
+- Think about it as an API
+- Naming conventions and telemetry schema
+- Objects emitting telemetry signals
+- Candidate data for telemetry
+- How customers use telemetry
+- Privacy
+
+## Think about it as an API
+
+Customers will build analytics and monitoring solutions on top of their telemetry data.
+
+Therefore, signal must be treated as any other API
+
+- documented
+- versioned
+- discoverable
+- non-breaking
+
+## Naming conventions and telemetry schema
+
+To make it easy for the consumer of telemetry to work with the data, please
+
+- use **PascalCasing**. This makes all fields in Application Insights look the same (signal logged through the AL LogMessage method will have "al" prefixed to dimension names.
+- **Do not use special characters or spaces** for fields/custom dimension keys. This makes the KQL queries so much easier to write
+- for custom dimensions, consider using prefixes that helps the telemetry consumer understand where the dimension is coming from (e.g. HttpStatusCode, SqlStatement, ...)
+
+Consider always having a **"message"** field that expresses in human readable form what the telemetry event is about.
+If you do, let message names follow the Object ActionInPastTense pattern
+Some examples
+
+- Web Service Called:
+- Email attempt failed
+- Authorization to environment succeeded
+
+```al
+local procedure ProcessHttpResponse(var Request: HttpRequestMessage; var Response: HttpResponseMessage)
+var
+ CustomTelemetryDimensions: Dictionary of [Text,Text];
+begin
+ if Response.HttpStatusCode <> 200 then begin
+ CustomTelemetryDimensions.Add('Url', Request.GetRequestUri);
+ CustomTelemetryDimensions.Add('HttpStatusCode', Format(Response.HttpStatusCode));
+ CustomTelemetryDimensions.Add('ReasonPhrase', Response.ReasonPhrase);
+ Session.LogMessage(
+ 'MyExt0001',
+ 'Web service call failed',
+ Verbosity::Error,
+ DataClassification::SystemMetadata,
+ TelemetryScope::ExtensionPublisher,
+ CustomTelemetryDimensions);
+ end;
+end;
+```
+
+## Objects emitting telemetry signals
+
+Telemetry data includes information about the object that emitted the telemetry signal. It's recommended to call Session.LogMessage() **from within the object** that causes a situation that you want to have telemetry for. That will make it easier to analyze where exactly in the code an issue occurred.
+
+Of course it is possible to have a single object as a central place to emit telemetry signals. The telemetry data includes a callstack, so eventually it would be possible to trace back to the exact place where an issue occurred. But that requires a more complicated query, so it would be better to emit telemetry signals right from place in the code where an issue occurred.
+
+## Candidate data for telemetry
+
+Telemetry must be **actionable** for the customer. Do not emit signals that they cannot act on (knowing about CPU performance counters on the database is useless if the partner cannot scale the database).
+
+Also, note that customers pay for data ingestion. So be mindful to not flood their telemetry resources. Consider to use TelemetryScope::ExtensionPublisher by default and only use TelemetryScope::All in case the customer can also act on the data.
+
+If you do not know where to start, consider using telemetry for deflection. In Dynamics 365 Business Central, they started with signal about authorization (successful/failed) to deflect support cases that was due to disabled users/wrong licenses.
+
+## How customers use telemetry
+
+The following are known scenarios for customer telemetry
+
+- The tenant admin (typically an IT-pro) wants to troubleshoot a performance problem and they need more details than what is provided in the analytics reports in the admin center.
+- The customer wants to analyze (and correct) errors happening in the environment (typically an IT-pro)
+- The customer wants to analyze usage of features (typically an analytics user, maybe with BI experience)
+
+Customers typically start in the Application Insights portal and then move on to use more advanced tools for analytics (KQL, Power BI, Excel, ...). Once they have seen the light, they will likely start alerting on telemetry using Azure Monitor Alerts or setting up Power Automate flows.
+
+Business Central have developed a telemetry maturity model (based on the Gartner BI maturity model) for how organizations can evolve to use telemetry proactively in their business processes.
+
+## Privacy
+
+Telemetry must be **privacy compliant**.
+
+For privacy reasons, events that have a DataClassification other than SystemMetadata aren't sent to Application Insight resources set up on the tenant. During development of your extension, it's good practice to have a privacy review of the use of LOGMESSAGE calls to ensure that customer data isn't mistakenly leaked into Application Insights resources.
\ No newline at end of file
diff --git a/content/docs/BestPractices/DeleteAll/index.md b/content/docs/BestPractices/DeleteAll/index.md
new file mode 100644
index 00000000..3ed623a5
--- /dev/null
+++ b/content/docs/BestPractices/DeleteAll/index.md
@@ -0,0 +1,27 @@
+---
+title: "DeleteAll"
+tags: ["AL","Performance"]
+categories: ["Best Practice"]
+---
+
+_Created by waldo, Described by waldo_
+
+## Description
+
+When you perform a "DeleteAll" when there is nothing to delete, it will still perform a lock. When you for example perform a DeleteAll on an empty table, it will result in a table lock.
+Therefore it's good practice to always check if the table is empty when performing a DeleteAll.
+
+## Bad code
+
+```al
+ EmptyTableWLD.SetRange(Code, 'AJ');
+ EmptyTableWLD.DeleteAll(true);
+```
+
+## Good code
+
+```al
+ EmptyTableWLD.SetRange(Code, 'AJ');
+ if not EmptyTableWLD.IsEmpty() then
+ EmptyTableWLD.DeleteAll(true);
+```
diff --git a/content/docs/BestPractices/SetLoadFields/Index.md b/content/docs/BestPractices/SetLoadFields/Index.md
new file mode 100644
index 00000000..e3aae546
--- /dev/null
+++ b/content/docs/BestPractices/SetLoadFields/Index.md
@@ -0,0 +1,69 @@
+---
+title: "SetLoadFields"
+tags: ["AL","Readability"]
+categories: ["Best Practice"]
+---
+
+See the documentation on learn.microsoft.com for more information about [SetLoadFields](https://learn.microsoft.com/en-us/dynamics365/business-central/dev-itpro/developer/methods-auto/record/record-setloadfields-method).
+
+For the performance of your code it is important that you use SetLoadFields as much as possible.
+
+If you want to retrieve a record from the database to check if the record is available always use SetLoadFields on the primary key fields of the table so only those fields will be retrieved from the database.
+
+## Bad code
+
+```AL
+if not Item.Get(ItemNo) then
+ exit();
+```
+
+## Good code
+
+```AL
+Item.SetLoadFields("No.");
+if not Item.Get(ItemNo) then
+ exit();
+```
+
+
+Place the SetLoadFields in the code before the line of the Get (or find). (there is no need to record filter fields in the SetLoadFields because these will be retrieved automatically).
+## Bad code
+
+```AL
+Item.SetLoadFields("Item Category Code");
+Item.SetRange("Third Party Item Exists", false);
+Item.FindFirst();
+```
+
+## Good code
+
+```AL
+Item.SetRange("Third Party Item Exists", false);
+Item.SetLoadFields("Item Category Code");
+Item.FindFirst();
+```
+
+Place the SetLoadFields in the code before the case statement
+## Bad code
+
+```AL
+Item.SetLoadFields("Item Category Code");
+ItemCategoryCode := FindItemCategoryCode;
+
+case true of
+ Item.Get(ItemNo):
+ SetItemCategoryCode(Item, ItemCategoryCode);
+end;
+```
+
+## Good code
+
+```AL
+ItemCategoryCode := FindItemCategoryCode;
+Item.SetLoadFields("Item Category Code");
+
+case true of
+ Item.Get(ItemNo):
+ SetItemCategoryCode(Item, ItemCategoryCode);
+end;
+```
diff --git a/content/docs/BestPractices/SubscriberCodeunits/index.md b/content/docs/BestPractices/SubscriberCodeunits/index.md
new file mode 100644
index 00000000..9f4e61ea
--- /dev/null
+++ b/content/docs/BestPractices/SubscriberCodeunits/index.md
@@ -0,0 +1,201 @@
+---
+title: "Subscriber Codeunits"
+tags: ["AL","Performance"]
+categories: ["Best Practice"]
+---
+
+_Created by waldo, Described by waldo_
+
+## Description
+
+In general, subscribers have to be put in codeunits. There are a few performance considerations that you should keep in the back of your minds, when designing such a codeunit.
+
+- Keep the codeunit as small as possible
+- Work with a single instance codeunit
+- only subscribe when necessary
+- Avoid generic OnInsert/OnModify/OnDelete
+
+Let's discuss all points
+
+## Keep the codeunit as small as possible
+
+Every time a subscriber gets called, a new instance of the codeunit is being loaded in memory, which takes memory and processing power. The smaller the codeunit, the less memory, and the faster it is.
+
+Therefore, it's suggested to split the subscribers by functionality and avoid putting business logic in the actual codeunit. Tip: put all business logic in an "[Method Codeunit](https://alguidelines.dev/docs/patterns/generic-method-pattern/)".
+
+Examples:
+
+- if you app does things on Sales and Purchase, create a Sales-subs codeunit, and a Purchase-subs.
+- if you have multiple functionalities in your app (let's call'm modules), create a subs-codeunit per module, and only add the subscribers in there that are necessary for that module.
+
+### Bad code
+
+```AL
+codeunit 2037325 "Setup Subs"
+{
+ SingleInstance = true;
+
+ [EventSubscriber(ObjectType::Codeunit, Codeunit::"Manual Setup", 'OnRegisterManualSetup', '', false, false)]
+ local procedure OnRegisterManualSetup(sender: Codeunit "Manual Setup")
+ var
+ AppId: ModuleInfo;
+ NameListLbl: Label 'Linked Texts Framework - List', Locked = true;
+ DescriptionListLbl: Label 'Edit linked texts', Locked = true;
+ KeyWordListLbl: Label 'LT,Distri,Technical,Functional,Reports', Locked = true;
+ NameReportLbl: Label 'Linked Texts Framework - Reports', Locked = true;
+ DescriptionReportLbl: Label 'View linked texts reports', Locked = true;
+ KeyWordReportLbl: Label 'LT,Distri,Technical,Functional,Reports', Locked = true;
+ begin
+ navapp.GetCurrentModuleInfo(AppId);
+ Sender.Insert(NameListLbl, DescriptionListLbl, KeyWordListLbl, page::"LTE Linked Text List", AppId.Id(), "Manual Setup Category"::General);
+ Sender.Insert(NameReportLbl, DescriptionReportLbl, KeyWordReportLbl, page::"LTE Linked Texts Reports", AppId.Id(), "Manual Setup Category"::General);
+ end;
+
+ [EventSubscriber(ObjectType::Codeunit, codeunit::"Manual Setup", 'OnRegisterManualSetup', '', false, false)]
+ local procedure OnRegisterManualSetup(sender: Codeunit "Manual Setup")
+ var
+ AppId: ModuleInfo;
+ NameLayoutLbl: Label 'Report Helper - Layout', Locked = true;
+ DescriptionLayoutLbl: Label 'Set up or update report layout list', Locked = true;
+ KeyWordLayoutLbl: Label 'RH,Distri,Technical,Functional,Reports,Layout', Locked = true;
+ NameCaptionsLbl: Label 'Report Helper - Captions', Locked = true;
+ DescriptionCaptionsLbl: Label 'Set up or update captions list', Locked = true;
+ KeyWordCaptionsLbl: Label 'RH,Distri,Technical,Functional,Reports,Captions', Locked = true;
+ NameFunctionsLbl: Label 'Report Helper - Functions', Locked = true;
+ DescriptionFunctionsLbl: Label 'Set up or disable functions', Locked = true;
+ KeyWordFunctionsLbl: Label 'RH,Distri,Technical,Functional,Reports,Functions', Locked = true;
+ NameDFCLbl: Label 'Report Helper - Default Footer', Locked = true;
+ DescriptionDFCLbl: Label 'Set up or update default footer', Locked = true;
+ KeyWordDFCLbl: Label 'RH,Distri,Technical,Functional,Reports,Default,Footer', Locked = true;
+ begin
+ navapp.GetCurrentModuleInfo(AppId);
+ Sender.Insert(NameLayoutLbl, DescriptionLayoutLbl, KeyWordLayoutLbl, page::"RHE Report Layout List", AppId.Id(), "Manual Setup Category"::General);
+ Sender.Insert(NameCaptionsLbl, DescriptionCaptionsLbl, KeyWordCaptionsLbl, page::"RHE Captions", AppId.Id(), "Manual Setup Category"::General);
+ Sender.Insert(NameFunctionsLbl, DescriptionFunctionsLbl, KeyWordFunctionsLbl, page::"RHE Functions", AppId.Id(), "Manual Setup Category"::General);
+ Sender.Insert(NameDFCLbl, DescriptionDFCLbl, KeyWordDFCLbl, page::"RHE Default Footer Card", AppId.Id(), "Manual Setup Category"::General);
+ end;
+}
+```
+
+### Good code
+
+Split into 2 codeunits, and move the business logic out.
+
+```AL
+codeunit 2037325 "LTE Setup Subs"
+{
+ SingleInstance = true;
+
+ [EventSubscriber(ObjectType::Codeunit, Codeunit::"Manual Setup", 'OnRegisterManualSetup', '', false, false)]
+ local procedure OnRegisterManualSetup(sender: Codeunit "Manual Setup")
+ var
+ RegisterLTEManualSetup: codeunit "Register LTE Manual Setup";
+ begin
+ RegisterLTEManualSetup.RegisterLTEManualSetup();
+ end;
+}
+
+codeunit 2037324 "RHE Setup Subs"
+{
+ SingleInstance = true;
+
+ [EventSubscriber(ObjectType::Codeunit, Codeunit::"Manual Setup", 'OnRegisterManualSetup', '', false, false)]
+ local procedure OnRegisterManualSetup(sender: Codeunit "Manual Setup")
+ var
+ RegisterRHEManualSetup: codeunit "Register RHE Manual Setup";
+ begin
+ RegisterRHEManualSetup.RegisterRHEManualSetup();
+ end;
+}
+
+
+```
+
+## Work with a single instance codeunit
+
+To avoid the extra "loading of the content" while a subscriber is being executed, use Single Instance codeunit for subscribers. Do take into account, of course, that it would share the state across the entire session.
+
+### Bad code
+
+```AL
+codeunit 2037324 "RHE Setup Subs"
+{
+ [EventSubscriber(ObjectType::Codeunit, Codeunit::"Manual Setup", 'OnRegisterManualSetup', '', false, false)]
+ local procedure OnRegisterManualSetup(sender: Codeunit "Manual Setup")
+ var
+ RegisterRHEManualSetup: codeunit "Register RHE Manual Setup";
+ begin
+ RegisterRHEManualSetup.RegisterRHEManualSetup();
+ end;
+}
+```
+
+### Good code
+
+```AL
+codeunit 2037324 "RHE Setup Subs"
+{
+ SingleInstance = true;
+
+ [EventSubscriber(ObjectType::Codeunit, Codeunit::"Manual Setup", 'OnRegisterManualSetup', '', false, false)]
+ local procedure OnRegisterManualSetup(sender: Codeunit "Manual Setup")
+ var
+ RegisterRHEManualSetup: codeunit "Register RHE Manual Setup";
+ begin
+ RegisterRHEManualSetup.RegisterRHEManualSetup();
+ end;
+}
+```
+
+## only subscribe when necessary
+
+If possible, only execute the subscriber when really necessary by using Manual Binding.
+
+### Bad code
+
+```AL
+ //subscriber - code should actually only run when Color=Red.
+ [EventSubscriber(ObjectType::Table, Database::"Just Some Table WLD", 'OnAfterValidateEvent', 'Message 2', false, false)]
+ local procedure JustDoSomthing(var Rec: Record "Just Some Table WLD"; var xRec: Record "Just Some Table WLD")
+ begin
+ if Rec.color <> 'RED' then
+ exit; //only execute when necessary
+
+ ...
+ end;
+
+ //business logic
+ if JustSomeTable.FindSet() then
+ repeat
+ JustSomeTable.Validate("Message 2", format(Random(1000)));
+ until JustSomeTable.Next() < 1;
+```
+
+### Good code
+
+```AL
+ if JustSomeTable.FindSet() then
+ repeat
+ if JustSomeTable.Color = 'RED' then
+ BindSubscription(DemoSubs);
+
+ JustSomeTable.Validate("Message 2", format(Random(1000)));
+
+ if JustSomeTable.Color = 'RED' then
+ UnbindSubscription(DemoSubs);
+ until JustSomeTable.Next() < 1;
+```
+
+## Avoid OnInsert/OnModify/OnDelete
+
+The reason for this is, that it breaks the batch-calls:
+
+- Any "OnInsert" subscriber breaks the bulk inserts, simply because it needs to perform an operation after every record that was inserted
+- Any "OnModify" subscriber slows down the "ModifyAll", simply because it needs to perform an operation after every record that was modified. I fact: 1 SQL call is turned into a loop of SQL calls.
+- Any "OnDelete" subscriber slows down the "DeleteAll", simply because it needs to perform an operation after every record that was deleted. I fact: 1 SQL call is turned into a loop of SQL calls.
+
+Avoid subscribers to these events.
+
+## References
+
+The [Generic Method Pattern](https://alguidelines.dev/docs/patterns/generic-method-pattern/)
diff --git a/content/docs/BestPractices/_index.md b/content/docs/BestPractices/_index.md
new file mode 100644
index 00000000..9684d0df
--- /dev/null
+++ b/content/docs/BestPractices/_index.md
@@ -0,0 +1,24 @@
+---
+title: "Best Practices"
+weight: 3
+description: >
+ AL Code Best Practices
+---
+
+This section will be cover things that aren't as simple as Design Patterns, but will help make sure your development is:
+
+- high-performance
+- complies with good designs
+- has high maintainability
+
+## Readability
+
+Generally, all readability rules are Microsoft style choices only. You can use them to keep consistency with the existing code.
+
+## Performance
+
+Some guidelines are simply better for performance considerations rather than readability or anything else. In this section, let's look into some..
+
+## Discussion
+
+All discussion related to Best Practice are to be found on the Github Repo's Discussion pages, found [here](https://github.com/microsoft/alguidelines/discussions/categories/bc-best-practices)
diff --git a/content/docs/BestPractices/api-page/index.md b/content/docs/BestPractices/api-page/index.md
new file mode 100644
index 00000000..cc23701e
--- /dev/null
+++ b/content/docs/BestPractices/api-page/index.md
@@ -0,0 +1,187 @@
+---
+title: "API Page / Query"
+tags: ["AL","API"]
+categories: ["Best Practice"]
+---
+
+_Created by Arend-Jan Kauffmann, Described by Arend-Jan Kauffmann_
+
+## Description
+
+API pages are different from UI pages. They require different properties and don't behave the same. Because API pages are used for integration with external applications, they should be treated as contracts. To achieve this, the following topics are important.
+
+- Separate API app
+- Page properties
+- Versioning
+- Field properties
+- Default fields
+
+## Separate API app
+
+It's a good practice to develop API pages in a separate app instead of combining them in a solution. By doing so, it provides better maintainability and is a good way of separation of concerns.
+
+## Page properties
+An API page must define a minimum set of properties. Some of these properties will be part of the URL of the API endpoint. It is recommended to define the properties in the same order as they appear in the URL.
+
+The properties that must be defined are:
+
+- PageType = API / QueryType = API
+- APIPublisher
+- APIGroup
+- APIVersion
+- EntitySetName
+- EntityName
+- DelayedInsert (only Page)
+- ODataKeyFields
+
+### APIPublisher
+The name of the API publisher is usually the company creating the API. It is the first custom part in the URL for a given endpoint. 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
new file mode 100644
index 00000000..5b6bac47
--- /dev/null
+++ b/content/docs/BestPractices/begin-as-an-afterword/index.md
@@ -0,0 +1,28 @@
+---
+title: "begin as an afterword"
+tags: ["AL","Readability"]
+categories: ["Best Practice"]
+---
+
+_Created by Microsoft, Described by waldo_
+
+## Description
+
+When `begin` follows `then`, `else`, `do`, it should be on the same line, preceded by one space character.
+
+## Bad code
+
+```al
+ if ICPartnerRefType = ICPartnerRefType::"Common Item No." then
+ begin
+ ...
+ end;
+```
+
+## Good code
+
+```al
+ if ICPartnerRefType = ICPartnerRefType::"Common Item No." then begin
+ ...
+ end;
+```
diff --git a/content/docs/BestPractices/begin-end/index.md b/content/docs/BestPractices/begin-end/index.md
new file mode 100644
index 00000000..d35dea99
--- /dev/null
+++ b/content/docs/BestPractices/begin-end/index.md
@@ -0,0 +1,65 @@
+---
+title: "Begin-End - Compound Only"
+tags: ["AL","Readability"]
+categories: ["Best Practice"]
+---
+
+_Created by Microsoft, Described by waldo_
+
+Only use begin..end to enclose [compound statements](https://docs.microsoft.com/en-us/cpp/c-language/compound-statement-c?view=msvc-170#:~:text=A%20compound%20statement%20%28also%20called%20a%20%22block%22%29%20typically,appear%20at%20the%20head%20of%20a%20compound%20statement.).
+
+## Example 1
+
+### Bad code
+
+```AL
+if FindSet() then begin
+ repeat
+ ...
+ until next() = 0;
+end;
+```
+
+### Good code
+
+```AL
+if FindSet() then
+ repeat
+ ...
+ until next() = 0;
+```
+
+## Example 2
+
+### Bad code
+
+```AL
+if IsAssemblyOutputLine then begin
+ TestField("Order Line No.", 0);
+end;
+```
+
+### Good code
+
+```AL
+if IsAssemblyOutputLine then
+ TestField("Order Line No.", 0);
+```
+
+## Exception
+
+```AL
+// Except for this case
+if X then begin
+ if Y then
+ //DO SOMETHING;
+end else
+ (not X)
+```
+
+## Tips
+
+The [AZ AL Dev Tools/AL Code Outline](https://marketplace.visualstudio.com/items?itemName=andrzejzwierzchowski.al-code-outline) extension adds two new commands to Visual Studio Code to remove begin..end around single statements.
+
+- `Remove Begin..End around Single Statements from the Active Editor` : removes begin..end around single statement from the current editor
+- `Remove Begin..End around Single Statements from the Active Project` : removes begin..end around single statement from the current project
diff --git a/content/docs/BestPractices/binary-operator-line-start/index.md b/content/docs/BestPractices/binary-operator-line-start/index.md
new file mode 100644
index 00000000..2f7ecae8
--- /dev/null
+++ b/content/docs/BestPractices/binary-operator-line-start/index.md
@@ -0,0 +1,27 @@
+---
+title: "Binary Operator to Start Line"
+tags: ["AL","Readability"]
+categories: ["Best Practice"]
+---
+
+_Created by Microsoft, Described by waldo_
+
+## Description
+
+Do not start a line with a binary operator.
+
+## Bad code
+
+```AL
+"Quantity to Ship" :=
+ Quantity
+ - "Quantity Shipped"
+```
+
+## Good code
+
+```AL
+"Quantity to Ship" :=
+ Quantity -
+ "Quantity Shipped"
+```
diff --git a/content/docs/BestPractices/blank-lines/index.md b/content/docs/BestPractices/blank-lines/index.md
new file mode 100644
index 00000000..7f1b8efe
--- /dev/null
+++ b/content/docs/BestPractices/blank-lines/index.md
@@ -0,0 +1,115 @@
+---
+title: "When not to use Blank Lines"
+tags: ["AL","Readability"]
+categories: ["Best Practice"]
+---
+
+## Description
+
+Do not use blank lines:
+
+- at the beginning or end of any functions (after `begin` and before `end`)
+- inside multiline expression
+- after blank lines
+
+## Example 1
+
+### Bad code
+
+```al
+procedure MATRIX_OnDrillDown(MATRIX_ColumnOrdinal: Integer)
+begin
+
+ SetupDrillDownCol(MATRIX_ColumnOrdinal);
+ DrillDown(false, ValueType);
+
+end;
+```
+
+### Good code
+
+```al
+procedure MATRIX_OnDrillDown(MATRIX_ColumnOrdinal: Integer)
+begin
+ SetupDrillDownCol(MATRIX_ColumnOrdinal);
+ DrillDown(false, ValueType);
+end;
+```
+
+## Example 2
+
+### Bad code
+
+```al
+if NameIsValid and
+
+ Name2IsValid
+then
+```
+
+### Good code
+
+```al
+if NameIsValid and
+ Name2IsValid
+then
+```
+
+## Example 3
+
+### Bad code
+
+```al
+var
+ GLSetup: Record "General Ledger Setup";
+ GLSetupRead: Boolean;
+
+
+local procedure GetGLSetup()
+begin
+ if not GLSetupRead then
+ GLSetup.Get();
+
+
+ GLSetupRead := true;
+
+
+ OnAfterGetGLSetup(GLSetup);
+end;
+
+
+[IntegrationEvent(false, false)]
+local procedure OnAfterGetGLSetup(var GLSetup: Record "General Ledger Setup")
+begin
+end;
+```
+
+### Good code
+
+```al
+var
+ GLSetup: Record "General Ledger Setup";
+ GLSetupRead: Boolean;
+
+local procedure GetGLSetup()
+begin
+ if not GLSetupRead then
+ GLSetup.Get();
+
+ GLSetupRead := true;
+
+ OnAfterGetGLSetup(GLSetup);
+end;
+
+[IntegrationEvent(false, false)]
+local procedure OnAfterGetGLSetup(var GLSetup: Record "General Ledger Setup")
+begin
+end;
+```
+
+## Tips
+
+The [AZ AL Dev Tools/AL Code Outline](https://marketplace.visualstudio.com/items?itemName=andrzejzwierzchowski.al-code-outline) extension adds two new commands to Visual Studio Code to remove empty duplicate lines.
+
+- `Remove Empty Lines from the Active Editor` : removes empty duplicate lines from the current editor
+- `Remove Empty Lines from the Active Project` : removes empty duplicate lines from the current project
diff --git a/content/docs/BestPractices/case-actions/index.md b/content/docs/BestPractices/case-actions/index.md
new file mode 100644
index 00000000..5062e53e
--- /dev/null
+++ b/content/docs/BestPractices/case-actions/index.md
@@ -0,0 +1,31 @@
+---
+title: "CASE Action on next line"
+tags: ["AL","Readability"]
+categories: ["Best Practice"]
+---
+
+_Created by Microsoft, Described by waldo_
+
+## Description
+
+A CASE action should start on a line after the possibility.
+
+## Bad code
+
+```AL
+ case Letter of
+ 'A': Letter2 := '10';
+ 'B': Letter2 := '11';
+ end;
+```
+
+## Good code
+
+```AL
+ case Letter of
+ 'A':
+ Letter2 := '10';
+ 'B':
+ Letter2 := '11';
+ end;
+```
diff --git a/content/docs/BestPractices/comments-spacing/index.md b/content/docs/BestPractices/comments-spacing/index.md
new file mode 100644
index 00000000..615f9e69
--- /dev/null
+++ b/content/docs/BestPractices/comments-spacing/index.md
@@ -0,0 +1,23 @@
+---
+title: "Comment Spacing"
+tags: ["AL","Readability"]
+categories: ["Best Practice"]
+---
+
+_Created by Microsoft, Described by waldo_
+
+## Description
+
+Always start comments with // followed by one space character.
+
+## Bad code
+
+```al
+RowNo += 1000; //Move way below the budget
+```
+
+## Good code
+
+```al
+RowNo += 1000; // Move way below the budget
+```
diff --git a/content/docs/BestPractices/end-else-pair/index.md b/content/docs/BestPractices/end-else-pair/index.md
new file mode 100644
index 00000000..29313176
--- /dev/null
+++ b/content/docs/BestPractices/end-else-pair/index.md
@@ -0,0 +1,35 @@
+---
+title: "end else pair"
+tags: ["AL","Readability"]
+categories: ["Best Practice"]
+---
+
+_Created by Microsoft, Described by waldo_
+
+## Description
+
+The `end else` pair should always appear on the same line.
+
+## Bad code
+
+```al
+ if OppEntry.Find('-') then
+ if SalesCycleStage.Find('-') then begin
+ ...
+ end
+ else
+ begin
+ ...
+ end;
+```
+
+## Good code
+
+```al
+ if OppEntry.Find('-') then
+ if SalesCycleStage.Find('-') then begin
+ ...
+ end else begin
+ ...
+ end;
+```
diff --git a/content/docs/BestPractices/if-not-find-then-exit/index.md b/content/docs/BestPractices/if-not-find-then-exit/index.md
new file mode 100644
index 00000000..6c097864
--- /dev/null
+++ b/content/docs/BestPractices/if-not-find-then-exit/index.md
@@ -0,0 +1,109 @@
+---
+title: "if not then exit"
+tags: ["AL"]
+categories: ["Best Practice"]
+---
+
+_Created by sirhc101, Described by sirhc101_
+
+## Description
+
+In general when we are working with tables we want to make sure, the filtered dataset includes records and does not result in a runtime error, so we use `if` to handle the result of `Find()`, `FindSet()`, `Get()`, etc.
+This automatically causes on indent in source code and often the source code does not just contain one but two or more tables involved which leads to multi-level indentation.
+
+Basically this is a result of bad coding structure but maybe sometimes necessary. On the other hand this causes multiple `end;` usages and leads to the usage of colorization and other helpers to see which `begin` belongs to which `end;`.
+
+Instead of using `if (Record.FindSet()) then` to fetch records from a database it's good practice to use `if (not Record.FindSet()) then` following by an `exit();` to not further process the source code and make it clear for other developers where they can stop reading in certain cases.
+
+Furthermore, this more or less automatically leads to smaller and better structured procedures and reduces the complexity of the source code.
+
+## Bad code
+
+```al
+ SalesHeader.Reset();
+ SalesHeader.SetRange("Document Type", SalesHeader."Document Type"::Order);
+ SalesHeader.SetRange(Status, SalesHeader.Status::Open);
+ if (SalesHeader.FindSet(false)) then begin
+ repeat
+ SalesLine.Reset();
+ SalesLine.SetRange("Document Type", SalesHeader."Document Type"::Order);
+ SalesLine.SetRange("Document No.", SalesHeader."No.");
+ if (SalesLine.FindSet(true)) then begin
+ repeat
+ DoSomething();
+ until SalesLine.Next() = 0;
+ end;
+ until SalesHeader.Next() = 0;
+
+ DoSomethingElse();
+ end;
+```
+
+or
+
+```al
+ SalesLine.Reset();
+ SalesLine.SetRange("Document Type", SalesHeader."Document Type"::Order);
+ if (SalesLine.FindSet(true)) then begin
+ repeat
+ case SalesLine."Type" of
+ SalesLine."Type"::Item:
+ DoSomethingItem();
+ SalesLine."Type"::Resource:
+ DoSomethingResource();
+ end;
+ until SalesLine.Next() = 0;
+ end;
+```
+
+## Good code
+
+```al
+ procedure DoSomethingSalesOrder()
+ var
+ SalesHeader: Record "Sales Header";
+ begin
+ SalesHeader.SetRange("Document Type", SalesHeader."Document Type"::Order);
+ SalesHeader.SetRange(Status, SalesHeader.Status::Open);
+ if (not SalesHeader.FindSet(false)) then
+ exit;
+
+ repeat
+ DoSomethingSalesLine(SalesHeader);
+ until SalesHeader.Next() = 0;
+
+ DoSomethingElse();
+ end;
+
+ procedure DoSomethingSalesLine(var SalesHeader: Record "Sales Header")
+ var
+ SalesLine: Record "Sales Line";
+ begin
+ SalesLine.Reset();
+ SalesLine.SetRange("Document Type", SalesHeader."Document Type"::Order);
+ SalesLine.SetRange("Document No.", SalesHeader."No.");
+ if (not SalesLine.FindSet(true)) then
+ exit;
+
+ repeat
+ DoSomething();
+ until SalesLine.Next() = 0;
+ end;
+```
+
+or
+
+```al
+ SalesLine.SetRange("Document Type", SalesHeader."Document Type"::Order);
+ if (not SalesLine.FindSet(true)) then
+ exit;
+
+ repeat
+ case SalesLine."Type" of
+ SalesLine."Type"::Item:
+ DoSomethingItem();
+ SalesLine."Type"::Resource:
+ DoSomethingResource();
+ end;
+ until SalesLine.Next() = 0;
+```
diff --git a/content/docs/BestPractices/istemporary-table-safeguard/index.md b/content/docs/BestPractices/istemporary-table-safeguard/index.md
new file mode 100644
index 00000000..7331b34c
--- /dev/null
+++ b/content/docs/BestPractices/istemporary-table-safeguard/index.md
@@ -0,0 +1,60 @@
+---
+title: "IsTemporary record safeguard"
+tags: ["AL"]
+categories: ["Best Practice"]
+---
+
+_Created by Kine, Described by Kine_
+
+## Description
+
+When you are working with temporary tables or real tables, you can have code, where you assume that Record variable is or is not temporary. Best practice is to not assume, but test it to be sure. In history,
+many developers went through painful period when they did unwanted "DeleteAll" over real table in production database, because they were only assuming something (mostly it happened only once to them).
+
+Therefore it is good practice to use [Record.IsTemporary()](https://docs.microsoft.com/en-us/dynamics365/business-central/dev-itpro/developer/methods-auto/record/record-istemporary-method) method to test this predicate, mainly when you are doing destructive action.
+
+Another case when it is good practice to use this test is when you are subscribing to triggers on table. In most cases, you do not want to run your code when the trigger is running over temporary record. And you cannot assume, that
+this specific table will not be used as temporary by someone else. Reacting to the trigger as if it was triggered by real table change could lead to corrupted data or unpredictable errors and the reason could be hard to find.
+
+## Bad code
+
+```al
+ ShouldBeTemporary.DeleteAll(true);
+```
+
+or
+
+```al
+ [EventSubscriber(ObjectType::Table, Database::"Some Table", 'OnAfterInsertEvent', '', false, false)]
+ local procedure DoSomethingOnAfterInsertSomeTable(var Rec: Record "Some Table")
+ begin
+ DoSomething(Rec);
+ end;
+```
+
+## Good code
+
+```al
+ if ShouldBeTemporary.IsTemporary() then
+ ShouldBeTemporary.DeleteAll(true);
+```
+
+or
+
+```al
+ if not ShouldBeTemporary.IsTemporary() then
+ Error(RecNotTemporaryErr);
+ ShouldBeTemporary.DeleteAll(true);
+```
+
+or
+
+```al
+ [EventSubscriber(ObjectType::Table, Database::"Some Table", 'OnAfterInsertEvent', '', false, false)]
+ local procedure DoSomethingOnAfterInsertSomeTable(var Rec: Record "Some Table")
+ begin
+ if Rec.IsTemporary() then
+ Exit;
+ DoSomething(Rec);
+ end;
+```
diff --git a/content/docs/BestPractices/keyboard-shortcuts/index.md b/content/docs/BestPractices/keyboard-shortcuts/index.md
new file mode 100644
index 00000000..a9b15863
--- /dev/null
+++ b/content/docs/BestPractices/keyboard-shortcuts/index.md
@@ -0,0 +1,39 @@
+---
+title: "Keyboard Shortcuts"
+tags: ["AL","Productivity"]
+categories: ["Best Practice"]
+---
+
+_Created by Christian Lenz, Described by Christian Lenz_
+
+## Description
+
+To increase developer productivity while coding, use keyboard shortcuts that are available in the specific context to execute actions faster.
+
+This is a selection of the community's favorites (more to come).
+
+**Windows**
+
+| What | Where | How |
+|---|---|---|
+| Delete word | Editor | CTRL + Backspace |
+
+
+**VS Code**
+
+| What | Where | How |
+|---|---|---|
+| Switch Tab | Editor | ALT + |
+| Move Line Up/Down | Editor | ALT + |
+| Copy Line Below/Above | Editor | ALT + SHIFT + |
+| Delete Line | Editor | CTRL + X (without selection) |
+| Add Selection To Next Match | Editor | CTRL + D |
+| Select All Occurences Of Find Match | Editor | CTRL + SHIFT + L |
+| Add Cursors To Line Ends | Editor | SHIFT + ALT + I (after selecting lines) |
+| Add Cursor Above/Below | Editor | CTRL + ALT + |
+| Place Multiple Cursors Manually | Editor | ALT + Click |
+| Fast Scrolling | Editor | ALT + Mouse Wheel |
+| Go To Symbol In File | Editor | CTRL + SHIFT + O |
+| Breadcrumbs - Open And Select | Editor | CTRL + SHIFT + . |
+| Go Back / Forward | Go To Definition | ALT + |
+
diff --git a/content/docs/BestPractices/keyword-pairs-indentation/index.md b/content/docs/BestPractices/keyword-pairs-indentation/index.md
new file mode 100644
index 00000000..b25ea498
--- /dev/null
+++ b/content/docs/BestPractices/keyword-pairs-indentation/index.md
@@ -0,0 +1,26 @@
+---
+title: "Keyword Pairs - Indentation"
+tags: ["AL","Readability"]
+categories: ["Best Practice"]
+---
+
+_Created by Microsoft, Described by waldo_
+
+## Description
+
+The `if..then` pair, `while..do` pair, and `for..do` pair must appear on the same line or the same level of indentation. If possible, you can align the lines it is even much more readable.
+
+## Bad code
+
+```al
+ if (x = y) and
+ (a = b) then
+```
+
+## Good code
+
+```al
+ if (x = y) and
+ (a = b)
+ then
+```
diff --git a/content/docs/BestPractices/line-start-keywords/index.md b/content/docs/BestPractices/line-start-keywords/index.md
new file mode 100644
index 00000000..113e587c
--- /dev/null
+++ b/content/docs/BestPractices/line-start-keywords/index.md
@@ -0,0 +1,31 @@
+---
+title: "Line Start Keywords"
+tags: ["AL","Readability"]
+categories: ["Best Practice"]
+---
+_Created by Microsoft, Described by waldo_
+
+## Description
+
+The `end`, `if`, `repeat`, `for`, `while`, `else` and `case` statement should always start a line.
+
+## Bad code
+
+```al
+ if IsContactName then ValidateContactName()
+ else if IsSalespersonCode then ValidateSalespersonCode()
+ else if IsSalesCycleCode then ValidatSalesCycleCode();
+```
+
+## Good code
+
+```al
+ if IsContactName then
+ ValidateContactName()
+ else
+ if IsSalespersonCode then
+ ValidateSalespersonCode()
+ else
+ if IsSalesCycleCode then
+ ValidatSalesCycleCode();
+```
diff --git a/content/docs/BestPractices/lonely-repeat/index.md b/content/docs/BestPractices/lonely-repeat/index.md
new file mode 100644
index 00000000..d15c0945
--- /dev/null
+++ b/content/docs/BestPractices/lonely-repeat/index.md
@@ -0,0 +1,24 @@
+---
+title: "Lonely Repeat"
+tags: ["AL","Readability"]
+categories: ["Best Practice"]
+---
+
+_Created by Microsoft, Described by waldo_
+
+## Description
+
+The `repeat` statement should always be alone on a line.
+
+## Bad code
+
+```al
+ if ReservEntry.FindSet() then repeat
+```
+
+## Good code
+
+```al
+ if ReservEntry.FindSet() then
+ repeat
+```
diff --git a/content/docs/BestPractices/named-invocations/index.md b/content/docs/BestPractices/named-invocations/index.md
new file mode 100644
index 00000000..395848e4
--- /dev/null
+++ b/content/docs/BestPractices/named-invocations/index.md
@@ -0,0 +1,29 @@
+---
+title: "Named Invocations"
+tags: ["AL","Readability"]
+categories: ["Best Practice"]
+---
+
+_Created by Microsoft, Described by waldo_
+
+## Description
+
+When calling an object statically use the Object Name, not the Object Id.
+
+## Bad code
+
+```al
+ Page.RunModal(525, SalesShptLine);
+```
+
+## Good code
+
+```al
+ Page.RunModal(Page::"Posted Sales Shipment Lines", SalesShptLine);
+```
+
+## Tips
+
+The [BusinessCentral.LinterCop](https://marketplace.visualstudio.com/items?itemName=StefanMaron.businesscentral-lintercop) extension adds a new rule to check your code for hardcoded object IDs.
+
+- [LC0012](https://github.com/StefanMaron/BusinessCentral.LinterCop/wiki/LC0012): Using hardcoded IDs in functions like Codeunit.Run() is not allowed.
diff --git a/content/docs/BestPractices/one-statement-per-line/index.md b/content/docs/BestPractices/one-statement-per-line/index.md
new file mode 100644
index 00000000..822c05c1
--- /dev/null
+++ b/content/docs/BestPractices/one-statement-per-line/index.md
@@ -0,0 +1,41 @@
+---
+title: "One Statement per Line"
+tags: ["AL","Readability"]
+categories: ["Best Practice"]
+---
+
+_Created by Microsoft, Described by waldo_
+
+## Description
+
+A line of code should not have more than one statement.
+
+## Example 1
+
+### Bad code
+
+```al
+ if OppEntry.Find('-') then exit;
+```
+
+### Good code
+
+```al
+ if OppEntry.Find('-') then
+ exit;
+```
+
+## Example 2
+
+### Bad code
+
+```al
+ TotalCost += Cost; TotalAmt += Amt;
+```
+
+### Good code
+
+```al
+ TotalCost += Cost;
+ TotalAmt += Amt;
+```
diff --git a/content/docs/BestPractices/separate-if-and-else/index.md b/content/docs/BestPractices/separate-if-and-else/index.md
new file mode 100644
index 00000000..805ff90c
--- /dev/null
+++ b/content/docs/BestPractices/separate-if-and-else/index.md
@@ -0,0 +1,29 @@
+---
+title: "Seperate if and else"
+tags: ["AL","Readability"]
+categories: ["Best Practice"]
+---
+
+_Created by Microsoft, Described by waldo_
+
+## Description
+
+`if` and `else` statements should be on separate lines.
+
+## Bad code
+
+```al
+ if Atom = '>' then HasLogicalOperator := true else begin
+ ...
+ end;
+```
+
+## Good code
+
+```al
+ if Atom = '>' then
+ HasLogicalOperator := true
+ else begin
+ ...
+ end;
+```
diff --git a/content/docs/BestPractices/spacing-binary-operators/index.md b/content/docs/BestPractices/spacing-binary-operators/index.md
new file mode 100644
index 00000000..e0bfaac8
--- /dev/null
+++ b/content/docs/BestPractices/spacing-binary-operators/index.md
@@ -0,0 +1,53 @@
+---
+title: "Spacing Binary Operators"
+tags: ["AL","Readability"]
+categories: ["Best Practice"]
+---
+
+_Created by Microsoft, Described by waldo_
+
+## Description
+
+There must be exactly one space character on each side of a binary operator such as = + - AND OR =. The parameter comma operator however, should have a space after the comma.
+
+## Example 1
+
+### Bad code
+
+```al
+ "Line Discount %" := "Line Discount Amount"/"Line Value"*100;
+```
+
+### Good code
+
+```al
+ "Line Discount %" := "Line Discount Amount" / "Line Value" * 100;
+```
+
+## Example 2
+
+### Bad code
+
+```al
+ StartDate := CalcDate('<+'+Format(Days+i)+'D\>',StartDate);
+```
+
+### Good code
+
+```al
+ StartDate := CalcDate('<+' + Format(Days + i) + 'D\>', StartDate);
+```
+
+## Example 3
+
+### Bad code
+
+```al
+ StartDate:=0D; // Initialize
+```
+
+### Good code
+
+```al
+ StartDate := 0D; // Initialize
+```
diff --git a/content/docs/BestPractices/suggested-abbreviations/index.md b/content/docs/BestPractices/suggested-abbreviations/index.md
new file mode 100644
index 00000000..722f1561
--- /dev/null
+++ b/content/docs/BestPractices/suggested-abbreviations/index.md
@@ -0,0 +1,361 @@
+---
+title: "Suggested Abbreviations"
+tags: ["AL","Readability"]
+categories: ["Best Practice"]
+---
+
+_Created by Microsoft, Described by waldo_
+
+## Description
+
+Whenever possible, do **not** use abbreviations in variables, functions and objects names.
+
+If there is no other choice, then use the suggestions below.
+
+| word | Abbreviation |
+|---|---|
+| absence | Abs |
+| account | Acc |
+| accounting | Acc |
+| accumulated | Accum |
+| action | Act |
+| activity | Activ |
+| additional | Add |
+| address | Addr |
+| adjust | Adj |
+| adjusted | Adjd |
+| adjustment | Adjmt |
+| agreement | Agrmt |
+| allocation | Alloc |
+| allowance | Allow |
+| alternative | Alt |
+| amount | Amt |
+| amounts | Amts |
+| answer | Ans |
+| applies | Appl |
+| application | Appln |
+| arrival | Arriv |
+| assembly | Asm |
+| assemble to order | ATO |
+| assignment | Assgnt |
+| associated | Assoc |
+| attachment | Attmt |
+| authorities | Auth |
+| automatic | Auto |
+| availability | Avail |
+| average | Avg |
+| ba db. | BA |
+| balance | Bal |
+| bill of materials | BOM |
+| blanket | Blnkt |
+| budget | Budg |
+| buffer | Buf |
+| business | Bus |
+| business interaction management | BIM |
+| buying | Buy |
+| calculate | Calc |
+| calculated | Calcd |
+| calculation | Calcu |
+| calendar | Cal |
+| capacity | Cap |
+| capacity requirements planning | CRP |
+| cash flow | CF |
+| cashflow | CF |
+| catalog | ctlg |
+| category | Cat |
+| Central Processing Unit | CPU |
+| center | Ctr |
+| change | Chg |
+| changes | Chgs |
+| character | Char |
+| characters | Chars |
+| charge | Chrg |
+| charges | Chrgs |
+| check | Chk |
+| classification | Class |
+| collection | coll |
+| column | col |
+| comment | Cmt |
+| company | Co |
+| component | Comp |
+| completion | Cmpltn |
+| components | Comps |
+| composition | Compn |
+| compression | Compr |
+| concurrent | Concrnt |
+| confidential | Conf |
+| confirmation | Cnfrmn |
+| conflict | Confl |
+| consolidate | Consol |
+| consolidation | Consolid |
+| consumption | Consump |
+| contact | Cont |
+| container | Cntr |
+| contract | Contr |
+| contracted | Contrd |
+| control | Ctrl |
+| controls | Ctrls |
+| conversion | Conv |
+| correction | Cor |
+| correspondence | Corres |
+| corresponding | Corresp |
+| cost | Cst |
+| sold | COGS |
+| credit | Cr |
+| cumulate | Cumul |
+| currency | Curr |
+| current | Crnt |
+| customer | Cust |
+| customer/vendor | CV |
+| daily | Dly |
+| dampener | Damp |
+| database management system | DBMS |
+| date | D |
+| definition | Def |
+| demonstration | Demo |
+| department | Dept |
+| department/project | DP |
+| depreciation | Depr |
+| description | Desc |
+| detail | Dtl |
+| detailed | Dtld |
+| details | Dtls |
+| deviation | Dev |
+| difference | Diff |
+| dimension | Dim |
+| direct | Dir |
+| discount | Disc |
+| discrete | Discr |
+| distribute | Distr |
+| distributed | Distrd |
+| distributor | Distbtr |
+| distribution | Distrn |
+| document | Doc |
+| duplicate | Dupl |
+| entered | Entrd |
+| engineering | Engin |
+| exchange | Exch |
+| excluding | Excl |
+| execute | Exec |
+| expected | Expd |
+| expedited | Exped |
+| expense | Exp |
+| expression | Expr |
+| expiration | Expir |
+| extended | Ext |
+| explode | Expl |
+| export | Expt |
+| final | Fnl |
+| finance | Fin |
+| fiscal | Fisc |
+| finished | Fnshd |
+| fixed asset | FA |
+| forward | Fwd |
+| freight | Frt |
+| general | Gen |
+| general ledger | GL |
+| group | Gr |
+| header | Hdr |
+| history | Hist |
+| holiday | Hol |
+| human resource | HR |
+| identification | ID |
+| import | Imp |
+| inbound | Inbnd |
+| including | Incl |
+| included | Incld |
+| incoming | Incmg |
+| independent software vendor | ISV |
+| industry | Indust |
+| information | Info |
+| initial | Init |
+| Intrastat | Intra |
+| interaction | Interact |
+| integration | Integr |
+| interest | Int |
+| Interim | Intm |
+| internal protocol | IP |
+| inventory | Invt |
+| inventoriable | Invtbl |
+| invoice | Inv |
+| invoiced | Invd |
+| item tracking | IT |
+| journal | Jnl |
+| language | Lang |
+| ledger | Ledg |
+| level | Lvl |
+| line | Ln |
+| list | Lt |
+| local currency | LCY |
+| location | Loc |
+| mailing | Mail |
+| maintenance | Maint |
+| management | Mgt |
+| manual | Man |
+| manufacturing | Mfg |
+| manufacturer | Mfr |
+| material | Mat |
+| marketing | Mktg |
+| maximum | Max |
+| measure | Meas |
+| message | Msg |
+| minimum | Min |
+| miscellaneous | Misc |
+| modify | Mod |
+| month | Mth |
+| negative | Neg |
+| non-inventoriable | NonInvtbl |
+| notification | Notif |
+| number | No |
+| numbers | Nos |
+| object | Obj |
+| operating | Oper |
+| opportunity | Opp |
+| order | Ord |
+| orders | Ords |
+| original | Orig |
+| organization | Org |
+| outbound | Outbnd |
+| Outgoing | Outg |
+| output | Out |
+| outstanding | Outstd |
+| overhead | Ovhd |
+| payment | Pmt |
+| percent | Pct |
+| personnel | Persnl |
+| physical | Phys |
+| picture | Pic |
+| planning | Plng |
+| posted | Pstd |
+| posting | Post |
+| positive | Pos |
+| precision | Prec |
+| prepayment | Prepmt |
+| product | Prod |
+| production | Prod |
+| production order | ProdOrd |
+| project | Proj |
+| property | Prop |
+| prospect | Prspct |
+| purchase | Purch |
+| purchases | Purch |
+| purchaser | Purchr |
+| purchase order | PurchOrd |
+| quality | Qlty |
+| quantity | Qty |
+| questionnaire | Questn |
+| quote | Qte |
+| radio frequency | RF |
+| range | Rng |
+| receipt | Rcpt |
+| received | Rcd |
+| record | Rec |
+| records | Recs |
+| reconcile | Recncl |
+| reconciliation | Recon |
+| recurring | Recur |
+| reference | Ref |
+| register | Reg |
+| registration | Regn |
+| registered | Regd |
+| relation | Rel |
+| relations | Rels |
+| relationship | Rlshp |
+| release | Rlse |
+| released | Rlsd |
+| remaining | Rem |
+| reminder | Rmdr |
+| replacement | Repl |
+| replenish | Rplnsh |
+| replenishment | Rplnsht |
+| report | Rpt |
+| represent | Rep |
+| represented | Repd |
+| request | Rqst |
+| required | Reqd |
+| requirement | Reqt |
+| requirements | Reqts |
+| requisition | Req |
+| reserve | Rsv |
+| reserved | Rsvd |
+| reservation | Reserv |
+| resolution | Resol |
+| resource | Res |
+| response | Rsp |
+| responsibility | Resp |
+| retain | Rtn |
+| retained | Rtnd |
+| return | Ret |
+| returns | Rets |
+| revaluation | Revaln |
+| reverse | Rev |
+| review | Rvw |
+| round | Rnd |
+| rounded | Rndd |
+| rounding | Rndg |
+| route | Rte |
+| routing | Rtng |
+| routine | Rout |
+| sales & receivables | Sales |
+| safety | Saf |
+| schedule | Sched |
+| second | Sec |
+| segment | Seg |
+| select | Sel |
+| selection | Selctn |
+| sequence | Seq |
+| serial | Ser |
+| serial number | SN |
+| service | Serv |
+| sheet | Sh |
+| shipment | Shpt |
+| source | Src |
+| special | Spcl |
+| specification | Spec |
+| specifications | Specs |
+| standard | Std |
+| frequency | SF |
+| statement | Stmt |
+| statistical | Stat |
+| statistics | Stats |
+| stock | Stk |
+| stockkeeping unit | SKU |
+| stream | Stm |
+| structured query language | SQL |
+| subcontract | Subcontr |
+| subcontracted | Subcontrd |
+| subcontracting | Subcontrg |
+| substitute | Sub |
+| substitution | Subst |
+| suggest | Sug |
+| suggested | Sugd |
+| suggestion | Sugn |
+| summary | Sum |
+| suspended | Suspd |
+| symptom | Sympt |
+| synchronize | Synch |
+| temporary | Temp |
+| total | Tot |
+| transaction | Transac |
+| transfer | Trans |
+| translation | Transln |
+| tracking | Trkg |
+| troubleshoot | Tblsht |
+| troubleshooting | Tblshtg |
+| unit of measure | UOM |
+| unit test | UT |
+| unrealized | Unreal |
+| unreserved | Unrsvd |
+| update | Upd |
+| valuation | Valn |
+| value | Val |
+| value added tax | VAT |
+| variance | Var |
+| vendor | Vend |
+| warehouse | Whse |
+| web shop | WS |
+| worksheet | Wksh |
+| g/l | GL |
+| % | Pct |
+| 3-tier | Three-Tier |
+| Outlook Synch | Osynch |
diff --git a/content/docs/BestPractices/unnecessary-else/index.md b/content/docs/BestPractices/unnecessary-else/index.md
new file mode 100644
index 00000000..4dd6d1bd
--- /dev/null
+++ b/content/docs/BestPractices/unnecessary-else/index.md
@@ -0,0 +1,34 @@
+---
+title: "Unnecessary else"
+tags: ["AL","Readability"]
+categories: ["Best Practice"]
+---
+
+_Created by Microsoft, Described by waldo_
+
+## Description
+
+`else` should not be used when the last action in the `then` part is an `exit`, `break`, `skip`, `quit`, `error`.
+
+## Bad code
+
+```al
+ procedure SomeProcedure()
+ begin
+ if IsAdjmtBinCodeChanged() then
+ Error(AdjmtBinCodeChangeNotAllowedErr, ...)
+ else
+ Error(BinCodeChangeNotAllowedErr, ...);
+ end;
+```
+
+## Good code
+
+```al
+ procedure SomeProcedure()
+ begin
+ if IsAdjmtBinCodeChanged() then
+ Error(AdjmtBinCodeChangeNotAllowedErr, ...)
+ Error(BinCodeChangeNotAllowedErr, ...);
+ end;
+```
diff --git a/content/docs/BestPractices/unnecessary-truefalse/index.md b/content/docs/BestPractices/unnecessary-truefalse/index.md
new file mode 100644
index 00000000..57d6864b
--- /dev/null
+++ b/content/docs/BestPractices/unnecessary-truefalse/index.md
@@ -0,0 +1,38 @@
+---
+title: "Unnecessary true/false"
+tags: ["AL","Readability"]
+categories: ["Best Practice"]
+---
+
+_Created by Microsoft, Described by waldo_
+
+## Description
+Do not use `true` or `false` keywords unnecessarily if the expression is already an logical expression.
+
+## Example 1
+
+### Bad code
+
+```al
+ if IsPositive() = true then
+```
+
+### Good code
+
+```al
+ if IsPositive() then
+```
+
+## Example 2
+
+### Bad code
+
+```al
+ if Complete <> true then
+```
+
+### Good code
+
+```al
+ if not Complete then
+```
diff --git a/content/docs/BestPractices/variable-naming/index.md b/content/docs/BestPractices/variable-naming/index.md
new file mode 100644
index 00000000..242d1d15
--- /dev/null
+++ b/content/docs/BestPractices/variable-naming/index.md
@@ -0,0 +1,63 @@
+---
+title: "Variable Naming"
+tags: ["AL","Readability"]
+categories: ["Best Practice"]
+---
+
+_Created by Microsoft, Described by waldo_
+
+## Description
+
+Variables that refer to a AL object must contain the objects name, abbreviated where necessary.
+
+A variable must begin with a capital letter.
+
+Blanks, periods, and other characters (such as parentheses) that would make quotation marks around a variable necessary must be omitted.
+
+If a variable is a compound of two or more words or abbreviations, each word or abbreviation should begin with a capital letter.
+
+## Example 1
+
+### Bad code
+
+```al
+ WIPBuffer: Record "Job WIP Buffer"
+```
+
+### Good code
+
+```al
+ JobWIPBuffer: Record "Job WIP Buffer"
+```
+
+## Example 2
+
+### Bad code
+
+```al
+ Postline: Codeunit "Gen. Jnl.-Post Line";
+```
+
+### Good code
+
+```al
+ GenJnlPostLine: Codeunit "Gen. Jnl.-Post Line";
+```
+
+## Example 3
+
+### Bad code
+
+```al
+ "Amount (LCY)": Decimal;
+```
+
+### Good code
+
+```al
+ AmountLCY: Decimal;
+```
+
+## Tips
+
+The [AL Variable Helper](https://marketplace.visualstudio.com/items?itemName=rasmus.al-var-helper) extension provides Intellisense support to assign correct variable names in AL.
diff --git a/content/docs/BestPractices/variables-declarations-order/index.md b/content/docs/BestPractices/variables-declarations-order/index.md
new file mode 100644
index 00000000..8f7e2131
--- /dev/null
+++ b/content/docs/BestPractices/variables-declarations-order/index.md
@@ -0,0 +1,48 @@
+---
+title: "Variables Declarations Order"
+tags: ["AL","Readability"]
+categories: ["Best Practice"]
+---
+
+_Created by Microsoft, Described by waldo_
+
+## Description
+
+Variables declarations should be ordered by type. In general, object and complex variable types are listed first followed by simple variables. The order should be:
+
+- Record
+- Report
+- Codeunit
+- XmlPort
+- Page
+- Query
+- Notification
+- BigText
+- DateFormula
+- RecordId
+- RecordRef
+- FieldRef
+- FilterPageBuilder
+
+(Ref: [Microsoft Docs](https://docs.microsoft.com/en-us/dynamics365/business-central/dev-itpro/developer/analyzers/codecop-aa0021))
+
+## Bad code
+
+```al
+ StartingDateFilter: Text;
+ Vendor: Record Vendor;
+```
+
+## Good code
+
+```al
+ Vendor: Record Vendor;
+ StartingDateFilter: Text;
+```
+
+## Tips
+
+The [AZ AL Dev Tools/AL Code Outline](https://marketplace.visualstudio.com/items?itemName=andrzejzwierzchowski.al-code-outline) extension adds two new commands to Visual Studio Code to sorts variables.
+
+- `Sort Variables in the Active Editor` : sorts variables in the current editor
+- `Sort Variables in the Active Project` : sorts variables in the current project
diff --git a/content/docs/Contributing/ForkAndPR/ForkedRepro.png b/content/docs/Contributing/ForkAndPR/ForkedRepro.png
new file mode 100644
index 00000000..481fd416
Binary files /dev/null and b/content/docs/Contributing/ForkAndPR/ForkedRepro.png differ
diff --git a/content/docs/Contributing/ForkAndPR/fork_button.jpg b/content/docs/Contributing/ForkAndPR/fork_button.jpg
new file mode 100644
index 00000000..b3888091
Binary files /dev/null and b/content/docs/Contributing/ForkAndPR/fork_button.jpg differ
diff --git a/content/docs/Contributing/ForkAndPR/index.md b/content/docs/Contributing/ForkAndPR/index.md
new file mode 100644
index 00000000..d8105e04
--- /dev/null
+++ b/content/docs/Contributing/ForkAndPR/index.md
@@ -0,0 +1,63 @@
++++
+chapter = true
+pre = ""
+title = "Guide to Fork & PR"
+weight = 100
++++
+
+## Abstract
+
+AL Guidelines is a community project, and as such YOU are encouraged to submit corrections and new ideas. In order to get your content included, you must submit a pull request to the GitHub Repository (Located here: https://github.com/microsoft/alguidelines\). All Pull Requests are subject to approval by a minimum of three admins.
+
+If You are toying with an idea, but You aren't ready to create a document just yet, you are encouraged to create a project discussion thread here: https://github.com/microsoft/alguidelines/discussions
+
+{{% alert title="Warning" color="warning" %}}
+This is a warning.
+If You haven't worked in collaboration with "external" GitHub repositories before, please familiarize yourself with that process by visiting:
+
+https://docs.github.com/en/pull-requests/collaborating-with-pull-requests
+
+{{% /alert %}}
+
+## Steps
+Now that You have decided that You are ready to contribute, these are the steps to take.
+{{% alert title="Note" color="info" %}}
+You can read more about this process here:
+
+https://docs.github.com/en/get-started/quickstart/contributing-to-projects
+
+{{% /alert %}}
+
+### Step 1: Fork
+In order to work on the repository, You must [Fork](https://docs.github.com/en/get-started/quickstart/fork-a-repo) the repository.
+
+By forking the repository, You essentially create a copy into Your own account.
+
+Start by going to the GitHub Repository (https://github.com/microsoft/alguidelines\), and press the Fork 
+
+Once You have successfully forked the repository, go to your own GitHub repository : 
+
+You are now able to clone your own repository to your local pc and start editing using your favorite editor. [Visual Studio Code](https://code.visualstudio.com/) is perfectly fine for this task.
+
+{{% alert title="Note" color="info" %}}
+You can read more about forking here:
+
+https://docs.github.com/en/get-started/quickstart/fork-a-repo
+{{% /alert %}}
+
+### Step 2: Branch
+While not necessarily a must, it is always good practice to create a branch off of your forked repository. That will allow you to work on multiple contributions at the same time and won't have to wait for pull requests to be approved before you can continue on your next contribution.
+
+{{% alert title="Note" color="info" %}}
+You can read more about Branches here:
+
+https://docs.github.com/en/pull-requests/collaborating-with-pull-requests/proposing-changes-to-your-work-with-pull-requests/about-branches
+{{% /alert %}}
+### Step 3: Pull Request
+Once you are happy with your contribution, it's time to create a pull request to propose changes into the main project! This is the final step in producing a fork of someone else's project, and arguably the most important. If you've made a change that you feel would benefit the community as a whole, you should definitely consider contributing back.
+
+To do so, head on over to the repository on GitHub where your project lives. For this example, it would be at `https://www.github.com//alguidelines`. You'll see a banner indicating that your branch is one commit ahead of microsoft:main. Click **Contribute** and then **Open a pull request.**
+
+GitHub will bring you to a page that shows the differences between your fork and the microsoft/alguidelines repository. Click **Create pull request.**
+
+GitHub will bring you to a page where you can enter a title and a description of your changes. _It's important to provide as much useful information and a rationale for why you're making this pull request in the first place._ The project owners needs to be able to determine whether your change is as useful to everyone as you think it is. Finally, click **Create pull request.**
diff --git a/content/docs/Contributing/FormattingTips/index.md b/content/docs/Contributing/FormattingTips/index.md
new file mode 100644
index 00000000..bf6f6a18
--- /dev/null
+++ b/content/docs/Contributing/FormattingTips/index.md
@@ -0,0 +1,126 @@
++++
+chapter = true
+pre = ""
+title = "Formatting tips"
+weight = 100
++++
+
+# Tips and tricks in terms of working with MarkDown and Hugo
+
+## Markdown Cheat Sheet
+Here you can find a rather interesting Cheat Sheet regarding markdown: [https://www.markdownguide.org/cheat-sheet](https://www.markdownguide.org/cheat-sheet)
+
+## Code Fences / Syntax highlighting
+
+The syntax to use codefences is with backticks. If you provide the language after the first block of backticks, github will automatically put that in decent syntax highlighting. So, A simple code fence with AL code, can simply be done by:
+````
+```AL
+ procedure ALGuidelinesRock()
+ var
+ Customer: Record Customer;
+ begin
+ Customer.Get('10000');
+ Customer.Name := 'waldo';
+ Customer.Modify(true);
+ end;
+```
+````
+
+Results in:
+```AL
+ procedure ALGuidelinesRock()
+ var
+ Customer: Record Customer;
+ begin
+ Customer.Get('10000');
+ Customer.Name := 'waldo';
+ Customer.Modify(true);
+ end;
+```
+
+## Diagrams with Mermaid
+
+When providing a documentation, diagrams come in handy. [Mermaid](https://mermaid-js.github.io/mermaid/#/) lets you create diagrams and visualizations using text and code.
+
+For example the following markdown section:
+
+````
+```mermaid
+classDiagram
+ Animal <|-- Duck
+ Animal <|-- Fish
+ Animal <|-- Zebra
+ Animal : +int age
+ Animal : +String gender
+ Animal: +isMammal()
+ Animal: +mate()
+ class Duck{
+ +String beakColor
+ +swim()
+ +quack()
+ }
+ class Fish{
+ -int sizeInFeet
+ -canEat()
+ }
+ class Zebra{
+ +bool is_wild
+ +run()
+ }
+```
+````
+
+Results in:
+
+```mermaid
+classDiagram
+ Animal <|-- Duck
+ Animal <|-- Fish
+ Animal <|-- Zebra
+ Animal : +int age
+ Animal : +String gender
+ Animal: +isMammal()
+ Animal: +mate()
+ class Duck{
+ +String beakColor
+ +swim()
+ +quack()
+ }
+ class Fish{
+ -int sizeInFeet
+ -canEat()
+ }
+ class Zebra{
+ +bool is_wild
+ +run()
+ }
+```
+
+Can't wait to get started? Use the Mermaid [Live Editor](https://mermaid.live/edit).
+
+## Hugo Shortcodes
+Since we're using "Hugo", we can use it's shortcode. Here is a reference: [https://gohugo.io/content-management/shortcodes/](https://gohugo.io/content-management/shortcodes/)
+
+It basically means we are able to use easy notations to do cool things. Let's point out some useful shortcodes:
+
+### Twitter
+
+```
+{{* tweet user="waldo1001" id="1458787011024805892" */>}}
+```
+makes:
+{{< tweet user="waldo1001" id="1458787011024805892" >}}
+
+### YouTube
+```
+{{* youtube QVOMCYitLEc */>}}
+```
+makes:
+{{< youtube QVOMCYitLEc >}}
+
+### Figure
+```
+{{* figure src="http://www.waldo.be/wp-content/uploads/2021/11/business-central-logo.png" title="Business Central" */>}}
+```
+makes:
+{{< figure src="http://www.waldo.be/wp-content/uploads/2021/11/business-central-logo.png" title="Business Central" >}}
diff --git a/content/docs/Contributing/InstallHugo/ManuallyOnWindows11/EnvironmentVariables.png b/content/docs/Contributing/InstallHugo/ManuallyOnWindows11/EnvironmentVariables.png
new file mode 100644
index 00000000..1f76acbe
Binary files /dev/null and b/content/docs/Contributing/InstallHugo/ManuallyOnWindows11/EnvironmentVariables.png differ
diff --git a/content/docs/Contributing/InstallHugo/ManuallyOnWindows11/EnvironmentVariablesPath.png b/content/docs/Contributing/InstallHugo/ManuallyOnWindows11/EnvironmentVariablesPath.png
new file mode 100644
index 00000000..7c4bb5f6
Binary files /dev/null and b/content/docs/Contributing/InstallHugo/ManuallyOnWindows11/EnvironmentVariablesPath.png differ
diff --git a/content/docs/Contributing/InstallHugo/ManuallyOnWindows11/Extract.png b/content/docs/Contributing/InstallHugo/ManuallyOnWindows11/Extract.png
new file mode 100644
index 00000000..35cb5f98
Binary files /dev/null and b/content/docs/Contributing/InstallHugo/ManuallyOnWindows11/Extract.png differ
diff --git a/content/docs/Contributing/InstallHugo/ManuallyOnWindows11/ExtractToBin.png b/content/docs/Contributing/InstallHugo/ManuallyOnWindows11/ExtractToBin.png
new file mode 100644
index 00000000..ba466115
Binary files /dev/null and b/content/docs/Contributing/InstallHugo/ManuallyOnWindows11/ExtractToBin.png differ
diff --git a/content/docs/Contributing/InstallHugo/ManuallyOnWindows11/HugoServe.png b/content/docs/Contributing/InstallHugo/ManuallyOnWindows11/HugoServe.png
new file mode 100644
index 00000000..569b5098
Binary files /dev/null and b/content/docs/Contributing/InstallHugo/ManuallyOnWindows11/HugoServe.png differ
diff --git a/content/docs/Contributing/InstallHugo/ManuallyOnWindows11/SearchForEnv.png b/content/docs/Contributing/InstallHugo/ManuallyOnWindows11/SearchForEnv.png
new file mode 100644
index 00000000..881136cd
Binary files /dev/null and b/content/docs/Contributing/InstallHugo/ManuallyOnWindows11/SearchForEnv.png differ
diff --git a/content/docs/Contributing/InstallHugo/ManuallyOnWindows11/SelectExtended.png b/content/docs/Contributing/InstallHugo/ManuallyOnWindows11/SelectExtended.png
new file mode 100644
index 00000000..a8adec9c
Binary files /dev/null and b/content/docs/Contributing/InstallHugo/ManuallyOnWindows11/SelectExtended.png differ
diff --git a/content/docs/Contributing/InstallHugo/ManuallyOnWindows11/SystemProperties.png b/content/docs/Contributing/InstallHugo/ManuallyOnWindows11/SystemProperties.png
new file mode 100644
index 00000000..ae8adee9
Binary files /dev/null and b/content/docs/Contributing/InstallHugo/ManuallyOnWindows11/SystemProperties.png differ
diff --git a/content/docs/Contributing/InstallHugo/ManuallyOnWindows11/index.md b/content/docs/Contributing/InstallHugo/ManuallyOnWindows11/index.md
new file mode 100644
index 00000000..7e4b8105
--- /dev/null
+++ b/content/docs/Contributing/InstallHugo/ManuallyOnWindows11/index.md
@@ -0,0 +1,57 @@
+---
+title: "Manually on Windows 11"
+---
+
+This guide will walk You thrugh installing Hugo on a Windows 11 PC. For the official install guide, You can wisit .
+
+## Download Binaries
+
+The path of least resistance is to just download the .zip file from the official Hugo GitHub Repository [here](https://github.com/gohugoio/hugo/releases).
+
+The theme that are used, needs the extended version of Hugo, so make sure to download the **extended** version by ensuring the file name starts with `hugo_extended...`
+
+{{% alert title="info" color="info" %}}
+For the purpose of this install guide, we are assuming You will want to follow the "standard" used by Hugo for installing. We will be creating a `Hugo` folder in the root of `C:\`. That folder will have a `Bin` folder for the binaries, and a `Sites` folder for each website You are building.
+
+Once You are done, You should have a folder structure that looks like this:
+
+```text
+C:\Hugo
+├── Bin # Binaries
+└┬─ Sites # Hugo Site Repositories
+ └── alguidelines # Hugo Source for alguidelines.dev
+```
+
+{{% /alert %}}
+
+
+
+After downloading the .zip file, extract the zip-file to `c:\Hugo\Bin`
+
+
+
+
+
+## Add Hugo to Path
+
+After copying the binaries to Your PC, You will need to add Hugo binaries to the `%PATH%` system environment variables.
+
+To do that, search for `environment`
+
+
+
+once you see the `Edit the system environment variables`, open it and select `Environment Variables`
+
+
+
+Once the Environment Variables screen is open, highlight the `Path` lines and press the `Edit...` button
+
+
+
+Now press `New` and add the `C:\Hugo\Bin` to the path. Press `OK` and `OK` to save the new `Path`
+
+
+
+Once complete. You should now be able to preview the Hugo site on by opening a command promt, and open the `C:\Hugo\Sites\alguidelines` folder and execute `Hugo Serve`
+
+
\ No newline at end of file
diff --git a/content/docs/Contributing/InstallHugo/TheShortcut/alguidelines codespace.mp4 b/content/docs/Contributing/InstallHugo/TheShortcut/alguidelines codespace.mp4
new file mode 100644
index 00000000..173ea8d5
Binary files /dev/null and b/content/docs/Contributing/InstallHugo/TheShortcut/alguidelines codespace.mp4 differ
diff --git a/content/docs/Contributing/InstallHugo/TheShortcut/alguidelines walkthrough.mp4 b/content/docs/Contributing/InstallHugo/TheShortcut/alguidelines walkthrough.mp4
new file mode 100644
index 00000000..269de8a0
Binary files /dev/null and b/content/docs/Contributing/InstallHugo/TheShortcut/alguidelines walkthrough.mp4 differ
diff --git a/content/docs/Contributing/InstallHugo/TheShortcut/index.md b/content/docs/Contributing/InstallHugo/TheShortcut/index.md
new file mode 100644
index 00000000..9f46fc7f
--- /dev/null
+++ b/content/docs/Contributing/InstallHugo/TheShortcut/index.md
@@ -0,0 +1,35 @@
+---
+Title: Devcontainer with VS Code
+---
+
+## Use a local devcontainer
+
+If you don't want any local setup (apart from Docker Desktop), but still run your own Hugo instance, you can make use of the preconfigured devcontainer. If you want to learn more about the concept, visit [https://code.visualstudio.com/docs/remote/containers](https://code.visualstudio.com/docs/remote/containers). To use it, you need to take the following steps:
+
+1. Start [Docker Desktop](https://www.docker.com/products/docker-desktop) and [switch to Linux containers](https://docs.docker.com/desktop/windows/#switch-between-windows-and-linux-containers) by right-clicking on the Docker logo in the system tray and selecting "Switch to Linux containers...". If you only see "Switch to Windows containers...", then you are already switched to Linux containers. If anything goes wrong, check if you are on the latest version of Docker Desktop and have [the WSL2 integration enabled](https://docs.docker.com/desktop/windows/wsl/#install). If you have all that in place and it still doesn't work, check the extended installation documentation [here](https://code.visualstudio.com/docs/remote/containers#_installation)
+{{% alert title="Consequences of switching" color="info" %}}
+When switching to Linux, you will probably see a warning that tells you that you "will not be able to manage the Windows containers until you switch back to Windows containers". That means that the Docker Desktop management GUI can only show either the Windows containers or the Linux containers and if you switch to Linux, you consequently won't see the Windows containers until you switch back. But the Windows containers will continue to run, you won't loose data and you can keep using them e.g. for Business Central development, you just can't manage them through the Docker Desktop GUI
+{{% /alert %}}
+2. Install the [Remote development extension pack](https://marketplace.visualstudio.com/items?itemName=ms-vscode-remote.vscode-remote-extensionpack) in Visual Studio Code
+3. Run the action "Remote containers: Clone Repository in Container Volume" and select the fork you created. If you haven't done that before, go through the [docs](https://alguidelines.dev/docs/contributing/forkandpr/#step-1-fork).
+4. Wait for a bit. When you do this for the first time, it can take a couple of minutes. Next time it will be faster...
+5. After a while, you will have VS Code with the cloned repository and the terminal should show something like "Done. Press any key to close the terminal."
+6. Run the action "Tasks: Run task" and select "Start local Hugo Server" from the list. If you don't see that entry, you might have to reload your VS Code window and try again
+7. After Hugo has generated the site, you will get a notification that offers you to "Open a browser". Click on that and you will see your local instance of the AL guidelines! Again, on the first try it will be a bit slow and sluggish, but the second one should be fast.
+8. Now you can make changes and just save them. If you open the terminal, you will see a message that tells you that a change was detected and the site was rebuilt. After that, the change should automatically appear in your browser
+
+Here is a walkthrough of the full process:
+
+
+
+## Use GitHub Codespaces
+
+What is also great about this, is that you can also use [GitHub Codespaces](https://github.com/features/codespaces) with that setup. In that case, steps 1-5 become two clicks... Here is another full walkthrough:
+
+
diff --git a/content/docs/Contributing/InstallHugo/UsingPowershellOnWindows11/index.md b/content/docs/Contributing/InstallHugo/UsingPowershellOnWindows11/index.md
new file mode 100644
index 00000000..3b64af8f
--- /dev/null
+++ b/content/docs/Contributing/InstallHugo/UsingPowershellOnWindows11/index.md
@@ -0,0 +1,22 @@
+---
+Title: Powershell on Windows 11
+description: >
+ Install Hugo with a simple Powershell Script and chocolatey
+---
+
+It is possible to use a powershell script and Chocolatey to install and other dependencies. Execute the following script:
+
+```powershell
+Set-ExecutionPolicy Bypass -Scope Process -Force
+[System.Net.ServicePointManager]::SecurityProtocol = [System.Net.ServicePointManager]::SecurityProtocol -bor 3072
+Invoke-Expression ((New-Object System.Net.WebClient).DownloadString('https://community.chocolatey.org/install.ps1'))
+choco install -y nodejs
+choco install -y hugo-extended
+```
+
+Once complete, in root of of the repository execute the following commands
+
+```powershell
+git submodule update --init --recursive --depth 1
+npm install
+```
diff --git a/content/docs/Contributing/InstallHugo/_index.md b/content/docs/Contributing/InstallHugo/_index.md
new file mode 100644
index 00000000..30a2f7f6
--- /dev/null
+++ b/content/docs/Contributing/InstallHugo/_index.md
@@ -0,0 +1,9 @@
+---
+title: Install Hugo
+---
+
+There are multiple ways to install Hugo for you to properly preview your contributions. Please select the scenario that matches your setup.
+
+For the official install guide, you can visit
+
+{{< youtube G7umPCU-8xc >}}
diff --git a/content/docs/Contributing/Templates/BestPractice/index.md b/content/docs/Contributing/Templates/BestPractice/index.md
new file mode 100644
index 00000000..7a82fbe5
--- /dev/null
+++ b/content/docs/Contributing/Templates/BestPractice/index.md
@@ -0,0 +1,31 @@
+---
+title: "Title Here"
+tags: ["AL"]
+categories: ["Best Practice"]
+---
+
+
+
+
+_Created by Described by _
+
+## Description
+
+In depth description on what this Pattern is all about
+
+- basic components
+- how the interact
+- steps to implement
+- considerations to take
+
+## Bad code
+
+```al
+PutCodeblocksHere()
+```
+
+## Good code
+
+```al
+PutCodeblocksHere()
+```
diff --git a/content/docs/Contributing/Templates/Patterns/index.md b/content/docs/Contributing/Templates/Patterns/index.md
new file mode 100644
index 00000000..7e39e182
--- /dev/null
+++ b/content/docs/Contributing/Templates/Patterns/index.md
@@ -0,0 +1,46 @@
+---
+title: "Title Here"
+tags: ["AL"]
+categories: ["Pattern"]
+---
+
+
+
+_Created by Described by _
+
+## Abstract
+
+Short, descriptive and easy to remember description of this pattern.
+
+## Context
+
+Sets the stage where the pattern takes place. 1-2 sentences.
+
+## Problem
+
+What happens before this pattern is used? How can it go wrong? 1-5 lines.
+
+## Description
+
+In depth description on what this Pattern is all about
+
+- basic components
+- how the interact
+- steps to implement
+- considerations to take
+
+## Usage
+
+If applicable: where is it used in an app. You can refer to any app, BaseApp, or a (public) ISV app.
+
+## Benefits
+
+1-2 sentences: what did you just solve
+
+## When not to use
+
+Usually, there are occasions where NOT to implement the pattern. List the disadvantages of this pattern here.
+
+## List of references
+
+Youtube-link? BaseApp? Tweet? ...
diff --git a/content/docs/Contributing/Templates/_index.md b/content/docs/Contributing/Templates/_index.md
new file mode 100644
index 00000000..55b88a6e
--- /dev/null
+++ b/content/docs/Contributing/Templates/_index.md
@@ -0,0 +1,12 @@
+---
+title: "Templates"
+---
+
+We have created some template-files that you can simply copy and use. Look at them as "Patterns for describing patterns"
+
+We currently offer the following templates:
+
+- for [Patterns](/contributing/templates/patterns/) ([raw](https://raw.githubusercontent.com/microsoft/alguidelines/main/content/docs/Contributing/Templates/Patterns/index.md))
+- for [Best Practice](/contributing/templates/bestpractice/) ([raw](https://raw.githubusercontent.com/microsoft/alguidelines/main/content/docs/Contributing/Templates/BestPractice/index.md))
+
+opening the "raw" link, will allow for the best copy/paste result.
diff --git a/content/docs/Contributing/TheApprovalProcess/index.md b/content/docs/Contributing/TheApprovalProcess/index.md
new file mode 100644
index 00000000..c95c3daf
--- /dev/null
+++ b/content/docs/Contributing/TheApprovalProcess/index.md
@@ -0,0 +1,9 @@
++++
+chapter = true
+pre = ""
+title = "Understanding the Approval Process"
+weight = 100
++++
+
+(coming soon)
+
diff --git a/content/docs/Contributing/_index.md b/content/docs/Contributing/_index.md
new file mode 100644
index 00000000..8c7a44b5
--- /dev/null
+++ b/content/docs/Contributing/_index.md
@@ -0,0 +1,27 @@
++++
+chapter = true
+pre = ""
+title = "Contributing"
+weight = 100
++++
+
+First off, we're glad you want to help! The project team is kind and helpful, so if you're not sure if you have a good first contribution, make a [Discussion](https://github.com/microsoft/alguidelines/discussions/) about it or even just create your first try. You'll get feedback and we will be happy to help refine it, if it even needs it.
+
+## Code of Conduct
+
+This project welcomes contributions and suggestions. Most contributions require you to agree to a Contributor License Agreement (CLA) declaring that you have the right to, and actually do, grant us the rights to use your contribution. For details, visit https://cla.opensource.microsoft.com.
+
+When you submit a pull request, a CLA bot will automatically determine whether you need to provide a CLA and decorate the PR appropriately (e.g., status check, comment). Simply follow the instructions provided by the bot. You will only need to do this once across all repos using our CLA.
+
+This project has adopted the Microsoft Open Source Code of Conduct. For more information see the Code of Conduct FAQ or contact opencode@microsoft.com with any additional questions or comments.
+
+## Steps to Contribute
+
+* [Guide to Fork & PR](/docs/contributing/forkandpr/)
+* Templates:
+ - For [patterns](/docs/contributing/templates/patterns/)
+ - For [guidelines](/docs/contributing/templates/guidelines/)
+* [Understanding the Approval Process](/docs/contributing/theapprovalprocess/)
+
+Here are some [tips and tricks in terms of working with Markdown and Hugo](/docs/contributing/formattingtips/).
+
diff --git a/content/docs/NAVPatterns/2-anti-patterns/_index.md b/content/docs/NAVPatterns/2-anti-patterns/_index.md
new file mode 100644
index 00000000..e7695291
--- /dev/null
+++ b/content/docs/NAVPatterns/2-anti-patterns/_index.md
@@ -0,0 +1,19 @@
++++
+chapter = true
+title = "2. Anti-Patterns"
+weight = 130
+tags = ["C/AL"]
++++
+Some of the software development practices, had **not** stood the test of time. Despite that, some are still being used today by developers everywhere.
+
+"An **anti-pattern** (or **antipattern**) is a common response to a recurring problem that is usually ineffective and risks being highly counterproductive" (from [Wikipedia][anchor0])
+
+Since almost the beginning of the NAV Design Patterns project, we talked about documenting the anti-patterns - but never found the time. Until, spontaneously, the April 1st 2015 article had practically wrote itself in a couple of hours, with priceless contributions coming from Andreas, Elly, Nikola - and last but not least, waldo.
+
+Best regards,
+
+Bogdana Botez
+
+
+
+[anchor0]: http://en.wikipedia.org/wiki/Anti-pattern
diff --git a/content/docs/NAVPatterns/2-anti-patterns/nav-upgrade/index.md b/content/docs/NAVPatterns/2-anti-patterns/nav-upgrade/index.md
new file mode 100644
index 00000000..244abe96
--- /dev/null
+++ b/content/docs/NAVPatterns/2-anti-patterns/nav-upgrade/index.md
@@ -0,0 +1,75 @@
++++
+title = "Nav Upgrade"
+weight = 840
+tags = ["C/AL"]
++++
+## Anti-Patterns in NAV Upgrade
+
+_By Carlos Raul Garcia and Bogdana Botez at Microsoft Development Center Copenhagen_
+
+[![ ][image0]][anchor0]
+
+**Context**: when NAV is upgraded, whether on-premises or in the cloud, developers have the chance to write upgrade code to move data across changing data structures. Writing good quality code will help successful upgrades.
+
+### General on upgrade
+
+**Problem:** assuming that the upgrade table contains data.
+
+If the table is empty, it means that either the upgrade has run, or there was no data in the original tenant; in both cases, the upgrade code should exit immediately.
+
+**Solution:** if using an upgrade table, always validate that the table contains data before doing anything.
+
+### Upgrade code, can it be rerun safely?
+
+**Problem**: if the upgrade code is not written in a way that makes it runnable twice ([idempotent][anchor1]), then several failures can happen, including something as critical as data corruption.
+
+In on premise NAV installations, if something fails at upgrade, there is no way to run only the "remaining" tasks. You will need to run the whole upgrade again, and might end-up with data that you cannot trust.
+
+What about the cloud? In Platform As A Service (PaaS), in some situations, the upgrade code needs to be run twice (for example, when moving tenants from a broken/frozen VM to a healthy one).
+
+**Solution:** Make sure each of your upgrade procedures only kicks in if it didn't run before.
+
+**Examples**
+
+The examples below have happened in real live NAV PaaS upgrade:
+
+**Table data overwrite**
+
+* **Problem**: at upgrade, a new column has been added to a table and initialized with default values. In the meanwhile, during production, some of the default values are changed to production real life values. The second time the upgrade runs, those values will be overwritten with defaults, any personalization lost.
+* **Solution:** before initializing with default values, check if non-default values exist.
+
+**Crash on math operations**
+
+* **Problem:** one tenant upgrade managed to divide by zero, by assuming a non-zero value.
+* **Solution:** don't assume values can never be zero, always check before using them in divisions.
+
+**Use of external components**
+
+* **Problem:** a one-time registration through web services to an external service failed when attempting to register a second time.
+* **Solution:** check if already registered, before attempting again.
+
+### Parallelism
+
+**Problem**: Upgrade procedures can be run in parallel, causing issues when different procedures attempt to modify the same table at the same time.
+
+When modifications to the same table are being made from two or more different procedures, there is no guarantee on sequential run, or on a certain order they will be run in.
+
+**Solution**: if sequential or ordered execution is needed, make the affected upgrade procedures local and call them all, in the desired sequence, from a public upgrade procedure.
+
+### Access to cloud machines
+
+**Problem:** dependencies on manual installation steps do not fit in the cloud.
+
+If Dynamics NAV is installed on-premises, then any additional setup (like dependencies of external dlls, manual configuration steps etc.) can be done manually or semi-manually by the IT admin, at first setup and upgrade.
+
+In the cloud, NAV partners don't have access to the machines -- hence they cannot deploy and configure those external dependencies as they did in the old on-premises installations.
+
+**Solution:** Don't assume you will have access to PaaS or SaaS machines. Build your solution in such a way that it doesn't depend on executing manual configurations on the host machine.
+
+
+
+[anchor0]: upgrade.png
+[anchor1]: http://stackoverflow.com/questions/1077412/what-is-an-idempotent-operation#1077421
+
+
+[image0]: upgrade.png
diff --git a/content/docs/NAVPatterns/2-anti-patterns/nav-upgrade/upgrade.png b/content/docs/NAVPatterns/2-anti-patterns/nav-upgrade/upgrade.png
new file mode 100644
index 00000000..86da9c88
Binary files /dev/null and b/content/docs/NAVPatterns/2-anti-patterns/nav-upgrade/upgrade.png differ
diff --git a/content/docs/NAVPatterns/2-anti-patterns/reusable-bugs/index.md b/content/docs/NAVPatterns/2-anti-patterns/reusable-bugs/index.md
new file mode 100644
index 00000000..09d31469
--- /dev/null
+++ b/content/docs/NAVPatterns/2-anti-patterns/reusable-bugs/index.md
@@ -0,0 +1,95 @@
++++
+title = "Reusable Bugs"
+weight = 1020
+tags = ["C/AL"]
++++
+_By Bogdana Botez, Andreas Moth, Eric Wauters (waldo), Elly Nkya, Nikola Kukrika_
+
+_April 1st, 2015_
+
+## Abstract
+
+It is difficult to come up with new and different bugs in each release, and it is a waste of resources to not use the ones which already had proven successful in the past. To avoid reinventing the wheel, we propose to come up with new innovative ways to create bugs that are reusable and generic enough to be used in various places of the application.
+
+## Examples
+
+**Option Strings**
+
+One example of a reusable bug is to find a heavily used table, like table 36 Sales Header, and observe one of the more important fields of type Option, for example Document Type. The OptionString property has the following value: Quote,Order,Invoice,Credit Memo,Blanket Order,Return Order, which you should update to add one option, for example Transport Order in the beginning.
+
+The main benefit of this reusable bug is that code changed in one place, has impact in multiple sides on the application: document creation and handling, posting etc.
+
+**Application Management**
+
+Codeunit 1, ApplicationManagement is a good place for creating reusable bugs. A simple INSERT on the RecRef on the first line of OnDatabaseInsert will create a sure and versatile bug that is reusable all over the application. Redundancy on database insertion ensures that data is surely conveyed to the data storage layer, twice.
+
+**God objects**
+
+We are considering to no longer make localization builds for countries. It consumes lab resources to keep running multiple country builds. Instead, we will merge everything into one single build. This is a bigger scale implementation of the ["God object"][anchor0]. Inside this unique build, we will use IF statements and sporadically CASE statements to select each country's behavior. For more help on how to use IFs, see also the IF .. THEN|ELSE C/AL Coding Guideline.
+
+**Field length economy**
+
+When you post a document, make sure to transfer data to a field that is smaller than the field you are assigning from. This will not immediately be caught and will only hurt a subset of the customers who uses Microsoft Dynamics NAV to its fullest.
+
+## Guidelines
+
+When bug addition is not entirely obvious, there is a second way to approach the problem. By following some general coding best practices like the ones described below, reliable bug innovation is facilitated and can come naturally as a collateral effect.
+
+**Code structure**
+
+Put everything in one function and only use comments to explain the structure of your code. And don't use functions - because this only complicates things... having to navigate from function to function, and completely lose track of where you are in the business logic.
+
+On top of that .. put everything in one codeunit. Because also that will simplify and make your structure more readable.
+
+Use Hungarian Notation on your variables, because at any time, you need to know what type, and what context your variable is on.
+
+Declare all your functions and variables global, so they are available at any time.
+
+**Don't do Unit Testing**
+
+Unit testing adds complexity and extra time to the stuff you're doing. Also, it eats up extra codeunits which means: it costs money. You will never be able to foresee all scenarios possible, so you're destined to forget and not test everything. So you will save time in not doing unit testing.
+
+**Never add images to actions**
+
+Because there is an image by default. When you don't provide an image on an action on a page, the application will foresee a ball... and when you never do it, your application will have a very consistent way of showing your actions, by providing that picture of that ball. On top of that, you'll save time.
+
+**Do not care about ControlIDs**
+
+When you're doing development of your product, do not care about ControlIDs, and just leave the Offset ID to the default value of 0\. This way, when merging, you will receive nice notifications, saying both you and Microsoft have added functions in that objects. You can use this feature to document all these places.
+
+**Hooks**
+
+Never apply the hook pattern. Hooks will only reduce upgrade time. This means, you will only shortly enjoy using the AMU (Application Merge Utilities). The more you change in default application, the longer it takes to upgrade, the longer you will enjoy the toolkit
+
+This can be taken one level higher. Simply you are not hardcore if you do not use notepad to resolve all of the merge issues.
+
+**How to use RecRef**
+
+Why fuss around declaring specific table variables, just generalize, all you need is one, two, or perhaps three RecRef variables, with a few IFs and CASEs here and there for reflection, to carry you all the way.
+
+**Arguments**
+
+Using only a few arguments on the functions is a sign of a weak developer. Stick in as many arguments as possible on the function, even if you are not using them, they could be useful in the future.
+
+**Just another field / action**
+
+Thinking of the design is overrated, each problem can be solved by adding an additional field or the table/page or with adding another action. We all know this has worked well in the past.
+
+**Reusability**
+
+We have decided that each time we fix a bug, we now also explain how it can be applied as a pattern. We then use anti-virus software to search for these patterns, to make sure we do not re-introduce these bugs anywhere else in NAV.
+
+**Business logic placement**
+
+As a best practice, we have also decided to move code into pages. Business logic should no longer be in tables and codeunits, but instead pages should know and be aware of the context and update it accordingly. As opposite to tables and codeunits, pages are aware of the context.
+
+## Conclusion
+
+Happy April Fools' Day.
+
+Disclaimer: this is inspired from IETF documentation published on April 1st, like for example the revolutionizing [IP over Avian Carriers][anchor1] standard.
+
+
+
+[anchor0]: http://en.wikipedia.org/wiki/God_object
+[anchor1]: http://en.wikipedia.org/wiki/IP_over_Avian_Carriers
diff --git a/content/docs/NAVPatterns/3-cal-coding-guidelines/_index.md b/content/docs/NAVPatterns/3-cal-coding-guidelines/_index.md
new file mode 100644
index 00000000..54e113cf
--- /dev/null
+++ b/content/docs/NAVPatterns/3-cal-coding-guidelines/_index.md
@@ -0,0 +1,28 @@
++++
+chapter = true
+title = "3. CAL Coding Guidelines"
+weight = 150
+tags = ["C/AL"]
+categories = ["Best Practice"]
++++
+We've decided to publish our current C/AL coding guidelines. They are actual, as per January 2015 when this is published (but might fall out of sync as time goes by).
+
+You can also [download the C/AL coding guidelines as pdf,][anchor0] all in one document. In contrast, on this wiki, the coding guidelines are published individually. The reason is: give you the chance to comment and share your oppinion on each one. Thanks to [waldo][anchor1] for this idea and for helping out.
+
+The guidelines are debatable - and it is good when they are generating debate. There is variation of opinion on the rules at Microsoft too. The plan is to simply expose what we use now. And more important, to say that guidelines could be used. Debate on individual guidelines can become heated for any programming language, but the benefit of using some guidelines stays.
+
+For us, those guidelines are enforced at check-in time - we are using a tool which verifies and only allows compliant check-ins. While this tool is internal and not ready to publish, we had anyways decided to open up and present the rules we use to the community, as inspiration.
+
+Question: Since we're having the guidelines, how come there is still C/AL code in NAV which doesn't respect them?
+
+Answer: all new C/AL code is bound to follow the guidelines (else it cannot be checked-in). However, the code that existed before the rules - it does not. We had done cleanup in a certain degree. Now we're gradually improving the old code base as we visit various objects in order to add new functionality, however chances are that code we didn't touch in a long time had remained in its old form.
+
+We're looking forward to your comments. Where you can, do provide concrete examples of the alternatives, Good and Bad.
+
+{{< youtube z6skKy0pkmU >}}
+
+
+
+[anchor0]: /files/CAL-Coding-Guidelines-at-Microsoft-Development-Center-Copenhagen.pdf "download the C/AL coding guidelines as pdf"
+[anchor1]: /members/waldo/default.aspx "waldo"
+[anchor2]: https://www.youtube.com/watch?v=z6skKy0pkmU&list=PLhZ3P-LY7CqmVszuvtJLujFyHpsVN0U_w&index=26
diff --git a/content/docs/NAVPatterns/3-cal-coding-guidelines/design/_index.md b/content/docs/NAVPatterns/3-cal-coding-guidelines/design/_index.md
new file mode 100644
index 00000000..f77a18f4
--- /dev/null
+++ b/content/docs/NAVPatterns/3-cal-coding-guidelines/design/_index.md
@@ -0,0 +1,11 @@
++++
+title = "Design"
+weight = 490
+tags = ["C/AL"]
+categories = ["Best Practice"]
++++
+## C/AL Coding Guidelines
+
+## **Design**
+
+Find the C/AL guidelines by expanding the menu in the left.
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
new file mode 100644
index 00000000..c58006b1
--- /dev/null
+++ b/content/docs/NAVPatterns/3-cal-coding-guidelines/design/by-reference-parameters/index.md
@@ -0,0 +1,27 @@
++++
+title = "By Reference Parameters"
+weight = 280
+tags = ["C/AL"]
+categories = ["Best Practice"]
++++
+Do not declare parameters by reference if their values are not intended to be changed.
+
+Unintentional value changes might propagate. Also, it might lead people to believe that value changes are intended.
+
+Bad code
+
+ LOCAL PROCEDURE ShowMessage@15(VAR Text@1000 : Text[250]);
+ BEGIN
+ Text := GetMessageText;
+ IF (Text <> '') AND GenJnlLineInserted THEN
+ MESSAGE(Text);
+ END;
+
+Good code
+
+ LOCAL PROCEDURE ShowMessage@15(Text@1000 : Text[250]);
+ BEGIN
+ Text := GetMessageText;
+ IF (Text <> '') AND GenJnlLineInserted THEN
+ MESSAGE(Text);
+ END;
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
new file mode 100644
index 00000000..aa47a30e
--- /dev/null
+++ b/content/docs/NAVPatterns/3-cal-coding-guidelines/design/class-coupling/index.md
@@ -0,0 +1,20 @@
++++
+title = "Class Coupling"
+weight = 320
+tags = ["C/AL"]
+categories = ["Best Practice"]
++++
+Do not write functions that have high class coupling. This makes the code hard to maintain.
+
+Bad code
+
+ Any procedure / trigger that has class coupling of > 30
+
+
+
+Good code
+
+ Any procedure / trigger that has class coupling of <= 30\.
+ Class coupling is computed by summing the unique instances of the following in a code block:
+ - every unique usage of a complex C/AL data type (table, codeunit, etc) as 1\.
+ - every unique usage of a DotNet type as 1\.
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
new file mode 100644
index 00000000..5a2aa8ac
--- /dev/null
+++ b/content/docs/NAVPatterns/3-cal-coding-guidelines/design/cyclomatic-complexity/index.md
@@ -0,0 +1,24 @@
++++
+title = "Cyclomatic Complexity"
+weight = 460
+tags = ["C/AL"]
+categories = ["Best Practice"]
++++
+Do not write functions that have high cyclomatic complexity. This makes the code hard to maintain.
+
+Bad code
+
+ Any procedure / trigger that has a cyclomatic complexity > 25, using the CC3 version mentioned in [this article][anchor0].
+
+
+
+Good code
+
+ Any procedure / trigger that has a cyclomatic complexity <= 25, using the CC3 version.
+ The CC3 version is computed by summing the following in a code block:
+ - each IF statement as 1\.
+ - each entire CASE as 1\.
+
+
+
+[anchor0]: http://www.aivosto.com/project/help/pm-complexity.html
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
new file mode 100644
index 00000000..04d84879
--- /dev/null
+++ b/content/docs/NAVPatterns/3-cal-coding-guidelines/design/encapsulate-local-functionality/index.md
@@ -0,0 +1,7 @@
++++
+title = "Encapsulate Local Functionality"
+weight = 530
+tags = ["C/AL"]
+categories = ["Best Practice"]
++++
+Any function used local must be defined as local.
diff --git a/content/docs/NAVPatterns/3-cal-coding-guidelines/design/findset-findfirst-findlast/index.md b/content/docs/NAVPatterns/3-cal-coding-guidelines/design/findset-findfirst-findlast/index.md
new file mode 100644
index 00000000..2dc93c17
--- /dev/null
+++ b/content/docs/NAVPatterns/3-cal-coding-guidelines/design/findset-findfirst-findlast/index.md
@@ -0,0 +1,31 @@
++++
+title = "FINDSET FINDFIRST FINDLAST"
+weight = 600
+tags = ["C/AL"]
+categories = ["Best Practice"]
++++
+FINDSET, FIND('+') or FIND('-') should only be used when NEXT is used and vice versa.
+
+Bad code
+
+ IF Cust.FIND('-') THEN
+ ERROR(CustIsBlockErr)
+
+Good code
+
+ IF Cust.FINDFIRST THEN
+ ERROR(CustIsBlockErr)
+
+Bad code
+
+ IF Cust.FINDFIRST THEN
+ REPEAT
+ ...
+ UNTIL Cust.NEXT = 0;
+
+Good code
+
+ IF Cust.FINDSET THEN
+ REPEAT
+ ...
+ UNTIL Cust.NEXT = 0;
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
new file mode 100644
index 00000000..35a05a85
--- /dev/null
+++ b/content/docs/NAVPatterns/3-cal-coding-guidelines/design/initialized-variables/index.md
@@ -0,0 +1,51 @@
++++
+title = "Initialized Variables"
+weight = 660
+tags = ["C/AL"]
+categories = ["Best Practice"]
++++
+Variables should always be set to a specific value, before they are used.
+
+Bad code
+
+ PROCEDURE SetPurchLine@22(VAR CurrentPurchLine@1000 : Record 39);
+ VAR
+ Pegging@1001 : Boolean;
+ BEGIN
+ IF Pegging THEN
+ CurrQuantity := CurrentPurchLine."Quantity (Base)"
+ ELSE
+ CurrQuantity := CurrentPurchLine."Outstanding Qty. (Base)";
+ END;
+
+Good code
+
+ PROCEDURE SetPurchLine@22(VAR CurrentPurchLine@1000 : Record 39);
+ VAR
+ Pegging@1001 : Boolean;
+ BEGIN
+ Pegging := IsPegging(CurrentPurchLine);
+ IF Pegging THEN
+ CurrQuantity := CurrentPurchLine."Quantity (Base)"
+ ELSE
+ CurrQuantity := CurrentPurchLine."Outstanding Qty. (Base)";
+ END;
+
+Bad code
+
+ // In the example below, the function will always return FALSE.
+ PROCEDURE GetItemsToPlan@22() : Boolean;
+ BEGIN
+ SETRANGE("Document Type","Document Type"::Order);
+ ...
+ FINDSET
+ END;
+
+Good code
+
+ PROCEDURE GetItemsToPlan@22() : Boolean;
+ BEGIN
+ SETRANGE("Document Type","Document Type"::Order);
+ ...
+ EXIT(FINDSET)
+ END;
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
new file mode 100644
index 00000000..051ac2fb
--- /dev/null
+++ b/content/docs/NAVPatterns/3-cal-coding-guidelines/design/maintainability-index/index.md
@@ -0,0 +1,43 @@
++++
+title = "Maintainability Index"
+weight = 770
+tags = ["C/AL"]
+categories = ["Best Practice"]
++++
+[Maintainability Index][anchor0]: Do not write functions that have a very low maintainability index. This makes the code hard to maintain.
+
+Bad code
+
+ Any procedure / trigger that has a maintainability index < 20
+
+
+
+Good code
+
+ Any procedure / trigger that has a maintainability index >= 20\.
+ The Maintainability Index is computed as a function:
+ - Lines Of Code (inverse proportional)
+ - the Halstead Volume
+ - Cyclomatic Complexity (inverse proportional).
+
+More info
+
+* [Halstead Volume][anchor1]
+* [Cyclomatic Complexity][anchor2]
+
+Bad code
+
+ Any procedure / trigger that is > 100 lines of code
+
+
+
+Good code
+
+ Any procedure / trigger that is <= 100 lines of code.
+ A full C/AL Statement counts as 1 line of code
+
+
+
+[anchor0]: http://blogs.msdn.com/b/codeanalysis/archive/2007/11/20/maintainability-index-range-and-meaning.aspx
+[anchor1]: http://en.wikipedia.org/wiki/Halstead_complexity_measures
+[anchor2]: http://www.aivosto.com/project/help/pm-complexity.html
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
new file mode 100644
index 00000000..8811e459
--- /dev/null
+++ b/content/docs/NAVPatterns/3-cal-coding-guidelines/design/parameter-placeholders/index.md
@@ -0,0 +1,35 @@
++++
+title = "Parameter Placeholders"
+weight = 920
+tags = ["C/AL"]
+categories = ["Best Practice"]
++++
+The number of parameters passed to a string must match the placeholders.
+
+Bad code
+
+ CannotDeleteLineErr@1005 : TextConst 'ENU=You cannot delete this line because one or more rating values exists.';
+ ...
+ ERROR(CannotDeleteLineErr,TABLECAPTION);
+
+Good code
+
+ CannotDeleteLineErr@1005 : TextConst 'ENU=You cannot delete this line because one or more rating values exists.';
+ ...
+ ERROR(CannotDeleteLineErr);
+
+
+
+Bad code
+
+ CannotUseThisFieldErr@1020 : TextConst 'ENU=You cannot use this field for %2 fields.';
+ ...
+ ERROR(CannotUseThisFieldErr,0,Field.Class);
+
+Good code
+
+ CannotUseThisFieldErr@1020 : TextConst 'ENU=You cannot use this field for %1 fields.';
+ ...
+ ERROR(CannotUseThisFieldErr,Field.Class);
+
+###
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
new file mode 100644
index 00000000..97e89c8a
--- /dev/null
+++ b/content/docs/NAVPatterns/3-cal-coding-guidelines/design/static-object-invocation/index.md
@@ -0,0 +1,23 @@
++++
+title = "Static Object Invocation"
+weight = 1160
+tags = ["C/AL"]
+categories = ["Best Practice"]
++++
+Call objects statically whenever possible. It reduces extra noise and removes extra variables. Downside: changing the name of the object which is called statically will need a code update.
+
+Bad code
+
+ LOCAL PROCEDURE Code@1();
+ VAR
+ CAJnlPostBatch@1001 : Codeunit 1103;
+ BEGIN
+ CAJnlPostBatch.Run(CostJnlLine);
+ END;
+
+Good code
+
+ LOCAL PROCEDURE Code@1();
+ BEGIN
+ CODEUNIT.RUN(CODEUNIT::"CA Jnl.-Post Batch",CostJnlLine);
+ END;
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
new file mode 100644
index 00000000..69f713e1
--- /dev/null
+++ b/content/docs/NAVPatterns/3-cal-coding-guidelines/design/unreachable-code/index.md
@@ -0,0 +1,27 @@
++++
+title = "Unreachable Code"
+weight = 1310
+tags = ["C/AL"]
+categories = ["Best Practice"]
++++
+Do not write code that will never be hit.
+
+It affects code readability and can lead to wrong assumptions.
+
+Bad code
+
+ IF Type <> Type::FIELD THEN BEGIN
+ ...
+ ERROR(...);
+ RecRef.CLOSE;
+ END;
+
+
+
+Good code
+
+ IF Type <> Type::FIELD THEN BEGIN
+ ...
+ RecRef.CLOSE;
+ ERROR(...);
+ END;
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
new file mode 100644
index 00000000..7e8f6738
--- /dev/null
+++ b/content/docs/NAVPatterns/3-cal-coding-guidelines/design/unused-initialized-variables/index.md
@@ -0,0 +1,40 @@
++++
+title = "Unused Initialized Variables"
+weight = 1320
+tags = ["C/AL"]
+categories = ["Best Practice"]
++++
+The value assigned to a variable must be used. Else the variable is not necessary.
+
+Bad code
+
+ PROCEDURE AddEntities@1(FilterStr@1000 : Text[250]);
+ VAR
+ Vendor@1001 : Record 23;
+ Count@1002 : Integer;
+ BEGIN
+ Count := 0;
+ Vendor.SETFILTER("No.",FilterStr);
+ IF Vendor.FINDSET THEN
+ REPEAT
+ "User ID" := USERID;
+ "Vendor No." := Vendor."No.";
+ IF INSERT THEN
+ Count += 1;
+ UNTIL Vendor.NEXT = 0;
+ END;
+
+Good code
+
+ PROCEDURE AddEntities@1(FilterStr@1000 : Text[250]);
+ VAR
+ Vendor@1001 : Record 23;
+ BEGIN
+ Vendor.SETFILTER("No.",FilterStr);
+ IF Vendor.FINDSET THEN
+ REPEAT
+ "User ID" := USERID;
+ "Vendor No." := Vendor."No.";
+ IF INSERT THEN;
+ UNTIL Vendor.NEXT = 0;
+ END;
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
new file mode 100644
index 00000000..d7bd6da8
--- /dev/null
+++ b/content/docs/NAVPatterns/3-cal-coding-guidelines/design/unused-variables/index.md
@@ -0,0 +1,46 @@
++++
+title = "Unused Variables"
+weight = 1330
+tags = ["C/AL"]
+categories = ["Best Practice"]
++++
+Do not declare variables that are unused.
+
+Unused variables affect readability.
+
+Bad code
+
+ PROCEDURE CheckPostingDate@23(CaptionEntryNo@1005 : Text[50]);
+ BEGIN
+ IF GenJnlCheckLine.DateNotAllowed(PostingDate) THEN
+ ERROR(DateNotAllowedErr,Caption,EntryNo)
+ IF PostingDate > MaxPostingDate THEN
+ MaxPostingDate := PostingDate;
+ END
+
+Good code
+
+ PROCEDURE CheckPostingDate@23();
+ BEGIN
+ IF GenJnlCheckLine.DateNotAllowed(PostingDate) THEN
+ ERROR(DateNotAllowedErr,Caption,EntryNo);
+ IF PostingDate > MaxPostingDate THEN
+ MaxPostingDate := PostingDate;
+ END;
+
+Bad code
+
+ PROCEDURE IsReturned@14(EntryNo@1002 : Integer) : Decimal;
+ VAR
+ ItemEntry@1000 : Record 32;
+ Quantity@1003 : Integer;
+ BEGIN
+ EXIT(-OutboundApplied(EntryNo,TRUE) - InboundApplied(EntryNo,TRUE));
+ END;
+
+Good code
+
+ PROCEDURE IsReturned@14(EntryNo@1002 : Integer) : Decimal;
+ BEGIN
+ EXIT(-OutboundApplied(EntryNo,TRUE) - InboundApplied(EntryNo,TRUE));
+ END;
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
new file mode 100644
index 00000000..0f3d5aab
--- /dev/null
+++ b/content/docs/NAVPatterns/3-cal-coding-guidelines/design/variable-capacity-mismatch/index.md
@@ -0,0 +1,61 @@
++++
+title = "Variable Capacity Mismatch"
+weight = 1410
+tags = ["C/AL"]
+categories = ["Best Practice"]
++++
+Do not assign a value to a variable whose capacity is smaller.
+
+It will throw an error at runtime.
+
+Bad code
+
+ FileName@1010 : Text[250];
+ ...
+ UploadedFileName@1016 : Text[1024];
+ ...
+ FileName := UploadedFileName;
+
+Good code
+
+ FileName@1010 : Text[1024];
+ ...
+ UploadedFileName@1016 : Text[1024];
+ ...
+ FileName := UploadedFileName;
+
+Bad code
+
+ FileName@1010 : Text[250];
+ ...
+ UploadedFileName@1016 : Text[1024];
+ ...
+ FileName := UploadedFileName;
+
+Good code
+
+ FileName@1010 : Text[250];
+ ...
+ UploadedFileName@1016 : Text[1024];
+ ...
+ FileName := COPYSTR(UploadedFileName,1,250); // In case only the first 250 chars are needed. Similar for fields
+
+Bad code
+
+ VAR
+ ExceededNumberTxt@001 : 'ENU=Warning: Exceeded number of unsent documents/requests'
+ Subject@1002 : Text[50];
+ ...
+ BEGIN
+ ...
+ Subject := ExceededNumberTxt;
+
+Good code
+
+ VAR
+ ExceededNumberTxt@001 : 'ENU=Warning: Exceeded number of unsent documents/requests'
+ Subject@1002 : Text[100];
+ ...
+ BEGIN
+ ...
+ Subject := ExceededNumberTxt';
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
new file mode 100644
index 00000000..d14ab704
--- /dev/null
+++ b/content/docs/NAVPatterns/3-cal-coding-guidelines/design/with-scope-name-collision/index.md
@@ -0,0 +1,32 @@
++++
+title = "WITH Scope Name Collision"
+weight = 1450
+tags = ["C/AL"]
+categories = ["Best Practice"]
++++
+Do not use the WITH scope when it has a variable whose name is the same as a local variable. This can lead to wrong code assumptions.
+
+**Given that**
+"Contract Type" is a field on table ServiceContractHeader, then in the following example there is a parameter name clash with the field name. Which one will be used?
+
+Bad code
+
+ PROCEDURE InsertData@1("Contract Type"@1000 : Option...);
+ ...
+ BEGIN
+ ...
+ WITH ServiceContractHeader DO BEGIN
+ ...
+ DimMgt.InsertServContractDim(...,"Contract Type","Contract No.",0,...);
+ END;
+
+Good code
+
+ PROCEDURE InsertData@1(ContractType@1000 : Option...);
+ ...
+ BEGIN
+ ...
+ WITH ServiceContractHeader DO BEGIN
+ ...
+ DimMgt.InsertServContractDim(...,ContractType,"Contract No.",0,...);
+ END;
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
new file mode 100644
index 00000000..cb1f1493
--- /dev/null
+++ b/content/docs/NAVPatterns/3-cal-coding-guidelines/internally-used-dot-net-types/index.md
@@ -0,0 +1,208 @@
++++
+title = "Internally used DotNet Types"
+weight = 690
+tags = ["C/AL"]
+categories = ["Best Practice"]
++++
+_(Dynamics NAV 2015)_
+
+
+**Dot Net Types**
+
+'mscorlib'.System.Convert
+
+'mscorlib'.System.Globalization.CultureInfo
+
+'mscorlib'.System.Globalization.DateTimeStyles
+
+'mscorlib'.System.Globalization.NumberStyles
+
+'mscorlib'.System.Type
+
+'mscorlib'.System.Array
+
+'mscorlib'.System.EventArgs
+
+'mscorlib'.System.Security.Cryptography.SHA512Managed
+
+'mscorlib'.System.Security.Cryptography.HashAlgorithm
+
+'mscorlib'.System.Text.Encoding
+
+'mscorlib'.System.Text.UTF8Encoding
+
+'mscorlib'.System.Environment
+
+'mscorlib'.System.IO.Directory
+
+'mscorlib'.System.IO.Path
+
+'mscorlib'.System.IO.File
+
+'mscorlib'.System.IO.FileAttributes
+
+'mscorlib'.System.Collections.ArrayList
+
+'mscorlib'.System.Collections.IEnumerator
+
+'mscorlib'.System.Collections.Generic.IEnumerator\`1
+
+'mscorlib'.System.TimeSpan
+
+'mscorlib'.System.DateTime
+
+'mscorlib'.System.DateTimeKind
+
+'mscorlib'.System.DateTimeOffset
+
+'mscorlib'.System.Decimal
+
+'mscorlib'.System.String
+
+'System'.System.Diagnostics.Process
+
+'System'.System.Diagnostics.ProcessStartInfo
+
+'System'.System.IO.Compression.CompressionMode
+
+'System'.System.IO.Compression.GZipStream
+
+'System'.System.Uri
+
+'System'.System.UriPartial
+
+'System.Data'.System.Data.DataColumn
+
+'System.Data'.System.Data.DataTable
+
+'System.Data'.System.Data.DataRow
+
+'System.Web'.System.Web.HttpUtility
+
+'System.Windows.Forms'.System.Windows.Forms.DialogResult
+
+'System.Windows.Forms'.System.Windows.Forms.FileDialog
+
+'System.Windows.Forms'.System.Windows.Forms.OpenFileDialog
+
+'System.Windows.Forms'.System.Windows.Forms.SaveFileDialog
+
+'System.Windows.Forms'.System.Windows.Forms.FolderBrowserDialog
+
+'System.Xml'.\*
+
+'DocumentFormat.OpenXml'.\*
+
+'mscorlib'.System.IO.DirectoryInfo
+
+'mscorlib'.System.IO.FileInfo
+
+'Microsoft.Dynamics.Nav.Client.CodeViewerTypes'.Microsoft.Dynamics.Nav.Client.CodeViewerTypes.BreakpointCollection
+
+'Microsoft.Dynamics.Nav.Client.CodeViewerTypes'.Microsoft.Dynamics.Nav.Client.CodeViewerTypes.VariableCollection
+
+'Microsoft.Dynamics.Nav.SMTP'.Microsoft.Dynamics.Nav.SMTP.SmtpMessage
+
+'Microsoft.Dynamics.Nav.Management.DSObjectPickerWrapper'.\*
+
+'Microsoft.Dynamics.Nav.Timer'.\*
+
+'Microsoft.Dynamics.Nav.DO.ClientProxyWrapper'.\*
+
+'Microsoft.Dynamics.Nav.Client.BusinessChart'.\*
+
+'Microsoft.Dynamics.Nav.Client.BusinessChart.Model'.\*
+
+'Microsoft.Dynamics.Nav.Integration.Office'.\*
+
+'Microsoft.Dynamics.Nav.Integration.Office.Mock'.\*
+
+'Microsoft.Dynamics.Nav.EwsWrapper'.\*
+
+'Microsoft.Dynamics.Nav.EwsWrapper.ALTestHelper'.\*
+
+'Microsoft.Dynamics.NAV.OLSync.OLSyncSupplier'.\*
+
+'Microsoft.Dynamics.Nav.OLSync.Common'.\*
+
+'Microsoft.Dynamics.Nav.NavUserAccount'.\*
+
+'Microsoft.Dynamics.Nav.OpenXml'.\*
+
+'Microsoft.Dynamics.Nav.RapidStart'.\*
+
+'Microsoft.Dynamics.Framework.RapidStart.Common'.\*
+
+'Microsoft.Dynamics.Nav.Client.TimelineVisualization'.Microsoft.Dynamics.Nav.Client.TimelineVisualization.
+
+VisualizationScenarios
+
+'Microsoft.Dynamics.Framework.UI.WinForms.DataVisualization.Timeline'.Microsoft.Dynamics.Framework.UI.
+
+WinForms.DataVisualization.TimelineVisualization.DataModel+TransactionChangesRow
+
+'Microsoft.Dynamics.Framework.UI.WinForms.DataVisualization.Timeline'.Microsoft.Dynamics.Framework.UI.
+
+WinForms.DataVisualization.TimelineVisualization.DataModel+TransactionChangesDataTable
+
+'Microsoft.Dynamics.Framework.UI.WinForms.DataVisualization.Timeline'.Microsoft.Dynamics.Framework.UI.
+
+WinForms.DataVisualization.TimelineVisualization.DataModel+TransactionRow
+
+'Microsoft.Dynamics.Framework.UI.WinForms.DataVisualization.Timeline'.Microsoft.Dynamics.Framework.UI.
+
+WinForms.DataVisualization.TimelineVisualization.DataModel+TransactionDataTable
+
+'Microsoft.Office.Interop.Word'.\*
+
+'Microsoft.Office.Interop.Excel'.\*
+
+'Microsoft.Dynamics.BAPIWrapper'.\*
+
+'Microsoft.Dynamics.Nav.Types'.Microsoft.Dynamics.Nav.Types.ConfigSettings
+
+'Microsoft.Dynamics.Nav.DocumentService'.\*
+
+'Microsoft.Dynamics.Nav.DocumentService.Types'.\*
+
+'mscorlib'.System.IO.StreamWriter
+
+'Microsoft.Dynamics.Nav.Client.TimelineVisualization'.Microsoft.Dynamics.Nav.Client.TimelineVisualization.
+
+InteractiveTimelineVisualizationAddIn
+
+'System'.System.ComponentModel.CancelEventArgs
+
+'System'.System.Text.RegularExpressions.Regex
+
+'System'.System.Text.RegularExpressions.RegexOptions
+
+'mscorlib'.System.IO.StreamReader
+
+'System.Windows.Forms'.System.Windows.Forms.Control
+
+'System.Windows.Forms'.System.Windows.Forms.ControlEventArgs
+
+'System.Windows.Forms'.System.Windows.Forms.DragEventArgs
+
+'System.Windows.Forms'.System.Windows.Forms.GiveFeedbackEventArgs
+
+'System.Windows.Forms'.System.Windows.Forms.HelpEventArgs
+
+'System.Windows.Forms'.System.Windows.Forms.InvalidateEventArgs
+
+'System.Windows.Forms'.System.Windows.Forms.KeyEventArgs
+
+'System.Windows.Forms'.System.Windows.Forms.KeyPressEventArgs
+
+'System.Windows.Forms'.System.Windows.Forms.LayoutEventArgs
+
+'System.Windows.Forms'.System.Windows.Forms.MouseEventArgs
+
+'System.Windows.Forms'.System.Windows.Forms.PaintEventArgs
+
+'System.Windows.Forms'.System.Windows.Forms.PreviewKeyDownEventArgs
+
+'System.Windows.Forms'.System.Windows.Forms.QueryAccessibilityHelpEventArgs
+
+'System.Windows.Forms'.System.Windows.Forms.UICuesEventArgs
diff --git a/content/docs/NAVPatterns/3-cal-coding-guidelines/internationalization/_index.md b/content/docs/NAVPatterns/3-cal-coding-guidelines/internationalization/_index.md
new file mode 100644
index 00000000..6edfff5a
--- /dev/null
+++ b/content/docs/NAVPatterns/3-cal-coding-guidelines/internationalization/_index.md
@@ -0,0 +1,12 @@
++++
+title = "Internationalization"
+weight = 700
+tags = ["C/AL"]
+categories = ["Best Practice"]
++++
+## C/AL Coding Guidelines
+
+## **Internationalization**
+
+
+Find the C/AL guidelines by expanding the menu in the left.
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
new file mode 100644
index 00000000..cf18657b
--- /dev/null
+++ b/content/docs/NAVPatterns/3-cal-coding-guidelines/internationalization/using-calcdate/index.md
@@ -0,0 +1,19 @@
++++
+title = "Using Calcdate"
+weight = 1370
+tags = ["C/AL"]
+categories = ["Best Practice"]
++++
+CALCDATE should only be used with DateFormula variables. Alternatively the string should be enclosed using the <> symbols.
+
+Bad code
+
+ IF ReservEntry."Expected Receipt Date" >
+ CALCDATE('-' + FORMAT("Dampener (Time)") + FirstDate)
+ THEN
+
+Good code
+
+ IF ReservEntry."Expected Receipt Date" >
+ CALCDATE('<-' + FORMAT("Dampener (Time)") + FirstDate + '>')
+ THEN
diff --git a/content/docs/NAVPatterns/3-cal-coding-guidelines/localizability/_index.md b/content/docs/NAVPatterns/3-cal-coding-guidelines/localizability/_index.md
new file mode 100644
index 00000000..714eeeb3
--- /dev/null
+++ b/content/docs/NAVPatterns/3-cal-coding-guidelines/localizability/_index.md
@@ -0,0 +1,11 @@
++++
+title = "Localizability"
+weight = 750
+tags = ["C/AL"]
+categories = ["Best Practice"]
++++
+## C/AL Coding Guidelines
+
+## **Localizability**
+
+Find the C/AL guidelines by expanding the menu in the left.
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
new file mode 100644
index 00000000..4cad77f9
--- /dev/null
+++ b/content/docs/NAVPatterns/3-cal-coding-guidelines/localizability/captionml-for-system-tables/index.md
@@ -0,0 +1,28 @@
++++
+title = "CaptionML on System Pages"
+weight = 300
+tags = ["C/AL"]
+categories = ["Best Practice"]
++++
+CaptionML should always be specified on a page field for a system table. By default, system tables do not have captions, so if you need to use them in the UI then captions need to be added.
+
+Bad code
+
+ ...
+ { 2 ;2 ;Field ;
+ SourceExpr=Name }
+ ...
+ OBJECT Table 2000000000 User
+ ...
+ { 2 ; ;Name ;Text50 }
+
+Good code
+
+ ...
+ { 2 ;2 ;Field ;
+ CaptionML=ENU=Name;
+ SourceExpr=Name }
+ ...
+ OBJECT Table 2000000000 User
+ ...
+ { 2 ; ;Name ;Text50 }
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
new file mode 100644
index 00000000..ae4c8bdc
--- /dev/null
+++ b/content/docs/NAVPatterns/3-cal-coding-guidelines/localizability/fieldcaption-and-tablecaption/index.md
@@ -0,0 +1,24 @@
++++
+title = "FIELDCAPTION and TABLECAPTION"
+weight = 580
+tags = ["C/AL"]
+categories = ["Best Practice"]
++++
+For user messages, errors etc., use FIELDCAPTION not FIELDNAME and TABLECAPTION not TABLENAME.
+
+Reason:
+
+1. The correct translation will be automatically used.
+2. If the caption/name changes, then there will be a single point of change needed.
+
+Bad code
+
+```al
+IF NOT CONFIRM(UpdateLocationQst,TRUE,FIELDNAME("Location Code"),...)
+```
+
+Good code
+
+```al
+IF NOT CONFIRM(UpdateLocationQst,TRUE,FIELDCAPTION("Location Code"),...)
+```
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
new file mode 100644
index 00000000..0f8c2f63
--- /dev/null
+++ b/content/docs/NAVPatterns/3-cal-coding-guidelines/localizability/global-text-constants/index.md
@@ -0,0 +1,26 @@
++++
+title = "Global Text Constants"
+weight = 610
+tags = ["C/AL"]
+categories = ["Best Practice"]
++++
+Declare Text Constant as global variables.
+
+Bad code
+
+```al
+PROCEDURE GetRequirementText@6(...) : Text[50];
+VAR
+ RequirementOptionsTxt@1002 : TextConst 'ENU=Shipment,Receive,Pick,Put-Away';
+BEGIN
+```
+
+Good code
+
+```al
+VAR
+ RequirementOptionsTxt@1002 : TextConst 'ENU=Shipment,Receive,Pick,Put-Away';
+...
+PROCEDURE GetRequirementText@6(...) : Text[50];
+BEGIN
+```
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
new file mode 100644
index 00000000..29129263
--- /dev/null
+++ b/content/docs/NAVPatterns/3-cal-coding-guidelines/localizability/use-text-constants/index.md
@@ -0,0 +1,47 @@
++++
+title = "Use Text Constants"
+weight = 1360
+tags = ["C/AL"]
+categories = ["Best Practice"]
++++
+Pass user messages using Text Constants. It makes translation easy.
+
+Bad code
+
+```al
+ImportAttachmentQst@1021 : TextConst 'ENU="Import attachment "';
+...
+IF CONFIRM(ImportAttachmentQst + Caption +'?',TRUE) THEN BEGIN
+```
+
+Good code
+
+```al
+ImportAttachmentQst@1021 : TextConst 'ENU="Import attachment %1?"';
+...
+IF CONFIRM(STRSUBSTNO(ImportAttachmentQst, Caption),TRUE) THEN BEGIN
+```
+
+Bad code
+
+```al
+...
+ IF NOT
+ CONFIRM(
+ STRSUBSTNO(
+ 'Difference on Periodic entries: %1 on %2' +
+ 'Do you want to continue?',Balance,Date),
+ TRUE)
+ THEN
+ ERROR('Program terminated by the user');
+```
+
+Good code
+
+```al
+DiffOnPeriodEntiesQst@100 : TextConst 'ENU="Difference on Periodic entries: %1 on %2\\ Do you want to continue?"';
+ProgramTerminatedErr@200 : TextConst 'ENU="Program terminated by the user"';
+...
+ IF NOT CONFIRM(STRSUBSTNO(DiffOnPeriodEntiesQst,Balance,Date),TRUE) THEN
+ ERROR(ProgramTerminatedErr);
+```
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
new file mode 100644
index 00000000..c02403b5
--- /dev/null
+++ b/content/docs/NAVPatterns/3-cal-coding-guidelines/localizability/using-optioncaptionml/index.md
@@ -0,0 +1,25 @@
++++
+title = "Using OptionCaptionML"
+weight = 1380
+tags = ["C/AL"]
+categories = ["Best Practice"]
++++
+The OptionCaptionML should be filled in for sourceexpression using option data types.
+
+Bad code
+
+ { 30 ;TextBox ;17850;0 ;150 ;423 ;Name=Selection;
+ SourceExpr=Selection;
+ DataSetFieldName=Selection }
+ ...
+ Selection@1008 : 'Open,Closed,Open and Closed';
+ ...
+
+Good code
+
+ { 30 ;TextBox ;17850;0 ;150 ;423 ;Name=Selection;
+ OptionCaptionML=ENU=Open,Closed,Open and Closed;
+ SourceExpr=Selection;
+ DataSetFieldName=Selection }
+ ...
+ Selection@1008 : 'Open,Closed,Open and Closed';
diff --git a/content/docs/NAVPatterns/3-cal-coding-guidelines/readability/_index.md b/content/docs/NAVPatterns/3-cal-coding-guidelines/readability/_index.md
new file mode 100644
index 00000000..0a930440
--- /dev/null
+++ b/content/docs/NAVPatterns/3-cal-coding-guidelines/readability/_index.md
@@ -0,0 +1,13 @@
++++
+title = "Readability"
+weight = 980
+tags = ["C/AL","Readability"]
+categories = ["Best Practice"]
++++
+## C/AL Coding Guidelines
+
+## **Readability**
+
+Generally, all readability rules are Microsoft style choices only. You can use them to keep consistency with the existing code.
+
+Find the C/AL guidelines by expanding the menu in the left.
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
new file mode 100644
index 00000000..498e7b86
--- /dev/null
+++ b/content/docs/NAVPatterns/3-cal-coding-guidelines/readability/begin-as-an-afterword/index.md
@@ -0,0 +1,24 @@
++++
+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.
+
+Bad code
+
+```al
+IF ICPartnerRefType = ICPartnerRefType::"Common Item No." THEN
+ BEGIN
+ ...
+END;
+```
+
+Good code
+
+```
+IF ICPartnerRefType = ICPartnerRefType::"Common Item No." THEN BEGIN
+ ...
+END;
+```
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
new file mode 100644
index 00000000..a0caf6ad
--- /dev/null
+++ b/content/docs/NAVPatterns/3-cal-coding-guidelines/readability/begin-end/index.md
@@ -0,0 +1,71 @@
++++
+title = "Begin-End - Compound Only"
+weight = 240
+tags = ["C/AL","Readability"]
+categories = ["Best Practice"]
++++
+Only use BEGIN..END to enclose compound statements.
+
+Bad code
+
+```al
+IF FINDSET THEN BEGIN
+ REPEAT
+ ...
+ UNTIL NEXT = 0;
+END;
+```
+
+Good code
+
+```al
+IF FINDSET THEN
+ REPEAT
+ ...
+ UNTIL NEXT = 0;
+```
+
+Bad code
+
+```al
+IF IsAssemblyOutputLine THEN BEGIN
+ TESTFIELD("Order Line No.",0);
+END;
+```
+
+Good code
+
+```al
+IF IsAssemblyOutputLine THEN
+ TESTFIELD("Order Line No.",0);
+```
+
+Bad code
+
+```al
+IF FINDSET THEN
+ REPEAT
+ BEGIN
+ ...
+ END;
+ UNTIL NEXT = 0;
+```
+
+Good code
+
+```al
+IF FINDSET THEN
+ REPEAT
+ ...
+ UNTIL NEXT = 0;
+```
+
+Exception
+
+```al
+// Except for this case
+IF X THEN BEGIN
+ IF Y THEN
+ DO SOMETHING;
+END ELSE (not X)
+```
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
new file mode 100644
index 00000000..8ff81042
--- /dev/null
+++ b/content/docs/NAVPatterns/3-cal-coding-guidelines/readability/binary-operator-line-start/index.md
@@ -0,0 +1,23 @@
++++
+title = "Binary Operator to Start Line"
+weight = 250
+tags = ["C/AL","Readability"]
+categories = ["Best Practice"]
++++
+Do not start a line with a binary operator.
+
+Bad code
+
+```al
+"Quantity to Ship" :=
+ Quantity
+ - "Quantity Shipped"
+```
+
+Good code
+
+```al
+"Quantity to Ship" :=
+ Quantity -
+ "Quantity Shipped"
+```
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
new file mode 100644
index 00000000..5929b91e
--- /dev/null
+++ b/content/docs/NAVPatterns/3-cal-coding-guidelines/readability/blank-lines/index.md
@@ -0,0 +1,46 @@
++++
+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.
+
+Bad code
+
+```al
+PROCEDURE MATRIX_OnDrillDown@1133(MATRIX_ColumnOrdinal : Integer);
+BEGIN
+
+ SetupDrillDownCol(MATRIX_ColumnOrdinal);
+ DrillDown(FALSE,ValueType);
+
+END;
+```
+
+Good code
+
+```al
+PROCEDURE MATRIX_OnDrillDown@1133(MATRIX_ColumnOrdinal : Integer);
+BEGIN
+ SetupDrillDownCol(MATRIX_ColumnOrdinal);
+ DrillDown(FALSE,ValueType);
+END;
+```
+
+Bad code
+
+```al
+IF NameIsValid AND
+
+ Name2IsValid
+THEN
+```
+
+Good code
+
+```al
+IF NameIsValid AND
+ Name2IsValid
+THEN
+```
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
new file mode 100644
index 00000000..d4bbea8c
--- /dev/null
+++ b/content/docs/NAVPatterns/3-cal-coding-guidelines/readability/case-actions/index.md
@@ -0,0 +1,25 @@
++++
+title = "CASE Action"
+weight = 310
+tags = ["C/AL","Readability"]
+categories = ["Best Practice"]
++++
+A CASE action should start on a line after the possibility.
+
+Bad code
+
+```al
+CASE Letter OF
+ 'A': Letter2 := '10';
+ 'B': Letter2 := '11';
+```
+
+Good code
+
+```al
+CASE Letter OF
+ 'A':
+ Letter2 := '10';
+ 'B':
+ Letter2 := '11';
+```
\ No newline at end of file
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
new file mode 100644
index 00000000..e02393fe
--- /dev/null
+++ b/content/docs/NAVPatterns/3-cal-coding-guidelines/readability/colon-usage-in-case/index.md
@@ -0,0 +1,23 @@
++++
+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.
+
+Bad code
+
+```al
+CASE DimOption OF
+ DimOption::"Global Dimension 1" :
+ DimValue."Dimension Code" := GLSetup."Global Dimension 1 Code";
+```
+
+Good code
+
+```al
+CASE DimOption OF
+ DimOption::"Global Dimension 1":
+ DimValue."Dimension Code" := GLSetup."Global Dimension 1 Code";
+```
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
new file mode 100644
index 00000000..049cbd86
--- /dev/null
+++ b/content/docs/NAVPatterns/3-cal-coding-guidelines/readability/comments-curly-brackets/index.md
@@ -0,0 +1,48 @@
++++
+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.
+
+Bad code
+
+```al
+PeriodTxt: {Period}
+```
+
+Good code
+
+```al
+PeriodTxt: // Period
+```
+
+
+Bad code
+
+```al
+PROCEDURE MATRIX_OnAfterGetRecord@10(MATRIX_ColumnOrdinal : Integer);
+BEGIN
+ {
+ IF ShowColumnName THEN
+ MatrixHeader := MatrixRecords[MATRIX_ColumnOrdinal].Name
+ ELSE
+ MatrixHeader := MatrixRecords[MATRIX_ColumnOrdinal].Code;
+ }
+ MatrixRecord := MatrixRecords[MATRIX_ColumnOrdinal];
+ AnalysisValue := CalcAmt(ValueType,TRUE);
+ MATRIX_CellData[MATRIX_ColumnOrdinal] := AnalysisValue;
+END;
+```
+
+Good code
+
+```al
+PROCEDURE MATRIX_OnAfterGetRecord@10(MATRIX_ColumnOrdinal : Integer);
+BEGIN
+ MatrixRecord := MatrixRecords[MATRIX_ColumnOrdinal];
+ AnalysisValue := CalcAmt(ValueType,TRUE);
+ MATRIX_CellData[MATRIX_ColumnOrdinal] := AnalysisValue;
+END;
+```
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
new file mode 100644
index 00000000..7203208c
--- /dev/null
+++ b/content/docs/NAVPatterns/3-cal-coding-guidelines/readability/comments-spacing/index.md
@@ -0,0 +1,20 @@
++++
+title = "Comment Spacing"
+weight = 360
+tags = ["C/AL","Readability"]
+categories = ["Best Practice"]
++++
+Always start comments with // followed by one space character.
+
+Bad code
+
+```al
+RowNo += 1000; //Move way below the budget
+```
+
+
+Good code
+
+```al
+RowNo += 1000; // Move way below the budget
+```
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
new file mode 100644
index 00000000..a31fb67e
--- /dev/null
+++ b/content/docs/NAVPatterns/3-cal-coding-guidelines/readability/end-else-pair/index.md
@@ -0,0 +1,28 @@
++++
+title = "END ELSE Pair"
+weight = 540
+tags = ["C/AL","Readability"]
+categories = ["Best Practice"]
++++
+The END ELSE pair should always appear on the same line.
+
+Bad code
+
+```al
+IF OppEntry.FIND('-') THEN
+ IF SalesCycleStage.FIND('-') THEN BEGIN
+ ...
+ END
+ELSE
+ ...
+```
+
+Good code
+
+```al
+IF OppEntry.FIND('-') THEN
+ IF SalesCycleStage.FIND('-') THEN BEGIN
+ ...
+END ELSE
+ ...
+```
\ No newline at end of file
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
new file mode 100644
index 00000000..614aab16
--- /dev/null
+++ b/content/docs/NAVPatterns/3-cal-coding-guidelines/readability/indentation/index.md
@@ -0,0 +1,114 @@
++++
+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.
+
+Bad code
+
+```al
+IF GLSetup."Unrealized VAT" OR
+ (GLSetup."Prepayment Unrealized VAT" AND NewCVLedgEntryBuf.Prepayment)
+```
+
+Good code
+
+```al
+IF GLSetup."Unrealized VAT" OR
+ (GLSetup."Prepayment Unrealized VAT" AND NewCVLedgEntryBuf.Prepayment)
+```
+
+Bad code
+
+```al
+IF GenJnlLine."Account No." <> ICPartner.Code THEN
+ ICPartner.GET("Account No.");
+ IF GenJnlLine.Amount \> 0 THEN BEGIN
+ ...
+```
+
+Good code
+
+```al
+IF GenJnlLine."Account No." <> ICPartner.Code THEN
+ ICPartner.GET("Account No.");
+IF GenJnlLine.Amount > 0 THEN BEGIN
+ ...
+```
+
+Bad code
+
+```al
+Dialog.OPEN(WindowTxt +
+ '@1@@@@@@@@@@@@@@@@@@@@@@@');
+```
+
+Good code
+
+```al
+Dialog.OPEN(
+ WindowTxt +
+ '@1@@@@@@@@@@@@@@@@@@@@@@@');
+```
+
+Bad code
+
+```al
+TempOldCustLedgEntry.DELETE;
+ // Find the next old entry for application of the new entry
+```
+
+Good code
+
+```al
+TempOldCustLedgEntry.DELETE;
+// Find the next old entry for application of the new entry
+```
+
+Bad code
+
+```al
+IF NOT ("Applies-to Doc. Type" IN
+ ["Applies-to Doc. Type"::Receipt,
+ "Applies-to Doc. Type"::"Return Shipment"])
+```
+
+Good code
+
+```al
+IF NOT ("Applies-to Doc. Type" IN
+ ["Applies-to Doc. Type"::Receipt,
+ "Applies-to Doc. Type"::"Return Shipment"])
+```
+
+Bad code
+
+```al
+WHILE (RemAmt > 0) OR
+ (RemAmtLCY > 0)
+DO
+```
+
+Good code
+
+```al
+WHILE (RemAmt > 0) OR
+ (RemAmtLCY > 0)
+DO
+```
+
+Bad code
+
+```al
+UNTIL (RemAmt > 0) AND
+ (RemAmtLCY > 0);
+```
+
+Good code
+
+```al
+UNTIL (RemAmt > 0) AND
+ (RemAmtLCY > 0)
+```
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
new file mode 100644
index 00000000..e109817a
--- /dev/null
+++ b/content/docs/NAVPatterns/3-cal-coding-guidelines/readability/keyword-pairs-indentation/index.md
@@ -0,0 +1,22 @@
++++
+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.
+
+Bad code
+
+```al
+IF (x = y) AND
+ (a = b) THEN
+```
+
+Good code
+
+```al
+IF (x = y) AND
+ (a = b)
+THEN
+```
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
new file mode 100644
index 00000000..58ce7884
--- /dev/null
+++ b/content/docs/NAVPatterns/3-cal-coding-guidelines/readability/line-start-keywords/index.md
@@ -0,0 +1,28 @@
++++
+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.
+
+Bad code
+
+```al
+IF IsContactName THEN ValidateContactName
+ ELSE IF IsSalespersonCode THEN ValidateSalespersonCode
+ ELSE IF IsSalesCycleCode THEN ValidatSalesCycleCode;
+```
+
+Good code
+
+```al
+IF IsContactName THEN
+ ValidateContactName
+ELSE
+ IF IsSalespersonCode THEN
+ ValidateSalespersonCode
+ ELSE
+ IF IsSalesCycleCode THEN
+ ValidatSalesCycleCode;
+```
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
new file mode 100644
index 00000000..a7bae1df
--- /dev/null
+++ b/content/docs/NAVPatterns/3-cal-coding-guidelines/readability/lonely-repeat/index.md
@@ -0,0 +1,22 @@
++++
+title = "Lonely Repeat"
+weight = 760
+tags = ["C/AL","Readability"]
+categories = ["Best Practice"]
++++
+The REPEAT statement should always be alone on a line.
+
+Bad code
+
+```al
+IF ReservEntry.FINDSET THEN REPEAT
+
+```
+
+
+Good code
+
+```al
+IF ReservEntry.FINDSET THEN
+ REPEAT
+```
\ No newline at end of file
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
new file mode 100644
index 00000000..64b8fff3
--- /dev/null
+++ b/content/docs/NAVPatterns/3-cal-coding-guidelines/readability/named-invocations/index.md
@@ -0,0 +1,20 @@
++++
+title = "Named Invocations"
+weight = 830
+tags = ["C/AL","Readability"]
+categories = ["Best Practice"]
++++
+When calling an object statically use the name, not the number
+
+Bad code
+
+```al
+PAGE.RUNMODAL(525,SalesShptLine)
+```
+
+
+Good code
+
+```al
+PAGE.RUNMODAL(PAGE::"Posted Sales Shipment Lines",SalesShptLine)
+```
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
new file mode 100644
index 00000000..3dcabde2
--- /dev/null
+++ b/content/docs/NAVPatterns/3-cal-coding-guidelines/readability/nested-withs/index.md
@@ -0,0 +1,28 @@
++++
+title = "Nested WITHs"
+weight = 850
+tags = ["C/AL","Readability"]
+categories = ["Best Practice"]
++++
+Do not nest WITHs that reference different types of objects.
+
+Bad code
+
+```al
+WITH PostedWhseShptLine DO BEGIN
+ ...
+ WITH ItemLedgEntry DO
+ InsertBufferRec(...,"Serial No.","Lot No.",...);
+ ...
+END;
+```
+
+Good code
+
+```al
+WITH PostedWhseShptLine DO BEGIN
+ ...
+ InsertBufferRec(...,ItemLedgEntry."Serial No.",ItemLedgEntry."Lot No.",...);
+ ...
+END;
+```
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
new file mode 100644
index 00000000..5d9e6379
--- /dev/null
+++ b/content/docs/NAVPatterns/3-cal-coding-guidelines/readability/one-statement-per-line/index.md
@@ -0,0 +1,38 @@
++++
+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.
+
+Bad code
+
+```al
+IF OppEntry.FIND('-') THEN EXIT
+
+```
+
+
+Good code
+
+```al
+IF OppEntry.FIND('-') THEN
+ EXIT
+
+```
+
+
+Bad code
+
+```al
+TotalCost += Cost; TotalAmt += Amt;
+
+```
+
+Good code
+
+```al
+TotalCost += Cost;
+TotalAmt += Amt;
+```
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
new file mode 100644
index 00000000..f1c05ab4
--- /dev/null
+++ b/content/docs/NAVPatterns/3-cal-coding-guidelines/readability/separate-if-and-else/index.md
@@ -0,0 +1,25 @@
++++
+title = "Separate IF and ELSE"
+weight = 1050
+tags = ["C/AL","Readability"]
+categories = ["Best Practice"]
++++
+IF and ELSE statements should be on separate lines.
+
+Bad code
+
+```al
+IF Atom[i+1] = '>' THEN HasLogicalOperator := TRUE ELSE BEGIN
+ ...
+END;
+```
+
+Good code
+
+```al
+IF Atom[i+1] = '>' THEN
+ HasLogicalOperator := TRUE
+ELSE BEGIN
+ ...
+END;
+```
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
new file mode 100644
index 00000000..ec18a52d
--- /dev/null
+++ b/content/docs/NAVPatterns/3-cal-coding-guidelines/readability/spacing-binary-operators/index.md
@@ -0,0 +1,48 @@
++++
+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.
+
+Bad code
+
+```al
+"Line Discount %" := "Line Discount Amount"/"Line Value"*100
+```
+
+
+Good code
+
+```al
+"Line Discount %" := "Line Discount Amount" / "Line Value" * 100;
+```
+
+
+Bad code
+
+```al
+StartDate := CALCDATE('<+'+FORMAT(Days + i)+'D>', StartDate);
+```
+
+
+Good code
+
+```al
+StartDate := CALCDATE('<+' + FORMAT(Days + i) + 'D>',StartDate);
+```
+
+
+Bad code
+
+```al
+StartDate := 0D; // Initialize
+```
+
+
+Good code
+
+```al
+StartDate := 0D; // Initialize
+```
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
new file mode 100644
index 00000000..087b1ac8
--- /dev/null
+++ b/content/docs/NAVPatterns/3-cal-coding-guidelines/readability/spacing-brackets-and/index.md
@@ -0,0 +1,48 @@
++++
+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.
+
+Bad code
+
+```al
+A[i] [j] := Amt;
+```
+
+
+Good code
+
+```al
+A[i][j] := Amt;
+```
+
+
+Bad code
+
+```al
+"Currency Exchange Rate"."Fix Exchange Rate Amount" :: Currency:
+```
+
+
+Good code
+
+```al
+"Currency Exchange Rate"."Fix Exchange Rate Amount"::Currency:
+```
+
+
+Bad code
+
+```al
+IF FIND (Which) THEN
+```
+
+
+Good code
+
+```al
+IF FIND(Which) THEN
+```
\ No newline at end of file
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
new file mode 100644
index 00000000..970b2d16
--- /dev/null
+++ b/content/docs/NAVPatterns/3-cal-coding-guidelines/readability/spacing-unary-operators/index.md
@@ -0,0 +1,34 @@
++++
+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).
+
+Bad code
+
+```al
+IF NOT(Type = Type::Item) THEN
+```
+
+
+Good code
+
+```al
+IF NOT (Type = Type::Item) THEN
+```
+
+
+Bad code
+
+```al
+DiscAmt := - "Discount Amount";
+```
+
+
+Good code
+
+```al
+DiscAmt := -"Discount Amount";
+```
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
new file mode 100644
index 00000000..97b84557
--- /dev/null
+++ b/content/docs/NAVPatterns/3-cal-coding-guidelines/readability/suggested-abbreviations/index.md
@@ -0,0 +1,1397 @@
++++
+title = "Suggested Abbreviations"
+weight = 1170
+tags = ["C/AL","Readability"]
+categories = ["Best Practice"]
++++
+### Suggested Abbreviations
+
+Whenever possible, do not use abbreviations in variables, functions and objects names.
+
+If there is no other choice, then use the suggestions below.
+
+**Abbreviation**
+
+**Text**
+
+Abs
+
+absence
+
+Acc
+
+account
+
+Acc
+
+accounting
+
+Accum
+
+accumulated
+
+Act
+
+action
+
+Activ
+
+activity
+
+Add
+
+additional
+
+Addr
+
+address
+
+Adj
+
+adjust
+
+Adjd
+
+adjusted
+
+Adjmt
+
+adjustment
+
+Agrmt
+
+agreement
+
+Alloc
+
+allocation
+
+Allow
+
+allowance
+
+Alt
+
+alternative
+
+Amt
+
+amount
+
+Amts
+
+amounts
+
+Ans
+
+answer
+
+Appl
+
+applies
+
+Appln
+
+application
+
+Arriv
+
+arrival
+
+Asm
+
+assembly
+
+ATO
+
+assemble to order
+
+Assgnt
+
+assignment
+
+Assoc
+
+associated
+
+Attmt
+
+attachment
+
+Auth
+
+authorities
+
+Auto
+
+automatic
+
+Avail
+
+availability
+
+Avg
+
+average
+
+BA
+
+ba db.
+
+Bal
+
+balance
+
+BOM
+
+bill of materials
+
+Blnkt
+
+blanket
+
+Budg
+
+budget
+
+Buf
+
+buffer
+
+Bus
+
+business
+
+BIM
+
+business interaction management
+
+Buy
+
+buying
+
+Calc
+
+calculate
+
+Calcd
+
+calculated
+
+Calcu
+
+calculation
+
+Cal
+
+calendar
+
+Cap
+
+capacity
+
+CRP
+
+capacity requirements planning
+
+CF
+
+cash flow
+
+CF
+
+cashflow
+
+ctlg
+
+catalog
+
+Cat
+
+category
+
+CPU
+
+Central Processing Unit
+
+Ctr
+
+center
+
+Chg
+
+change
+
+Chgs
+
+changes
+
+Char
+
+character
+
+Chars
+
+characters
+
+Chrg
+
+charge
+
+Chrgs
+
+charges
+
+Chk
+
+check
+
+Class
+
+classification
+
+coll
+
+collection
+
+col
+
+column
+
+Cmt
+
+comment
+
+Co
+
+company
+
+Comp
+
+component
+
+Cmpltn
+
+completion
+
+Comps
+
+components
+
+Compn
+
+composition
+
+Compr
+
+compression
+
+Concrnt
+
+concurrent
+
+Conf
+
+confidential
+
+Cnfrmn
+
+confirmation
+
+Confl
+
+conflict
+
+Consol
+
+consolidate
+
+Consolid
+
+consolidation
+
+Consump
+
+consumption
+
+Cont
+
+contact
+
+Cntr
+
+container
+
+Contr
+
+contract
+
+Contrd
+
+contracted
+
+Ctrl
+
+control
+
+Ctrls
+
+controls
+
+Conv
+
+conversion
+
+Cor
+
+correction
+
+Corres
+
+correspondence
+
+Corresp
+
+corresponding
+
+Cst
+
+cost
+
+COGS
+
+sold
+
+Cr
+
+credit
+
+Cumul
+
+cumulate
+
+Curr
+
+currency
+
+Crnt
+
+current
+
+Cust
+
+customer
+
+CV
+
+customer/vendor
+
+Dly
+
+daily
+
+Damp
+
+dampener
+
+DBMS
+
+database management system
+
+D
+
+date
+
+Def
+
+definition
+
+Demo
+
+demonstration
+
+Dept
+
+department
+
+DP
+
+department/project
+
+Depr
+
+depreciation
+
+Desc
+
+description
+
+Dtl
+
+detail
+
+Dtld
+
+detailed
+
+Dtls
+
+details
+
+Dev
+
+deviation
+
+Diff
+
+difference
+
+Dim
+
+dimension
+
+Dir
+
+direct
+
+Disc
+
+discount
+
+Discr
+
+discrete
+
+Distr
+
+distribute
+
+Distrd
+
+distributed
+
+Distbtr
+
+distributor
+
+Distrn
+
+distribution
+
+Doc
+
+document
+
+Dupl
+
+duplicate
+
+Entrd
+
+entered
+
+Engin
+
+engineering
+
+Exch
+
+exchange
+
+Excl
+
+excluding
+
+Exec
+
+execute
+
+Expd
+
+expected
+
+Exped
+
+expedited
+
+Exp
+
+expense
+
+Expr
+
+expression
+
+Expir
+
+expiration
+
+Ext
+
+extended
+
+Expl
+
+explode
+
+Expt
+
+export
+
+Fnl
+
+final
+
+Fin
+
+finance
+
+Fisc
+
+fiscal
+
+Fnshd
+
+finished
+
+FA
+
+fixed asset
+
+Fwd
+
+forward
+
+Frt
+
+freight
+
+Gen
+
+general
+
+GL
+
+general ledger
+
+Gr
+
+group
+
+Hdr
+
+header
+
+Hist
+
+history
+
+Hol
+
+holiday
+
+HR
+
+human resource
+
+ID
+
+identification
+
+Imp
+
+import
+
+Inbnd
+
+inbound
+
+Incl
+
+including
+
+Incld
+
+included
+
+Incmg
+
+incoming
+
+ISV
+
+independent software vendor
+
+Indust
+
+industry
+
+Info
+
+information
+
+Init
+
+initial
+
+Intra
+
+Intrastat
+
+Interact
+
+interaction
+
+Integr
+
+integration
+
+Int
+
+interest
+
+Intm
+
+Interim
+
+IP
+
+internal protocol
+
+Invt
+
+inventory
+
+Invtbl
+
+inventoriable
+
+Inv
+
+invoice
+
+Invd
+
+invoiced
+
+IT
+
+item tracking
+
+Jnl
+
+journal
+
+Lang
+
+language
+
+Ledg
+
+ledger
+
+Lvl
+
+level
+
+Ln
+
+line
+
+Lt
+
+list
+
+LCY
+
+local currency
+
+Loc
+
+location
+
+Mail
+
+mailing
+
+Maint
+
+maintenance
+
+Mgt
+
+management
+
+Man
+
+manual
+
+Mfg
+
+manufacturing
+
+Mfr
+
+manufacturer
+
+Mat
+
+material
+
+Mktg
+
+marketing
+
+Max
+
+maximum
+
+Meas
+
+measure
+
+Msg
+
+message
+
+Min
+
+minimum
+
+Misc
+
+miscellaneous
+
+Mod
+
+modify
+
+Mth
+
+month
+
+Neg
+
+negative
+
+NonInvtbl
+
+non-inventoriable
+
+Notif
+
+notification
+
+No
+
+number
+
+Nos
+
+numbers
+
+Obj
+
+object
+
+Oper
+
+operating
+
+Opp
+
+opportunity
+
+Ord
+
+order
+
+Ords
+
+orders
+
+Orig
+
+original
+
+Org
+
+organization
+
+Outbnd
+
+outbound
+
+Outg
+
+Outgoing
+
+Out
+
+output
+
+Outstd
+
+outstanding
+
+Ovhd
+
+overhead
+
+Pmt
+
+payment
+
+Pct
+
+percent
+
+Persnl
+
+personnel
+
+Phys
+
+physical
+
+Pic
+
+picture
+
+Plng
+
+planning
+
+Pstd
+
+posted
+
+Post
+
+posting
+
+Pos
+
+positive
+
+Prec
+
+precision
+
+Prepmt
+
+prepayment
+
+Prod
+
+product
+
+Prod
+
+production
+
+ProdOrd
+
+production order
+
+Proj
+
+project
+
+Prop
+
+property
+
+Prspct
+
+prospect
+
+Purch
+
+purchase
+
+Purch
+
+purchases
+
+Purchr
+
+purchaser
+
+PurchOrd
+
+purchase order
+
+Qlty
+
+quality
+
+Qty
+
+quantity
+
+Questn
+
+questionnaire
+
+Qte
+
+quote
+
+RF
+
+radio frequency
+
+Rng
+
+range
+
+Rcpt
+
+receipt
+
+Rcd
+
+received
+
+Rec
+
+record
+
+Recs
+
+records
+
+Recncl
+
+reconcile
+
+Recon
+
+reconciliation
+
+Recur
+
+recurring
+
+Ref
+
+reference
+
+Reg
+
+register
+
+Regn
+
+registration
+
+Regd
+
+registered
+
+Rel
+
+relation
+
+Rels
+
+relations
+
+Rlshp
+
+relationship
+
+Rlse
+
+release
+
+Rlsd
+
+released
+
+Rem
+
+remaining
+
+Rmdr
+
+reminder
+
+Repl
+
+replacement
+
+Rplnsh
+
+replenish
+
+Rplnsht
+
+replenishment
+
+Rpt
+
+report
+
+Rep
+
+represent
+
+Repd
+
+represented
+
+Rqst
+
+request
+
+Reqd
+
+required
+
+Reqt
+
+requirement
+
+Reqts
+
+requirements
+
+Req
+
+requisition
+
+Rsv
+
+reserve
+
+Rsvd
+
+reserved
+
+Reserv
+
+reservation
+
+Resol
+
+resolution
+
+Res
+
+resource
+
+Rsp
+
+response
+
+Resp
+
+responsibility
+
+Rtn
+
+retain
+
+Rtnd
+
+retained
+
+Ret
+
+return
+
+Rets
+
+returns
+
+Revaln
+
+revaluation
+
+Rev
+
+reverse
+
+Rvw
+
+review
+
+Rnd
+
+round
+
+Rndd
+
+rounded
+
+Rndg
+
+rounding
+
+Rte
+
+route
+
+Rtng
+
+routing
+
+Rout
+
+routine
+
+Sales
+
+sales & receivables
+
+Saf
+
+safety
+
+Sched
+
+schedule
+
+Sec
+
+second
+
+Seg
+
+segment
+
+Sel
+
+select
+
+Selctn
+
+selection
+
+Seq
+
+sequence
+
+Ser
+
+serial
+
+SN
+
+serial number
+
+Serv
+
+service
+
+Sh
+
+sheet
+
+Shpt
+
+shipment
+
+Src
+
+source
+
+Spcl
+
+special
+
+Spec
+
+specification
+
+Specs
+
+specifications
+
+Std
+
+standard
+
+SF
+
+frequency
+
+Stmt
+
+statement
+
+Stat
+
+statistical
+
+Stats
+
+statistics
+
+Stk
+
+stock
+
+SKU
+
+stockkeeping unit
+
+Stm
+
+stream
+
+SQL
+
+structured query language
+
+Subcontr
+
+subcontract
+
+Subcontrd
+
+subcontracted
+
+Subcontrg
+
+subcontracting
+
+Sub
+
+substitute
+
+Subst
+
+substitution
+
+Sug
+
+suggest
+
+Sugd
+
+suggested
+
+Sugn
+
+suggestion
+
+Sum
+
+summary
+
+Suspd
+
+suspended
+
+Sympt
+
+symptom
+
+Synch
+
+synchronize
+
+Temp
+
+temporary
+
+Tot
+
+total
+
+Transac
+
+transaction
+
+Trans
+
+transfer
+
+Transln
+
+translation
+
+Trkg
+
+tracking
+
+Tblsht
+
+troubleshoot
+
+Tblshtg
+
+troubleshooting
+
+UOM
+
+unit of measure
+
+UT
+
+unit test
+
+Unreal
+
+unrealized
+
+Unrsvd
+
+unreserved
+
+Upd
+
+update
+
+Valn
+
+valuation
+
+Val
+
+value
+
+VAT
+
+value added tax
+
+Var
+
+variance
+
+Vend
+
+vendor
+
+Whse
+
+warehouse
+
+WS
+
+web shop
+
+Wksh
+
+worksheet
+
+GL
+
+g/l
+
+Pct
+
+%
+
+Three-Tier
+
+3-tier
+
+Osynch
+
+Outlook Synch
+
+##
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
new file mode 100644
index 00000000..bbd36a69
--- /dev/null
+++ b/content/docs/NAVPatterns/3-cal-coding-guidelines/readability/temporary-variable-naming/index.md
@@ -0,0 +1,33 @@
++++
+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.
+
+Bad code
+
+```al
+JobWIPBuffer@1002 : TEMPORARY Record 1018;
+```
+
+
+Good code
+
+```al
+TempJobWIPBuffer@1002 : TEMPORARY Record 1018;
+```
+
+Bad code
+
+```al
+TempJobWIPBuffer@1002 : Record 1018;
+```
+
+
+Good code
+
+```al
+CopyOfJobWIPBuffer@1002 : Record 1018;
+```
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
new file mode 100644
index 00000000..7de92fbf
--- /dev/null
+++ b/content/docs/NAVPatterns/3-cal-coding-guidelines/readability/textconst-suffixes/index.md
@@ -0,0 +1,117 @@
++++
+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.
+
+Bad code
+
+```al
+CannotDeleteLine@1005 : TextConst 'ENU=You cannot delete this line because one or more rating values exists.';
+...
+ERROR(CannotDeleteLine,TABLECAPTION);
+```
+
+Good code
+
+```al
+CannotDeleteLineErr@1005 : TextConst 'ENU=You cannot delete this line because one or more rating values exists.';
+...
+ERROR(CannotDeleteLineErr,TABLECAPTION);
+```
+
+Bad code
+
+```al
+Text000@1011 : TextConst 'ENU="has been changed (initial a %1: %2= %3, %4= %5)"';
+...
+SalesLine.FIELDERROR(Type,STRSUBSTNO(Text000,...);
+...
+```
+
+Good code
+
+```al
+TypeHasBeenChangedErr@1011 : TextConst 'ENU="has been changed (initial a %1: %2= %3, %4= %5)"';
+...
+SalesLine.FIELDERROR(Type,STRSUBSTNO(TypeHasBeenChangedErr,...);
+...
+```
+
+Bad code
+
+```al
+Text004@1004 : TextConst 'ENU=Indenting the Job Tasks \#1\#\#\#\#\#\#\#\#\#\#.';
+...
+Window@1007 : Dialog;
+...
+ Window.OPEN(Text004);
+```
+
+Good code
+
+```al
+IndentingMsg@1004 : TextConst 'ENU=Indenting the Job Tasks \#1\#\#\#\#\#\#\#\#\#\#.';
+...
+Window@1007 : Dialog;
+...
+ Window.OPEN(IndentingMsg);
+```
+
+Bad code
+
+```al
+Text002@1005 : TextConst 'ENU=You cannot delete a %1 that is used in one or more setup windows.\\ Do you want to open the G/L Account No. Where-Used List Window?';
+...
+IF CONFIRM(Text002,TRUE,GLAcc.TABLECAPTION) THEN
+```
+
+Good code
+
+```al
+OpenWhereUsedWindowQst@1005 : TextConst 'ENU=You cannot delete a %1 that is used in one or more setup windows.\\ Do you want to open the G/L Account No. Where-Used List Window?';
+...
+IF CONFIRM(OpenWhereUsedWindowQst,TRUE,GLAcc.TABLECAPTION) THEN
+```
+
+Bad code
+
+```al
+Selection := STRMENU(Text003,2);
+...
+Text003@1002 : TextConst 'ENU=&Copy dimensions from BOM,&Retrieve dimensions from components';
+```
+
+Good code
+
+```al
+ Selection := STRMENU(CopyFromQst,2);
+ ...
+ CopyFromQst@1002 : TextConst 'ENU=&Copy dimensions from BOM,&Retrieve dimensions from components';
+```
+
+Bad code
+
+```al
+DATASET
+{
+...
+ { 1 ;1 ;Column ;Chart_of_AccountsCaption;
+ SourceExpr=Chart_of_AccountsCaption }
+...
+Chart_of_AccountsCaption@9647 : TextConst 'ENU=Chart of Accounts';
+```
+
+Good code
+
+```al
+DATASET
+{
+...
+ { 1 ;1 ;Column ;Chart_of_AccountsCaption;
+ SourceExpr=ChartOfAccountsLbl }
+...
+ChartOfAccountsLbl@9647 : TextConst 'ENU=Chart of Accounts';
+```
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
new file mode 100644
index 00000000..11ad406e
--- /dev/null
+++ b/content/docs/NAVPatterns/3-cal-coding-guidelines/readability/unary-operator-line-end/index.md
@@ -0,0 +1,21 @@
++++
+title = "Unary Operator Line End"
+weight = 1250
+tags = ["C/AL","Readability"]
+categories = ["Best Practice"]
++++
+Do not end a line with unary operator.
+
+Bad code
+
+```al
+"Quantity Handled (Base)" := -
+ "Quantity Handled (Base)");
+```
+
+Good code
+
+```al
+"Quantity Handled (Base)" :=
+ - "Quantity Handled (Base)");
+```
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
new file mode 100644
index 00000000..127418c2
--- /dev/null
+++ b/content/docs/NAVPatterns/3-cal-coding-guidelines/readability/unnecessary-compound-parenthesis/index.md
@@ -0,0 +1,34 @@
++++
+title = "Unnecessary Compound Parenthesis"
+weight = 1260
+tags = ["C/AL","Readability"]
+categories = ["Best Practice"]
++++
+Use parenthesis only to enclose compound expressions inside compound expressions.
+
+Bad code
+
+```al
+IF ("Costing Method" = "Costing Method"::Standard) THEN
+```
+
+
+Good code
+
+```al
+IF "Costing Method" = "Costing Method"::Standard THEN
+```
+
+
+Bad code
+
+```al
+ProfitPct = -(Profit) / CostAmt * 100;
+```
+
+
+Good code
+
+```al
+ProfitPct = -Profit / CostAmt * 100;
+```
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
new file mode 100644
index 00000000..f3755ab2
--- /dev/null
+++ b/content/docs/NAVPatterns/3-cal-coding-guidelines/readability/unnecessary-else/index.md
@@ -0,0 +1,24 @@
++++
+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.
+
+Bad code
+
+```al
+IF IsAdjmtBinCodeChanged THEN
+ ERROR(AdjmtBinCodeChangeNotAllowedErr,...)
+ELSE
+ ERROR(BinCodeChangeNotAllowedErr,...);
+```
+
+Good code
+
+```al
+IF IsAdjmtBinCodeChanged THEN
+ ERROR(AdjmtBinCodeChangeNotAllowedErr,...)
+ERROR(BinCodeChangeNotAllowedErr,...);
+```
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
new file mode 100644
index 00000000..9750286a
--- /dev/null
+++ b/content/docs/NAVPatterns/3-cal-coding-guidelines/readability/unnecessary-function-parenthesis/index.md
@@ -0,0 +1,34 @@
++++
+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.
+
+Bad code
+
+```al
+IF ReservMgt.IsPositive() THEN
+```
+
+
+Good code
+
+```al
+IF ReservMgt.IsPositive THEN
+```
+
+
+Bad code
+
+```al
+IF ChangeStatusForm.RUNMODAL() <> ACTION::Yes THEN
+```
+
+
+Good code
+
+```al
+IF ChangeStatusForm.RUNMODAL <> ACTION::Yes THEN
+```
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
new file mode 100644
index 00000000..ecefcdf6
--- /dev/null
+++ b/content/docs/NAVPatterns/3-cal-coding-guidelines/readability/unnecessary-separators/index.md
@@ -0,0 +1,20 @@
++++
+title = "Unnecessary Separators"
+weight = 1290
+tags = ["C/AL","Readability"]
+categories = ["Best Practice"]
++++
+There should be no unnecessary separators.
+
+Bad code
+
+```al
+IF Customer.FINDFIRST THEN;;
+```
+
+
+Good code
+
+```al
+IF Customer.FINDFIRST THEN;
+```
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
new file mode 100644
index 00000000..181b2067
--- /dev/null
+++ b/content/docs/NAVPatterns/3-cal-coding-guidelines/readability/unnecessary-truefalse/index.md
@@ -0,0 +1,34 @@
++++
+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.
+
+Bad code
+
+```al
+IF IsPositive() = TRUE THEN
+```
+
+
+Good code
+
+```al
+IF IsPositive THEN
+```
+
+
+Bad code
+
+```
+IF Complete <> TRUE THEN
+```
+
+
+Good code
+
+```al
+IF NOT Complete THEN
+```
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
new file mode 100644
index 00000000..f24f9358
--- /dev/null
+++ b/content/docs/NAVPatterns/3-cal-coding-guidelines/readability/variable-already-scoped/index.md
@@ -0,0 +1,41 @@
++++
+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.
+
+Bad code
+
+```al
+ReturnRcptHeader.SETRANGE(ReturnRcptHeader."Return Order No.","Document No.");
+```
+
+
+Good code
+
+```al
+ReturnRcptHeader.SETRANGE("Return Order No.","Document No.");
+```
+
+
+Bad code
+
+```al
+WITH ChangeLogSetupTable DO BEGIN
+ ...
+ IF ChangeLogSetupTable.DELETE THEN
+ ...
+END;
+```
+
+Good code
+
+```al
+WITH ChangeLogSetupTable DO BEGIN
+ ...
+ IF DELETE THEN
+ ...
+END;
+```
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
new file mode 100644
index 00000000..a678e0a3
--- /dev/null
+++ b/content/docs/NAVPatterns/3-cal-coding-guidelines/readability/variable-naming/index.md
@@ -0,0 +1,71 @@
++++
+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.
+
+A variable must begin with a capital letter.
+
+Blanks, periods, and other characters (such as parentheses) that would make quotation marks around a variable necessary must be omitted.
+
+If a variable is a compound of two or more words or abbreviations, each word or abbreviation should begin with a capital letter.
+
+Bad code
+
+```al
+...
+ WIPBuffer@1002 : Record 1018
+...
+OBJECT Table Job WIP Buffer
+```
+
+Good code
+
+```al
+...
+ JobWIPBuffer@1002 : Record 1018
+...
+OBJECT Table Job WIP Buffer
+```
+
+Bad code
+
+```al
+...
+ Postline@1004 : Codeunit 12;
+...
+OBJECT Codeunit Gen. Jnl.-Post Line
+```
+
+Good code
+
+```al
+...
+ GenJnlPostLine@1004 : Codeunit 12;
+...
+OBJECT Codeunit Gen. Jnl.-Post Line
+```
+
+Bad code
+
+```al
+LOCAL PROCEDURE HandleCustDebitCredit@17(...;"Amount (LCY)"@1001 : Decimal;...);
+BEGIN
+ IF ((... ("Amount (LCY)" \> 0)) ...) OR
+ ((... ("Amount (LCY)" < 0)) ...)
+ THEN BEGIN
+ ...
+```
+
+Good code
+
+```al
+LOCAL PROCEDURE HandleCustDebitCredit@17(...;AmountLCY@1001 : Decimal;...);
+BEGIN
+ IF ((... (AmountLCY \> 0)) ...) OR
+ ((... (AmountLCY < 0)) ...)
+ THEN BEGIN
+ ...
+```
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
new file mode 100644
index 00000000..d9b7cc08
--- /dev/null
+++ b/content/docs/NAVPatterns/3-cal-coding-guidelines/readability/variables-declarations-order/index.md
@@ -0,0 +1,21 @@
++++
+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.
+
+Bad code
+
+```al
+StartingDateFilter@1002 : Text[30];
+Vend@1003 : Record 23;
+```
+
+Good code
+
+```al
+Vend@1003 : Record 23;
+StartingDateFilter@1002 : Text[30];
+```
diff --git a/content/docs/NAVPatterns/3-cal-coding-guidelines/ux/_index.md b/content/docs/NAVPatterns/3-cal-coding-guidelines/ux/_index.md
new file mode 100644
index 00000000..cc4c977b
--- /dev/null
+++ b/content/docs/NAVPatterns/3-cal-coding-guidelines/ux/_index.md
@@ -0,0 +1,11 @@
++++
+title = "UX"
+weight = 1390
+tags = ["C/AL"]
+categories = ["Best Practice"]
++++
+## C/AL Coding Guidelines
+
+## **User eXperience**
+
+Find the C/AL guidelines by expanding the menu in the left.
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
new file mode 100644
index 00000000..863d0609
--- /dev/null
+++ b/content/docs/NAVPatterns/3-cal-coding-guidelines/ux/actions-images/index.md
@@ -0,0 +1,24 @@
++++
+title = "Actions - Images"
+weight = 200
+tags = ["C/AL"]
+categories = ["Best Practice"]
++++
+All actions must have an image assigned to them.
+
+Bad code
+
+```al
+{ 7 ;1 ;Action ;
+ CaptionML=ENU=Customer - &Balance;
+ RunObject=Report 121 }
+```
+
+Good code
+
+```al
+{ 7 ;1 ;Action ;
+ CaptionML=ENU=Customer - &Balance;
+ RunObject=Report 121 }
+ Image=Report }
+```
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
new file mode 100644
index 00000000..155b747d
--- /dev/null
+++ b/content/docs/NAVPatterns/3-cal-coding-guidelines/ux/confirm/index.md
@@ -0,0 +1,23 @@
++++
+title = "CONFIRM"
+weight = 380
+tags = ["C/AL"]
+categories = ["Best Practice"]
++++
+Always end CONFIRM with a question mark.
+
+Bad code
+
+```al
+ChangeAllOpenedEntriesQst@1000 : TextConst 'ENU=Do you want to change all open entries for every customer and vendor that are not blocked';
+...
+IF CONFIRM(ChangeAllOpenedEntriesQst,TRUE) THEN
+```
+
+Good code
+
+```al
+ChangeAllOpenedEntriesQst@1000 : TextConst 'ENU=Do you want to change all open entries for every customer and vendor that are not blocked?';
+...
+IF CONFIRM(ChangeAllOpenedEntriesQst,TRUE) THEN
+```
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
new file mode 100644
index 00000000..1f87ff3a
--- /dev/null
+++ b/content/docs/NAVPatterns/3-cal-coding-guidelines/ux/fielderror/index.md
@@ -0,0 +1,23 @@
++++
+title = "FIELDERROR"
+weight = 590
+tags = ["C/AL"]
+categories = ["Best Practice"]
++++
+Never use FIELDERROR with a period as it is automatically inserted.
+
+Bad code
+
+```al
+InvalidValue@1025 : TextConst 'ENU=is invalid.';
+...
+Cust.FIELDERROR("No.",InvalidValue);
+```
+
+Good code
+
+```al
+InvalidValue@1025 : TextConst 'ENU=is invalid';
+...
+Cust.FIELDERROR("No.",InvalidValue);
+```
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
new file mode 100644
index 00000000..93bce63a
--- /dev/null
+++ b/content/docs/NAVPatterns/3-cal-coding-guidelines/ux/message-and-error/index.md
@@ -0,0 +1,23 @@
++++
+title = "MESSAGE and ERROR"
+weight = 790
+tags = ["C/AL"]
+categories = ["Best Practice"]
++++
+Always end MESSAGE or ERROR with a period.
+
+Bad code
+
+```al
+CustIsBlockedErr@1025 : TextConst 'ENU=You cannot %1 this type of document when Customer %2 is blocked with type %3';
+...
+ERROR(CustIsBlockedErr,...);
+```
+
+Good code
+
+```al
+CustIsBlockedErr@1025 : TextConst 'ENU=You cannot %1 this type of document when Customer %2 is blocked with type %3.';
+...
+ERROR(CustIsBlockedErr,...);
+```
diff --git a/content/docs/NAVPatterns/4-get-involved/_index.md b/content/docs/NAVPatterns/4-get-involved/_index.md
new file mode 100644
index 00000000..43d27a4a
--- /dev/null
+++ b/content/docs/NAVPatterns/4-get-involved/_index.md
@@ -0,0 +1,43 @@
++++
+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.**
+---
+
+**Spread the info**
+
+NAV Design Patterns are excellent materials for training and knowledge transfer. In your company, you can help new developer get to speed with NAV by recommending them to read and then present to the team one of the most common patterns: [No. Series][anchor0], [Setup Table][anchor1] and definitely [Hooks ][anchor2]which will be a great investment in reducing your upgrade time. The more experienced developers can read directly the newer patterns, like [Surrogate Key][anchor3], [Easy Update][anchor4], [Totals on Subpages][anchor5], [Using Queries instead of nested loops][anchor6] etc.
+
+**Become a NAV Design Pattern author**
+
+You have a pattern that you have used successfully? You have ideas on new patterns? You've found some existing design patterns which are used in the product but nobody (except a few) knows how it works, but you find it worth it to explain it for the NAV C/AL developers out there?
+
+Send your pattern idea to [Bogdana Botez][anchor7] as a private message on the community webpage. Once your first pattern is ready, we will review it as a team, and when signed off, you get author permissions on the Wiki site and from then on, you can continue publishing and editing existing patterns. However, only publish on the Wiki materials that we had signed-off (we don't have moderation capabilities yet, so we count on each author to only make meaningful/agreed changes).
+
+You and your company also get credit by being mentioned on the pattern and also on our patterns authors page.
+
+Once you have the idea, writing it down shouldn't take long. You will be helped by adopting [the template ][anchor8]that we've used.
+
+**Remember the rules**
+
+When handling design patterns, content quality is critical. We are trying our best to only publish content that is correct, relevant and has been reviewed by multiple developers. Therefore, we review and sign-off all patterns before publishing them (except for the videos made prior to 2015). All text content found on this Wiki and on the NAV Team Blog has been through one, usually multiple iterations of review. If you find something to correct, please comment on the pattern or contact [Bogdana Botez][anchor9], and we will review and update it.
+
+We are working on creating a set of rules, which would help keeping the content clean and the project on the correct track. [Find the rules here][anchor10].
+
+
+
+[anchor0]: /navpatterns/1-patterns/no-series/ "No. Series"
+[anchor1]: /navpatterns/1-patterns/singleton/singleton-table/setup-table/ "Setup Table"
+[anchor2]: /navpatterns/1-patterns/hooks/ "Hooks"
+[anchor3]: /navpatterns/1-patterns/implementation-of-surrogate-keys-using-autoincrement-pattern/
+[anchor4]: /navpatterns/1-patterns/easy-update-of-setup-or-supplementary-information/
+[anchor5]: /navpatterns/1-patterns/totals-and-discounts-on-subpages-sales-and-purchases/
+[anchor6]: /navpatterns/1-patterns/queries/use-queries-to-replace-nested-loops/
+[anchor7]: /members/bogdana-botez/default.aspx
+[anchor8]: /navpatterns/4-get-involved/template-for-writing-nav-design-patterns/
+[anchor9]: /members/bogdana-botez/default.aspx "Bogdana Botez"
+[anchor10]: /navpatterns/4-get-involved/code-of-conduct/ "Find the rules here"
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
new file mode 100644
index 00000000..76927c3e
--- /dev/null
+++ b/content/docs/NAVPatterns/4-get-involved/code-of-conduct/index.md
@@ -0,0 +1,13 @@
++++
+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.
+
+1. Only use materials published in text on the NAV Design Patterns Wiki site. If you received from us, any unpublished materials, please observe that they are subject to change and have not been approved for external use.
+2. When referencing a NAV Design Pattern, you must remember to also reference its author and company where the author is employed. You will find the author and his/her company at the beginning of each pattern, under the title.
+3. When referencing a NAV Design Patterns project, you must make it clear that this is a community project, driven by Microsoft, with multiple developers involved from both Microsoft and the community.
+4. When using published text content of NAV Design Patterns, do not alter the text in any way that was published on the Wiki site, unless is has been reviewed by the patterns team and signed off by someone at Microsoft in writing.
+5. If you have other materials which have not received explicit signoff in writing from me, where I have specifically stated that they are valid design patterns ready for publishing, please do not name them "NAV Design Patterns" (or anything similar). You are free to use your own content, but do not associate it in any way with NAV Design Patterns unless it is signed off in writing.
+6. If you do choose to use your own content, you must make it clear that it is not a NAV Design Pattern.
diff --git a/content/docs/NAVPatterns/4-get-involved/patterns-authors/index.md b/content/docs/NAVPatterns/4-get-involved/patterns-authors/index.md
new file mode 100644
index 00000000..104b6b21
--- /dev/null
+++ b/content/docs/NAVPatterns/4-get-involved/patterns-authors/index.md
@@ -0,0 +1,168 @@
++++
+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.
+
+Project administrator: [Bogdana Botez][anchor1].
+
+Authors (in alphabetical order):
+
+* Abshishek Ghosh, Microsoft (4 patterns)
+ * Using Query Objects to Detect Duplicates
+ * Blocked Entity
+ * Single-Record (Setup) Table
+ * Temporary Dataset Report
+
+* Anders Larsen, Microsoft (1 pattern)
+
+* Easy Update of Setup or Supplementary Information
+
+* Andreas Moth, Microsoft (1 pattern)
+
+* Anti-pattern: Reusable bugs
+
+* Bogdan Sturzoiu, Microsoft (4 patterns)
+
+* Feature Localization for Data Structures
+* Copy Document
+* Using C/AL Query Objects Instead of Nested Loops
+* Data-Driven Blocked Entity
+
+* Bogdana Botez, Microsoft (18 patterns)
+
+* Silent File Upload and Download
+* Standard Journal
+* No. Series
+* Data Model Proxy
+* Journal Error Processing
+* Journal Template-Batch-Line
+* Multilanguage Application Data
+* SELECT DISTINCT using Queries
+* Anti-patterns: reusable bugs
+* Sensitive Data Encapsulation
+* Data Encryption
+* Single Point of Access
+* Masked Text
+* SSL in NAV
+* Singleton
+* Singleton Codeunit
+* Anti-Patterns in NAV Upgrade
+* Cue table
+
+* Carlos Raul Garcia, Microsoft (1 pattern)
+
+* Anti-Patterns in NAV Upgrade
+
+* Ciprian Iordache, Microsoft (2 patterns)
+
+* Activity Log
+* Totals and Discounts on Subpages (Sales and Purchases)
+
+* David Bastide, Microsoft (3 patterns)
+* Notification Lifecycle Management pattern
+* Data Migration Façade
+* Extending the Role Center Headlines
+
+* Elly Nkya, Microsoft (2 patterns)
+
+* Singleton Table
+* Anti-patterns: reusable bugs
+
+* Eric Wauters (waldo), iFacto, PRS (6 patterns)
+
+* Hooks
+* Posting Routine - Select Behaviour
+* Variant Facade
+* Argument Table
+* Discovery Event
+* Anti-patterns: reusable bugs
+
+* Gary Winter, agiles (1 pattern)
+ * Variant Façade
+
+* Henrik Langbak, Kim Ginnerup, Bording Data A/S (2 patterns)
+
+* Currently Active Record
+* Released Entity
+
+* Jan Hoek, IDYN (2 patterns)
+
+* Conditional Cascading Update
+* Setup Specificity Fallback
+
+* Jesper Schulz, Microsoft (1 pattern)
+
+* Error Message Processing, part I
+
+* Martin Dam, Microsoft (1 pattern)
+
+* Multi-File Download
+
+* Mike Borg Cardona, Microsoft (1 pattern)
+
+* Creating URLs to NAV Clients
+
+* Mostafa Balat, Microsoft (3 patterns)
+
+* .NET Exception Handling
+* Cached Web Service Calls
+* Try Method
+
+* Nikola Kukrika, Microsoft (7 patterns)
+
+* Totals and Discounts on Subpages (Sales and Purchases)
+* Create Data from Templates
+* Argument Table
+* Instructions in UI
+* Creating Custom Charts
+* Variant Façade
+* Anti-patterns: reusable bugs
+
+* Nikolai L'Estrange, from TVision Technology Ltd. in the UK_
+_ (2 patterns)
+
+* Observer
+* Cross Session Events
+
+* Raed Selim, Microsoft (1 pattern)
+ * Product Name
+
+* Soumya Dutta (2 patterns)
+ * In-context notifications
+ * Data Migration Façade
+
+* Søren Klemmensen, 360 Visibility, PRS (2 patterns)
+
+* Master Data
+* Surrogate Key
+
+* Tim Grant, Trendmicro (1 pattern)
+
+* Read Once Setup Record
+* Report Selector (this pattern was started by 2 workgroups by multiple authors, but finalized and corrected by Tim).
+
+* Xavier Garonnat, knk Ingénierie (1 pattern)
+
+* Document
+
+They have also invested their time and energy in this project:
+
+* Eva Dupont, Microsoft - our publisher on MSDN NAV Team Blog.
+* Kurt Juvyns, Microsoft - coordinator of pattern videos
+
+Pattern evangelists:
+
+* Eric Wauters (waldo), iFacto, PRS
+* Gary Winter, agiles, PRS
+* Mark Brummel, Brummel Dynamics Services, PRS
+* Mike Doster, Mergetool
+* Søren Klemmensen, 360 Visibility, PRS
+
+And last but not least, we have collaborated with Plataan who hired Eric Wauters from ifacto and Mark Brummel from Brummel Dynamics Services and PRS, to publish on video some of our patterns.
+
+
+
+[anchor0]: /navpatterns/4-get-involved/
+[anchor1]: /members/bogdana-botez/default.aspx "NAV Design Patterns project administrator"
diff --git a/content/docs/NAVPatterns/4-get-involved/template-for-writing-nav-design-patterns/PatternLogo.png b/content/docs/NAVPatterns/4-get-involved/template-for-writing-nav-design-patterns/PatternLogo.png
new file mode 100644
index 00000000..86321ea8
Binary files /dev/null and b/content/docs/NAVPatterns/4-get-involved/template-for-writing-nav-design-patterns/PatternLogo.png differ
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
new file mode 100644
index 00000000..a47abbd6
--- /dev/null
+++ b/content/docs/NAVPatterns/4-get-involved/template-for-writing-nav-design-patterns/index.md
@@ -0,0 +1,60 @@
++++
+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).
+
+<_Your name here in italics, plus your company name_\>
+
+## **<\>**
+
+Short, descriptive and easy to remember.
+
+## **Pattern Logo**
+
+Black & white, no text on it.
+
+[![ ][image0]][anchor0]
+
+**Context**: Sets the stage where the pattern takes place. 1-2 sentences.
+
+**Problem**: What happens before this pattern is used? How can it go wrong? 1-5 lines.
+
+**Forces:** (explain why the problem is difficult to solve; state the considerations that must be taken into account when choosing a solution to a problem)
+
+* <<**Force 1: **short description (What is the impact of not using this pattern? Or using only partially?) \>\>
+* <<**Force 2: **short description \>\>
+* ...
+
+**Solution:** 1-2 sentences. The full description will come below.
+
+<\>
+
+<\>
+
+**Usage**: <\>
+
+<\>
+
+<\>
+
+**Benefits:**
+
+* **<< Benefit 1: **solves Force 1, short description\>\>
+* **<< Benefit 2: **solves Force 2, short description\>\>
+* ...
+
+**Consequences:**
+
+* **<\>**
+* **...**
+
+**List of references**
+
+
+
+[anchor0]: PatternLogo.png
+
+
+[image0]: PatternLogo.png
diff --git a/content/docs/NAVPatterns/_index.md b/content/docs/NAVPatterns/_index.md
new file mode 100644
index 00000000..22ecc24f
--- /dev/null
+++ b/content/docs/NAVPatterns/_index.md
@@ -0,0 +1,17 @@
++++
+title = "NAV Patterns Archive"
+weight = 4
+tags = ["C/AL"]
++++
+
+## About the archive
+
+This section of the site is a careful reproduction of the content of the Original Microsoft Community NAV Design Patterns project, created with permission.
+
+## Reading the archive
+
+Bear in mind, many of the style and formatting guidelines in this section have been brought forward into:
+- The automatic formatting provided by the AL Extension
+- The Code Analyzers
+
+Additionally, a variety of topics around the Windows Client and DotNet are outdated, and should only be used for either reference or if working in older environments.
\ No newline at end of file
diff --git a/content/docs/NAVPatterns/patterns/_index.md b/content/docs/NAVPatterns/patterns/_index.md
new file mode 100644
index 00000000..527ceb23
--- /dev/null
+++ b/content/docs/NAVPatterns/patterns/_index.md
@@ -0,0 +1,12 @@
+---
+title: "1. Patterns"
+weight: 110
+tags: ["C/AL"]
+categories: ["Pattern"]
+description: >
+ Patterns described to be used with Microsoft Dynamics NAV
+---
+
+{{% alert title="Warning" color="warning" %}}
+Please note that these patterns may not be up-to-date with the patterns for AL and Business Central Development.
+{{% /alert %}}
diff --git a/content/docs/NAVPatterns/patterns/activity-log/Activity-Log-NAV.jpg b/content/docs/NAVPatterns/patterns/activity-log/Activity-Log-NAV.jpg
new file mode 100644
index 00000000..3e79a015
Binary files /dev/null and b/content/docs/NAVPatterns/patterns/activity-log/Activity-Log-NAV.jpg differ
diff --git a/content/docs/NAVPatterns/patterns/activity-log/Activity-Log.jpg b/content/docs/NAVPatterns/patterns/activity-log/Activity-Log.jpg
new file mode 100644
index 00000000..eb67c90e
Binary files /dev/null and b/content/docs/NAVPatterns/patterns/activity-log/Activity-Log.jpg differ
diff --git a/content/docs/NAVPatterns/patterns/activity-log/index.md b/content/docs/NAVPatterns/patterns/activity-log/index.md
new file mode 100644
index 00000000..f6acd62a
--- /dev/null
+++ b/content/docs/NAVPatterns/patterns/activity-log/index.md
@@ -0,0 +1,118 @@
++++
+title = "Activity Logs"
+weight = 210
+tags = ["C/AL"]
+categories = ["Pattern"]
++++
+_Originally by Ciprian Iordache at Microsoft Development Center Copenhagen_
+
+## Activity Log
+
+**Abstract**
+
+The Activity Log pattern tracks execution of activities. This is a Dynamics NAV specific implementation of the [Audit Log][anchor0] pattern.
+
+[![ ][image0]][anchor1]
+
+**Problem**
+
+In general, integrating with external systems can be very challenging, due to the complexity of the situation -- connectivity issues, asynchronous operations, user errors, etc. These challenges require sometimes re-trying several times, polling the external system, re-send/re-get data as all these activities can succeed but can very well fail.
+
+Similar challenges exist in situations where a lengthy, complex task, composed of different steps is to be executed by various people in various timeframes. In case of errors (but sometimes also in case of success) there will be a need to track these activities to see what happened and the actual person which did a specific step.
+
+In all these cases, we need to be able to troubleshoot.
+
+A tracking/logging functionality could be implemented for each activity/step separately, but this would lead to code duplication and problems in maintaining the code in future.
+
+In NAV there is already the Change Log functionality which can record all the data changes that have been done to specific tables, specific fields. However, this functionality is not available for activities. Also, there are few places where separate logging/tracking implementations were done but the current pattern proposes an unified, central way of data recording and enables the user to track all/most of the activities.
+
+**Solution**
+
+The Activity Log pattern tracks specific outcome of the activities, in order to be able to assess what went wrong/fine or who performed a specific activity.
+
+Activity Log pattern
+
+* records the activity and its outcome (error or success messages)
+* assembles all messages in one central view and presents them to the user filtered for the specific activity and ordered in reverse chronological order.
+
+Figure below illustrates the how the Activity Log manifests in the UI. The figure shows a part of an activity log for a posted document that was sent to the document exchange service and illustrates both successful and failed activities.
+
+[![ ][image1]][anchor2]
+
+This functionality is implemented in the following way - Activity Log table (TAB710) contains a simple function that allows you to log the result of a task or activity:
+
+ActivityLog.LogActivity(ContextRecordID,ActivityLog.Status::Failed,ContextDescription,ActivityDescription,ActivityMessage);
+
+Similar to TAB700 for Error Messaging, the Activity Log table contains a RECORDID that is a link to the parent/context entity. That permits the Activity Log to be used in a generic way, for any kind of entities (tables) and it also permits filtering the data to a specific related entity only before being presenting to the user.
+
+The following parameters should be provided to the function:
+
+* RecordID: The record/context for which the activity is logged
+* Status: The task/activity outcome
+* Descriptions/Messages: fields that will clearly describe the state and outcome of the task
+
+To show the log, add a page action, with the caption including the name " Log" and link it to the image named "Log":
+
+```AL
+{ ;1 ;Action ;
+ Name=ActivityLog;
+ CaptionML=ENU='Activity Log';
+ ToolTipML=ENU='View the status and any errors if the document was sent as an electronic document or OCR file through the document exchange service.';
+ ApplicationArea=#Basic,#Suite;
+ Image=Log;
+ OnAction=
+ VAR
+ ActivityLog@1000 : Record 710;
+ BEGIN
+ ActivityLog.ShowEntries(RECORDID);
+ END;
+ }
+```
+
+
+**NAV usages**
+
+In Dynamics NAV 2016, there is a new feature for sending documents in electronic format to a document exchange service. In this case, sending documents requires multiple steps as it is an asynchronous activity and as such, in order to keep track of what's happening and when the Activity Log functionality was used. That offers later the possibility to see who sent and when a document was sent, when it was dispatched, if any dispatch errors and how many tries have been made until the document was finally dispatched or rejected.
+
+So as usages in NAV 2016, we have the document exchange and OCR features plus the related posted documents involved in the document exchange feature.
+
+* COD1294.TXT
+* COD1410.TXT
+* PAG1270.TXT
+* PAG1275.TXT
+* PAG143.TXT
+* PAG144.TXT
+* PAG189.TXT
+* TAB112.TXT
+* TAB114.TXT
+* TAB130.TXT
+
+**Ideas for improvement**
+
+Replace the scattered similar functionality (as mentioned above, we have several places having close functionality or similar requirements) with this new pattern.
+
+**Consequences**
+
+* Use with caution, similar to the Change Log functionality, as if the pattern will be used extensively in all the activities/operations within NAV, the table might become large containing many records and might cause some performance issues when presenting the data to the client (filtering on the specific activity).
+* Do not log private or confidential information (passwords, amounts, salaries, sensitive data), unless you are ok with this data to be showed to all users (even to users which normally would not have access to this data), thus overriding the permission sets.
+* Log only essential information (quality over quantity). Can the logged data be used to analyze the problem, or is it just junk data?
+
+**NAV Versions**
+
+Supported from NAV 2016
+
+**Related Topics**
+
+Error Message Processing -- provides a similar view and uses similar concepts: has a generic implementation (uses as link the same RECORDID feature) and uses same filtering functionality when displaying the data to the user.
+
+Audit Log -- as mentioned in the beginning, this pattern is a NAV specific implementation of the audit log pattern.
+
+
+
+[anchor0]: http://martinfowler.com/eaaDev/AuditLog.html
+[anchor1]: Activity-Log.jpg
+[anchor2]: Activity-Log-NAV.jpg
+
+
+[image0]: Activity-Log.jpg
+[image1]: Activity-Log-NAV.jpg
diff --git a/content/docs/NAVPatterns/patterns/argument-table/0218.Argument-Table-image.png b/content/docs/NAVPatterns/patterns/argument-table/0218.Argument-Table-image.png
new file mode 100644
index 00000000..a6d14a33
Binary files /dev/null and b/content/docs/NAVPatterns/patterns/argument-table/0218.Argument-Table-image.png differ
diff --git a/content/docs/NAVPatterns/patterns/argument-table/index.md b/content/docs/NAVPatterns/patterns/argument-table/index.md
new file mode 100644
index 00000000..b413e0c8
--- /dev/null
+++ b/content/docs/NAVPatterns/patterns/argument-table/index.md
@@ -0,0 +1,125 @@
++++
+title = "Argument Table"
+weight = 220
+tags = ["C/AL"]
+categories = ["Pattern"]
++++
+_Originally By Nikola Kukrika and waldo_
+
+### Abstract
+
+The Argument Table pattern is used to provide an extension point for adding new arguments without changing the signature. By grouping multiple arguments into a table the code becomes more readable (function signature and the usage of the function).
+
+[![ ][image0]][anchor0]
+
+### Problem
+
+In CAL overloading function signature is not supported. It is also not possible to provide default values for the function arguments.
+
+When an argument needs to be added to the function, the existing function needs to be extracted to a new method with an additional argument and the original function will call new method. This will cause an upgrade problem in the future, since the entire body of the method is replaced.
+
+Second commonly occurring problem is option duplication. In order to pass options often they are duplicated in the signature.
+
+The last problem that can be solved is high number of arguments. Functions with a high number of arguments are hard to understand. Having arguments grouped within the table with a meaningful name will improve readability and make code easier to understanding.
+
+Few examples of the bad implementations are as illustrated here:
+
+#### Bad example 1
+
+
+```AL
+PROCEDURE FillInVATReturnData@1200001(VAR DeclarationID@1200000 : Code [20];VAR LineID@1200001 : Code [20];VAR PeerID@1200002 : Code [20]; VAR DocumentNo@1200003: Code[20]; VAR NumberOfCopies@1200007: Integer; VAR Uploaded@1200004 : Boolean; VAR Correction@1200005 : Boolean; VAR HasValidationErr@1200006 : Boolean);
+```
+
+**Call**
+
+
+```AL
+FillInVATReturnData(NoSeries, NextLineID, CustomerID, DocumentNo, SingleCopy, ???, ??, ...., ...)
+```
+
+In this example the code is hard to read and understand. Adding an additional argument will require refactoring of the existing function. Each time a new argument is added a new function will be created.
+
+#### Bad example 2
+
+```AL
+LOCAL PROCEDURE GetTableSyncSetupW1@3(OldTableId@1002 : Integer; VAR UpgradeTableId@1001 : Integer; VAR TableUpgradeMode@1000 : 'Check, Copy, Move, Force') : Boolean;
+BEGIN
+ CASE OldTableId OF
+ DATABASE::"Sales Header":
+ SetTableSyncSetup(0,TableUpgradeMode::Check,UpgradeTableId,TableUpgradeMode);
+ DATABASE::"Posting Exch. Column Def":
+ SetTableSyncSetup(104025,TableUpgradeMode::Copy,UpgradeTableId,TableUpgradeMode);
+ DATABASE::"Payment Export Data":
+ SetTableSyncSetup(0,TableUpgradeMode::Force,UpgradeTableId,TableUpgradeMode);
+ ELSE
+ EXIT(FALSE);
+ END;
+ EXIT(TRUE);
+END;
+```
+
+In this example each time a new argument is added all function calls will have to be updated. Option is duplicated in the signature, which will cause issues if a new option is defined or the existing options are renamed.
+
+### Solution
+
+By grouping the arguments within the table it is possible to add additional argument and reuse it where it is needed without changing the signature.
+
+Multiple parameters are grouped within the single object with a meaningful name so the code becomes more readable.
+
+It is possible to assign default values and to have the code validation.
+
+Argument table should preferably be a temporary table since the implementation is simpler.
+
+The examples of usages addressing problems shown above are:
+
+#### Good example 1
+
+New table
+```AL
+TAB 50003 VAT Return Data
+PROCEDURE FillInVATReturnData@1200001(VAR VATReturnData@1200000 : Record 50003);
+
+VATReturnData.INIT;
+VATReturnData.NumberOfCopies := GetDefaultNumberOfCopies;
+VATReturnData.Uploaded := FALSE;
+
+FillInVATReturnData(VATReturnData);
+```
+
+By introducing an argument table, code is much more readable since there is a single argument for a function. It is easy to see which arguments are passed in and which are modified in a function.
+
+#### Good example 2
+
+Good example
+```AL
+PROCEDURE GetTableSyncSetupW1@3(VAR TableSynchSetup@1000 : Record 2000000135);
+BEGIN
+ SetTableSyncSetup(DATABASE::"Sales Header",0,TableSynchSetup.Mode::Check);
+ SetTableSyncSetup(DATABASE::"Posting Exch. Column Def",104025,TableSynchSetup.Mode::Copy);
+ SetTableSyncSetup(DATABASE::"Payment Export Data",0,TableSynchSetup.Mode::Force);
+END;
+```
+
+Option definition is not encapsulated within the table. Arguments are grouped and we can add additional arguments without the need to change the signature.
+
+### Downsides
+
+You need to create one more table
+
+Complex types can't be embedded as fields in tables (cannot have a record field type etc).
+
+### NAV Usages
+
+Upgrade Codeunits
+
+### Related Patterns
+
+Posting Routine, Select behavior: Setting fields on existing records in order not to change the signatures.
+
+
+
+[anchor0]: 0218.Argument-Table-image.png
+
+
+[image0]: 0218.Argument-Table-image.png
diff --git a/content/docs/NAVPatterns/patterns/blocked-entity/2260.BlockedEntityPattern.png b/content/docs/NAVPatterns/patterns/blocked-entity/2260.BlockedEntityPattern.png
new file mode 100644
index 00000000..d4b6e853
Binary files /dev/null and b/content/docs/NAVPatterns/patterns/blocked-entity/2260.BlockedEntityPattern.png differ
diff --git a/content/docs/NAVPatterns/patterns/blocked-entity/3056.BlockedEntityPattern_5F00_5F00_5F00_Option.png b/content/docs/NAVPatterns/patterns/blocked-entity/3056.BlockedEntityPattern_5F00_5F00_5F00_Option.png
new file mode 100644
index 00000000..ffba43d8
Binary files /dev/null and b/content/docs/NAVPatterns/patterns/blocked-entity/3056.BlockedEntityPattern_5F00_5F00_5F00_Option.png differ
diff --git a/content/docs/NAVPatterns/patterns/blocked-entity/8637.BlockedEntityPattern_5F00_5F00_5F00_Boolean.png b/content/docs/NAVPatterns/patterns/blocked-entity/8637.BlockedEntityPattern_5F00_5F00_5F00_Boolean.png
new file mode 100644
index 00000000..817956c4
Binary files /dev/null and b/content/docs/NAVPatterns/patterns/blocked-entity/8637.BlockedEntityPattern_5F00_5F00_5F00_Boolean.png differ
diff --git a/content/docs/NAVPatterns/patterns/blocked-entity/_index.md b/content/docs/NAVPatterns/patterns/blocked-entity/_index.md
new file mode 100644
index 00000000..402ba137
--- /dev/null
+++ b/content/docs/NAVPatterns/patterns/blocked-entity/_index.md
@@ -0,0 +1,108 @@
++++
+title = "Blocked Entity"
+weight = 270
+tags = ["C/AL"]
+categories = ["Pattern"]
++++
+_Originally by Abhishek Ghosh at Microsoft Development Center Copenhagen_
+
+## Abstract
+
+The Blocked Entity is used when it is required to stop transactions for an entity (mostly master data), temporarily or permanently.
+
+[![ ][image0]][anchor0]
+
+## Description
+
+To block entities through metadata, read this pattern. To do the same thing through data, read [Data Driven Blocked Entity pattern][anchor1].
+
+The business entity holds a state that controls if a given transaction is allowed. The state is used by the logic controlling transactions. The change of state could either be temporary or permanent.
+
+An example of a temporary halt is when a retail chain selling items has received lot of complaints about an item, and the company wants to stop all transactions, both purchase and sale, with that item until the dealer has clarified the issue with his supplier and possibly received a replacement for the defective stock. Another common example is during counting the physical inventory using cycle counting where the counting is done in one section of a warehouse at a time, so that the regular operations can continue in the other parts of the warehouse. In these situations, it is necessary to block all transactions, such as picks and put-aways, for a bin while warehouse counting is in progress for that bin.
+
+In contrast, a permanent halt to transactions could be required when an item has become obsolete (or is about to become obsolete), and the company wants to stop further purchase or sale of the item. However, the company wants to maintain the transaction history of the item and, therefore, does not want to delete the item record.
+
+A simple design implementation of such requirements in Microsoft Dynamics NAV is to add a Blocked field in the entity table (and on the associated page). The implementation takes this state into the logic and checks for the value of this field in related transactions. For most simple scenarios, it is sufficient to have two states on the Blocked field, specifying whether it is allowed to perform transactions for the entity or not.
+
+In certain situations, however, there could be different levels of blocking. For example, the company could block all sales to a customer that has overdue payments, and the company does not want to allow transactions with this customer until the payments are received. In other situations, the customer may have raised objections about an invoice, and the company has decided not to generate new invoices for the customer until the issue has been resolved. However, the company does want to continue shipping goods to the customer so as not to impact the customer's operations. In these scenarios, it may be necessary to have multiple states on the Blocked field depending on the level of restriction that is needed.
+
+## Usage
+
+As mentioned in the previous section, there are two implementations depending on business requirements: The 2-state Boolean field for simple implementations and the multi-state option field for more complex requirements. The implementation flow is similar for both patterns, except how the validation is implemented. The following discusses the two scenarios one by one.
+
+### Boolean Implementation
+
+Add a Boolean field named Blocked in the table.
+
+In the relevant logic, add a condition to check the status of the Blocked flag. The cheapest way is to use a TESTFIELD:
+
+```AL
+.TESTFIELD(Blocked,FALSE);
+```
+
+Alternatively, you can throw a custom error message. However, you should only do that if the default error message thrown by TESTFIELD is not sufficient.
+
+### Option-Field Implementation
+
+Add an option field named Blocked in the table. The option values will reflect the different blocked states required by the company.
+
+Add this field on the card page (or on the List page if the entity does not have a card). As with the Boolean implementation, the convention is to add this field in the right-hand column in the General FastTab of the card page.
+
+Implement a function in the table that takes the transaction context as input and evaluates the Blocked field to decide whether the transaction should be allowed or not. Optionally, the function can be responsible for notifying the user and bubble up an error message straight away.
+
+Note: the option field assumes that only one of the multiple options can be active at a time. In other words, the options should be mutually exclusive.
+
+How not to use the option field in this case: if we want to block an item from sale and/or purchase, the 4 combined options would be **Block none** | **Block Sales** | **Block Purchases** | **Block Sales and Purchases**. This doesn't scale, because if now we need to block another transaction, the number of option would grow too fast. In this situations, it is better to use two Boolean fields: **Blocked Sale**: **true|false** and **Blocked Purchase: true|false**.
+
+A good example of usage would be for varying the behavior depending on the chosen option, for example by displaying a different error message depending on the reason an Item is blocked. In this case we can have the item **Not Blocked** | **Blocked due to defect** | **Blocked waiting for approval**, etc.
+
+## NAV Specific Example
+
+### Boolean Implementation
+
+[![ ][image1]][anchor2]
+
+An example of the Boolean implementation on the Item card.
+
+In codeunit 22 -- Item Jnl.-Post Line, the following lines of code have implemented a check based on the value of the Blocked field:
+
+```AL
+IF NOT CalledFromAdjustment THEN
+ Item.TESTFIELD(Blocked,FALSE);
+```
+### Option-Field Implementation
+
+[![ ][image2]][anchor3]
+
+An example of the option field implementation on the Customer card.
+
+The CheckBlockedCustOnDocs and CheckBlockedCustOnJnls functions in the Customer table are responsible for validating the Blocked state with respect to the input document type. These functions are invoked in several areas, such as posting routines, where a status check on the Blocked field is required. This is a good practice where the Blocked implementation gets more complex, as this encourages reuse and ensures uniformity of implementation.
+
+## NAV Usages
+
+Entities where the Blocked Entity has been implemented include:
+
+* Item
+* G/L Account
+* Customer
+* Vendor
+* Bin
+
+## Related Topics
+
+The [Released Entity][anchor4].
+
+{{% alert title="Note" %}}
+There was previously a video demonstration of this pattern, but it is no longer available.
+{{% /alert %}}
+
+[anchor0]: 2260.BlockedEntityPattern.png
+[anchor1]: /navpatterns/1-patterns/blocked-entity/data-driven-blocked-entity/
+[anchor2]: 8637.BlockedEntityPattern_5F00_5F00_5F00_Boolean.png
+[anchor3]: 3056.BlockedEntityPattern_5F00_5F00_5F00_Option.png
+[anchor4]: /navpatterns/1-patterns/released-entity/
+
+
+[image0]: 2260.BlockedEntityPattern.png
+[image1]: 8637.BlockedEntityPattern_5F00_5F00_5F00_Boolean.png
+[image2]: 3056.BlockedEntityPattern_5F00_5F00_5F00_Option.png
diff --git a/content/docs/NAVPatterns/patterns/blocked-entity/data-driven-blocked-entity/attention.jpg b/content/docs/NAVPatterns/patterns/blocked-entity/data-driven-blocked-entity/attention.jpg
new file mode 100644
index 00000000..bd5f1a88
Binary files /dev/null and b/content/docs/NAVPatterns/patterns/blocked-entity/data-driven-blocked-entity/attention.jpg differ
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
new file mode 100644
index 00000000..3e4413b3
--- /dev/null
+++ b/content/docs/NAVPatterns/patterns/blocked-entity/data-driven-blocked-entity/index.md
@@ -0,0 +1,113 @@
++++
+title = "Data Driven Blocked Entity"
+weight = 470
+tags = ["C/AL"]
+categories = ["Pattern"]
++++
+_Written by Bogdan Andrei Sturzoiu, at Microsoft Development Center Copenhagen_
+
+## Abstract
+
+This pattern implements a generic mechanism for dynamically restricting and allowing usage of a record by the business process administrator.
+
+## Problem
+
+A NAV record can be used in a number of functionalities across the app. There are situations, however, when the administrator wants to restrict the consumption of such a record, as well as lift the restriction when it is no longer relevant.
+
+For example, a new customer record should not be used for posting documents until it is approved by the relevant approver.
+
+We could solve this by using the [Blocked Entity pattern][anchor1], but it requires database schema changes, which have an upgrade impact.
+
+The blocked entity pattern involves:
+
+1. Adding a "blocked" status field on the record (either a Boolean or in the more advanced cases, an option field refining the usage).
+2. Adding specific code for the record in every place where the restriction needs to be enforced.
+
+In contrast, the Data-driven Blocked Entity pattern involves adding a new record (data change) to mark the restriction, instead of adding a new field (metadata change).
+
+## Solution
+
+This pattern describes a generic mechanism of adding and lifting restrictions for any type of record.
+
+The restriction mechanism has the following elements:
+
+1. Adding a restriction record for a specific reason (e.g. the record requires approval), which will act as a surrogate key (unique identifier) for the restricted record. This can be implemented through a workflow response, or directly, by calling the Restriction Management codeunit function.
+2. Lifting the restriction when it is no longer necessary. Again, this can be done using a workflow response or directly by calling the dedicated function.
+3. Consuming the restriction in the places of interest for a specific purpose. This is an application feature that requires a call to the Restriction Management codeunit to check for restrictions.
+
+Currently, the restrictions are record-based and type-less. They act as simple tokens, and they have:
+
+* A reason (e.g. the record requires approval)
+* A purpose (e.g. the record cannot be posted).
+
+You must make sure to differentiate between the reason and the purpose. That is because the restriction can only be added once per record, but consumed in multiple places.
+
+
+
+## Example
+
+For example, we want to restrict posting Gen. Journal Lines if a customer has not been added in Account No. field.
+
+For this, the following components are needed:
+
+1. When a Gen. Journal Line is inserted, call RestrictRecordUsage in COD1550, either directly in the trigger or using an event subscriber.
+2. When you validate a Customer No. as Account no. and Customer as Account Type, lift the restrictions by calling AllowRecordUsage in COD1550\.
+3. The consumption of the restriction at posting is already implemented as an event in TAB81, OnCheckGenJournalLinePostRestrictions. No further action necessary.
+
+## NAV Usage
+
+All the approval workflows include a response that restricts usage of a record, and then, at the end of an approval loop, a response that allows the usage again by lifting the restriction. See responses "Add record restriction" and "Remove record restriction" implemented in COD1521\.[
+][anchor2]
+
+The code behind the "Add record restriction" workflow response:
+
+```AL
+RecRef.GETTABLE(Variant);
+Workflow.GET(WorkflowStepInstance."Workflow Code");
+RecordRestrictionMgt.RestrictRecordUsage(RecRef.RECORDID,STRSUBSTNO(RestrictUsageDetailsTxt,Workflow.Code,Workflow.Description));
+```
+
+The code behind the "Remove record restriction" response:
+
+```AL
+RecRef.GETTABLE(Variant);
+CASE RecRef.NUMBER OF
+ DATABASE::"Approval Entry":
+ BEGIN
+ RecordRestrictionMgt.AllowRecordUsage(RecRef.RECORDID);
+ RecRef.SETTABLE(ApprovalEntry);
+ RecRef.GET(ApprovalEntry."Record ID to Approve");
+ AllowRecordUsage(RecRef);
+ END;
+ DATABASE::"Gen. Journal Batch":
+ BEGIN
+ RecRef.SETTABLE(GenJournalBatch);
+ RecordRestrictionMgt.AllowGenJournalBatchUsage(GenJournalBatch);
+ END
+ ELSE
+ RecordRestrictionMgt.AllowRecordUsage(RecRef.RECORDID);
+END;
+```
+
+Notice how lifting a restriction for a Gen. Journal Batch involves lifting all the restrictions for the individual journal lines in the batch (hence the special branching of the code).
+
+
+
+## Consequences
+
+Currently, there can only be one restriction per record. There are no restriction types.
+
+In the future, a type field should be added to the restriction table, to allow adding restrictions for different purposes, and to refine their consumption. For example, a posting restriction might only be enforced for restrictions originating from approvals.
+
+## NAV Versions
+
+This pattern has been introduced in Dynamics NAV 2016\.
+
+
+
+[anchor0]: attention.jpg
+[anchor1]: /navpatterns/1-patterns/blocked-entity/
+[anchor2]: https://microsoft.sharepoint.com/teams/DynamicsNAV/Wiki/Nav%20Wiki%20Documents/NAV%20App%20Patterns/NAV%20App%20Patterns%20for%20Review/Data-Driven%20Blocked%20Entity.docx#_msocom_2
+
+
+[image0]: attention.jpg
diff --git a/content/docs/NAVPatterns/patterns/cached-web-service-calls/Cached_5F00_Web_5F00_Service_5F00_Calls_5F00_Diagram.png b/content/docs/NAVPatterns/patterns/cached-web-service-calls/Cached_5F00_Web_5F00_Service_5F00_Calls_5F00_Diagram.png
new file mode 100644
index 00000000..44cb3432
Binary files /dev/null and b/content/docs/NAVPatterns/patterns/cached-web-service-calls/Cached_5F00_Web_5F00_Service_5F00_Calls_5F00_Diagram.png differ
diff --git a/content/docs/NAVPatterns/patterns/cached-web-service-calls/index.md b/content/docs/NAVPatterns/patterns/cached-web-service-calls/index.md
new file mode 100644
index 00000000..fd64729e
--- /dev/null
+++ b/content/docs/NAVPatterns/patterns/cached-web-service-calls/index.md
@@ -0,0 +1,120 @@
++++
+title = "Cached Web Server Calls"
+weight = 290
+tags = ["C/AL"]
+categories = ["Pattern"]
++++
+_Originally by Mostafa Balat, Microsoft Development Center Copenhagen_
+
+## Abstract
+
+In a service-oriented deployment, web services are used to extend NAV's functionality and reach. Depending on how volatile this data is and the corresponding usage scheme, it is expected to be up-to-date within a pre-defined period of time (e.g. once a day).
+
+## Description
+
+When NAV is integrated with external services, then the user scenarios become dependent on the data and functions offered by such services. Eventually, there are different approaches through which the external data can be retrieved, stored and used.
+
+* Dynamic: either exposed by the external service itself or by a separate catalog that NAV can query.
+ * Advantage: data is always up-to-date
+ * Disadvantage: it requires constant connection to the data source.
+* Static: hard-coded in the database for the user to benefit from.
+ * Advantage: data is promptly available when needed.
+ * Disadvantage: if data changes at some point, it will require a maintenance effort, which exposes the business process to a risk of failure.
+* Cached: offered through an external service and gets pulled according to a pre-defined refresh rate or manually.
+ * Advantage: data is 'up-to-date' within the rules acceptable by the business process, without extra load on the network resources or the external service.
+ * Disadvantage: if data changes while the auto-refresh did not happen yet, the user may not have access to the latest data; however, the user can manually force a refresh of the data, if asked to do so.
+
+### When to Use It
+
+Offer data in lookups that were cached from an external service.
+
+### Diagram
+
+[![ ][image0]][anchor0]
+
+## Usage
+
+Set up an NAV feature to consume the data from an external service. Refresh the data on a pre-defined refresh rate (e.g. once a day) or when enforced by a power user or an admin. Cache the data in a table and offer it in lookups, as applicable.
+
+## NAV Specific Example
+
+### Overview
+
+**PAG1259 Bank Name - Data Conv. List** offers the required functionality to refresh and display the list of bank names needed to specify which file format to convert to. The page can be accessed from **PAG1260 Bank Data Conv. Service Setup** to display all available bank names. It is also used as a lookup on **PAG370 Bank Account Card**, where it offers a filtered view of the cached bank names based on the Country/Region Code field.
+
+If **PAG1259 Bank Name - Data Conv. List** is being open and the cached data is 'old', it refreshes the cache. The cached data is stored in **TAB1259 Bank Data Conv. Bank**. Meanwhile, the user has the chance to refresh the data using
+
+### Code Sample
+
+```AL
+OnInit=BEGIN
+ ShortTimeout := 5000;
+ LongTimeout := 30000;
+END;
+
+OnOpenPage=VAR
+ BankDataConvBank@1002 : Record 1259;
+ ImpBankListExtDataHndl@1000 : Codeunit 1289;
+ CountryRegionCode@1004 : Text;
+ HideErrors@1003 : Boolean;
+BEGIN
+ CountryRegionCode := IdentifyCountryRegionCode(Rec,GETFILTER("Country/Region Code"));
+ IF BankDataConvBank.ISEMPTY THEN BEGIN
+ ImpBankListExtDataHndl.GetBankListFromConversionService(HideErrors,CountryRegionCode,ShortTimeout);
+ EXIT;
+ END;
+ RefreshBankNamesOlderThanToday(CountryRegionCode,HideErrors,ShortTimeout);
+END;
+
+OnAction=VAR
+ ImpBankListExtDataHndl@1000 : Codeunit 1289;
+ FilterNotUsed@1001 : Text;
+ ShowErrors@1003 : Boolean;
+BEGIN
+ ShowErrors := TRUE;
+ ImpBankListExtDataHndl.GetBankListFromConversionService(ShowErrors,FilterNotUsed,LongTimeout);
+END;
+
+LOCAL PROCEDURE IdentifyCountryRegionCode@1(VAR BankDataConvBank@1002 : Record 1259;Filter@1000 : Text) : Text;
+VAR
+ CompanyInformation@1001 : Record 79;
+ BlankFilter@1003 : Text;
+BEGIN
+ BlankFilter := '''''';
+
+ IF Filter = BlankFilter THEN BEGIN
+ CompanyInformation.GET;
+ BankDataConvBank.SETFILTER("Country/Region Code",CompanyInformation."Country/Region Code");
+ EXIT(BankDataConvBank.GETFILTER("Country/Region Code"));
+ END;
+
+ EXIT(Filter);
+END;
+
+LOCAL PROCEDURE RefreshBankNamesOlderThanToday@5(CountryRegionCode@1000 : Text;ShowErrors@1002 : Boolean;Timeout@1004 : Integer);
+VAR
+ BankDataConvBank@1001 : Record 1259;
+ ImpBankListExtDataHndl@1003 : Codeunit 1289;
+BEGIN
+ IF CountryRegionCode <> '' THEN
+ BankDataConvBank.SETFILTER("Country/Region Code",CountryRegionCode);
+ BankDataConvBank.SETFILTER("Last Update Date",'<%1',TODAY);
+ IF BankDataConvBank.FINDFIRST THEN
+ ImpBankListExtDataHndl.GetBankListFromConversionService(ShowErrors,CountryRegionCode,Timeout);
+END;
+```
+
+## NAV Usages
+
+Bank name lookup on the Bank Account card for dynamically identifying the format to use to generate a bank-specific payment file.
+
+## Ideas for Improvement
+
+Expose the refresh rate through a setup table to make it easily configurable without changing the code.
+
+
+
+[anchor0]: Cached_5F00_Web_5F00_Service_5F00_Calls_5F00_Diagram.png
+
+
+[image0]: Cached_5F00_Web_5F00_Service_5F00_Calls_5F00_Diagram.png
diff --git a/content/docs/NAVPatterns/patterns/conditional-cascading-update/3124.T18_5F00_Name_5F00_OnValidate.png b/content/docs/NAVPatterns/patterns/conditional-cascading-update/3124.T18_5F00_Name_5F00_OnValidate.png
new file mode 100644
index 00000000..d6710dd7
Binary files /dev/null and b/content/docs/NAVPatterns/patterns/conditional-cascading-update/3124.T18_5F00_Name_5F00_OnValidate.png differ
diff --git a/content/docs/NAVPatterns/patterns/conditional-cascading-update/index.md b/content/docs/NAVPatterns/patterns/conditional-cascading-update/index.md
new file mode 100644
index 00000000..2b29989d
--- /dev/null
+++ b/content/docs/NAVPatterns/patterns/conditional-cascading-update/index.md
@@ -0,0 +1,40 @@
++++
+title = "Conditional Cascading Update"
+weight = 370
+tags = ["C/AL"]
+categories = ["Pattern"]
++++
+_Originally by Jan Hoek at IDYN_
+
+## Abstract
+
+The Conditional Cascading Update pattern is used to intelligently populate fields whose values depend on other field values. In this pattern description, the field triggering the update will be called "source field", and the depending field will be called "target field".
+
+## Description
+
+The value of one table field sometimes depends on the value of another field, typically following an application-defined transformation (note that we're talking about transformations of field values here. This has nothing to do with e.g. form transformation), such as conversion to uppercase, removal of certain characters etc.
+
+If the target field is non-editable, said transformation is usually the only way for the target field to receive new values, so no irreproducible information can be lost. However, if the target field is editable, the user may have cared enough to override the default (transformed) value, in which case revalidating the source field should not blindly replace the target field's value.
+
+## Usage
+
+In the OnValidate trigger of the source field, test if the target field value is either blank, or equal to the transformed value of the source field's previous contents. If it is, populate the target field's value with the transformed source field value. If it is not, do nothing (effectively preserving the value set by the user).
+
+## NAV Specific Example
+
+In the base application, this pattern can be found in Search Name/Search Description fields, which are updated with the uppercase value from the corresponding Name/Description field when the latter is validated, only if the Search Name/Description in question is currently blank, or equal to the (uppercase equivalent) of the previous contents of the Name/Description field.
+
+[![ ][image0]][anchor0]
+
+In this particular case, the transformation between source and target fields is implicit and due to the different data types of the fields (text vs. code). Note how the field triggers of the Search Name field itself do not contain any logic linked to this pattern.
+
+## Consequences
+
+There is a case when this pattern should not be used. If the target field is non-editable, this pattern will not add any value, since there won't be any user-overridden values to protect.
+
+
+
+[anchor0]: 3124.T18_5F00_Name_5F00_OnValidate.png
+
+
+[image0]: 3124.T18_5F00_Name_5F00_OnValidate.png
diff --git a/content/docs/NAVPatterns/patterns/copy-document/clip_5F00_image006.jpg b/content/docs/NAVPatterns/patterns/copy-document/clip_5F00_image006.jpg
new file mode 100644
index 00000000..874cea81
Binary files /dev/null and b/content/docs/NAVPatterns/patterns/copy-document/clip_5F00_image006.jpg differ
diff --git a/content/docs/NAVPatterns/patterns/copy-document/clip_5F00_image008.jpg b/content/docs/NAVPatterns/patterns/copy-document/clip_5F00_image008.jpg
new file mode 100644
index 00000000..6746d61f
Binary files /dev/null and b/content/docs/NAVPatterns/patterns/copy-document/clip_5F00_image008.jpg differ
diff --git a/content/docs/NAVPatterns/patterns/copy-document/clip_5F00_image010.jpg b/content/docs/NAVPatterns/patterns/copy-document/clip_5F00_image010.jpg
new file mode 100644
index 00000000..7216baff
Binary files /dev/null and b/content/docs/NAVPatterns/patterns/copy-document/clip_5F00_image010.jpg differ
diff --git a/content/docs/NAVPatterns/patterns/copy-document/clip_image002.gif-750x0.png b/content/docs/NAVPatterns/patterns/copy-document/clip_image002.gif-750x0.png
new file mode 100644
index 00000000..a447a4bf
Binary files /dev/null and b/content/docs/NAVPatterns/patterns/copy-document/clip_image002.gif-750x0.png differ
diff --git a/content/docs/NAVPatterns/patterns/copy-document/clip_image004.gif-750x0.png b/content/docs/NAVPatterns/patterns/copy-document/clip_image004.gif-750x0.png
new file mode 100644
index 00000000..35ebe222
Binary files /dev/null and b/content/docs/NAVPatterns/patterns/copy-document/clip_image004.gif-750x0.png differ
diff --git a/content/docs/NAVPatterns/patterns/copy-document/index.md b/content/docs/NAVPatterns/patterns/copy-document/index.md
new file mode 100644
index 00000000..98f1bd0b
--- /dev/null
+++ b/content/docs/NAVPatterns/patterns/copy-document/index.md
@@ -0,0 +1,121 @@
++++
+title = "Copy Document"
+weight = 390
+tags = ["C/AL"]
+categories = ["Pattern"]
++++
+_By Bogdan Sturzoiu at Microsoft Development Center Copenhagen_
+
+## Abstract
+
+The goal of the Copy Document pattern is to create a replica of an existing open or closed document (posted or not posted), by moving the lines and, optionally, the header information from the source document to a destination document.
+
+## Description
+
+Documents are widely used by most of our customers. Many times, a significant portion of these documents are similar to each other, either by sharing the same customer, vendor, type, or line structure. Being able to re-use a document as a base for creating a new one is therefore an important means of saving time.
+
+Other business scenarios require that a newly created document is applied to an existing document. For example, in returns management, a return order can be the reversal of an existing order and can therefore be copied from the original order. Other times, there is even a legal requirement to match the document to its source. For example, credit memos need to be applied to the originating Invoice.
+
+For these reasons, NAV supports the copying of documents as a method to re-use or link documents.
+
+The Copy Document functionality is used in the following situations:
+
+* The user wants to create a new open sales document (Quote, Order, Blanket Order, Invoice, Return Order, Credit Memo) based on an existing posted or non-posted sales document (Quote, Blanket Order, Order, Invoice, Return Order, Credit Memo, Posted Shipment, Posted Invoice, Posted Return Receipt, Posted Credit Memo).
+* The user wants to create a new open purchase document (Quote, Order, Blanket Order, Invoice, Return Order, Credit Memo) based on an existing posted or non-posted purchase document (Quote, Blanket Order, Order, Invoice, Return Order, Credit Memo, Posted Shipment, Posted Invoice, Posted Return Receipt, Posted Credit Memo).
+* The user wants to create a new production order (Simulated, Planned, Firm Planned or Released) based on an existing production order (Simulated, Planned, Firm Planned, Released or Finished).
+* The user wants to create a new assembly order based on an existing assembly document (Quote, Blanket Order, Order and Posted Order).
+* The user wants to create a new service contract or quote based on an existing service contract or quote.
+* The user wants to create all relevant return-related documents. For example, from a sales return order, the user can recreate the involved supply chain documentation, by copying the information upwards to a purchase return order (if the items need to be returned to the vendor), purchase order (if the items need to be reordered), and sales order (if the items need to be re-sent to the customer).
+
+**Note**
+
+* Not all to and from combinations are allowed. For example, you can only copy to open document types, since the posted documents are not editable.
+* The destination document needs to have the header fully created. For example, a Sales Order will need to have the Sell-To Customer No. populated.
+
+## Usage
+
+The Dynamics NAV application developer can take into account using the Copy Document design pattern when they have requirements such as:
+
+* To provide a quick and efficient way of moving content from a document to another.
+* To allow reusing the document history as a template for new documents.
+* To allow linking of documents that need to be applied to each other.
+
+The Copy Document pattern involves the following entities:
+
+1. Source document tables for document header and line. For example,Sales Header/Line.
+2. Destination document tables for document header and line.
+**Note:** The source document header/line and destination document header/line tables do not need to be the same. For example, you can copy a Sales Shipment Header/Lines into a Sales Header/Lines.
+
+3. Copy Document engine: COD6620, Copy Document Mgt.
+4. Copy Document report for a specific document type. The report requires the following parameters:
+ * Source Document Type
+ * Source Document No.
+ * Include Header (optional)
+ * Recalculate Lines (optional)
+
+Example: REP901, Copy Assembly Document
+
+[![ ][image0]][anchor0]
+
+## Usage Sequence
+
+**Precondition**: The user creates a new destination document Header, filling up the required information.
+
+**Step 1**: The user runs the Copy Document report (element no. 4), filling up the parameters:
+
+* Source Document Type
+* Source Document No.
+* Include Header and/or Recalculate Lines (not all Copy Document reports have these).
+
+**Step 2**: The report copies the information in the source tables (Header and Line) into the destination tables (Header and Line).
+
+**Post processing**: The user performs additional editing of the destination document.
+
+The sequence flow of the pattern is described in the following diagram.
+
+[![ ][image1]][anchor1]
+
+Example: Copy Sales Document for Credit Memos.
+
+In the standard version of Microsoft Dynamics NAV, the Copy Document functionality is implemented in the Sales Credit Memo window as shown in the following section.
+
+****
+
+**Precondition**: The user enters data in PAGE44, Sales Credit Memo.
+
+[![ ][image2]][anchor2]
+
+**Step 1**: The user runs REP292, Copy Sales Document from the Sales Credit Memo window, populating the required parameters. The Include Header and Recalculate Lines fields are selected.
+
+[![ ][image3]][anchor3]
+
+**Step 2**: The Sales Credit Memo window is populated with information from the source sales document.
+
+[![ ][image4]][anchor4]
+
+**Post processing**: The user can now do additional editing of the sales credit memo.
+
+# NAV Implementations
+
+1. Copy Sales Document (REP292)
+2. Copy Purchase Document (REP492)
+3. Copy Service Document (REP5979)
+4. Copy Assembly Document (REP901)
+
+{{< youtube aTiwroXwW0 >}}
+
+
+
+[anchor0]: clip_image002.gif-750x0.png
+[anchor1]: clip_image004.gif-750x0.png
+[anchor2]: clip_5F00_image006.jpg
+[anchor3]: clip_5F00_image008.jpg
+[anchor4]: clip_5F00_image010.jpg
+[anchor5]: https://www.youtube.com/watch?v=aTiwroXwW_0&list=PLhZ3P-LY7CqmVszuvtJLujFyHpsVN0U_w&index=17
+
+
+[image0]: clip_image002.gif-750x0.png
+[image1]: clip_image004.gif-750x0.png
+[image2]: clip_5F00_image006.jpg
+[image3]: clip_5F00_image008.jpg
+[image4]: clip_5F00_image010.jpg
diff --git a/content/docs/NAVPatterns/patterns/create-data-from-templates/2134.Picture6.png b/content/docs/NAVPatterns/patterns/create-data-from-templates/2134.Picture6.png
new file mode 100644
index 00000000..68676a7a
Binary files /dev/null and b/content/docs/NAVPatterns/patterns/create-data-from-templates/2134.Picture6.png differ
diff --git a/content/docs/NAVPatterns/patterns/create-data-from-templates/2816.Picture3.png b/content/docs/NAVPatterns/patterns/create-data-from-templates/2816.Picture3.png
new file mode 100644
index 00000000..7fbf3572
Binary files /dev/null and b/content/docs/NAVPatterns/patterns/create-data-from-templates/2816.Picture3.png differ
diff --git a/content/docs/NAVPatterns/patterns/create-data-from-templates/3482.Picture-5.png b/content/docs/NAVPatterns/patterns/create-data-from-templates/3482.Picture-5.png
new file mode 100644
index 00000000..1e62e630
Binary files /dev/null and b/content/docs/NAVPatterns/patterns/create-data-from-templates/3482.Picture-5.png differ
diff --git a/content/docs/NAVPatterns/patterns/create-data-from-templates/4118.Picture1.png b/content/docs/NAVPatterns/patterns/create-data-from-templates/4118.Picture1.png
new file mode 100644
index 00000000..02767054
Binary files /dev/null and b/content/docs/NAVPatterns/patterns/create-data-from-templates/4118.Picture1.png differ
diff --git a/content/docs/NAVPatterns/patterns/create-data-from-templates/4341.Picture4.png b/content/docs/NAVPatterns/patterns/create-data-from-templates/4341.Picture4.png
new file mode 100644
index 00000000..93d000e2
Binary files /dev/null and b/content/docs/NAVPatterns/patterns/create-data-from-templates/4341.Picture4.png differ
diff --git a/content/docs/NAVPatterns/patterns/create-data-from-templates/7271.Picture4.png b/content/docs/NAVPatterns/patterns/create-data-from-templates/7271.Picture4.png
new file mode 100644
index 00000000..514963fc
Binary files /dev/null and b/content/docs/NAVPatterns/patterns/create-data-from-templates/7271.Picture4.png differ
diff --git a/content/docs/NAVPatterns/patterns/create-data-from-templates/index.md b/content/docs/NAVPatterns/patterns/create-data-from-templates/index.md
new file mode 100644
index 00000000..d51d6c92
--- /dev/null
+++ b/content/docs/NAVPatterns/patterns/create-data-from-templates/index.md
@@ -0,0 +1,211 @@
++++
+title = "Create Data from Templates"
+weight = 400
+tags = ["C/AL"]
+categories = ["Pattern"]
++++
+_Originally by Nikola Kukrika at Microsoft Development Center Copenhagen_
+
+## Abstract
+
+For many records, such as Items, Customers, and Vendors, users have to enter the same sets of data again and again. This is tedious, error-prone (users forget to enter a field or they choose the wrong group), and difficult to learn for some users.
+
+We can group sets of data as templates to speed up and simplify the process of entering data in Microsoft Dynamics NAV. For example, the process of creating a new customer could be simplified so that users only have to enter information that is specific for every individual customer, e.g. Name and Address/Contact.
+
+## Description
+
+This pattern solves the problem of creating new records based on their type. You should use it whenever there is a large set of data that could be grouped in a meaningful way.
+
+In Microsoft Dynamics NAV 2013 R2, we have extended the Configuration Templates feature so that templates can be used in different languages than the language they were created in. We have also added the ability to set related templates so that related records can be inserted, such as dimensions for customers, items, and vendors.
+
+The pattern consists of two parts:
+
+1\. Using templates to create new records or applying templates to existing records
+
+2\. Defining and updating existing templates
+
+## Usage
+
+[![ ][image0]][anchor0]
+
+Using the patterns involves three steps.
+
+1) As a first step, we must insert a record. This can be done either through C/AL code or by letting the user create a record using the **New** action.
+
+2) After the record is created, we must apply the template. This is done by using the **UpdateRecord** function in the **Config. Template Management** codeunit (8612).
+
+**Config. Template Lines** records reference one **Config. Template Header** record (lines pattern). The lines can be of type:
+
+* **Field** - Stores a field value that will be applied to the record
+* **Related** Template -- References a Config. Template Header record for a related template.
+
+The **UpdateRecord** function applies values to the record one line at the time. One of the requirements was to be possible to use configuration templates in different language/regional settings than the template was created in.
+
+To support this scenario, when applying the **Config. Template Line** record, **GLOBALLANGUAGE** is set to the language ID of the field. This is important because the default value is stored as text, so we need to use the same formatting that NAV was running on when the template was created. Otherwise, data types, such as Boolean, Date, etc., will raise validation errors.
+
+Any updates to a **Config. Template Line** record will automatically update the language ID to the current one. Since lines are applied one by one, it is supported to have lines with different language IDs belonging to the same template.
+
+3) After we have applied the template to the record, we can insert related templates. For example, when you insert an item, you may want to insert dimensions as well. You must implement the logic to apply or then modify the related templates, since this depends on the logic and the relationship between the records. Lines with **Type = Related Template** are used to reference related templates.
+
+Code example (Insert a record, apply a template, and insert the related templates):
+
+```al
+// First insert a record Customer.INSERT(TRUE);
+
+// Apply a template RecRef.GETTABLE(Customer);
+
+ConfigTemplateMgt.UpdateRecord(ConfigTemplateHeader,RecRef);
+RecRef.SETTABLE(Customer);
+
+// Insert Dimensions -- related templates
+
+MiniDimensionsTemplate.InsertDimensionsFromTemplates(ConfigTemplateHeader,Customer."No.",DATABASE::Customer);
+```
+
+Code to insert related templates (dimensions):
+
+```al
+FUNCTION InsertDimensionsFromTemplates(ConfigTemplateHeader : Record "Config. Template Header";MasterRecordNo : Code[20];TableID : Integer)
+
+ // There are multiple records (multiple dimensions per master record)
+ // We have to set filter
+ ConfigTemplateLine.SETRANGE(Type,ConfigTemplateLine.Type::"Related Template");
+ ConfigTemplateLine.SETRANGE("Data Template Code",ConfigTemplateHeader.Code
+ IF ConfigTemplateLine.FINDSET THEN
+ REPEAT
+ ConfigTemplateHeader.GET(ConfigTemplateLine."Template Code");
+
+ // Ensure that the table where the template belongs to is Dimensions
+ // We could have other related templates
+ IF ConfigTemplateHeader."Table ID" = DATABASE::"Default Dimension" THEN
+ InsertDimensionFromTemplate(ConfigTemplateHeader,MasterRecordNo,TableID);
+ UNTIL ConfigTemplateLine.NEXT = 0;
+
+// Create a new Dimensions Record and link it to the Master Record
+FUNCTION InsertDimensionFromTemplate(ConfigTemplateHeader : Record "Config. Template Header";MasterRecordNo : Code[20];TableID : Integer)
+ DefaultDimension.INIT;
+ DefaultDimension."No." := MasterRecordNo;
+ DefaultDimension."Table ID" := TableID;
+ DefaultDimension."Dimension Code" := GetDefaultDimensionCode(ConfigTemplateHeader);
+ DefaultDimension.INSERT;
+ RecRef.GETTABLE(DefaultDimension);
+ ConfigTemplateMgt.UpdateRecord(ConfigTemplateHeader,RecRef);
+ RecRef.SETTABLE(DefaultDimension);
+```
+
+**To surface the action in the product, you have three options:**
+
+1. **Recommended** - Implement a separate action called **New from Template**.
+2. **Optional** - Implement the apply template function on the document itself. This is especially good in scenarios where users are allowed to change the template.
+3. **Alternative** - Remove the new action by configuration or set Insert Allowed to FALSE on the list (this will block the creation of new records from the lookup). Implement an application action named **New** and tie it to your code.
+
+**Note:** In Microsoft Dynamics C5 2014, we chose to remove the **New** action with configuration since we wanted to promote the functionality and avoid the confusion in having too many options. However this might be difficult to maintain with a larger set of pages.
+
+**To view or edit templates, you have two options:**
+
+1. Use the **Config. Template List** table (8620) and the **Config. Template Header Card** table (8618).
+
+This is a generic solution that is not very usable and is error-prone (no lookups, checks for length, table relation checks, etc.) The default value is a text field of 250 characters, which might be much more than the field length, and may lead to validation errors when used. Users will most likely not be able to use this page.
+
+[![ ][image1]][anchor1]
+
+2\. Implement custom pages resembling the document.
+
+This is optional if you want to enable the users to create and modify templates. In C5 2014, we created temporary tables with the same fields as the main record. Based on this temporary record, we built a page that resembles a document.
+
+Example of the **Customer Template** page:
+
+[![ ][image2]][anchor2]
+
+The goals of this solution were:
+
+* To make the setup page resemble a document page so that it is easy to use with basic validation and lookups.
+* To have only one place to store templates and maintain only one business logic for applying them, namely in the **Configuration Template Header** table.
+* To avoid any lateral effects of doing validation on the temporary master record. Doing validation on fields, even though the record itself is temporary, could permanently modify other data in the database. For example, if you insert a new record in the **Customer** table, even in a temporary table, a Contact record is created, which will not be temporary.
+* Testability: It is easy to test through RecordRef that the template table matches the main table. We can compare field lengths, data types, table relations, etc. The test is able to detect that they are out of sync, so it is easy to prevent errors.
+
+One example in the product is the **Mini Customer Template** table (1300).
+
+The table itself contains very little code. OnModify, OnInsert, and OnDelete triggers update the **Configuration Header** and **Configuration Lines** tables. The following functions in the **Config. Template Management** codeunit (8612), are used for this:
+
+* ConfigTemplateManagement.CreateConfigTemplateAndLines
+* ConfigTemplateManagement.UpdateConfigTemplateAndLines
+* ConfigTemplateManagement.DeleteRelatedTemplates
+
+The CreateFieldRefArray function is used as an interface function on all the temporary template tables. It builds data to be read/written to the configuration templates.
+
+To further enhance the usability, we have provided the following additional functionality:
+
+* Create a template from the existing record: The user opens an existing record and creates a template from that record. All the fields that are defined in the CreateFieldRefArray function are used to create the new template.
+* Templates list: This page is used by users to select templates or create new ones. Depending on which templates they are working on, we show different template cards.
+
+## NAV Specific Example
+
+In C5 2014, this is the workflow:
+
+The user opens the **Customers List** window and selects **New**
+
+[![ ][image3]][anchor3]
+
+From this page, the user can view the template, edit it, or create a new one. Selecting a template will populate the customer card and open a new record. From the existing record, the user has options to save as a template or opening a list of templates to maintain available templates. Selecting a template will populate the customer card and open a new record. From the existing record, the user has options to save as a template or opening a list of templates to maintain available templates.
+
+[![ ][image4]][anchor4]
+
+From the **Customer Card Template** window, we can invoke the **Dimensions** action, through which we can define the dimensions that will be inserted together with the template:
+
+[![ ][image5]][anchor5]
+
+## NAV Usages
+
+This pattern is used in Microsoft Dynamics C5 2014 in the following objects:
+
+* Temporary template tables:
+ * **Mini Customer Template** table (1300)
+ * **Mini Item Template** table (1301)
+ * **Mini Dimensions Template** table(1302)
+ * **Mini Vendor Template** table (1303)
+
+* Pages to define templates:
+ * **Mini Customer Template Card** page (1341)
+ * **Mini Item Template Card** page (,1342)
+ * **Mini Dimensions Template List** page (1343)
+ * **Mini Vendor Template Card** page (1344)
+
+* Pages that use the templates:
+ * **Mini Customer List** page (1301)
+ * **Mini Item List** page ( 1303)
+ * **Mini Vendor List** page (1331)
+
+In the standard version of Microsoft Dynamics NAV, we use the **Apply Template** action on the following pages:
+
+* **Customer Card** page (21)
+* **Vendor Card** page (26)
+* **Item Card** page (30)
+* **Resource Card** page (, 76)
+* Other similar cards.
+
+## Ideas for improvement
+
+Implement the solution in the standard version of Microsoft Dynamics NAV and extend the Apply Template functionality to insert dimensions.
+
+It is possible that users end up with a large number of templates if they need many different data combinations. An improvement could be to split templates into smaller groups, grouping only part of the fields that are related, and then apply only these.
+
+{{< youtube F0CTvoyKSmI >}}
+
+
+
+[anchor0]: 4118.Picture1.png
+[anchor1]: 2816.Picture3.png
+[anchor2]: 7271.Picture4.png
+[anchor3]: 4341.Picture4.png
+[anchor4]: 3482.Picture-5.png
+[anchor5]: 2134.Picture6.png
+[anchor6]: https://www.youtube.com/watch?v=F0CTvoyKSmI&list=PLhZ3P-LY7CqmVszuvtJLujFyHpsVN0U_w&index=20
+
+
+[image0]: 4118.Picture1.png
+[image1]: 2816.Picture3.png
+[image2]: 7271.Picture4.png
+[image3]: 4341.Picture4.png
+[image4]: 3482.Picture-5.png
+[image5]: 2134.Picture6.png
diff --git a/content/docs/NAVPatterns/patterns/create-urls-to-nav-clients/1778.url1.jpg b/content/docs/NAVPatterns/patterns/create-urls-to-nav-clients/1778.url1.jpg
new file mode 100644
index 00000000..5f99e021
Binary files /dev/null and b/content/docs/NAVPatterns/patterns/create-urls-to-nav-clients/1778.url1.jpg differ
diff --git a/content/docs/NAVPatterns/patterns/create-urls-to-nav-clients/7802.url2.jpg b/content/docs/NAVPatterns/patterns/create-urls-to-nav-clients/7802.url2.jpg
new file mode 100644
index 00000000..61d080c4
Binary files /dev/null and b/content/docs/NAVPatterns/patterns/create-urls-to-nav-clients/7802.url2.jpg differ
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
new file mode 100644
index 00000000..5f604ef4
--- /dev/null
+++ b/content/docs/NAVPatterns/patterns/create-urls-to-nav-clients/index.md
@@ -0,0 +1,144 @@
++++
+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_
+
+## Abstract
+
+This article illustrates NAV platform functionality to be used by C/AL developers.
+
+The URL builder function, GETURL, is released in Microsoft Dynamics NAV 2013 R2 to reduce coding time for developers who need to create various URL strings to run application objects in either the win client, the web client, or on web services. In addition, the GETURL function makes multitenancy features more transparent to C/AL developers.
+
+## Description
+
+Ever had to construct win client URLs like the one below?
+
+dynamicsnav://myserver:7046/myInstance/myCompany/runpage?page=26
+
+Today, Microsoft Dynamics NAV also provides a web client. This means that you must update your code to construct web client URLs too. What about multitenancy? The URL Builder should know if it is running in a multitenant setup and it should know how to choose the right tenant. What about maintaining this code?
+
+The good news is that GETURL has been introduced to handle all URL building for you.
+
+GETURL automatically handles:
+
+* Multitenancy
+* Correct URL format for each client
+* Publicly accessible hostnames.
+
+## Usage
+
+The format is:
+
+[String :=] GETURL(ClientType[, Company][, Object Type][, Object Id][, Record])
+
+Where:
+
+* **Client Type** can be: Current, Default, Windows, Web, SOAP, or OData. This enables a range of scenarios for the C/AL developer, such as moving to the web client without changing code to decide where the URL should point to. This is done either by setting Client Type to Current, and just ensuring that web is used to invoke the link creation, or by setting Client Type to Default and changing its value to Web when it is ready to move to the web platform.
+* **Object Type** and **Object ID** define the type of the application object to run (Table, Page, Report, Codeunit, Query, or XMLport) and its ID.
+* **Record** specifies the actual data to run the URL on, such as:
+
+```al
+Vendor.GET("Account No.");
+
+GETURL(CLIENTTYPE:WEB,COMPANYNAME, OBJECTTYPE::Page,27,Vendor)
+```
+
+**Note**: It is currently not possible to set filters on the record that you sent as a last parameter to the GETURL function. However, it is possible to write your own code to compute and append the filter string to the URL that is created by the GETURL function.
+
+The server name and instance are extracted automatically by GETURL and do not need to be specified by the C/AL developer. Furthermore, the multitenancy setup is transparent to the C/AL developer. No multitenancy parameters are specified when you call GETURL, because the function knows from the server setup if it is running in a multitenant environment and if so, it will add a string like "&tenant=MyTenant" to the URL.
+
+## When to Use
+
+The GETURL function can generally be used every time a URL must be created. The following are some scenarios where the function is particularly useful.
+
+* Document approvals. For more information, see the "NAV Usage Example" section.
+* Reports containing drill-down links. (Beware of the resource cost of adding a new URL element to the Report dataset.)
+* When planning to write code for, or migrate to, various display targets (Microsoft Dynamics NAV Windows client, Microsoft Dynamics NAV web client, Microsoft Dynamics NAV web services) without having to explicitly specify which client to use.
+
+## Examples of Usage
+
+The following are examples of calls to GETURL and their corresponding return value:
+
+Command | URL
+--------|------
+GETURL(CLIENTTYPE::Win) | dynamicsnav://MyServer:7046/DynamicsNAV71//
+GETURL(CLIENTTYPE::Web) | https://navwebsrvr:443/DynamicsNAV71_Instance1/Webclient
+GETURL(CLIENTTYPE::OData) | http://MyServer:7048/DynamicsNAV71/OData
+GETURL(CLIENTTYPE::SOAP) | http://MyServer:7047/DynamicsNAV71/WS/Services
+GETURL(CLIENTTYPE::Current) ie. When running this code on a Win client session | dynamicsnav://MyServer:7046/DynamicsNAV71//
+GETURL(CLIENTTYPE::Default) ie. When the Server config key DefaultClient is set to Windows | dynamicsnav://MyServer:7046/DynamicsNAV71//
+GETURL(CLIENTTYPE::Windows,COMPANYNAME) | dynamicsnav://MyServer:7046/DynamicsNAV71/CRONUS/
+GETURL(CLIENTTYPE::Windows,'') | dynamicsnav://MyServer:7046/DynamicsNAV71//
+GETURL(CLIENTTYPE::Windows,'NONEXISTING Corp') | dynamicsnav://MyServer:7046/DynamicsNAV71/NONEXISTING Corp/
+GETURL(CLIENTTYPE::Web,COMPANYNAME) | https://navwebsrvr:443/DynamicsNAV71_Instance1/Webclient?company=CRONUS
+GETURL(CLIENTTYPE::Web,'') | https://navwebsrvr:443/DynamicsNAV71_Instance1/Webclient
+GETURL(CLIENTTYPE::Web,'NONEXISTING Corp') | https://navwebsrvr:443/DynamicsNAV71_Instance1/Webclient?company=NONEXISTING Corp
+GETURL(CLIENTTYPE::OData,COMPANYNAME) | http://MyServer:7048/DynamicsNAV71/OData/Company('CRONUS')
+GETURL(CLIENTTYPE::OData,'') | http://MyServer:7048/DynamicsNAV71/OData
+GETURL(CLIENTTYPE::OData,'NONEXISTING Corp') | http://MyServer:7048/DynamicsNAV71/OData/Company('NONEXISTING Corp')
+GETURL(CLIENTTYPE::SOAP,COMPANYNAME) | http://MyServer:7047/DynamicsNAV71/WS/CRONUS/Services
+GETURL(CLIENTTYPE::SOAP,'') | http://MyServer:7047/DynamicsNAV71/WS/Services
+GETURL(CLIENTTYPE::SOAP,'NONEXISTING Corp') | http://MyServer:7047/DynamicsNAV71/WS/NONEXISTING Corp/Services
+GETURL(CLIENTTYPE::Windows,COMPANYNAME,OBJECTTYPE::Table,27) | dynamicsnav://MyServer:7046/DynamicsNAV71/CRONUS/runtable?table=27
+GETURL(CLIENTTYPE::Windows,COMPANYNAME,OBJECTTYPE::Page,27) | dynamicsnav://MyServer:7046/DynamicsNAV71/CRONUS/runpage?page=27
+GETURL(CLIENTTYPE::Windows,COMPANYNAME,OBJECTTYPE::Report,6) | dynamicsnav://MyServer:7046/DynamicsNAV71/CRONUS/runreport?report=6
+GETURL(CLIENTTYPE::Windows,COMPANYNAME,OBJECTTYPE::Codeunit,5065) | dynamicsnav://MyServer:7046/DynamicsNAV71/CRONUS/runcodeunit?codeunit=5065
+GETURL(CLIENTTYPE::Windows,COMPANYNAME,OBJECTTYPE::Query,9150) | dynamicsnav://MyServer:7046/DynamicsNAV71/CRONUS/runquery?query=9150
+GETURL(CLIENTTYPE::Windows,COMPANYNAME,OBJECTTYPE::XmlPort,5150) | dynamicsnav://MyServer:7046/DynamicsNAV71/CRONUS/runxmlport?xmlport=5150
+GETURL(CLIENTTYPE::OData,COMPANYNAME,OBJECTTYPE::Page,27) ie. When the Web Service is published | http://MyServer:7048/DynamicsNAV71/OData/Company('CRONUS')/PAG27Vendors
+GETURL(CLIENTTYPE::OData,COMPANYNAME,OBJECTTYPE::Query,9150) ie. When the Web Service is published | http://MyServer:7048/DynamicsNAV71/OData/Company('CRONUS')/QUE9150MyCustomers
+GETURL(CLIENTTYPE::SOAP,COMPANYNAME,OBJECTTYPE::Page,27)Â ie. When the Web Service is published | http://MyServer:7047/DynamicsNAV71/WS/CRONUS/Page/PAG27Vendors
+GETURL(CLIENTTYPE::SOAP,COMPANYNAME,OBJECTTYPE::Codeunit,5065) ie. When the Web Service is published | http://MyServer:7047/DynamicsNAV71/WS/CRONUS/Codeunit/COD5065EmailLogging
+GETURL(CLIENTTYPE::Windows,COMPANYNAME,OBJECTTYPE::Page,27,record) List Page | dynamicsnav://MyServer:7046/DynamicsNAV71/CRONUS/runpage?page=27&bookmark=23;FwAAAAJ7/0kAQwAxADAAMwAw
+GETURL(CLIENTTYPE::Windows,COMPANYNAME,OBJECTTYPE::Page,26,record) Card Page | dynamicsnav://MyServer:7046/DynamicsNAV71/CRONUS/runpage?page=26&bookmark=23;FwAAAAJ7/0kAQwAxADAAMwAw
+GETURL(CLIENTTYPE::Web,COMPANYNAME,OBJECTTYPE::Page,27,record) List Page | https://navwebsrvr:443/DynamicsNAV71_Instance1/Webclient?company=CRONUS&page=27&bookmark=23;FwAAAAJ7/0kAQwAxADAAMwAw
+GETURL(CLIENTTYPE::Web,COMPANYNAME,OBJECTTYPE::Page,26,record) Card Page | https://navwebsrvr:443/DynamicsNAV71_Instance1/Webclient?company=CRONUS&page=26&bookmark=23;FwAAAAJ7/0kAQwAxADAAMwAw
+GETURL(CLIENTTYPE::OData,COMPANYNAME,OBJECTTYPE::Page,27,record) | http://MyServer:7048/DynamicsNAV71/OData/Company('CRONUS')/PAG27Vendors('IC1030')
+GETURL(CLIENTTYPE::Web,COMPANYNAME,OBJECTTYPE::Page,27) | https://navwebsrvr:443/DynamicsNAV71_Instance1/Webclient?company=CRONUS&page=27
+GETURL(CLIENTTYPE::Web,COMPANYNAME,OBJECTTYPE::Report,6) | https://navwebsrvr:443/DynamicsNAV71_Instance1/Webclient?company=CRONUS&report=6
+
+If the GETURL function is called with invalid parameters, it will return an empty string. In that case, you can find the related error text by calling the GETLASTERRORTEXT function.
+
+Function Call | Error Message
+--------|------
+GETURL(CLIENTTYPE::Web,COMPANYNAME,OBJECTTYPE::Table,27) | The specified object type parameter for the GetUrl function is not valid.
+GETURL(CLIENTTYPE::Web,COMPANYNAME,OBJECTTYPE::Codeunit,5065) | The specified object type parameter for the GetUrl function is not valid.
+GETURL(CLIENTTYPE::Web,COMPANYNAME,OBJECTTYPE::Query,9150) | The specified object type parameter for the GetUrl function is not valid.
+GETURL(CLIENTTYPE::Web,COMPANYNAME,OBJECTTYPE::XmlPort,5150) | The specified object type parameter for the GetUrl function is not valid.
+GETURL(CLIENTTYPE::OData,COMPANYNAME,OBJECTTYPE::Table,27) | The specified object type parameter for the GetUrl function is not valid.
+GETURL(CLIENTTYPE::OData,COMPANYNAME,OBJECTTYPE::Page,27) | The Page object, 27, that is specified for the GetUrl function has not been published in the Web Services table.
+GETURL(CLIENTTYPE::OData,COMPANYNAME,OBJECTTYPE::Report,6) | The specified object type parameter for the GetUrl function is not valid.
+GETURL(CLIENTTYPE::OData,COMPANYNAME,OBJECTTYPE::Codeunit,5065) | The specified object type parameter for the GetUrl function is not valid.
+GETURL(CLIENTTYPE::OData,COMPANYNAME,OBJECTTYPE::Query,9150) | The Query object, 9150, that is specified for the GetUrl function has not been published in the Web Services table.
+GETURL(CLIENTTYPE::OData,COMPANYNAME,OBJECTTYPE::XmlPort,5150) | The specified object type parameter for the GetUrl function is not valid.
+GETURL(CLIENTTYPE::SOAP,COMPANYNAME,OBJECTTYPE::Table,27) | The specified object type parameter for the GetUrl function is not valid.
+GETURL(CLIENTTYPE::SOAP,COMPANYNAME,OBJECTTYPE::Page,27) | The Page object, 27, that is specified for the GetUrl function has not been published in the Web Services table.
+GETURL(CLIENTTYPE::SOAP,COMPANYNAME,OBJECTTYPE::Report,6) | The specified object type parameter for the GetUrl function is not valid.
+GETURL(CLIENTTYPE::SOAP,COMPANYNAME,OBJECTTYPE::Codeunit,5065) | The Codeunit object, 5065, that is specified for the GetUrl function has not been published in the Web Services table.
+GETURL(CLIENTTYPE::SOAP,COMPANYNAME,OBJECTTYPE::Query,9150) | The specified object type parameter for the GetUrl function is not valid.
+GETURL(CLIENTTYPE::SOAP,COMPANYNAME,OBJECTTYPE::XmlPort,5150) | The specified object type parameter for the GetUrl function is not valid.
+GETURL(CLIENTTYPE::SOAP,COMPANYNAME,OBJECTTYPE::Page,27,record) | You cannot specify a record parameter for the GetUrl function when the object type is SOAP
+
+## NAV Specific Example
+
+The following example shows how to use the GETURL function in codeunit 440 to ensure that the notification mail in Document Approvals can link to both the Microsoft Dynamics NAV Windows client and the Microsoft Dynamics NAV web client:
+
+[![ ][image0]][anchor0]
+
+This resulting UI looks as follows.
+
+[![ ][image1]][anchor1]
+
+The first link opens the approval document in the Microsoft Dynamics NAV Windows client. The second link (Web view) opens the same document in the Microsoft Dynamics NAV web client.
+
+
+
+[anchor0]: 1778.url1.jpg
+[anchor1]: 7802.url2.jpg
+
+
+[image0]: 1778.url1.jpg
+[image1]: 7802.url2.jpg
diff --git a/content/docs/NAVPatterns/patterns/creating-custom-charts/0246.Picture6.png b/content/docs/NAVPatterns/patterns/creating-custom-charts/0246.Picture6.png
new file mode 100644
index 00000000..8a71988e
Binary files /dev/null and b/content/docs/NAVPatterns/patterns/creating-custom-charts/0246.Picture6.png differ
diff --git a/content/docs/NAVPatterns/patterns/creating-custom-charts/1411.Picture2.png b/content/docs/NAVPatterns/patterns/creating-custom-charts/1411.Picture2.png
new file mode 100644
index 00000000..c93679c0
Binary files /dev/null and b/content/docs/NAVPatterns/patterns/creating-custom-charts/1411.Picture2.png differ
diff --git a/content/docs/NAVPatterns/patterns/creating-custom-charts/1541.Picture7.png b/content/docs/NAVPatterns/patterns/creating-custom-charts/1541.Picture7.png
new file mode 100644
index 00000000..b05555d8
Binary files /dev/null and b/content/docs/NAVPatterns/patterns/creating-custom-charts/1541.Picture7.png differ
diff --git a/content/docs/NAVPatterns/patterns/creating-custom-charts/1781.Picture5.png b/content/docs/NAVPatterns/patterns/creating-custom-charts/1781.Picture5.png
new file mode 100644
index 00000000..636e0240
Binary files /dev/null and b/content/docs/NAVPatterns/patterns/creating-custom-charts/1781.Picture5.png differ
diff --git a/content/docs/NAVPatterns/patterns/creating-custom-charts/1803.Picture7.png b/content/docs/NAVPatterns/patterns/creating-custom-charts/1803.Picture7.png
new file mode 100644
index 00000000..d6b26d7c
Binary files /dev/null and b/content/docs/NAVPatterns/patterns/creating-custom-charts/1803.Picture7.png differ
diff --git a/content/docs/NAVPatterns/patterns/creating-custom-charts/2553.Picture8.png b/content/docs/NAVPatterns/patterns/creating-custom-charts/2553.Picture8.png
new file mode 100644
index 00000000..cec97fe8
Binary files /dev/null and b/content/docs/NAVPatterns/patterns/creating-custom-charts/2553.Picture8.png differ
diff --git a/content/docs/NAVPatterns/patterns/creating-custom-charts/5153.Picture1.png b/content/docs/NAVPatterns/patterns/creating-custom-charts/5153.Picture1.png
new file mode 100644
index 00000000..aa9b7263
Binary files /dev/null and b/content/docs/NAVPatterns/patterns/creating-custom-charts/5153.Picture1.png differ
diff --git a/content/docs/NAVPatterns/patterns/creating-custom-charts/5545.Picture9.png b/content/docs/NAVPatterns/patterns/creating-custom-charts/5545.Picture9.png
new file mode 100644
index 00000000..b9f5764f
Binary files /dev/null and b/content/docs/NAVPatterns/patterns/creating-custom-charts/5545.Picture9.png differ
diff --git a/content/docs/NAVPatterns/patterns/creating-custom-charts/5582.Picture10.png b/content/docs/NAVPatterns/patterns/creating-custom-charts/5582.Picture10.png
new file mode 100644
index 00000000..353d5fdf
Binary files /dev/null and b/content/docs/NAVPatterns/patterns/creating-custom-charts/5582.Picture10.png differ
diff --git a/content/docs/NAVPatterns/patterns/creating-custom-charts/index.md b/content/docs/NAVPatterns/patterns/creating-custom-charts/index.md
new file mode 100644
index 00000000..64d58fd4
--- /dev/null
+++ b/content/docs/NAVPatterns/patterns/creating-custom-charts/index.md
@@ -0,0 +1,211 @@
++++
+title = "Creating Custom Charts"
+weight = 420
+tags = ["C/AL"]
+categories = ["Pattern"]
++++
+_Originally by Nikola Kukrika at Microsoft Development Center Copenhagen_
+
+## Abstract
+
+The goal of this solution is to enable you to:
+
+1. Use charts in the web client.
+2. Create charts with custom functionality.
+
+## Description
+
+This pattern enables you to implement a business chart (Specific Chart type) in a way that is maintainable and reusable on other pages. This also enables you to provide specific functionality that is not possible with the Generic Chart type and it enables you to show charts in the web client.
+
+The Business Chart add-in is a special because it is a combination of .NET and Javascript add-ins depending on the display target. In the web client, it renders a JavaScript control, while in the win client, it renders a .Net control. Because of this behavior, you can expect minor differences in how the chart is presented in the win client versus in the web client. Note that this implementation is specific to NAV platform code, because it is not possible to create add-ins that combines .NET and JavaScript by using a framework API.
+
+[![ ][image0]][anchor0]
+
+Example of the same chart in the win client:
+
+[![ ][image1]][anchor1]
+
+The most obvious differences in chart rendering in the two clients are: Slightly different line heights, slightly different chart height, legends in web-client charts can be used as toggle filters to show/hide groups (this is not possible in the win client).
+
+### Implementation Overview
+
+[![ ][image2]][anchor2]
+
+### Add-in Buffer Table
+
+This table is used to encapsulate the logic of the Business Chart Add-in. The table handles the following logic:
+
+* Storing chart values and conversion from .NET to C/AL and vice versa
+* Handling of captions: We must use C/AL to provide multilanguage text in add-ins. In addition, the multilanguage text must be encapsulated in a single place, because we pass/read the same dataset from/to the add-in.
+* DrillDown logic
+* Other helper data related functions, for displaying date, periods, etc.[Bogdana1] [NK2] [NK3]
+
+**Note:** It is recommended that you reuse the **Business Chart Buffer table** (485) as a buffer table or extend. It is a generic table which should cover most of the use cases. Implement a new buffer table only if this table does not meet your needs.
+
+### CardPart page
+
+The CardPart page hosts the Business Chart add-in and must use the add-in buffer table as a source table.
+
+On the page, you must implement the following triggers:
+
+* **AddInReady** -- Executed when the page is done rendering. Used to initialize the add-in.
+* **DataPointClicked** -- Single-click on an element on the chart.
+* **DataPointDoubleClicked** -- Double-click on an element on the chart
+
+The CardPart usually contains a StatusText variable to provide more information about the chart or dataset and a set of actions to control the chart.
+
+The most commonly used actions are:
+
+* **Select Chart**, **Previous Chart**, **Next Chart**
+* **Set Period**, **Work Date**
+* Actions to filter the data set
+* **Refresh**
+* **Chart Information** -- a tooltip with a description of the chart and how data is calculated.
+
+### Optional: Preserving User Personalization
+
+One of the most common functionalities is personalization. If the chart can be customized by the user, you should store the settings that the user has entered and apply them the next time the chart is loaded.
+
+To do this, you need the following:
+
+* A setup record to store the data. You can use the **Business Chart User Setup** table (487) or create a new setup table if you need to store more information.
+* A management codeunit to write/apply the settings to the chart and to encapsulate other logic. Since we should not write code on pages, the code for the actions and other logic that does not apply to the setup record should go in this codeunit.
+* Setup pages where users can customize how the chart is shown and set different settings.
+
+The relation between the components is visualized in the following diagram:
+
+[![ ][image3]][anchor3]
+
+### Optional: Show Multiple Charts within a Single CardPart
+
+This option is useful on a Role Center where you want to show multiple charts using different datasets within a single part. In that case, you need a record to store the last chart selection, the setup records, and code units for separate charts.
+
+See, for example, the implementation of the **Mini Generic Chart** page (1390), which uses **MiniChartManagment** CodeUnit to manage separate management codeunits for charts and their setup records. The last selected chart is stored in a separate table, **Mini Chart Definition** (1310).
+
+[![ ][image4]][anchor4]
+
+## Usage
+
+To implement the pattern, create a new ChartPart and set the source table to **Business Chart Buffer**.
+
+Add a field named **BusinessChart** and set the ControlAddIn property to Microsoft.Dynamics.Nav.Client.BusinessChart.
+
+Then implement the AddInReady event. This event is executed when the page is done rendering. Code within this method must call the Update method from the **Business Chart Buffer** table, Update(CurrPage.BusinessChart) to initialize the chart and assign initial values.
+
+If you need a setup record and codeunit, then it is a good idea to encapsulate this logic within a method.
+
+### NAV Specific Example 1
+
+Implementation of the **Finance Performance Chart** page (762)
+
+```al
+BusinessChart::AddInReady()
+
+UpdateChart(Period::" ");
+
+LOCAL UpdateChart(Period : ',Next,Previous')
+
+MoveAndUpdateChart(Period,0);
+
+LOCAL MoveAndUpdateChart(Period : ',Next,Previous';Move : Integer)
+
+AccSchedChartManagement.GetSetupRecordset(AccountSchedulesChartSetup,AccountSchedulesChartSetup.Name,Move);
+
+AccSchedChartManagement.UpdateData(Rec,Period,AccountSchedulesChartSetup);
+
+Update(CurrPage.BusinessChart);
+
+StatusText := GetCurrentSelectionText("Period Filter Start Date","Period Filter End Date");
+```
+
+In the MoveAndUpdateChart method, the AccSchedChartManagement codeunit gets a setup record and updates it if necessary. Then, it initializes the chart with setup data and sets the StatusText to show the period for which data is displayed. The same method is used by the actions to move and update the chart so that there is no code duplication.
+
+The following code is used to implement **DataPointClicked**
+
+```al
+BusinessChart::DataPointClicked(point : DotNet "Microsoft.Dynamics.Nav.Client.BusinessChart.BusinessChartDataPoint")
+
+SetDrillDownIndexes(point);
+
+AccSchedChartManagement.DrillDown(Rec,AccountSchedulesChartSetup);
+```
+
+SetDrillDownindexes is a method from the **Business Chart Buffer** table that maps the DotNet point variable to C/AL data, so it must be used. The next method that you must implement is the action to be performed on Drilldown.
+
+The **DataPointDoubleClicked** trigger has the same implementation logic as the DataPointClicked trigger.
+
+### NAV Specific Example 2
+
+Implementation of chart part 1390 on the **Small Business Role Center** page (9022)
+
+[![ ][image5]][anchor5]
+
+This chart part contains data from multiple charts within a single part. The **Status Text** field shows the name of the chart and the current period. Users can browse through the charts with **Next Chart** and **Previous Chart** or use **Select Chart** to choose from a list of available charts.
+
+[![ ][image6]][anchor6]
+
+On this dialog, users can choose if a chart should be enabled or disabled. If the chart is not enabled, it will be skipped on the **Previous Chart** and **Next Chart** actions. Charts used by this part use different codeunits and setup records. If the user changes the selected chart, this option will be saved and applied next time role center is opened.
+
+Users can also change the period length.
+
+[![ ][image7]][anchor7]
+
+Choosing the **Chart Information** button opens a short description of the chart.
+
+[![ ][image8]][anchor8]
+
+## NAV Usages
+
+Implementation of multiple charts within a single part:
+
+* Page 1390, **Mini Generic Chart**
+
+Charts that use a setup record and select the chart with **Customize Chart Setup** pages:
+
+* Page 772, **Inventory Performance**
+* Page 771, **Purchase Performance**
+* Page 770, **Sales Performance**
+* Page 762, **Finance Performance**
+
+Chart that uses the **Business Chart User Setup** table:
+
+* Page 768, **Aged Acc. Receivable Chart**
+
+Other implementations:
+
+* Page 972, **Time Sheet Chart**
+* Page 869, **Cash Flow Chart**
+* Page 760, **Trailing Sales Orders Chart**
+
+## Ideas for improvement
+
+We should consider making a generic table for the last chart that the user has used.
+
+We should investigate if we could make generic code for selecting periods and other common functionality by using RecordRefs.
+
+Add-In improvements -- Different ways to visualize the data and to pick colors for categories.
+
+As a nice-to-have feature, we could implement functionality to cycle through the charts with a timer.
+
+
+
+[anchor0]: 5153.Picture1.png
+[anchor1]: 1411.Picture2.png
+[anchor2]: 1781.Picture5.png
+[anchor3]: 0246.Picture6.png
+[anchor4]: 1803.Picture7.png
+[anchor5]: 1541.Picture7.png
+[anchor6]: 2553.Picture8.png
+[anchor7]: 5545.Picture9.png
+[anchor8]: 5582.Picture10.png
+
+
+[image0]: 5153.Picture1.png
+[image1]: 1411.Picture2.png
+[image2]: 1781.Picture5.png
+[image3]: 0246.Picture6.png
+[image4]: 1803.Picture7.png
+[image5]: 1541.Picture7.png
+[image6]: 2553.Picture8.png
+[image7]: 5545.Picture9.png
+[image8]: 5582.Picture10.png
diff --git a/content/docs/NAVPatterns/patterns/cross-session-events/PubSub.png b/content/docs/NAVPatterns/patterns/cross-session-events/PubSub.png
new file mode 100644
index 00000000..ff915cd2
Binary files /dev/null and b/content/docs/NAVPatterns/patterns/cross-session-events/PubSub.png differ
diff --git a/content/docs/NAVPatterns/patterns/cross-session-events/index.md b/content/docs/NAVPatterns/patterns/cross-session-events/index.md
new file mode 100644
index 00000000..b9aecd0b
--- /dev/null
+++ b/content/docs/NAVPatterns/patterns/cross-session-events/index.md
@@ -0,0 +1,170 @@
++++
+title = "Cross Session Events"
+weight = 430
+tags = ["C/AL"]
+categories = ["Pattern"]
++++
+
+_By Nikolai L'Estrange, from TVision Technology Ltd. in the UK_
+
+## Abstract
+
+Track things that happen in other NAV Sessions.
+
+[![ ][image0]][anchor0]
+
+## Problem
+
+In Microsoft Dynamics NAV you can fire a function whenever something changes within your session (and from NAV 2016 this is even easier with the new Event model), however there is not an easy way to know what is happening in other sessions. Sometimes you would like to know what has happened since your last read, without reading everything again, e.g. when you need to pass a large dataset to a Control Add-in.
+
+A common way of handling this with Ledger Tables is to make note of the last record you read, and continuously poll to see if there are any new records. However this is restricted to strictly sequentially entered tables.
+
+## Solution
+
+There is a common pattern in many other languages called [Publish-Subscribe][anchor1] (or PubSub) that solves the same issue. We can implement the same pattern in NAV using a Table as a message queue platform and polling this table. We have named this pattern "Cross Session Events" in order to avoid confusion with the standard NAV Events which use the terms Publisher and Subscriber, and to try and describe more accurately when you would need this pattern.
+
+The pattern has four components:
+
+* **Publisher(s)**: These push messages to the Message Broker.
+* **Subscriber Records**: Identifies the Subscriber and store filters to say what messages the Subscriber and interested in receiving.
+* **Message Broker**: This distributes all messages sent in to all Subscribers that have expressed an interest (i.e. the message is within their filters).
+* **Message Queue**: To hold the messages for each Subscriber. Generally once these messages are read, they are deleted.
+
+## Example
+
+An example of this would be when we have multiple users looking at the same set of data and we want their screens to update in "real time" whenever one of them makes a change, without doing a full refresh. We will use the [Observer pattern][anchor2] to capture the change (act as the Publisher) and then create a Table to hold Subscribers and Filters (Change Observer), a Table to be the Message Queue (Change Notification), and a Codeunit to be the Message Broker and help with the polling (ObserverMgt).
+
+Below are the table definitions:
+
+**Change Observer:** | |
+-----|------|-----
+"Table ID" | Integer | "Observable Table"
+"Server ID" | Integer
+"Session ID" | Integer
+
+**Change Notification:** | |
+-----|------|-----
+"Table ID" | Integer | "Observable Table"
+"Server ID" | Integer
+"Session ID" | Integer
+"Entry No." | Integer | AutoIncrement
+"Type of Change" | Option | Insert,Modify,Delete,Rename
+"Record ID" | RecordID
+... (other fields to indicate what has changed)
+
+The Change Observer table identifies the Subscriber using Server ID and Session ID, and then in this example there is only one filter, which is the Table ID we want to listen to any changes. In this case all three fields are in the Primary Key.
+
+The Change Notification table then has the same three fields plus an Entry No. as its Primary Key, and in this example borrows heavily from the Change Log code to fill in the rest of the message.
+
+_**Note:**_ Other examples of the pattern could have very different fields to identify the Subscriber, Filters and then whatever fields needed for content of the Message.
+
+Our Message Broker Codeunit will also serve as a central place to create Subscribers (Listen and StopListening functions) and a place to Poll for Messages. Note that the Poll function deletes the Messages as it reads them.
+```al
+Listen(TableID : Integer)
+WITH Observer DO BEGIN
+ "Table ID" := TableID;
+ "Server ID" := SERVICEINSTANCEID;
+ "Session ID" := SESSIONID;
+ INSERT(TRUE);
+ COMMIT;
+END;
+
+StopListening(TableID : Integer)
+WITH Observer DO BEGIN
+ RESET;
+ SETRANGE("Server ID",SERVICEINSTANCEID);
+ SETRANGE("Session ID",SESSIONID);
+ SETRANGE("Table ID",TableID);
+ DELETEALL(TRUE);
+ COMMIT;
+END;
+
+NotifyAll(ChangeNotification : Record "Change Notification")
+WITH Observer DO BEGIN
+ RESET;
+ SETRANGE("Table ID",ChangeNotification."Table ID");
+ IF FINDSET THEN REPEAT
+ Notify(Observer,ChangeNotification);
+ UNTIL NEXT = 0;
+END;
+
+Notify(Observer : Record "Change Observer";ChangeNotification : Record "Change Notification")
+WITH ChangeNotification DO BEGIN
+ "Server ID" := Observer."Server ID";
+ "Session ID" := Observer."Session ID";
+ "Entry No." := 0;
+ INSERT;
+END;
+
+Poll(TableID : Integer;VAR TempChangeNotification : TEMPORARY Record "Change Notification")
+WITH ChangeNotification DO BEGIN
+ TempChangeNotification.RESET;
+ TempChangeNotification.DELETEALL;
+
+ RESET;
+ SETRANGE("Table ID",TableID);
+ SETRANGE("Server ID",SERVICEINSTANCEID);
+ SETRANGE("Session ID",SESSIONID);
+
+ IF FINDSET THEN REPEAT
+ TempChangeNotification := ChangeNotification;
+ TempChangeNotification.INSERT;
+ MARK(TRUE);
+ UNTIL NEXT = 0;
+
+ MARKEDONLY(TRUE);
+ DELETEALL;
+END;
+```
+
+The final part of this example is an object that calls the functions above. In this example we will use a Page with a PingPong Timer Control to do the polling in (almost) real time. These are the functions on the page:
+
+```al
+OnQueryClosePage(CloseAction : Action None) : Boolean
+ObserverMgt.StopListening(DATABASE::"NAV Whiteboard Booking");
+
+Timer::AddInReady()
+IF ObserverMgt.Listen(DATABASE::"NAV Whiteboard Booking") THEN
+CurrPage.Timer.Ping(1000);
+
+Timer::Pong()
+CallUpdate;
+CurrPage.Timer.Ping(1000);
+
+LOCAL CallUpdate()
+ObserverMgt.Poll(DATABASE::"NAV Whiteboard Booking",TempChangeNotification);
+
+WITH TempChangeNotification DO BEGIN
+ IF FINDSET THEN REPEAT
+ IF "Type of Change" = "Type of Change"::Delete THEN BEGIN
+ ...
+ END ELSE IF RecRef.GET("Record ID") THEN BEGIN
+ ...
+ END;
+ UNTIL NEXT = 0;
+END;
+```
+
+## Consequences
+
+The PingPong control is only available on the Windows Client, so if you want to use another client you will need to use another solution to Poll for Messages. Therefore this pattern is not always going to be "real time".
+
+## Related Topics
+
+This pattern was originally described in the following blog:
+
+[https://geeknikolai.wordpress.com/2015/10/30/pubsub-pattern-in-dynamics-nav-2016/][anchor3]
+
+Below is the Wikipedia link to the PubSub pattern
+
+[https://en.wikipedia.org/wiki/Publish%E2%80%93subscribe_pattern][anchor1]
+
+
+
+[anchor0]: PubSub.png
+[anchor1]: https://en.wikipedia.org/wiki/Publish%E2%80%93subscribe_pattern
+[anchor2]: /navpatterns/1-patterns/observer/
+[anchor3]: https://geeknikolai.wordpress.com/2015/10/30/pubsub-pattern-in-dynamics-nav-2016/
+
+
+[image0]: PubSub.png
diff --git a/content/docs/NAVPatterns/patterns/currently-active-record/6545.Table.png b/content/docs/NAVPatterns/patterns/currently-active-record/6545.Table.png
new file mode 100644
index 00000000..84f5db39
Binary files /dev/null and b/content/docs/NAVPatterns/patterns/currently-active-record/6545.Table.png differ
diff --git a/content/docs/NAVPatterns/patterns/currently-active-record/index.md b/content/docs/NAVPatterns/patterns/currently-active-record/index.md
new file mode 100644
index 00000000..ad7e9e2e
--- /dev/null
+++ b/content/docs/NAVPatterns/patterns/currently-active-record/index.md
@@ -0,0 +1,155 @@
++++
+title = "Currently Active Record"
+weight = 450
+tags = ["C/AL"]
+categories = ["Pattern"]
++++
+_Authors: Henrik Langbak and Kim Ginnerup, Bording Data_
+
+## Abstract
+
+Date controlled data is expensive to find in the database. This pattern describes how using a view with a sub-select and a linked table object will minimize the returned dataset.
+A side effect is reduced and simplified code, increased performance and a more scalable solution that is almost independent of the amount of records in the table.
+
+## Description
+
+There is no way in NAV to get a set of records from the database, which all have the newest starting date, that is less than or equal to today's date. Having an ending date on the record will help, but it introduces some other problems. In Dynamics NAV this is normally done by reading too many records, either at the SQL Server level or in the middle tier and throw away the ones you do not need. That is a waste of resources:
+
+* The SQL Server is reading too many records
+
+* There would be too much data sent over the network.
+(If the SQL Server and the NAV Service tier are on different machines.)
+
+* The NAV Service Tier receives and throws away data.
+
+## Ending Date Problem
+
+Ending Date may introduce some problems of its own.
+
+If your design requires to have one and only one active record per key in a dataset, then Ending Date introduces the possibility for overlapping or holes in the timeline.
+Ending Date creates a dependency between two records. Changing a Starting Date, requires you to update the previous record. Changing the Ending Date requires you to update the next record.
+If you add a record in between you will have to update both the before and the after record.
+
+The pattern we describe here will work whether there is an Ending Date or Not.
+
+The pattern is also relevant for other types than date. The pattern is usable whenever you have dependencies between rows in a table.
+
+Use the pattern whenever you read a set of data containing a Starting Date and you need to implement a loop to throw away unwanted records. An example could be Codeunit 7000 "Sales Price Calc. Mgt.". In this codeunit there are many loop constructs to find prices and discounts.
+
+## Usage
+
+In the following example, we have a fictive table containing: Code, Starting Date and Price. The Primary Key consist of Code, Starting Date. The Database is the Demo Database, and the Company is Cronus.
+
+[![ ][image0]][anchor0]
+
+
+### 1. Create the view
+
+You will need to create the view before you define the Table Object.
+You will need to create a view for every company in the database.
+```sql
+CREATE VIEW [dbo].[CRONUS$PriceView]
+AS
+SELECT [Code], [Starting Date], [Price]
+FROM dbo.[CRONUS$Price] AS A
+WHERE [Starting Date] =
+(SELECT MAX([Starting Date])
+FROM dbo.[CRONUS$Price] AS B
+WHERE B.[Code] = A.[Code] AND
+B.[Starting Date] <= GETDATE())
+```
+
+Test the view to ensure that you get the correct result. It is much easier to test now than later.
+
+### 2. Create the Table object
+
+Remember to set the link table property before you save it.
+
+### 3. Implement the code
+
+```al
+IF PriceView.FINDSET THEN // You have them
+```
+
+### 4. Create a deployment codeunit
+
+Create a SQL Deployment codeunit to manage your views.
+The codeunit needs to Create or Alter the views for all companies.
+To see an example of how to talk to SQL Server using .NET see waldo's blog here:
+[http://dynamicsuser.net/blogs/waldo/archive/2011/07/19/net-interop-calling-stored-procedures-on-sql-server-example-1.aspx][anchor1]
+
+### 5. Deployment
+
+You need to deploy in three steps:
+
+1. Delete the table objects referencing the views
+2. Deploy and run the deployment codeunit
+3. Deploy the new table objects that reference the views
+
+### General precaution
+
+If you later want to change the view, you need to follow these rules:
+
+* If you add columns, you need to add them to the view first and then add them to the Table Object.
+* If you want to remove columns from the view, you need to delete the Table Object, then change the view and last recreate the Table Object without the new columns.
+
+### Code example that accomplish the same but without using the pattern
+
+This following example will give you the same result but the performance will deteriorate as time goes by and you get more and more old data.
+```al
+Price.SETCURRENTKEY(Code,"Starting Date");
+Price.SETFILTER("Starting Date",'..%1', TODAY
+IF Price.FINDSET THEN BEGIN
+ REPEAT
+ Price.SETRANGE(Code, Price.Code);
+ Price.FINDLAST;
+ Price.SETRANGE(Code);
+ PriceTemp := Price;
+ PriceTemp.INSERT;
+ UNTIL Price.NEXT = 0;
+END;
+// PriceTemp will contain the Prices
+```
+
+### Comparison
+
+The above NAV example is for a very simple date controlled solution and is provided to give an idea of what the pattern changes seen from a NAV development point of view. But consider the following:
+
+* The table has a more complex key.
+This will require setting and clearing more filters
+* You need to read from more than one table.
+Say you need to apply discount from a separate table.
+This may give several lines in PriceTemp.
+* If the Code field is controlled by a Type field.
+The Code field reference keys in different tables
+
+All three examples above can be implemented directly in the view. By using the pattern, it will still only require a single line of NAV code.
+
+Using the pattern will only issue one SQL call and thereby one trip to the server.
+The NAV Example will require an unknown number of SQL calls and thereby an unknown number of trips to the server. The number of SQL calls is dependent on the number of distinct Code values.
+The NAV example will require SQL Server to read all data older than or equal to TODAY, but only return one row per Code. Over time, as old data piles up in the system, the NAV code will perform slower because the SQL statements will be slower.
+The Pattern makes a scalable solution with a predictable performance. The performance will not deteriorate at the same rate as the NAV code example.
+
+## NAV Usages
+
+The pattern does not exist in NAV (yet J). We have used it several times in our code for an Add-On.
+
+## Ideas for improvement
+
+Query Object should be able to handle sub-selects and Unions. A simple solution could be to allow the NAV developer to specify the actual Select statement inside the query Object in clear text. Opening up for writing your own queries and map the projection to the Query-defined fields will make the query Object very versatile and remove the pressure from Microsoft trying to create all the different permutations that a select statement can have. Microsoft and others have all tried to create wizards that can create SQL select statement. They all end up having a clear text option.
+
+An alternative would be better support for linked table objects, specifically views. The current implementation is very fragile.
+
+The pattern only supports fetching data for a given date (normally today). This is because you cannot control the where-clause of the sub-select.
+
+## Related Topics
+
+The idea of having a linked table object pointing to a view could be a pattern of its own.
+
+
+
+[anchor0]: 6545.Table.png
+[anchor1]: http://dynamicsuser.net/blogs/waldo/archive/2011/07/19/net-interop-calling-stored-procedures-on-sql-server-example-1.aspx
+
+
+[image0]: 6545.Table.png
diff --git a/content/docs/NAVPatterns/patterns/data-migration-facade/8308.logo.png b/content/docs/NAVPatterns/patterns/data-migration-facade/8308.logo.png
new file mode 100644
index 00000000..443a891f
Binary files /dev/null and b/content/docs/NAVPatterns/patterns/data-migration-facade/8308.logo.png differ
diff --git a/content/docs/NAVPatterns/patterns/data-migration-facade/NoStagingTableNew2.png b/content/docs/NAVPatterns/patterns/data-migration-facade/NoStagingTableNew2.png
new file mode 100644
index 00000000..67784877
Binary files /dev/null and b/content/docs/NAVPatterns/patterns/data-migration-facade/NoStagingTableNew2.png differ
diff --git a/content/docs/NAVPatterns/patterns/data-migration-facade/StagingTableNew2.png b/content/docs/NAVPatterns/patterns/data-migration-facade/StagingTableNew2.png
new file mode 100644
index 00000000..78061079
Binary files /dev/null and b/content/docs/NAVPatterns/patterns/data-migration-facade/StagingTableNew2.png differ
diff --git a/content/docs/NAVPatterns/patterns/data-migration-facade/errorhandling1.png b/content/docs/NAVPatterns/patterns/data-migration-facade/errorhandling1.png
new file mode 100644
index 00000000..3f649988
Binary files /dev/null and b/content/docs/NAVPatterns/patterns/data-migration-facade/errorhandling1.png differ
diff --git a/content/docs/NAVPatterns/patterns/data-migration-facade/errorhandling2.png b/content/docs/NAVPatterns/patterns/data-migration-facade/errorhandling2.png
new file mode 100644
index 00000000..c6922a1a
Binary files /dev/null and b/content/docs/NAVPatterns/patterns/data-migration-facade/errorhandling2.png differ
diff --git a/content/docs/NAVPatterns/patterns/data-migration-facade/index.md b/content/docs/NAVPatterns/patterns/data-migration-facade/index.md
new file mode 100644
index 00000000..e4e5aa4e
--- /dev/null
+++ b/content/docs/NAVPatterns/patterns/data-migration-facade/index.md
@@ -0,0 +1,290 @@
++++
+title = "Data Migration Façade"
+weight = 480
+tags = ["C/AL"]
+categories = ["Pattern"]
++++
+_By David Bastide and Soumya Dutta at Microsoft Development Center Copenhagen_
+
+[![ ][image0]][anchor0]
+
+## Context:
+
+This pattern is describing how you can migrate data using the Data Migration Façade.
+
+## Problem:
+
+Writing migration code from an external source, such as a product from a competitor, can be time consuming, as you need to tackle the problems of what to migrate, in which order, exception handling... and can result in code that is fragile due to deep dependencies on the NAV data model (high coupling). Any change to the NAV data model can easily break this code.
+
+## Solution:
+
+The idea of the Data Migration Façade is to provide an API that creates and updates master data and related entities (including transactions) without referencing NAV tables.
+
+Additionally, the framework around the Data Migration Façade provides tools for error handling, and a way to integrate the migration in the Data Migration Overview page (page 1799).
+
+The façade framework has the following components:
+
+* Two management codeunits:
+ * **Data Migration Façade** (codeunit 6100): Integrates the extension to the Data Migration Wizard. Starts a migration, or restarts a migration that failed for some records.
+ * **Data Migration Status Facade** (codeunit 6101): Initializes and updates the status of the migration. The status displays in the **Data Migration Overview** page (page 1799).
+
+* Several master data migration façade codeunits that create and update entities. Each codeunit also contains events that help ensure that data is created in the correct order:
+ * **GL Acc. Data Migration Façade** (codeunit 6110)
+ * **Vendor Data Migration Façade** (codeunit 6111)
+ * **Customer Data Migration Façade** (codeunit 6112)
+ * **Item Data Migration Façade** (codeunit 6113)
+ * If you want to migrate other entities, it is possible to define your own codeunit that will contain your custom code (see 'Usage' below).
+
+* A page where you can view the status and progress of the migration. It shows one line for each master data entity (item, customer, vendor, general ledger account) that was chosen for migration. You can also stop a migration by choosing the **Stop Migration** action.
+
+ * **Data Migration Overview** (page 1799)
+
+## Usage:
+
+There are the following use cases:
+
+* Migration with staging tables, where data from another product is exported to a file or set of files, and the exported data is then imported to buffer tables, before running the migration logic. In this case, the migration is implemented in your extension codeunits, and it is called through events, one record at a time.
+
+* Migration without staging tables, for example, when you migrate data by importing it from an external tool such as external APIs and webservices. In this case, the migration is also implemented in your extension codeunits but it will be called through the OnRun procedure. You will be responsible for looping on the records to migrate, and you must migrate all records in this unique OnRun call for a given entity.
+
+To initialize and start the data migration, you must call the following procedures:
+
+* **"Data Migration Status Façade".InitStatusLine('My Migration Type',Database::Item,42000,0,Codeunit::"My extension Item migration codeunit")**
+This deletes existing status lines for migrating Items for 'My Migration Type', and initializes a new status line with 0 records migrated out of 42000\.
+
+**"Data Migration Façade".StartMigration('My Migration Type',false)**
+This starts the migration. False means this is not a retry. A re-try is when you migrate one or more records from the **Show Errors** page, which is described later in this document. Retry = true is only used by the **Show Errors** page and should not be used from extensions.
+
+### Usage without staging tables:
+
+The overall workflow is:
+
+1. Integrate your extension in the **Data Migration Wizard** by subscribing to the events exposed by the **Data Migration Façade**.
+2. From there, initialize the status of the migration so it can be displayed in the Data Migration Overview. When initializing the status lines, you provide the codeunit ID that will be called for each entity: **"Data Migration Status Façade".InitStatusLine('My Migration Type',Database::Item,42000,0,Codeunit::"My extension Item migration codeunit")**.
+3. Launch the migration: **"Data Migration Façade".StartMigration('My Migration Type',false).**
+4. Your migration codeunits are called one at a time (**OnRun**) in the following order:
+ 1. G/L accounts (first, because customer/vendor posting groups refer to G/L accounts)
+ 2. Customers
+ 3. Vendors
+ 4. Items (because discounts may refer to customer groups)
+ 5. Others
+5. Loop on all of the records to migrate. You can update the increment of the amount of records migrated: **"Data Migration Status Facade".IncrementStatusLine('My Migration Type',Database::Item,42).**
+
+[![ ][image1]][anchor1]
+
+_Figure 1: sequence diagram of the data migration without staging tables_
+
+The following example shows how to migrate items without staging tables:
+```al
+trigger OnRun();
+var
+ ItemDataMigrationFacade: Codeunit "Item Data Migration Facade";
+ ItemNumber: Integer;
+ ItemJson: Text;
+begin
+ // loop on items retrieved through a web service for example
+ for ItemNumber := 1 to ExternalWebService.GetItemCount do begin
+ ExternalWebService.GetItem(ItemNumber,ItemJson);
+
+ // create item using the facade
+ if not ItemDataMigrationFacade.CreateItemIfNeeded(ItemJson.ItemNumber,ItemJson.ItemName1,
+ ItemJson.ItemName2,ConvertItemType(ItemJson.ItemType)) then
+ exit; // item already exists
+
+ // set some fields using the facade
+ ItemDataMigrationFacade.SetVendorItemNo(ItemJson.VendItemNumber);
+ ItemDataMigrationFacade.SetUnitVolume(ItemJson.Volume);
+ ItemDataMigrationFacade.SetAlternativeItemNo(ItemJson.AltItemNumber);
+ if ItemJson.PrimaryVendor <> '' then
+ ItemDataMigrationFacade.SetVendorNo(ItemJson.PrimaryVendor);
+
+ // migrate dependencies
+ MigrateItemUnitOfMeasure(ItemDataMigrationFacade,ItemJson);
+
+ // modify the item (+run trigger) to save the changes made by setters
+ ItemDataMigrationFacade.ModifyItem(true);
+ // update the status in the migration dashboard
+ DataMigrationStatusFacade.IncrementMigratedRecordCount('My Migration Type',Database::Item,1);
+ end;
+end;
+
+procedure MigrateItemUnitOfMeasure(ItemDataMigrationFacade : Codeunit "Item Data Migration Facade"; ItemJson : Text)
+var
+ MyUnitCodeStagingTable: Record "My Unit Code Staging Table";
+ DataMigrationStatusFacade: Codeunit "Data Migration Status Facade";
+ DescriptionToSet: Text[10];
+ UnitCodeJson: Text;
+begin
+ if ItemJson.UnitCode = '' then
+ // log an error using the Data migration façade
+ DataMigrationStatusFacade.RegisterErrorNoStagingTablesCase(
+ 'My Migration Type',Database::Item,'Unit of measure is empty.');
+
+ if ExternalWebService.GetUnitCode(ItemJson.UnitCode,UnitCodeJson) then
+ DescriptionToSet := UnitCodeJson.Description;
+ ItemDataMigrationFacade.CreateUnitOfMeasureIfNeeded(ItemJson.UnitCode, DescriptionToSet);
+
+ // set the unit of measure on the item
+ ItemDataMigrationFacade.SetBaseUnitOfMeasure(ItemJson.UnitCode);
+end;
+```
+
+_Figure 2: Example of Item and Item Unit of Measure migration without staging tables_
+
+### Usage with staging tables:
+
+The overall workflow is:
+
+* Integrate your extension in the Data Migration Wizard by subscribing to the events exposed by the **Data Migration Façade**.
+
+* From there, initialize the status of the migration so it can be displayed in the **Data Migration Overview: "Data Migration Status Façade".InitStatusLine('My Migration Type',Database::Item,42000,Database::"My extension Staging table for items",0)**.
+* You can either fill the staging tables from the wizard events, or in a subscriber to the event dedicated to filling staging tables (**'OnFillStagingTables'** in codeunit 6100): in this case, the import of data from the files to the staging tables will be done in the background.
+* Launch the migration: **"Data Migration Façade".StartMigration('My Migration Type',false)**.
+* Subscribe to the events to migrate entities and their dependencies.
+* From the event subscribers, call the façade procedures to create entities and set their field values:
+
+ * **"Item Data Migration Façade".CreateItemIfNeeded('ITEM042','My Item Description';'My Item Description 2';ItemTypeToSet::Inventory)**
+ * **"Item Data Migration Façade".SetBaseUnitOfMeasure('BOX')**
+ * **"Item Data Migration Façade".ModifyItem(true)**
+
+[![ ][image2]][anchor2]
+
+_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)
+var
+ MyItemStagingTable : Record "My Item Staging Table";
+begin
+ // handle the event if it targets this extension's staging table
+ if RecordIdToMigrate.TableNo <> Database::"My Item Staging Table" then
+ exit;
+ MyItemStagingTable.Get(RecordIdToMigrate);
+
+ // create item using the facade
+ if not Sender.CreateItemIfNeeded(MyItemStagingTable.ItemNumber,MyItemStagingTable.ItemName1,
+ MyItemStagingTable.ItemName2,ConvertItemType(MyItemStagingTable.ItemType)) then
+ exit; // item already exists
+
+ // set some fields using the facade
+ Sender.SetVendorItemNo(MyItemStagingTable.VendItemNumber);
+ Sender.SetUnitVolume(MyItemStagingTable.Volume);
+ Sender.SetAlternativeItemNo(MyItemStagingTable.AltItemNumber);
+ if MyItemStagingTable.PrimaryVendor <> '' then
+ Sender.SetVendorNo(MyItemStagingTable.PrimaryVendor);
+
+ // modify the item (+run trigger) to save the changes made by setters
+ Sender.ModifyItem(true);
+end;
+```
+
+_Figure 4: Example of event subscriber for Item migration_
+
+Below is another example showing how to use additional events to set fields that reference other tables, here the unit of measure:
+
+```al
+[EventSubscriber(ObjectType::Codeunit, Codeunit::"Item Data Migration Facade", 'OnMigrateItemUnitOfMeasure', '', true, true)]
+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";
+ DescriptionToSet: Text[10];
+begin
+ // handle the event if it targets this extension's staging table
+ if RecordIdToMigrate.TableNo <> Database::"My Item Staging Table" then
+ exit;
+
+ MyItemStagingTable.Get(RecordIdToMigrate);
+ if MyItemStagingTable.UnitCode = '' then
+ error('Unit of measure is empty.');
+ MyUnitCodeStagingTable.SetRange(UnitCode,MyItemStagingTable.UnitCode);
+ if MyUnitCodeStagingTable.FindFirst then
+ DescriptionToSet := MyUnitCodeStagingTable.Description;
+
+ // create the unit of measure through the facade
+ Sender.CreateUnitOfMeasureIfNeeded(MyItemStagingTable.UnitCode, DescriptionToSet);
+
+ // set the unit of measure on the item
+ Sender.SetBaseUnitOfMeasure(MyItemStagingTable.UnitCode);
+
+ // modify the item to save the changes made by setter
+ Sender.ModifyItem(false);
+end;
+```
+
+_Figure 5: Example of event subscriber for Item Unit of Measure migration_
+
+### Combining both approaches:
+
+If you want to migrate additional entities, the **Data Migration** framework lets you initialize the migration with entities other than master data. In this case, the **Data Migration Overview** page will show additional lines. Item, vendor, customer, an general ledger accounts are migrated with an event driven approach, and the additional entities are migrated by calling an extension codeunit **OnRun** method.
+
+## Error handling with staging tables:
+
+The migration starts by calling **RUN** on the façade codeunit. Errors thrown during the call are captured by **GETLASTERRORTEXT** and displayed when you choose the **Show Errors** action on the **Data Migration Overview** page.
+
+[![ ][image3]][anchor3]
+
+_Figure 6: List of errors shown when clicking **Show Errors** on the **Data Migration Overview** page_
+
+The **Edit Record** action opens a view of the staging table, where you can edit fields to fix errors. Figure 4 shows an example of a page for the vendor staging table in a migration from C5\.
+
+[![ ][image4]][anchor4]
+
+_Figure 7: Edit a staging table record_
+
+The **Staging Table ID** determines the page to open, so it is important that the page ID is equal to the **Staging Table ID**, at least for the master data staging tables, for example, for **G/L Accounts, Items, Customers** and **Vendor**. You should ensure that pages to edit related entities are linked on this page by means of new actions. For example, Figure 4 uses the **C5 Purchaser** action.
+
+After you fix the staging table record, you can choose the **Migrate** action to mark the selected records as records to retry, and then run **StartMigration** with the **Retry** flag set to true. This is the only place where the retry flag should be set to true in the **StartMigration** procedure.
+
+Error handling without staging tables
+
+When migrating data without staging tables, errors can be registered manually by the extension using **DataMigrationStatusFacade.RegisterErrorNoStagingTablesCase**. Otherwise they can be registered automatically if the codeunits fail when called on their **OnRun** procedure.
+
+Errors will be displayed in the error list, but you cannot open and edit records because there is no staging table. The Edit action will not be available.
+
+## Limitations:
+
+* Data migration will fail if there are customers, vendors, items in the database and if these entities are selected for migration. For example, if you choose to migrate items and your company already contains items, you will get an error. This should not be an issue if you migrate your data from another tool to NAV, in which case you will most likely start on a fresh empty company. However, if you just want to import additional items to a company with existing items, then it is not supported by the framework. however, you can still use the different functions provided by the different codeunits (such as **Item Data Migration Facade**) to create the entities without strong coupling on the NAV data model.
+* **G/L entries** are deleted automatically.
+
+* There is no automated rollback in case of failure: data that is successfully migrated will be commited, and data that is not successfully migrated with be shown in the errors list. The retry feature (in case of staging tables) then makes it possible for you to retry individual entities or ignore them.
+
+## Usages in NAV:
+
+The Data Migration Façade is available starting from version 2018\.
+
+The following Façade codeunits for data migration management are available:
+
+* COD6100 (**Data Migration Facade**)
+
+* COD6101 (**Data Migration Status Facade**)
+
+The following Entity data migration façade codeunits are available:
+
+* COD6110 (**GL Acc. Data Migration Facade**)
+* COD6111 (**Vendor Data Migration Facade**)
+* COD6112 (**Customer Data Migration Facade**)
+
+* COD6113 (**Item Data Migration Facade**)
+* COD6114 (**Ex. Rate Data Migration Facade**)
+
+## References:
+
+Façade pattern on Wikipedia: https://en.wikipedia.org/wiki/Facade_pattern
+
+
+
+[anchor0]: 8308.logo.png
+[anchor1]: NoStagingTableNew2.png
+[anchor2]: StagingTableNew2.png
+[anchor3]: errorhandling1.png
+[anchor4]: errorhandling2.png
+
+
+[image0]: 8308.logo.png
+[image1]: NoStagingTableNew2.png
+[image2]: StagingTableNew2.png
+[image3]: errorhandling1.png
+[image4]: errorhandling2.png
diff --git a/content/docs/NAVPatterns/patterns/discovery-event/Pic2.jpg b/content/docs/NAVPatterns/patterns/discovery-event/Pic2.jpg
new file mode 100644
index 00000000..31139410
Binary files /dev/null and b/content/docs/NAVPatterns/patterns/discovery-event/Pic2.jpg differ
diff --git a/content/docs/NAVPatterns/patterns/discovery-event/Pic2b.jpg b/content/docs/NAVPatterns/patterns/discovery-event/Pic2b.jpg
new file mode 100644
index 00000000..00ad1fc3
Binary files /dev/null and b/content/docs/NAVPatterns/patterns/discovery-event/Pic2b.jpg differ
diff --git a/content/docs/NAVPatterns/patterns/discovery-event/Pic3.jpg b/content/docs/NAVPatterns/patterns/discovery-event/Pic3.jpg
new file mode 100644
index 00000000..f5c02e12
Binary files /dev/null and b/content/docs/NAVPatterns/patterns/discovery-event/Pic3.jpg differ
diff --git a/content/docs/NAVPatterns/patterns/discovery-event/Pic4.jpg b/content/docs/NAVPatterns/patterns/discovery-event/Pic4.jpg
new file mode 100644
index 00000000..0d3a1677
Binary files /dev/null and b/content/docs/NAVPatterns/patterns/discovery-event/Pic4.jpg differ
diff --git a/content/docs/NAVPatterns/patterns/discovery-event/ServiceConnections.jpg b/content/docs/NAVPatterns/patterns/discovery-event/ServiceConnections.jpg
new file mode 100644
index 00000000..4819bdef
Binary files /dev/null and b/content/docs/NAVPatterns/patterns/discovery-event/ServiceConnections.jpg differ
diff --git a/content/docs/NAVPatterns/patterns/discovery-event/index.md b/content/docs/NAVPatterns/patterns/discovery-event/index.md
new file mode 100644
index 00000000..d8cbf195
--- /dev/null
+++ b/content/docs/NAVPatterns/patterns/discovery-event/index.md
@@ -0,0 +1,89 @@
++++
+title = "Discovery Event"
+weight = 500
+tags = ["C/AL"]
+categories = ["Pattern"]
++++
+_by waldo_
+
+# Abstract
+
+The "Discovery Event" pattern is a way for a generic functionality, to call out to other functionalities that want to make use of it, by raising an event, so that they have an event to subscribe to. This is usually done to set itself up within the generic app.
+
+# The problem
+
+Let's suppose you have a generic piece of functionality, that hooks into lots of places (modules) in your application. To set this up, you might have to hook into all these parts of the application. Well, this pattern turns this setup around: let all the different modules set itself up in the generic app by raising a "discovery event".
+
+# Usage
+
+The pattern is most easily described when you look at an example. This example is an actual usage of the pattern within the application, in page **Service Connections.**
+
+[![ ][image0]][anchor0]
+
+The goal of this functionality is to:
+
+* List all the different connections to external services,
+* Have a central place to navigate to the corresponding setup of the service.
+
+The functionality (**Service Connections**) itself, is not aware of the state nor setup nor any context of all the different services in the list. All it does is:
+
+* It raises an event as an opportunity for all services within the NAV application to subscribe to,
+* It has a public function **InsertServiceConnection** that the subscribers can use to register itself at the Service Connection.
+
+The event **OnRegisterServiceConnection** is raised when the page (1279 - **Service Connections**) is opened.
+
+One example of a subscription is the SMTP setup. In Codeunit 400 you'll find the subscriber function **HandleSMTPRegisterServiceConnection** which subscribes to this discovery event, and calls the **InsertServiceConnection** to register itself.
+
+# Description
+
+The main idea of this pattern is: "Discover the settings, the context, the records, ... which I need for my functionality" or "Discover the configuration for my functionality". In any case, "discover" is the main idea. It's a pattern where using both publishers and subscribers in one application makes a lot of sense.
+Let's break down to the steps that are needed to implement the pattern.
+
+## Step 1: Publish the event
+
+In the below example, I create a table **Module Status** with a published event **OnDiscoverModuleStatuses**.
+
+[![ ][image1]][anchor1]
+
+You see that I also include the sender. This way, I will be able to access the methods on my table (which I use as a class). Obviously, other patterns can be applied here as well, like the Argument Table pattern.
+
+## Step 2: Raise the event on the right place
+
+When you publish an event, it should obviously be raised somewhere in the code as well. In the below example, I want to raise the event simply by a method which I want to call from a page. So I create a global function where I raise the event:
+
+[![ ][image2]][anchor2]
+
+## Step 3: Create one or more global functions, so that your subscriber can call into your functionality to configure, set up, or do whatever it needs to do to make itself discoverable
+
+The generic functionality that I want to call, should be part of the main class - in this case the **Module Discovery** class, or better, the table (**Module Status**). In this table, I create this global function, because I want to make it available for the subscribers:
+
+[![ ][image3]][anchor3]
+The business logic doesn't really matter for this pattern. This is obviously dependent on the functionality where you would like to implement the pattern.
+
+## Step 4: subscribe from the places in the app to this event, use the global function(s)
+
+This could be anywhere. Any module within your vertical, of within the main application, can subscribe to the event. In the example below, I create the subscriber in Codeunit80, as I was interested in the status of the Sales-module in default NAV.
+The exact place of the subscriber is up to you. The main message is that it's part of the module that wants to subscribe, and not part of the **Module Status** module in the application.
+Here is the subscriber (and one small helper function):
+
+[![ ][image4]][anchor4]
+You see I can use the "sender" as a normal Record-variable. I access the previously created global function to "register" this sales-module.
+
+# Microsoft Dynamics NAV Versions
+
+This pattern only works with Microsoft Dynamics **NAV 2016 and up.**
+
+
+
+[anchor0]: ServiceConnections.jpg
+[anchor1]: Pic2.jpg
+[anchor2]: Pic2b.jpg
+[anchor3]: Pic3.jpg
+[anchor4]: Pic4.jpg
+
+
+[image0]: ServiceConnections.jpg
+[image1]: Pic2.jpg
+[image2]: Pic2b.jpg
+[image3]: Pic3.jpg
+[image4]: Pic4.jpg
diff --git a/content/docs/NAVPatterns/patterns/document/0005.Document-Pattern-UML-Class-Diagram.jpg b/content/docs/NAVPatterns/patterns/document/0005.Document-Pattern-UML-Class-Diagram.jpg
new file mode 100644
index 00000000..8a6bf1f0
Binary files /dev/null and b/content/docs/NAVPatterns/patterns/document/0005.Document-Pattern-UML-Class-Diagram.jpg differ
diff --git a/content/docs/NAVPatterns/patterns/document/2086.Design-Pattern-Document-SubPage-Properties.png b/content/docs/NAVPatterns/patterns/document/2086.Design-Pattern-Document-SubPage-Properties.png
new file mode 100644
index 00000000..aaa2c9a9
Binary files /dev/null and b/content/docs/NAVPatterns/patterns/document/2086.Design-Pattern-Document-SubPage-Properties.png differ
diff --git a/content/docs/NAVPatterns/patterns/document/index.md b/content/docs/NAVPatterns/patterns/document/index.md
new file mode 100644
index 00000000..66120a29
--- /dev/null
+++ b/content/docs/NAVPatterns/patterns/document/index.md
@@ -0,0 +1,114 @@
++++
+title = "Document"
+weight = 510
+tags = ["C/AL"]
+categories = ["Pattern"]
++++
+_By Xavier Garonnat, knk Ingénierie (France), xgaronnat@knk.fr_
+
+## Abstract
+
+A document structure contains a header and a set of lines. Each line is linked to the header and could have common data with header.
+
+## Description
+
+This pattern should be used as a basis to build any document, showing a header and multiple lines in the same page. Basically, a document is at least composed of two tables and three pages, as shown below:
+
+[![ ][image0]][anchor0]
+
+## Usage
+
+You should use it any time you have to capture and store a document.
+
+## Example
+
+To build this example from scratch, you will need:
+
+* Two tables, one for the header (called "Document Header"), and one for the document lines (called "Document Line"). Each document will be composed of "1 to N" line(s).
+* Three pages, one for the header, one for the subpage (lines), and the last for the document list obviously.Table "Document Header"
+
+**Table "Document Header"**: Is the "header" table of your document (like Sales Header, Purchase Header, Transfer Header ...)
+
+* Add a field "No." (Code 20): Should be the first field and primary key of your documents, to be driven by Serial No. (See corresponding design pattern)
+
+For this sample, I just added a "Sell-to Customer No." to this table. Don't forget to manage deletion of lines with trigger OnDelete().
+
+**Table "Document Line"**: will store the lines of the document
+
+* Add a field "Document No." (Code 20): Should be the first field and is related to table "Document Header": set TableRelation to your "Document Header" table
+* Add a field "Line No." (Integer): this field will be populated automatically by the subpage Page (see AutoSplitKey)
+
+First (Primary) Key must be "Document No.,Line No.". On table properties, set PasteIsValid to No (to avoid copying/pasting lines, will be implemented by "Copy document", another pattern).
+
+For my sample, I just add a couple of fields: "Item No." and "Quantity" to this table (just copy/paste standard fields from "Sales Line" table and delete trigger code, this will insure that each field will be well designed)
+
+**Page "Document Subpage"**: will display the lines in the main form, and will be in charge of assigning line number automatically.
+
+Create the page for table "Document Line" with the wizard by selecting the ListPart template, add all yours fields except the primary key ("Document No." and "Line No.").
+
+Then edit the properties:
+
+* Set AutoSplitKey, DelayedInsert and MultipleNewLines to Yes: this combination will make your subpage work as required.
+* AutoSplitKey is used to set NAV calculate the last field of the key ("Line No.") with proper numbers (10000, 20000...).
+
+Set caption to "Lines". Save your page, we will use it on the next step.
+
+**Page "Document"**: will display the document, and the lines with subpage.
+
+Create the page for "Document Header" Table with the wizard by selecting the Document template:
+
+* Add a General FastTab
+* Add all the revelant fields for the user (or at least "No.")
+* Click Finish to close the wizard
+
+Then simply add your subpage as new line in the designer, and adjust the property "SubFormPerLink" with "Document No.=FIELD(No.)" to link header and lines :
+
+[![ ][image1]][anchor1]
+
+**Page "Document List":** Use the Page wizard to create a List page based on the Document table and add fields, FactBox (RecordLinks, Notes...), etc.
+
+Once created:
+
+* Set Editable to No on the List
+* CardPageID to Page "Document" to enable New/Edit/... Pane actions.
+
+Save our page and add it to the Role Page "Order Processor Role Center" for example.
+
+Now, observe how "Line No." is calculated on the first line, and when inserting a new line between the first and second one.
+
+Code sample (copy link to your browser) : https://knk1fr-my.sharepoint.com/personal/xgaronnat_knk_fr/_layouts/15/guestaccess.aspx?guestaccesstoken=hL0P%2fyQ1ZreY5KlSPc%2b8dHrO4zjUkqQbg8DnGSbgd1Y%3d&docid=02b3cb93e1ff1459380891795fb8441fc
+
+## NAV Usages
+
+So many: Sales Order, Purchase Order, Transfer Order, Assembly Order...
+
+For posted document, it's quite similar, but you don't have to setup subpage properties like AutoSplitKey, used for data entry purpose only (and your pages content should be mainly read-only / non editable).
+
+## Ideas for improvement
+
+* A new property like "AutoSplitStartNumber", enabled if AutoSplitKey=Yes, default value with <10000\>. Allow to change the numbers of created line.
+* Be able to copy/paste header AND lines or import header and line from an Excel file.
+
+## When it should not be used
+
+This pattern is mainly used for Documents, and may not be used directly for Master data or any other table (Setup, Supplemental, etc...).
+
+## Related Topics
+
+Use Series No. Pattern for your documents, and Copy Document to implement document duplication.
+
+## References
+
+Walkthrough: Creating a Document Page : [http://msdn.microsoft.com/en-us/library/dd338599(v=nav.71).aspx][anchor2]
+{{< youtube S9cRD2D4c0>}}
+
+
+
+[anchor0]: 0005.Document-Pattern-UML-Class-Diagram.jpg
+[anchor1]: 2086.Design-Pattern-Document-SubPage-Properties.png
+[anchor2]: http://msdn.microsoft.com/en-us/library/dd338599(v=nav.71).aspx "http://msdn.microsoft.com/en-us/library/dd338599(v=nav.71).aspx"
+[anchor3]: https://www.youtube.com/watch?v=S9cRD2D4c_0&list=PLhZ3P-LY7CqmVszuvtJLujFyHpsVN0U_w&index=27
+
+
+[image0]: 0005.Document-Pattern-UML-Class-Diagram.jpg
+[image1]: 2086.Design-Pattern-Document-SubPage-Properties.png
diff --git a/content/docs/NAVPatterns/patterns/easy-update-of-setup-or-supplementary-information/0654.easy-update-1.png b/content/docs/NAVPatterns/patterns/easy-update-of-setup-or-supplementary-information/0654.easy-update-1.png
new file mode 100644
index 00000000..9813a3cb
Binary files /dev/null and b/content/docs/NAVPatterns/patterns/easy-update-of-setup-or-supplementary-information/0654.easy-update-1.png differ
diff --git a/content/docs/NAVPatterns/patterns/easy-update-of-setup-or-supplementary-information/4024.easy-update-2.png b/content/docs/NAVPatterns/patterns/easy-update-of-setup-or-supplementary-information/4024.easy-update-2.png
new file mode 100644
index 00000000..02b417a5
Binary files /dev/null and b/content/docs/NAVPatterns/patterns/easy-update-of-setup-or-supplementary-information/4024.easy-update-2.png differ
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
new file mode 100644
index 00000000..f652f12f
--- /dev/null
+++ b/content/docs/NAVPatterns/patterns/easy-update-of-setup-or-supplementary-information/index.md
@@ -0,0 +1,100 @@
++++
+title = "Easy Update Of Setup Or Supplementary Information"
+weight = 520
+tags = ["C/AL"]
+categories = ["Pattern"]
++++
+_Originally by Anders Larsen at Microsoft Development Center Copenhagen_
+
+## Abstract
+
+Users or the administrator must regularly update setup or supplementary information in the day-to-day business, such as setting up a new type of customer. This setup task often arrives when their focus is on registration and execution instead of setup.
+
+The navigation experience around these extra steps is often quite troublesome and time-consuming. To enable users to easily perform the needed update, a guide often gives the best support.
+
+To guide users, we can prompt them with a dialog on which they can update the setup or supplementary information instantly and easily, so that they can proceed with the business task without being side-tracked.
+
+## Usage
+
+Define two functions in the setup or supplementary table: One for verifying if the needed information is available, and another for exposing the page that contains the fields that the user must update.
+
+Call the code. For example:
+```
+Local IsXAvailable : Boolean
+If field X <> '' then
+Exit(True)
+Exit(false)
+
+VerifyAndSetX
+If IsXAvailable then
+Exit;
+If Confirm('Field X is missing a value. Do you want to update it now?') then
+Open the card page in edit mode
+
+If not IsXAvailable then
+Error(Field X is missing a value. Please correct it.)
+```
+
+The calling code
+
+```
+..
+
+SetupTable.VerifyAndSetX
+
+..
+```
+## NAV Specific Example
+
+In the **Sales & Receivables Setup** table (311) for the DK version, the following procedures have been added:
+
+```al
+Local Procedure IsOIOUBLPathAvailable(...)
+
+Procedure VerifyAndSetOIOUBLPath(...)
+```
+
+The code in the **Sales & Receivables Setup** table can now be called directly from the related processing codeunit, such as the **Sales-Post + Print** codeunit (82).
+
+Were the code is called:
+
+```al
+IF ("EAN No." <> '') THEN
+ SalesSetup.VerifyAndSetOIOUBLPathSetup(SalesHeader."Document Type");
+```
+
+If the setup is not updated properly, the user is prompted to update it as follows.
+
+[![ ][image0]][anchor0]
+
+Choosing **Yes** opens the related setup page.
+
+[![ ][image1]][anchor1]
+
+## NAV Usages
+
+* Report 206, **Sales invoice**
+* Table 79, **Company Information**
+* In OIOUBL fields (DK version) during posting/printing of a sales invoice.
+
+## Ideas for Improvement
+
+Make a more generic platform implementation that launches the corresponding card page for Rec on Rec.testfield with an asterisk mark for the field that needs a proper value.
+
+## Related Topics
+
+The anti-pattern is to do a testfield on a field that is not in the table that you are currently updating.
+
+The test field message can often be confusing because the pages are often named differently than the tables, which can lead to misunderstanding and context-switching.
+
+{{< youtube oeASJN-zqTo>}}
+
+
+
+[anchor0]: 0654.easy-update-1.png
+[anchor1]: 4024.easy-update-2.png
+[anchor2]: https://www.youtube.com/watch?v=oeASJN-zqTo&list=PLhZ3P-LY7CqmVszuvtJLujFyHpsVN0U_w&index=18
+
+
+[image0]: 0654.easy-update-1.png
+[image1]: 4024.easy-update-2.png
diff --git a/content/docs/NAVPatterns/patterns/error-message-processing/image001.png b/content/docs/NAVPatterns/patterns/error-message-processing/image001.png
new file mode 100644
index 00000000..82c8cfd9
Binary files /dev/null and b/content/docs/NAVPatterns/patterns/error-message-processing/image001.png differ
diff --git a/content/docs/NAVPatterns/patterns/error-message-processing/image003.png b/content/docs/NAVPatterns/patterns/error-message-processing/image003.png
new file mode 100644
index 00000000..bfebcaf8
Binary files /dev/null and b/content/docs/NAVPatterns/patterns/error-message-processing/image003.png differ
diff --git a/content/docs/NAVPatterns/patterns/error-message-processing/index.md b/content/docs/NAVPatterns/patterns/error-message-processing/index.md
new file mode 100644
index 00000000..07d9c27c
--- /dev/null
+++ b/content/docs/NAVPatterns/patterns/error-message-processing/index.md
@@ -0,0 +1,108 @@
++++
+title = "Error Message Processing"
+weight = 550
+tags = ["C/AL"]
+categories = ["Pattern"]
++++
+_By Jesper Schulz at Microsoft Development Center Copenhagen_
+
+Note: This pattern describes new functionality which makes it possible to generalize the [Journal Error Processing][anchor0] pattern.
+
+## Abstract
+
+Missing, invalid or incomplete data is a common issue during data processing in NAV. This article describes how to collect all error messages during processing using the error message component and present them to the user in a unified way, which enables the user to correct the errors efficiently. By leveraging the integrated error message logging functions, you can log a message with a single line of code and present it to the user with another one-liner.
+
+## Description
+
+This article describes how to use the Error Message component in NAV, which in short gives you the possibility to:
+
+1. Link an error message to the page which enables you to resolve the problem.
+2. Assemble all error messages in one central view instead of having to encounter them one by one.
+
+Validating data is a common task during data processing in NAV. Unfortunately, validation is often done using NAV's integrated ERROR and TESTFIELD functions, which halt execution of the process. The user will then have to locate the invalid / missing data, correct it and reinitiate the process, possibly running into the next error, making the cycle repeat itself. This can be a very tedious, time-consuming and frustrating process. The error message component aims at improving this experience by providing a lightweight framework for error message logging and this article will explain how to leverage this functionality in your code. By doing so, all error messages are gathered during (pre-)processing and are finally presented to the user. The user then has the possibility to click on the error message, which will open the record where the invalid / missing data is located, thereby enabling the user to correct all mistakes efficiently, from one central place and in one go.
+
+The example below comes from a Mexican localization, where the user has to export financial balances and transactions into an XML file for government audit purposes. In order to generate valid files, some mandatory data needs to be entered in the system. By leveraging the error message component, the user will be presented with the following page, if missing or invalid data was discovered:
+
+[![ ][image0]][anchor1]
+
+By clicking on the error message, the user will be presented with the entity, where the invalid / missing data should be corrected / added. This is done by applying the related pattern [Easy Update of Setup or Supplementary Information][anchor2].
+
+[![ ][image1]][anchor3]
+
+## Usage
+
+In the processing function, define a temporary record of type "Error Message". Use the functions on that record to populate the record with error message, a few of them being:
+
+* **LogIfEmpty**
+* **LogIfLengthExceeded**
+* **LogIfInvalidCharacters**
+* **LogIfOutsideRange**
+* **LogIfGreaterThan**
+* **LogIfEqualTo**
+* **LogMessage**
+
+The following parameters must be provided to these functions:
+
+* **Record:** The record which you want to check
+* **Field Number:** The field number of the field you want to verify the value of
+* **Condition:** The condition the field must meet (e.g. length, range, valid characters)
+* **Message Type:** The type of message, which can be Error, Warning or Message
+
+When the processing is complete, you can check if any error messages of type "Error" were logged by calling the **HasErrors** function and you can show the list or error messages by calling the **ShowErrorMessages** function. You can also integrate the error messages list as a FactBox, but that is not part of this example.
+
+## NAV Specific Example
+
+The code below is an example of how the error message component was used in one part of the before mentioned Mexican feature. This code iterates over all G/L Accounts and pipes information out into an XML file. While doing so, it is validated that all mandatory fields have values and meet certain conditions. And only if that is the case, is the XML document actually exported. Also notice, that an error message is logged, in case no G/L Accounts are found given the provided filters. That way, the user can be guided to setup the system correctly.
+
+```AL
+PROCEDURE ExportChartOfAccounts@1(Year@1000 : Integer;Month@1001 : Integer);
+VAR
+ TempErrorMessage@1003 : TEMPORARY Record 700;
+BEGIN
+ TempErrorMessage.ClearLog; // only necessary if variable is global
+ ...
+ CreateXMLHeader(Document,RootNode,CatalogoNodeTxt,Namespace,Year,Month,'1.1');
+
+ IF GLAccount.FINDSET THEN BEGIN
+ REPEAT
+ TempErrorMessage.LogIfEmpty (GLAccount,GLAccount.FIELDNO(Name),TempErrorMessage."Message Type"::Error);
+
+ XMLDOMManagement.AddElement(RootNode,'Ctas','',Namespace,Node);
+ XMLDOMManagement.AddAttribute(Node,'CodAgrup',GLAccount."SAT Account Code");
+ ...
+ CASE GLAccount."Debit/Credit" OF
+ GLAccount."Debit/Credit"::Debit:
+ XMLDOMManagement.AddAttribute(Node,'Natur','D');
+ GLAccount."Debit/Credit"::Credit:
+ XMLDOMManagement.AddAttribute(Node,'Natur','A');
+ ELSE
+ TempErrorMessage.LogMessage(
+ GLAccount,GLAccount.FIELDNO("Debit/Credit"),TempErrorMessage."Message Type"::Error,
+ STRSUBSTNO(GLAccountTypeErr,GLAccount."Debit/Credit",GLAccount.RECORDID));
+ END;
+ UNTIL GLAccount.NEXT = 0;
+ END ELSE
+ TempErrorMessage.LogSimpleMessage(TempErrorMessage."Message Type"::Error,NoSATAccountDefinedErr);
+
+ IF NOT TempErrorMessage.HasErrors(TRUE) THEN
+ SaveXMLToClient(Document,Year,Month,'CT');
+ TempErrorMessage.ShowErrorMessages(FALSE);
+END;
+```
+
+One could also do pre-processing in a function of its own, and only if the pre-processing results in no error messages of type "Error" would the processing continue.
+
+## Ideas for improvement
+
+By using this easy to use component, we have the possibility to extend this functionality going forward. A nice addition to the error message component would be the possibility to log the error messages persistently in a grouped manner, thereby allowing 3rd parties to see the issues the users bump into the most, or allowing 3rd parties to get an detailed insight into what happened, thereby enabling them to provide better support.
+
+
+
+[anchor0]: /navpatterns/1-patterns/journal-error-processing/
+[anchor1]: image001.png
+[anchor2]: /navpatterns/1-patterns/easy-update-of-setup-or-supplementary-information/
+[anchor3]: image003.png
+
+
+[image0]: image001.png
+[image1]: image003.png
diff --git a/content/docs/NAVPatterns/patterns/extending-the-role-center-headlines/0724.Headline-sequence-diagram-v2.png b/content/docs/NAVPatterns/patterns/extending-the-role-center-headlines/0724.Headline-sequence-diagram-v2.png
new file mode 100644
index 00000000..d6442002
Binary files /dev/null and b/content/docs/NAVPatterns/patterns/extending-the-role-center-headlines/0724.Headline-sequence-diagram-v2.png differ
diff --git a/content/docs/NAVPatterns/patterns/extending-the-role-center-headlines/3733.logo.png b/content/docs/NAVPatterns/patterns/extending-the-role-center-headlines/3733.logo.png
new file mode 100644
index 00000000..9ebb1d36
Binary files /dev/null and b/content/docs/NAVPatterns/patterns/extending-the-role-center-headlines/3733.logo.png differ
diff --git a/content/docs/NAVPatterns/patterns/extending-the-role-center-headlines/Headline.png b/content/docs/NAVPatterns/patterns/extending-the-role-center-headlines/Headline.png
new file mode 100644
index 00000000..56b3cd2c
Binary files /dev/null and b/content/docs/NAVPatterns/patterns/extending-the-role-center-headlines/Headline.png differ
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
new file mode 100644
index 00000000..3f5b4615
--- /dev/null
+++ b/content/docs/NAVPatterns/patterns/extending-the-role-center-headlines/index.md
@@ -0,0 +1,157 @@
++++
+title = "Extending the Role Center Headlines"
+weight = 560
+tags = ["C/AL"]
+categories = ["Pattern"]
++++
+_By David Bastide at Microsoft Development Center Copenhagen_
+
+[![ ][image0]][anchor0]
+
+## Context
+
+
+Headlines are designed as a page of type HeadlinePart containing at least one text field. The part is added to the top of Role Center pages.
+This document provides an elegant and extensible pattern about how to extend the Role Center headlines to add your own business headlines based on your data, and display them only if relevant.
+
+## Description
+
+
+The Dynamics 365 Business Central release (April 2018) introduces a new HeadlinePart page type. This page type defines a page that rotates a display of several headlines after another, in the web client. A user can also click to switch to another headline. Headlines can also include a drilldown action that will be invoked when the user clicks the headline Text of the payload can be emphasized.
+Headlines are divided in 2 parts: the qualifier, and the payload as you can see in the figure below.
+
+[![ ][image1]][anchor1]
+
+_Figure 1: Qualifier, Payload and emphasized text._
+
+## Usage
+
+A page part has been added to each major Role Center:
+
+* Page 9006 Order Processor Role Center, contains Page 1441 Headline RC Order Processor
+* Page 9015 Job Project Manager RC, contains Page 1443 Headline RC Project Manager
+* Page 9022 Business Manager Role Center, contains Page 1440 Headline RC Business Manager
+* Page 9024 Security Admin Role Center, contains Page 1445 Headline RC Administrator
+* Page 9026 Sales & Relationship Mgr. RC, contains Page 1444 Headline RC Relationship Mgt.
+* Page 9027 Accountant Role Center, contains Page 1442 Headline RC Accountant
+* Page 9028 Team Member Role Center, contains Page 1446 Headline RC Team Member
+* Page 9010 Production Planner Role Center, contains Page 1447 Headline RC Prod. Planner
+* Page 9016 Service Dispatcher Role Center, contains Page 1448 Headline RC Serv. Dispatcher
+
+You can create extensions that extend these pages to add your own headlines.
+If no headline is added on these pages, fallback headlines will be displayed.
+
+The process to extend the headlines of a Role Center is simple:
+
+1. In a V2 extension, extend the pages (PAG1440 to 1446) with one or more fields you want to add as headlines. The field and its visibility should be variables that are populated in OnAfterGetRecord.
+2. Subscribe to the OnComputeHeadlines event from the codeunits associated with the page (same ID and name as the page). Here you can compute your headlines. You should store the result in a table in your extension, so you can quickly get the results in step 3\. The computation is done in a background task, not to decrease the performance of the role center pages.
+3. Subscribe to the OnIsAnyExtensionHeadlineVisible event from the page. This event is used to determine if any extension has visible headlines, and if so, hide the fallback headlines. You should set the ExtensionHeadlinesVisible variable to true if your extension has headlines to display at the time of the event. Otherwise, do nothing.
+4. In the page, in the OnAfterGetRecord trigger, get the headline text and visibility values and copy them to your added fields.
+
+To format headlines, you should use Codeunit 1439 Headline Management functions:
+
+* Truncate: to truncate a text gracefully when possible with "...". For example, HeadlineManagement.Truncate('the text',6) returns "the...".
+* Emphasize: to emphasize part of the headline payload. Emphasized text is shown with a different style.
+* GetHeadlineText: to build the headline text. You provide the headline qualifier and payload, and you get the headline in a format that will be interpreted and formatted correctly by the client. It returns false if the qualifier exceeds its maximum length (50 characters) or payload exceeds its maximum length (75 characters). In that case it will not return the headline. If the qualifier you specify is empty, the default text "HEADLINE" will be displayed in the qualifier area. The payload must not be empty.
+
+### Examples:
+
+#### 1. Extending the page with a new headline:
+```al
+group(LargestSale)
+{
+ Visible = LargestSaleVisible;
+ ShowCaption=false;
+ Editable=false;
+
+ field(LargestSaleText;LargestSaleText)
+ {
+ ApplicationArea = Basic, Suite;
+ DrillDown=true;
+
+ trigger OnDrillDown()
+ var
+ EssentialBusHeadlineMgt: Codeunit "Essential Bus. Headline Mgt.";
+ begin
+ EssentialBusHeadlineMgt.OnDrillDownLargestSale();
+ end;
+ }
+}
+```
+
+#### 2. Subscribing to the OnComputeHeadlines event, and computing headlines
+```al
+[EventSubscriber(ObjectType::Codeunit, Codeunit::"Headline RC Business Manager", 'OnComputeHeadlines', '', true, true)]
+ procedure OnComputeHeadlinesBusinessManager()
+ begin
+ // [...] compute headline, and init the EssentialBusinessHeadline record
+ if not ShowHeadline then
+ exit; // not enough data to compute headline
+
+ if not HeadlineManagement.GetHeadlineText(
+ 'Insight from last week',
+ StrSubstNo('The largest posted sales invoice was for %1',
+ HeadlineManagement.Emphasize(Format(CustomerLedgerEntry.Amount, 0, TypeHelper.GetAmountFormatWithUserLocale('$'))))
+ EssentialBusinessHeadline."Headline Text")
+ then
+ exit;
+
+ EssentialBusinessHeadline.Validate("Headline Visible", true);
+ EssentialBusinessHeadline.Modify();
+end;
+```
+
+#### 3. Subscribing to the OnIsAnyExtensionHeadlineVisible event
+
+```al
+[EventSubscriber(ObjectType::Page, Page::"Headline RC Business Manager", 'OnIsAnyExtensionHeadlineVisible', '', true, true)]
+procedure OnIsAnyExtensionHeadlineVisible(var ExtensionHeadlinesVisible: Boolean)
+var
+ EssentialBusinessHeadline: Record "Essential Business Headline";
+ AtLeastOneHeadlineVisible: Boolean;
+begin
+ EssentialBusinessHeadline.SetRange("Headline Visible", true);
+ EssentialBusinessHeadline.SetFilter("Headline Name",'%1|%2|%3|%4|%5',
+ EssentialBusinessHeadline."Headline Name"::LargestOrder,
+ EssentialBusinessHeadline."Headline Name"::LargestSale,
+ EssentialBusinessHeadline."Headline Name"::BusiestResource,
+ EssentialBusinessHeadline."Headline Name"::MostPopularItem,
+ EssentialBusinessHeadline."Headline Name"::SalesIncrease,
+ EssentialBusinessHeadline."Headline Name"::TopCustomer);
+
+ AtLeastOneHeadlineVisible := not EssentialBusinessHeadline.IsEmpty();
+ // only modify the var if this extension is making some headlines visible, setting to false could override some other extensions setting the value to true
+ if AtLeastOneHeadlineVisible then
+ ExtensionHeadlinesVisible := true;
+end;
+```
+####
+
+#### 4. Setting the headline text on the page
+
+```al
+trigger OnAfterGetRecord()
+begin
+ EssentialBusinessHeadline.GetHeadline(EssentialBusinessHeadline."Headline Name"::LargestSale);
+ LargestSaleVisible := EssentialBusinessHeadline."Headline Visible";
+ LargestSaleText := EssentialBusinessHeadline."Headline Text";
+end;
+```
+
+[![ ][image2]][anchor2]
+
+_Figure 2: Sequence diagram of headline usage_
+
+## Usages in NAV:
+
+* Essential Business Headlines extension
+
+
+[anchor0]: 3733.logo.png
+[anchor1]: Headline.png
+[anchor2]: 0724.Headline-sequence-diagram-v2.png
+
+
+[image0]: 3733.logo.png
+[image1]: Headline.png
+[image2]: 0724.Headline-sequence-diagram-v2.png
diff --git a/content/docs/NAVPatterns/patterns/feature-localization-for-data-structures/3058.Feature-localization-for-data-structures-3.png b/content/docs/NAVPatterns/patterns/feature-localization-for-data-structures/3058.Feature-localization-for-data-structures-3.png
new file mode 100644
index 00000000..8da249fe
Binary files /dev/null and b/content/docs/NAVPatterns/patterns/feature-localization-for-data-structures/3058.Feature-localization-for-data-structures-3.png differ
diff --git a/content/docs/NAVPatterns/patterns/feature-localization-for-data-structures/5123.Feature-localization-for-data-structures-1.png b/content/docs/NAVPatterns/patterns/feature-localization-for-data-structures/5123.Feature-localization-for-data-structures-1.png
new file mode 100644
index 00000000..c63ae3f4
Binary files /dev/null and b/content/docs/NAVPatterns/patterns/feature-localization-for-data-structures/5123.Feature-localization-for-data-structures-1.png differ
diff --git a/content/docs/NAVPatterns/patterns/feature-localization-for-data-structures/6052.Feature-localization-for-data-structures-2.png b/content/docs/NAVPatterns/patterns/feature-localization-for-data-structures/6052.Feature-localization-for-data-structures-2.png
new file mode 100644
index 00000000..7fca9371
Binary files /dev/null and b/content/docs/NAVPatterns/patterns/feature-localization-for-data-structures/6052.Feature-localization-for-data-structures-2.png differ
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
new file mode 100644
index 00000000..cd110eb1
--- /dev/null
+++ b/content/docs/NAVPatterns/patterns/feature-localization-for-data-structures/index.md
@@ -0,0 +1,264 @@
++++
+title = "Feature Localization For Data Structures"
+weight = 570
+tags = ["C/AL"]
+categories = ["Pattern"]
++++
+_Originally by Bogdan Sturzoiu at Microsoft Development Center Copenhagen_
+
+## Abstract
+
+This pattern shows a solution for integrating W1 features to pre-existing country features that use different tables to achieve similar functionality.
+
+## Description
+
+It sometimes happens that certain features are requested in a NAV-supported country, but they are not initially considered generic enough to be included in the W1 build. This is how local features, such as Subcontracting in Italy and India, were created or specific banking and payments functionality in Italy, France, Spain, and others.
+
+Then, at some point in time, a decision is made to create a W1 feature that is closely related to the local functionality but uses a completely different set of tables, pages, etc. The developers now face the following problem: How to enable the newly-developed W1 feature into a country, such that the customers who are accustomed to their local structures can seamlessly continue working without completely (or immediately) switching to the W1 objects.
+
+This was the issue that was tackled in the NAV 2013 R2, in relation to the SEPA Credit Transfers functionality.
+
+### Using a Proxy
+
+The generic Proxy pattern is "a class functioning as an interface to something else" ([Wikipedia][anchor0]).
+
+[![ ][image0]][anchor1]
+
+Figure 1\. Proxy in UML
+
+### Pattern Elements
+
+The NAV data model translation of the proxy pattern can be used as explained below.
+
+The RealSubject is the NAV data model. Variations in table structures, relationships, and numbers are particular to each country. The W1 model is the base for the country-localized data models. However, some countries have heavy localizations which cannot be directly processed by the W1 core objects.
+
+The proxy is a codeunit that gathers data from wherever it is stored and transforms it to fit into a standard table, which is later used across all localizations.
+
+The interface is the fixed form in which the data is presented to be consumed by the client.
+
+The client can be an XML port that is fed from the common data interface. It can also be any other data processor (a codeunit fed to another table, etc.) or data display object (page or report).
+
+### Pattern Steps
+
+1. The user creates records in the local tables.
+2. The user invokes an action that must be processed using the W1 feature code.
+
+1. The proxy codeunit moves the data from the local tables to the W1 tables, either into a temporary or persistent set of records, as needed.
+
+1. The W1 code now performs the action on the W1 table data.
+
+## Usage
+
+In NAV 2013 R2, we released the SEPA Credit Transfer functionality. It involves exporting vendor payments to an XML file that is subsequently processed by the customer's bank. The payments are exported from the Payment Journal page through a configurable XMLport. Therefore, the data source for these payment lines is the Gen. Journal Line table (81).
+
+In various countries, we already had payment export functionality, usually into flat bank files. However, the files are generated from different tables than in W1\. For example, in Italy, vendor payments are handled through the Vendor Bill Header table (12181) and the Vendor Bill Line table (12182). They are the RealSubject.
+
+The W1 feature flow is as follows:
+
+[![ ][image1]][anchor2]
+
+Figure 2\. W1 object call sequence
+
+**Note:** CT = Credit Transfers, pain = payments initiation (the XML format used for SEPA Credit Transfers and SEPA Direct Debit).
+
+The key question is: Where to tap into this flow when using a local data structure. For this purpose, a proxy codeunit has been added in W1, called 1222 -- SEPA CT-Prepare Source. This codeunit feeds the client (XML1000) data in a standard format (the interface is the Gen. Journal Line table (81)).
+
+In W1, the codeunit simply outputs the same set of general journal lines that it receives as an input:
+
+```AL
+OnRun(VAR Rec : Record "Gen. Journal Line")
+
+GenJnlLine.COPYFILTERS(Rec);
+
+CopyJnlLines(GenJnlLine,Rec);
+
+LOCAL CopyJnlLines(VAR FromGenJnlLine : Record "Gen. Journal Line";VAR TempGenJnlLine : TEMPORARY Record "Gen. Journal Line")
+
+IF FromGenJnlLine.FINDSET THEN BEGIN
+
+GenJnlBatch.GET(FromGenJnlLine."Journal Template Name",FromGenJnlLine."Journal Batch Name");
+
+REPEAT
+
+TempGenJnlLine := FromGenJnlLine;
+
+TempGenJnlLine.INSERT;
+
+UNTIL FromGenJnlLine.NEXT = 0
+
+END ELSE
+
+CreateTempJnlLines(FromGenJnlLine,TempGenJnlLine);
+
+LOCAL CreateTempJnlLines(VAR FromGenJnlLine : Record "Gen. Journal Line";VAR TempGenJnlLine : TEMPORARY Record "Gen. Journal Line")
+
+// To fill TempGenJnlLine from the source identified by filters set on FromGenJnlLine
+
+TempGenJnlLine := FromGenJnlLine;
+```
+
+In a country, such as Italy, the codeunit will have the following functions:
+
+1. Gets an empty set of general journal lines that carry the local payment document key as a filter on the Document No. field (as opposed to W1 that gets the real set of records to be exported). This is done so that the local data can be extracted at runtime.
+
+1. Selects the local payment data, for example in Italy, in the Vendor Bill Header and Vendor Bill Lines tables.
+
+1. Transforms the local payment data into temporary records of the Gen. Journal Line table.
+
+1. Outputs the temporary general journal lines that will be further processed and exported, exactly as in W1\.
+
+```AL
+OnRun(VAR Rec : Record "Gen. Journal Line")
+
+GenJnlLine.COPYFILTERS(Rec);
+
+CopyJnlLines(GenJnlLine,Rec);
+
+LOCAL CopyJnlLines(VAR FromGenJnlLine : Record "Gen. Journal Line";VAR TempGenJnlLine : TEMPORARY Record "Gen. Journal Line")
+
+IF FromGenJnlLine.FINDSET THEN BEGIN
+
+GenJnlBatch.GET(FromGenJnlLine."Journal Template Name",FromGenJnlLine."Journal Batch Name");
+
+REPEAT
+
+TempGenJnlLine := FromGenJnlLine;
+
+TempGenJnlLine.INSERT;
+
+UNTIL FromGenJnlLine.NEXT = 0
+
+END ELSE
+
+CreateTempJnlLines(FromGenJnlLine,TempGenJnlLine);
+
+LOCAL CreateTempJnlLines(VAR FromGenJnlLine : Record "Gen. Journal Line";VAR TempGenJnlLine : TEMPORARY Record "Gen. Journal Line")
+
+PaymentDocNo := FromGenJnlLine.GETFILTER("Document No.");
+
+VendorBillHeader.GET(PaymentDocNo);
+
+VendorBillLine.RESET;
+
+VendorBillLine.SETCURRENTKEY("Vendor Bill List No.","Vendor No.","Due Date","Vendor Bank Acc. No.","Cumulative Transfers");
+
+VendorBillLine.SETRANGE("Vendor Bill List No.",VendorBillHeader."No.");
+
+VendorBillLine.SETRANGE("Cumulative Transfers",TRUE);
+
+IF VendorBillLine.FINDSET THEN BEGIN
+
+CumulativeAmount := 0;
+
+PrevVendorBillLine := VendorBillLine;
+
+REPEAT
+
+VendorBillLine.TESTFIELD("Document Type",VendorBillLine."Document Type"::Invoice);
+
+IF ((VendorBillLine."Vendor No." <> PrevVendorBillLine."Vendor No.") OR (VendorBillLine."Vendor Bank Acc. No." <> PrevVendorBillLine."Vendor Bank Acc. No.")) THEN BEGIN InsertTempGenJnlLine(TempGenJnlLine,VendorBillHeader,PrevVendorBillLine,CumulativeAmount);
+
+CumulativeAmount := VendorBillLine."Amount to Pay";
+
+END ELSE
+
+CumulativeAmount += VendorBillLine."Amount to Pay";
+
+PrevVendorBillLine := VendorBillLine;
+
+UNTIL VendorBillLine.NEXT = 0; InsertTempGenJnlLine(TempGenJnlLine,VendorBillHeader,PrevVendorBillLine,CumulativeAmount);
+
+END;
+
+VendorBillLine.SETRANGE("Cumulative Transfers",FALSE);
+
+IF VendorBillLine.FINDSET THEN
+
+REPEAT
+
+VendorBillLine.TESTFIELD("Document Type",VendorBillLine."Document Type"::Invoice); InsertTempGenJnlLine(TempGenJnlLine,VendorBillHeader,VendorBillLine,VendorBillLine."Amount to Pay");
+
+UNTIL VendorBillLine.NEXT = 0;
+
+LOCAL InsertTempGenJnlLine(VAR TempGenJnlLine : TEMPORARY Record "Gen. Journal Line";VendorBillHeader : Record "Vendor Bill Header";VendorBillLine : Record "Vendor Bill Line";AmountToPay : Decimal)
+
+WITH TempGenJnlLine DO BEGIN
+
+INIT;
+
+"Journal Template Name" := '';
+
+"Journal Batch Name" := '';
+
+"Document Type" := "Document Type"::Payment;
+
+"Document No." := VendorBillLine."Vendor Bill List No.";
+
+"Line No." := VendorBillLine."Line No.";
+
+"Account No." := VendorBillLine."Vendor No.";
+
+"Account Type" := TempGenJnlLine."Account Type"::Vendor;
+
+"Bal. Account Type" := TempGenJnlLine."Bal. Account Type"::"Bank Account";
+
+"Bal. Account No." := VendorBillHeader."Bank Account No.";
+
+"Applies-to Ext. Doc. No." := VendorBillLine."External Document No.";
+
+Amount := AmountToPay;
+
+"Applies-to Doc. Type" := VendorBillLine."Document Type";
+
+"Applies-to Doc. No." := VendorBillLine."Document No.";
+
+"Currency Code" := VendorBillHeader."Currency Code";
+
+"Due Date" := VendorBillLine."Due Date";
+
+"Posting Date" := VendorBillHeader."Posting Date";
+
+"Recipient Bank Account" := VendorBillLine."Vendor Bank Acc. No.";
+
+Description := VendorBillLine.Description;
+
+"Message to Recipient" := VendorBillLine."Description 2";
+
+INSERT;
+
+END;
+```
+
+The derived local feature flow is as follows:
+
+[![ ][image2]][anchor3]
+
+Figure 3\. The local country object flow
+
+As we can see from the diagram, this solution allows integration of the local and W1 features with a minimum amount of changes in W1 code. The only two differences are:
+
+1. The entry point of the flow is the local table/page.
+
+1. Codeunit 1222 is overloaded to prepare general journal lines from the local records.
+
+## NAV Usages
+
+The data mapping technique has been used for the SEPA Credit Transfer feature, and will be used in subsequent local integration projects.
+
+## Ideas for improvement
+
+A weak point for this pattern is the need to set a filter on the empty journal line in order to retrieve the local data when exporting from a local page. This can cause problems if the size of the local table document number is larger than the Document No. field (ID 20) in the Gen. Journal line table (81).
+
+Also, there is a strong need for thorough testing when using this pattern, because there might be differences in the behavior of the local table and table 81\. Whatever is acceptable for the local table may not be acceptable for the W1 table. A deep functional analysis is needed to see if the local export feature uses the same constraints as the W1 feature.
+
+
+
+[anchor0]: http://en.wikipedia.org/wiki/Proxy_pattern
+[anchor1]: 5123.Feature-localization-for-data-structures-1.png
+[anchor2]: 6052.Feature-localization-for-data-structures-2.png
+[anchor3]: 3058.Feature-localization-for-data-structures-3.png
+
+
+[image0]: 5123.Feature-localization-for-data-structures-1.png
+[image1]: 6052.Feature-localization-for-data-structures-2.png
+[image2]: 3058.Feature-localization-for-data-structures-3.png
diff --git a/content/docs/NAVPatterns/patterns/hooks/5383.HookPattern1.png b/content/docs/NAVPatterns/patterns/hooks/5383.HookPattern1.png
new file mode 100644
index 00000000..7e48236c
Binary files /dev/null and b/content/docs/NAVPatterns/patterns/hooks/5383.HookPattern1.png differ
diff --git a/content/docs/NAVPatterns/patterns/hooks/6378.HookPattern2.png b/content/docs/NAVPatterns/patterns/hooks/6378.HookPattern2.png
new file mode 100644
index 00000000..333aced2
Binary files /dev/null and b/content/docs/NAVPatterns/patterns/hooks/6378.HookPattern2.png differ
diff --git a/content/docs/NAVPatterns/patterns/hooks/8156.HookPattern3.png b/content/docs/NAVPatterns/patterns/hooks/8156.HookPattern3.png
new file mode 100644
index 00000000..acb15838
Binary files /dev/null and b/content/docs/NAVPatterns/patterns/hooks/8156.HookPattern3.png differ
diff --git a/content/docs/NAVPatterns/patterns/hooks/8875.HookPattern4.png b/content/docs/NAVPatterns/patterns/hooks/8875.HookPattern4.png
new file mode 100644
index 00000000..4f14a725
Binary files /dev/null and b/content/docs/NAVPatterns/patterns/hooks/8875.HookPattern4.png differ
diff --git a/content/docs/NAVPatterns/patterns/hooks/index.md b/content/docs/NAVPatterns/patterns/hooks/index.md
new file mode 100644
index 00000000..9134887d
--- /dev/null
+++ b/content/docs/NAVPatterns/patterns/hooks/index.md
@@ -0,0 +1,104 @@
++++
+title = "Hooks"
+weight = 620
+tags = ["C/AL"]
+categories = ["Pattern"]
++++
+_By Eric Wauters ([waldo][anchor0]), Partner-Ready-Software_
+
+## Abstract
+
+As a partner, adding new code to NAV means interfering with code shipped by Microsoft. Minimize your footprint of changes in Microsoft code, so that, when a new NAV version is shipped, you avoid conflicts and upgrade impact. The core NAV code is the "danger zone" - the less you touch it, the happier your upgrade will be.
+
+## Description
+
+When doing development over years, by different developers with different mindsets, the standard codebase gets changed a lot, adding multiple lines of code, adding local and global variants, adding or changing keys, changing existing business logic, ... . In other terms, the standard text objects are being changed all over the place.. .
+
+After years, it's not clear why a change was done, and what was the place where the change was intended to be done. And the latter is quite important in an upgrade process, when code in the base product is being refactored: if the exact place of the posting of the Customer Entry is being redesigned to a separate number, the first thing I need to know, is that I did a certain change at the place: "where the posting of the Customer Entry starts". The definition of that place, we call a "Hook".
+
+By minimizing the code in already existing application objects, you will make the upgrade process much easier, and all customization business logic will be grouped in new objects. When using atomic coding, it will be very readable what is being customized on a certain place in an existing part of the application.
+
+To minimize the impact of customizations, the idea of hooks is:
+
+* First of all, name the places in the already existing code where customization is needed;
+* Second, place your business logic completely outside the already existing application code.
+
+I recommend to use this concept on:
+
+* All objects of the default applications that need to be changed
+* On objects that should not hold any business logic (like tables, pages, XMLPorts)
+
+## Usage
+
+**Step 1** - if it doesn't exist yet - you create your Hook Codeunit. As the name assumes .. this is always a codeunit. We apply the following rules to it:
+
+* One Hook always hooks into one object. Which basically means that I will only declare this new codeunit in one other object (which is its parent object)
+* The naming convention is: "The_Original_Object_Name Hook". Naming conventions are important, just to find your mapped object, and also to be able to group the Hooks.
+
+**Step 2**, you create the hook, which is basically a method (function) in your codeunit. The naming is important:
+
+* The naming of the hook should NOT describe what it is going to do (So, examples like "CheckMandatoryFields", "FillCustomFields" should not be used as a hook)
+* The naming of the hook should describe WHERE the hook is placed, not what the hook will be doing (as nobody is able to look into the future .. :-))
+* To help with the naming, it is a good convention to use the "On"-prefix for these triggers. This way, it's very clear what are hooks, and what aren't..
+
+**Step 3**, it's time to hook it to its corresponding object and right place in the business logic of that object. You do this by declaring your codeunit as a global in your object, and using the created hook function on its place in the business logic. This way, these one-liners apply:
+
+* A Hook Codeunit is only used once in one object only (its corresponding object)
+* A Hook (function) is used only once in that object. As a consequence, changing the parameters has no consequence: you only need to change one function-call
+* The codeunit is declared as a global. That exact global is the only custom declaration in the existing object .. Everything else is pushed to the hook-codeunit.
+
+**Step 4**, implement your business logic in the hook. Do this in the most atomic way, as there is a good chance that this same hook is going to be used for other business logic as well. Best is to use a one-line-function-call to business logic, so that the Hook Function itself stays readable.
+
+## Example
+
+Suppose, we want to add business logic just before posting a sales document. In that case, we have to look for the most relevant place, which is somewhere in the "Sales-Post" codeunit. So:
+
+**Step 1**: create codeunit "Sales-Post Hook"
+
+[![ ][image0]][anchor1]
+
+**Step 2**: create the hook function "OnBeforePostDocument"
+
+[![ ][image1]][anchor2]
+
+**Step 3**: declare a global in the "Sales-Post"-codeunit, called "SalesPostHook". Then, call the Hook Function that you created in Step 2 in the right place.
+
+[![ ][image2]][anchor3]
+
+**Step 4**: implement the business logic, by calling out to a new function. And implement the test-codeunit.
+
+[![ ][image3]][anchor4]
+
+## Consequences
+
+This pattern can be used in all cases to put business logic. But I see three possible approaches
+
+1. You only declare the most important and most used hooks
+ * This way, you limit the amount of objects and hooks to only a few in the default product
+ * For example, only the OnBeforePostSalesHeader, OnBeforeReleaseSalesDocument, .. And no field validation or such...
+2. Only at objects of the default application which you are customizing.
+ * This way, you don't want to create hooks for your own objects, only default existing objects.
+3. (recommended) You create hooks on all places you don't want to write business logic, and on all existing objects which you would like to customize.
+ * This is a very consistent way of working, as in any case, your business logic ends up in either a hook, or in its corresponding objects from a specific design pattern. But you know that the entry point is always a hook.
+ * You know what to expect in any case, both changed business logic in existing code and business logic in newly created code is entered from a hook.
+
+## Related Topics
+
+Atomic coding: It's important that the hook function is readable in the most extreme way. For this, we recommend to use the "Atomic Coding" concept.
+
+See here a comparison / extension of hooks : [http://www.waldo.be/2016/02/29/nav-2016-hooks-or-events/][anchor5]
+
+
+
+[anchor0]: http://www.waldo.be "waldo's blog"
+[anchor1]: 5383.HookPattern1.png
+[anchor2]: 6378.HookPattern2.png
+[anchor3]: 8156.HookPattern3.png
+[anchor4]: 8875.HookPattern4.png
+[anchor5]: http://www.waldo.be/2016/02/29/nav-2016-hooks-or-events/
+
+
+[image0]: 5383.HookPattern1.png
+[image1]: 6378.HookPattern2.png
+[image2]: 8156.HookPattern3.png
+[image3]: 8875.HookPattern4.png
diff --git a/content/docs/NAVPatterns/patterns/implementation-of-surrogate-keys-using-autoincrement-pattern/0333.Figure-3.PNG b/content/docs/NAVPatterns/patterns/implementation-of-surrogate-keys-using-autoincrement-pattern/0333.Figure-3.PNG
new file mode 100644
index 00000000..a5b92cbe
Binary files /dev/null and b/content/docs/NAVPatterns/patterns/implementation-of-surrogate-keys-using-autoincrement-pattern/0333.Figure-3.PNG differ
diff --git a/content/docs/NAVPatterns/patterns/implementation-of-surrogate-keys-using-autoincrement-pattern/0458.Figure-1.PNG b/content/docs/NAVPatterns/patterns/implementation-of-surrogate-keys-using-autoincrement-pattern/0458.Figure-1.PNG
new file mode 100644
index 00000000..ef41da07
Binary files /dev/null and b/content/docs/NAVPatterns/patterns/implementation-of-surrogate-keys-using-autoincrement-pattern/0458.Figure-1.PNG differ
diff --git a/content/docs/NAVPatterns/patterns/implementation-of-surrogate-keys-using-autoincrement-pattern/0552.Example-Figure-5.png b/content/docs/NAVPatterns/patterns/implementation-of-surrogate-keys-using-autoincrement-pattern/0552.Example-Figure-5.png
new file mode 100644
index 00000000..4f23c91a
Binary files /dev/null and b/content/docs/NAVPatterns/patterns/implementation-of-surrogate-keys-using-autoincrement-pattern/0552.Example-Figure-5.png differ
diff --git a/content/docs/NAVPatterns/patterns/implementation-of-surrogate-keys-using-autoincrement-pattern/0638.Figure-2.PNG b/content/docs/NAVPatterns/patterns/implementation-of-surrogate-keys-using-autoincrement-pattern/0638.Figure-2.PNG
new file mode 100644
index 00000000..d32b76a3
Binary files /dev/null and b/content/docs/NAVPatterns/patterns/implementation-of-surrogate-keys-using-autoincrement-pattern/0638.Figure-2.PNG differ
diff --git a/content/docs/NAVPatterns/patterns/implementation-of-surrogate-keys-using-autoincrement-pattern/1488.Example-Figure-1.png b/content/docs/NAVPatterns/patterns/implementation-of-surrogate-keys-using-autoincrement-pattern/1488.Example-Figure-1.png
new file mode 100644
index 00000000..1b9271de
Binary files /dev/null and b/content/docs/NAVPatterns/patterns/implementation-of-surrogate-keys-using-autoincrement-pattern/1488.Example-Figure-1.png differ
diff --git a/content/docs/NAVPatterns/patterns/implementation-of-surrogate-keys-using-autoincrement-pattern/1884.Example-Figure-7.png b/content/docs/NAVPatterns/patterns/implementation-of-surrogate-keys-using-autoincrement-pattern/1884.Example-Figure-7.png
new file mode 100644
index 00000000..d03541ac
Binary files /dev/null and b/content/docs/NAVPatterns/patterns/implementation-of-surrogate-keys-using-autoincrement-pattern/1884.Example-Figure-7.png differ
diff --git a/content/docs/NAVPatterns/patterns/implementation-of-surrogate-keys-using-autoincrement-pattern/2068.Example-Figure-4.png b/content/docs/NAVPatterns/patterns/implementation-of-surrogate-keys-using-autoincrement-pattern/2068.Example-Figure-4.png
new file mode 100644
index 00000000..023a947f
Binary files /dev/null and b/content/docs/NAVPatterns/patterns/implementation-of-surrogate-keys-using-autoincrement-pattern/2068.Example-Figure-4.png differ
diff --git a/content/docs/NAVPatterns/patterns/implementation-of-surrogate-keys-using-autoincrement-pattern/4477.Example-Figure-6.png b/content/docs/NAVPatterns/patterns/implementation-of-surrogate-keys-using-autoincrement-pattern/4477.Example-Figure-6.png
new file mode 100644
index 00000000..a32c6b86
Binary files /dev/null and b/content/docs/NAVPatterns/patterns/implementation-of-surrogate-keys-using-autoincrement-pattern/4477.Example-Figure-6.png differ
diff --git a/content/docs/NAVPatterns/patterns/implementation-of-surrogate-keys-using-autoincrement-pattern/4532.Example-Figure-3.png b/content/docs/NAVPatterns/patterns/implementation-of-surrogate-keys-using-autoincrement-pattern/4532.Example-Figure-3.png
new file mode 100644
index 00000000..463d2358
Binary files /dev/null and b/content/docs/NAVPatterns/patterns/implementation-of-surrogate-keys-using-autoincrement-pattern/4532.Example-Figure-3.png differ
diff --git a/content/docs/NAVPatterns/patterns/implementation-of-surrogate-keys-using-autoincrement-pattern/4682.Example-Figure-2.png b/content/docs/NAVPatterns/patterns/implementation-of-surrogate-keys-using-autoincrement-pattern/4682.Example-Figure-2.png
new file mode 100644
index 00000000..4a95a938
Binary files /dev/null and b/content/docs/NAVPatterns/patterns/implementation-of-surrogate-keys-using-autoincrement-pattern/4682.Example-Figure-2.png differ
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
new file mode 100644
index 00000000..a89b6b1b
--- /dev/null
+++ b/content/docs/NAVPatterns/patterns/implementation-of-surrogate-keys-using-autoincrement-pattern/index.md
@@ -0,0 +1,161 @@
++++
+title = "Surrogate keys using Autoincrement Pattern"
+weight = 630
+tags = ["C/AL"]
+categories = ["Pattern"]
++++
+_By [Soren Klemmensen][anchor0], [_Partner-Ready-Software_ ][anchor1] & [360 Visibility][anchor2]_
+
+## Abstract
+
+This Pattern is meant to create generic & reusable links between tables. The goal is to have an easy generic way to link a generically designed sub table to a record on a main table which can be used for other links too.
+
+To minimize the impact of customizations and to keep modules as generic and reusable as possible the idea of the Implementation of surrogate keys using AutoIncrement pattern is:
+
+* To create a generic and reusable auto generated link (A Surrogate Key), Immune to natural key data & requirement changes, on a main table with minimum impact on the table.
+* To create generic and reusable sub tables that effortless can be reused anywhere in the application.
+
+## Description
+
+Over years of development many things are repeated across different implementation and even inside the same application. A typical example could be adding comments to an area just as it is done in Microsoft Dynamics NAV multiple times. There can be reasons for doing this again and again, but not only does this need to be maintained and upgraded over the years, but all the implementations of comments also needs to be tested separately. If a standard and generic comment could be developed and a generic way of connecting it to a main table this could be resolved. This is exactly what this pattern is trying accomplish.
+
+[![ ][image0]][anchor3]
+
+_Figure 1: Table structure for linking a Document Header and Line Table with a Document Comment Table._
+
+[![ ][image1]][anchor4]
+
+_Figure 2: Table Structure for linking a Master Data Table with a Master Data Comment Table_
+
+A typical way of linking a table to master data or to a document has been to use the primary key of the table being linked to. This causes some issues as the linked table now is designed specifically for the main table and it functionality cannot be reused. In case of renames the linked table needs to be renamed too which is costly in processing. Code also needs to be added on the delete trigger of the table to ensure that the attached records get removed if needed which increases the over all footprint of any change.
+
+[![ ][image2]][anchor5]
+
+_Figure 3: A Generic Way of creating a Comment table and linking it in a generic way to the main table no matter what this table might be. The Unique Record Identifier on the main tables is an Integer with AutoIncrement set to yes._
+
+It is recommended using this pattern in all tables which need sub tables unless specific reasons exists for not doing this.
+
+## Usage
+
+**Step 1**: Create a generic Unique Record Identifier in the main table
+
+The Pattern is implemented by adding a Field (I have called it Unique Record Identifier for this article) in a table (the main Table) where links are needed to be established to. Set the Property Data Type to Integer, Editable to No & AutoIncrement to Yes.
+
+**Step 2**: Create a generic link-able sub table.
+
+Create a new table (Sub Table) which you would like to be reusable with a primary key containing
+
+* Table No. of Data Type Integer
+* Unique Record Identifier of Data Type Integer
+* A 3 field for allowing multiple entries to exist per record in the main table if so needed. This could be a Line No. of Data Type Integer or a Code field of Data Type Code(10) and so on.
+
+The important part here is that the 2 or 3 first fields in the tables primary key is the Table No. and the Unique Record Identifier. If a 3rd field was added to allow for multiple entries to exist per record in the main table this should also be included in the primary key.
+
+Make sure to also add any other fields containing the information you wish to store as needed to the table.
+
+The sub table will be immune to renames from the main table as the main tables primary key is not used in the primary key of the sub table. The Insert, Modify and Rename triggers are not causing any issues and only the delete trigger will need to be considered. This can be dealt with generically from the OnDatabaseDelete trigger in Codeunit 1\. I would recommend to use a Hook Pattern for this.
+
+**Step 3**: Create a page showing the sub table information.
+
+Now create a page showing the data in the sub table.
+
+**Step 4**: Add an Action or factbox.
+
+Create an action or factbox on the pages showing the main table data linking to the subpage with Table ID filtered to a constant of the Table No. of the main table and the Unique Record Identifier of the sub table filtering to the Unique Record Identifier of the main table.
+
+**Step 5**: Create a Hook for Function OnDatabaseDelete in Codeunit 1 ApplicationManagement if one doesn't already exist. See the Hook pattern.
+
+**Step 6**: Create a code to delete records in the Sub table if a main table record is deleted if this is required. This function needs to be called from the Hook created in Step 5\.
+
+## **NAV Specific Example**
+
+Let's assume we would like to create comment for a larger number of very different tables in the system.
+
+**Update the Customer table (Step 1)**: Go to table 18 Customer and add Field 50000 Unique Record Identifier. Set the Property Data Type to Integer, Editable to No & AutoIncrement to Yes. Save the changes.
+
+[![ ][image3]][anchor6]
+
+_[![ ][image4]][anchor7]
+
+
+**Create Comment table (Step 2)**: Create a new table called Comment. Add 3 fields Table No., Unique Record Identifier & Line No. all of Data Type Integer. Make these 3 fields into the primary key for the table. Add a 4 field called Comment with Data Type Text(80). Save the changes.
+
+[![ ][image5]][anchor8]
+
+**Create the Comments page (Step 3)**: Start the page wizard and create a Page based on table comment created above as a List only showing the Comment field. Set AutoSplitKey property to Yes on the page.
+
+_[![ ][image6]][anchor9]_
+
+**Update the Customer Card (Step 4)**: Add an action to the Customer Card to open the Comments. Go to Actions and create an action called Smart Comment. Populate the properties RunObject, RunPageView and RunPageLink as see in the picture below.
+
+[![ ][image7]][anchor10]
+
+**Create a hook OnDatabaseDelete (Step 5)**:
+
+Create a codeunit 50000 called "ApplicationManagement Hook" with one function called OnAfterOnDatabaseDelete taking the parameter RecRef of Data Type RecordRef. Add it as a global variable to Codeunit 1 ApplicationManagement and call the function as the last line in OnDatabaseDelete. Please read about the hook pattern before implementing it.
+
+[![ ][image8]][anchor11]
+
+**Create the code needed to delete comments linked to a deleted record (Step6)**:
+
+Create a DeleteComments function taking the RecRef of Data Type RecordRef and add the code as shown blow.
+
+[![ ][image9]][anchor12]
+
+It is assumed in this example for simplicity that the Field 50000 is reserved across the entire application for the Unique Record Identifier as defined in Step 1\.
+
+The comments are now fully working if we look away from the fact that we did not deal with a few things specific to Sales orders like RecreateSalesLines function, Archiving and Copy Document. All of which can easily be dealt with.
+
+These comments are now completely reusable everywhere else in the system. Sales Document is a perfect example as the primary keys of the Sales Header and the Sales line are both different from the customer and normally we would not be able to use the comments header or the line. All that needs to be done is adding the Field 50000 Unique Record Identifier to the Sales Header & Sales Line (Disregarding the posted documents in this example) and creating the actions on the Page Actions with the needed filters. Deletion is already handled of comments no matter what the main table might be.
+
+Looking at the main table the Unique Record Identifier is also completely reusable for any other linking needed.
+
+Other ideas for use of this pattern could be. An Error table, Tags, Dynamically defined fields and Generic fact boxes. Only the imagination sets limits for its use.
+
+Upgrade wise there can be an impact if data is moved out of tables to be reinserted again because of a change to a database structure. This will cause the Unique Record Identifier to change, unless steps are taken to avoid this, and the links will need to be reestablished.
+
+Other risks could be if Transfer Fields are used and data is being copied unintentionally.
+
+## NAV Usages
+
+This is a new pattern not currently used in Microsoft Dynamics NAV.
+
+## Ideas for improvement
+
+The reason for doing it this way is that you now can reuse your code again and again with only creating the same link on other tables instead of building it from scratch each time reducing testing needed and improving the quality of the overall product.
+
+## Consequences (When it should not be used)
+
+This can be used on any table when linking anything to it that can be considered a generic module which can be reused. That said it should not be used in cases where there is a risk of tables growing so much that performance could be impacted. There are ways to reduce performance impact when using high volume tables, but that is outside the scope of this pattern.
+
+## Related Topics
+
+This is related to the hook pattern as far as they both try to reduce the footprint changes have on the standard application, by creating reusable ways to interact with the standard code. That said the hook pattern is more about hooking the functionality to existing while this pattern is more about creating reusable ways of creating functionality.
+
+
+
+[anchor0]: http://mvp.microsoft.com/en-us/mvp/Soren%20Klemmensen-5001002 "Soren Klemmensen"
+[anchor1]: http://partner-ready-software.com/ "Partner-Ready-Software"
+[anchor2]: http://www.360visibility.com/ "360 Visibility"
+[anchor3]: 0458.Figure-1.PNG
+[anchor4]: 0638.Figure-2.PNG
+[anchor5]: 0333.Figure-3.PNG
+[anchor6]: 1488.Example-Figure-1.png
+[anchor7]: 4682.Example-Figure-2.png
+[anchor8]: 4532.Example-Figure-3.png
+[anchor9]: 2068.Example-Figure-4.png
+[anchor10]: /cfs-file.ashx/__key/communityserver-wikis-components-files/00-00-00-00-42/0552.Example-Figure-5.png
+[anchor11]: /cfs-file.ashx/__key/communityserver-wikis-components-files/00-00-00-00-42/4477.Example-Figure-6.png
+[anchor12]: /cfs-file.ashx/__key/communityserver-wikis-components-files/00-00-00-00-42/1884.Example-Figure-7.png
+
+
+[image0]: 0458.Figure-1.PNG
+[image1]: 0638.Figure-2.PNG
+[image2]: 0333.Figure-3.PNG
+[image3]: 1488.Example-Figure-1.png
+[image4]: 4682.Example-Figure-2.png
+[image5]: 4532.Example-Figure-3.png
+[image6]: 2068.Example-Figure-4.png
+[image7]: 0552.Example-Figure-5.png
+[image8]: 4477.Example-Figure-6.png
+[image9]: 1884.Example-Figure-7.png
diff --git a/content/docs/NAVPatterns/patterns/instructions-in-the-ui/2804.Picture-2.png b/content/docs/NAVPatterns/patterns/instructions-in-the-ui/2804.Picture-2.png
new file mode 100644
index 00000000..d6df77d4
Binary files /dev/null and b/content/docs/NAVPatterns/patterns/instructions-in-the-ui/2804.Picture-2.png differ
diff --git a/content/docs/NAVPatterns/patterns/instructions-in-the-ui/5707.Picture-4.png b/content/docs/NAVPatterns/patterns/instructions-in-the-ui/5707.Picture-4.png
new file mode 100644
index 00000000..5976b18c
Binary files /dev/null and b/content/docs/NAVPatterns/patterns/instructions-in-the-ui/5707.Picture-4.png differ
diff --git a/content/docs/NAVPatterns/patterns/instructions-in-the-ui/6215.picture-1.png b/content/docs/NAVPatterns/patterns/instructions-in-the-ui/6215.picture-1.png
new file mode 100644
index 00000000..cf794efa
Binary files /dev/null and b/content/docs/NAVPatterns/patterns/instructions-in-the-ui/6215.picture-1.png differ
diff --git a/content/docs/NAVPatterns/patterns/instructions-in-the-ui/6685.picture-3.png b/content/docs/NAVPatterns/patterns/instructions-in-the-ui/6685.picture-3.png
new file mode 100644
index 00000000..f75ce0c1
Binary files /dev/null and b/content/docs/NAVPatterns/patterns/instructions-in-the-ui/6685.picture-3.png differ
diff --git a/content/docs/NAVPatterns/patterns/instructions-in-the-ui/7217.picture-1.png b/content/docs/NAVPatterns/patterns/instructions-in-the-ui/7217.picture-1.png
new file mode 100644
index 00000000..9f15bb4e
Binary files /dev/null and b/content/docs/NAVPatterns/patterns/instructions-in-the-ui/7217.picture-1.png differ
diff --git a/content/docs/NAVPatterns/patterns/instructions-in-the-ui/7245.Picture-2.png b/content/docs/NAVPatterns/patterns/instructions-in-the-ui/7245.Picture-2.png
new file mode 100644
index 00000000..d0184198
Binary files /dev/null and b/content/docs/NAVPatterns/patterns/instructions-in-the-ui/7245.Picture-2.png differ
diff --git a/content/docs/NAVPatterns/patterns/instructions-in-the-ui/index.md b/content/docs/NAVPatterns/patterns/instructions-in-the-ui/index.md
new file mode 100644
index 00000000..fdbe7999
--- /dev/null
+++ b/content/docs/NAVPatterns/patterns/instructions-in-the-ui/index.md
@@ -0,0 +1,167 @@
++++
+title = "Instructions in the UI"
+weight = 670
+tags = ["C/AL"]
+categories = ["Pattern"]
++++
+_Originally by Nikola Kukrika at Microsoft Development Center Copenhagen_
+
+## Abstract
+
+To mitigate usability problems with learnability or discoverability of NAV functionality, it is possible to embed instructions in the UI in connection with the task that the user is performing. The goal is to explain how to use the product or feature without impairing the user's productivity after user has learned how to use a feature.
+
+## Description
+
+Users must often go through a few days of training to learn how to use NAV, and even then, many users rely on super users to help them mitigate difficulties using NAV. In addition, because of low discoverability and learnability, many useful features are not being used at all.
+
+Users' expectations are changing. They expect the software to be usable out-of-the-box because this is the trend in software generally.
+
+One of the cheapest and most effective methods to solve usability issues is to embed instructional messages in the product. From a user-experience point of view, **this should be used as a last resort**. UI should be self-explanatory, efficient, and simple to use. Accordingly, you should only implement this pattern if simplifying and improving a scenario is not possible or is too expensive.
+
+In this connection, the most important requirement is not to impair productivity of the users. One of the biggest and most common UX mistakes that developers make is to "optimize for new users". After the user has learned how to use the product, all the instruction texts and dialogs that we added to the UI will clutter the page and make information less visible. Instructional dialogs on routine tasks will become annoying. Therefore, we must make all instructions dismissible.
+
+In the Mini App solution we have used following elements:
+
+1. Dismissible dialogs
+2. FastTabs with instructional text
+3. Help tiles on a Role Center
+4. Tooltips on actions and fields
+5. Task-oriented page Help
+
+## Usage
+
+The following pattern applies to dismissible parts in the UI.
+
+We have a table that stores the instructional code ID and the UserID, so that we can track which user has turned off which instruction. All the logic handling is done from a codeunit. It is the responsibility of the codeunit to show/hide dialogs if needed.
+
+[![ ][image0]][anchor0]
+
+## Dismissible Dialogs
+
+Dismissible dialogs show the instructional message about the functionality, with the user option to select "Don't show this again". This is a good solution to problems where users enter text in the wrong place, or to explain behavior of a somewhat hidden feature.
+
+[![ ][image1]][anchor1]
+
+On a recent usability study of the **Description** field on sales or purchase lines, most users ignored the **No.** field and started entering text in the description field before proceeded to enter a quantity. In the solution in question, text only is treated as a line comment if the **No.** field is blank. The fix was to update the field name to **Description/Comment** and to provide a message that typing in the field creates a comment only. Users that often use comments can then choose "Do not show again" to get rid of the instructional text.
+
+**When to use:** Recommendation is to use only when many users are entering data in the wrong way and modifying the code is costly. This is an interrupting dialog, but the benefits are that it is very hard to overlook this dialog.
+
+## FastTabs with Instructional Text
+
+Instructional text on FastTabs is ideal for showing larger amounts of text in the UI.
+
+[![ ][image2]][anchor2]
+
+When a user changes a filter in the **Navigate** page, we blank the grid. This may confuse the users as to how to proceed. The **Notification** FastTab provides information on what has happened and gives instructions on how to proceed.
+
+Similar implementation is to have the FastTab always visible with the **Do not show again** check box present, so that users can dismiss it after they have read the message. This is less intrusive than using a dialog, and it has the benefit of being always visible compared to the dialog. The drawback is that users may not read it or may not dismiss it.
+
+## Information Tiles on Role Centers
+
+On the **Small Business Role Center** page (9022), we have implemented a **Getting Started** group containing action tiles. Clicking on the first two tiles will play instructional videos. Clicking on the third tile launches a dedicated help topic. Since these tiles will get in the way of the experienced users, an option to hide the entire group is provided.
+
+[![ ][image3]][anchor3]
+
+**Note**: in NAV 2013 R2, actions appear as tiles in the web client only. In the win client, they appear as links. In the web client, the actions only appear if they are in a group for themselves (without Stack Queues -- empty group with only actins defined).
+
+### To implement tiles for instructional videos
+
+1. Upload a video to a video hosting service (check if licensing is allowing you to use if for this usage. Examples of video hosting services are: YouTube, Vimeo, Yahoo Video.)
+2. Get the code to embed the video (select option embed).
+3. You can reuse the code on the **Mini Video Player Page** page (1395) or implement a custom one.
+
+Important parts:
+**VideoPlayerAddIn.SetFrameAttribute** function is used to set an attribute to the iframe that will be playing the video.
+
+
+Example of the embed code:
+
+```js
+
+```
+
+You must assign **src attribute** to src of the embed code, for example:
+
+```al
+ VideoPlayer.SetFrameAttribute('src', 'https://www.youtube.com/embed/7SGp9pA9cAY');
+```
+
+Without this, the video will not play. You can use the same function to assign other attributes, for example to remove frame border use:
+
+```al
+ VideoPlayer.SetFrameAttribute(' frameborder', '0');
+```
+
+Height and width should be set by using the following functions, since they ensure that the video will be centered on the page.
+
+```al
+ VideoPlayerAddIn.SetHeight(Height) and VideoPlayerAddIn.SetWidth(Width);
+```
+
+If you would like to reuse the **Mini Video Player Page** page (1395), then use:
+
+ SetParameters(Height,Width,Src,Caption), which uses the functions described above.
+
+4\. As a last step you need to implement the action on the group and assign a video icon
+
+**Note:**
+
+Videos are implemented to be Web Client only. This is done because Flash player control that is used by most of the providers is not working well with WebBrowser control that the Windows Client is using.
+
+For displaying the videos on the Windows client, the simplest option is to provide an action with a link that opens a video page in a browser or a page hosting all of the instructional videos you have provided. Optionally you can implement a .NET add-in control that would be able to play the video from selected provider.
+
+### To implement tiles for help topics
+
+You only need to add an empty action with a **TileHelp** icon. Platform will render the action and will generate the logic to trigger a help call when user clicks on the icon. On the Help Server create an help topic that matches the URL.
+
+## Tooltips on actions and fields
+
+Platform improvements in NAV 2013 R2 provide ability to create tooltips for actions and all kinds of fields in the web client simply by filling the **TooltipML** property on the page object.
+
+[![ ][image4]][anchor4]
+
+**Note**: In NAV 2013 R2, tooltips (in the 1330-range pages only) are extracted from intro paragraphs in the related field topic and inserted build-time using an infrastructure system.
+
+## Task-oriented page Help
+
+Every page in NAV 2013 R2 has a help icon in top right corner that should open a Task-oriented help topic that should be related to this page. We recommend providing help topics for new task pages that you provide with your solutions.
+
+[![ ][image5]][anchor5]
+
+## NAV Usages
+
+Dismissible dialogs - Used in the **Description** field in pages 1305, 1325, 1355, 1373, ....
+
+FastTabs with instructional text -- **Navigate** page (344).
+
+Help Tiles on Role Center -- **Small Business Role Center** page (9022) and **Mini Activities** page (1310).
+
+Tooltips -- All pages in the 1300 number range.
+
+Task-oriented page help -- all task pages in 1300 number range
+
+## Ideas for Improvement
+
+Provide the support for the invoking any Help topics (URL on the Help Server from C/AL code. Then we would be able to promote help actions anywhere or launch them from C/AL code if needed.
+
+Implement tooltips across the application and in all country versions. (Requires a run-time infrastructure system.)
+
+{{< youtube loobQ1TVO3o>}}
+
+
+
+[anchor0]: 6215.picture-1.png
+[anchor1]: 2804.Picture-2.png
+[anchor2]: 6685.picture-3.png
+[anchor3]: 5707.Picture-4.png
+[anchor4]: 7217.picture-1.png
+[anchor5]: 7245.Picture-2.png
+[anchor6]: https://www.youtube.com/watch?v=loobQ1TVO3o&list=PLhZ3P-LY7CqmVszuvtJLujFyHpsVN0U_w&index=14
+
+
+[image0]: 6215.picture-1.png
+[image1]: 2804.Picture-2.png
+[image2]: 6685.picture-3.png
+[image3]: 5707.Picture-4.png
+[image4]: 7217.picture-1.png
+[image5]: 7245.Picture-2.png
diff --git a/content/docs/NAVPatterns/patterns/integration-of-addresses/index.md b/content/docs/NAVPatterns/patterns/integration-of-addresses/index.md
new file mode 100644
index 00000000..95521bba
--- /dev/null
+++ b/content/docs/NAVPatterns/patterns/integration-of-addresses/index.md
@@ -0,0 +1,11 @@
++++
+title = "Integration of Addresses"
+weight = 680
+tags = ["C/AL"]
+categories = ["Pattern"]
++++
+{{< youtube 60Wrx9N-gfY>}}
+
+
+
+[anchor0]: https://www.youtube.com/watch?v=60Wrx9N-gfY&list=PLhZ3P-LY7CqmVszuvtJLujFyHpsVN0U_w&index=19
diff --git a/content/docs/NAVPatterns/patterns/journal-error-processing/0005.Journal-Error-Processing-2.jpg b/content/docs/NAVPatterns/patterns/journal-error-processing/0005.Journal-Error-Processing-2.jpg
new file mode 100644
index 00000000..6a4f4601
Binary files /dev/null and b/content/docs/NAVPatterns/patterns/journal-error-processing/0005.Journal-Error-Processing-2.jpg differ
diff --git a/content/docs/NAVPatterns/patterns/journal-error-processing/0777.Journal-Error-Processing.jpg b/content/docs/NAVPatterns/patterns/journal-error-processing/0777.Journal-Error-Processing.jpg
new file mode 100644
index 00000000..75313cb7
Binary files /dev/null and b/content/docs/NAVPatterns/patterns/journal-error-processing/0777.Journal-Error-Processing.jpg differ
diff --git a/content/docs/NAVPatterns/patterns/journal-error-processing/5518.Journal-Error-Processing-1.jpg b/content/docs/NAVPatterns/patterns/journal-error-processing/5518.Journal-Error-Processing-1.jpg
new file mode 100644
index 00000000..5598704a
Binary files /dev/null and b/content/docs/NAVPatterns/patterns/journal-error-processing/5518.Journal-Error-Processing-1.jpg differ
diff --git a/content/docs/NAVPatterns/patterns/journal-error-processing/5661.Journal-Error-Processing-8.jpg b/content/docs/NAVPatterns/patterns/journal-error-processing/5661.Journal-Error-Processing-8.jpg
new file mode 100644
index 00000000..d5dcc1c6
Binary files /dev/null and b/content/docs/NAVPatterns/patterns/journal-error-processing/5661.Journal-Error-Processing-8.jpg differ
diff --git a/content/docs/NAVPatterns/patterns/journal-error-processing/8640.Journal-Error-Processing-3.jpg b/content/docs/NAVPatterns/patterns/journal-error-processing/8640.Journal-Error-Processing-3.jpg
new file mode 100644
index 00000000..c1231312
Binary files /dev/null and b/content/docs/NAVPatterns/patterns/journal-error-processing/8640.Journal-Error-Processing-3.jpg differ
diff --git a/content/docs/NAVPatterns/patterns/journal-error-processing/index.md b/content/docs/NAVPatterns/patterns/journal-error-processing/index.md
new file mode 100644
index 00000000..7dbcd05e
--- /dev/null
+++ b/content/docs/NAVPatterns/patterns/journal-error-processing/index.md
@@ -0,0 +1,170 @@
++++
+title = "Journal Error Processing"
+weight = 710
+tags = ["C/AL"]
+categories = ["Pattern"]
++++
+_Originally by Bogdana Botez at Microsoft Development Center Copenhagen_
+
+## Abstract
+
+This pattern describes an optimized way to handle invalid, incomplete, or inconsistent data that users enter in journals.
+
+## Description
+
+Scenario: A user has entered data on a journal line and proceeds to invoke a processing action on it, such as posting or exporting to electronic payments. NAV validates the data before it is committed. If any validation errors are found, the user must be informed of validation errors in the most optimal way.
+
+One design is that when an error is found, stop execution and prompt the user to correct the error. After correcting the error, the user restarts processing and is stopped again at the next error, and so on. Stopping and showing each error is time-consuming and frustrating for the user.
+
+Another design is that processing does not stop when an error is found. Instead, all errors are gathered in a table and displayed all at once at the end of processing. This way, the processing is ideally invoked only once, reducing the time and effort spent by the user to expose and correct all data validation errors.
+
+In both designs, the processing is not finalized if any errors are found (for example, exporting to electronic payments is not done, until the data error is resolved).
+
+This document describes how to implement the second error-handling design: Showing all errors at the end.
+
+## Usage
+
+The example below comes from the implementation of SEPA Credit Transfer.
+
+After setting up SEPA-specific configurations, the user can start entering vendor payments that will later be exported to the payment file. (The setup depends on the country, but generally involves choosing number series for SEPA export files, choosing the export format, and enabling SEPA Credit Transfer.)
+
+In the W1 solution (and most of the countries), payment lines are created in the Payment Journal page, from where the user can invoke the Export Payments to File action, which will attempt to create a SEPA-compliant XML file containing the description of the journal payments that are to be made by the bank.
+
+When the Export Payments to File function is invoked, NAV validates the journal line data. If the data must be completed or updated, then no file will be created and the user sees the following message:
+
+[![ ][image0]][anchor0]
+
+To give a visual overview, the lines that need corrections are highlighted in red. The factbox is context-sensitive, meaning that it shows only the errors that relate to the currently selected line.
+
+When the first payment journal line is selected, the FactBox show errors for the first line.
+
+[![ ][image1]][anchor1]
+
+When the second payment journal line is selected, the FactBox shows errors for the second line.
+
+[![ ][image2]][anchor2]
+
+## Application Objects
+
+In the following table, the Generic Object column contains the objects that you can use as a base for your implementation.
+
+Generic Object | Description | Sample W1 implementation of SEPA Credit Transfer
+------|------|------
+Journal Page | This is the journal list page where the user invokes the processing action. | Payment Journal
+Action on Page | The processing action invoked by the user on the journal list page. | Export Payments to File
+Errors Page List Part | A FactBox that displays any journal line validation errors.
+To improve user experience, the developer can highlight the lines with errors in red and conveniently sort the lines with errors at the top. | Payment Journal Errors Part
+Validation codeunit | Contains code that checks that the journal line contains correct, complete, and coherent data and that the line is ready for whatever process must be done next. | SEPA CT-Check Line
+Processing codeunit | Executes the processing of the journal lines. | SEPA CT-Export File
+
+**Generic Object:** Journal Error Text Table
+
+**Description:**
+
+Contains:
+* The error messages
+* Link information about where the error messages belong. For example, in table 1228, Payment Jnl. Export Error Text, the error is linked uniquely to a journal line by the following fields:
+* Journal Template Name, with TableRelation="Gen. Journal Template"
+* Journal Batch Name, with TableRelation="Gen. Journal Batch".Name WHERE (Journal Template Name=FIELD(Journal Template Name))
+* Journal Line No.
+
+Other related information can be added, such as document number of the original source document, if the current journal line originates from a document.
+
+An extra improvement would be to add a drilldown or a link to the page where the user can fix the error. This would significantly simplify the scenario by excluding manual navigation and investigation by the user to find the page where the error can be fixed.
+
+**Sample W1 implementation of SEPA Credit Transfer:** Payment Jnl. Export Error Text
+
+\* The W1 implementation of file export for SEPA Credit Transfer contains the generic SEPA functionality. However, due to differences in data models and user scenarios in various country implementations, the selected local versions contain adaptations of the generic functionality.
+
+## Flow
+
+Find below a diagram describing the flow between the objects involved in the journal error processing.
+
+[![ ][image3]][anchor3]
+
+## Code
+
+Following the flow above, the code (in the SEPA Credit Transfer example) is as follows.
+
+[![ ][image4]][anchor4]
+
+The public interface of this table contains simple functionality for adding/deleting errors and for interrogation on if any errors are associated with the current journal template and batch.
+
+```AL
+CreateNew(GenJnlLine : Record "Gen. Journal Line";NewText : Text)
+
+SetLineFilters(GenJnlLine);
+
+IF FINDLAST THEN;
+
+"Journal Template Name" := GenJnlLine."Journal Template Name";
+
+"Journal Batch Name" := GenJnlLine."Journal Batch Name";
+
+"Document No." := GenJnlLine."Document No.";
+
+"Journal Line No." := GenJnlLine."Line No.";
+
+"Line No." += 1;
+
+"Error Text" := COPYSTR(NewText,1,MAXSTRLEN("Error Text"));
+
+INSERT;
+
+JnlLineHasErrors(GenJnlLine : Record "Gen. Journal Line") : Boolean
+
+SetLineFilters(GenJnlLine);
+
+EXIT(NOT ISEMPTY);
+
+JnlBatchHasErrors(GenJnlLine : Record "Gen. Journal Line") : Boolean
+
+SetBatchFilters(GenJnlLine);
+
+EXIT(NOT ISEMPTY);
+
+DeleteJnlLineErrors(GenJnlLine : Record "Gen. Journal Line")
+
+IF JnlLineHasErrors(GenJnlLine) THEN
+
+DELETEALL;
+
+DeleteJnlBatchErrors(GenJnlLine : Record "Gen. Journal Line")
+
+IF JnlBatchHasErrors(GenJnlLine) THEN
+
+DELETEALL;
+```
+
+## NAV Usages
+
+* SEPA Credit Transfer feature - for export of vendor payments
+
+* SEPA Direct Debit feature for export of customer payment instructions
+
+The same concept of storing error messages (but with a different flow) is also present in:
+
+* Planning Error Log table (5430) - Supply Planning feature
+
+* Costing table (5890) - Costing feature
+
+## Ideas for Improvement
+
+Older code in NAV does not use this pattern yet. It would be good for consistency reasons, and also for overall user experience, to extend this pattern to replace the error processing in more areas.
+
+We can also improve by helping users find the place where they must fix the error by providing auto-navigation to the required page.
+
+
+
+[anchor0]: 5518.Journal-Error-Processing-1.jpg
+[anchor1]: 0005.Journal-Error-Processing-2.jpg
+[anchor2]: 8640.Journal-Error-Processing-3.jpg
+[anchor3]: 0777.Journal-Error-Processing.jpg
+[anchor4]: 5661.Journal-Error-Processing-8.jpg
+
+
+[image0]: 5518.Journal-Error-Processing-1.jpg
+[image1]: 0005.Journal-Error-Processing-2.jpg
+[image2]: 8640.Journal-Error-Processing-3.jpg
+[image3]: 0777.Journal-Error-Processing.jpg
+[image4]: 5661.Journal-Error-Processing-8.jpg
diff --git a/content/docs/NAVPatterns/patterns/journal-template-batch-line/2438.Journal-Template-Batch-Line-1.jpg b/content/docs/NAVPatterns/patterns/journal-template-batch-line/2438.Journal-Template-Batch-Line-1.jpg
new file mode 100644
index 00000000..f5388a26
Binary files /dev/null and b/content/docs/NAVPatterns/patterns/journal-template-batch-line/2438.Journal-Template-Batch-Line-1.jpg differ
diff --git a/content/docs/NAVPatterns/patterns/journal-template-batch-line/8103.Journal-Template-Batch-Line-2.jpg b/content/docs/NAVPatterns/patterns/journal-template-batch-line/8103.Journal-Template-Batch-Line-2.jpg
new file mode 100644
index 00000000..05389680
Binary files /dev/null and b/content/docs/NAVPatterns/patterns/journal-template-batch-line/8103.Journal-Template-Batch-Line-2.jpg differ
diff --git a/content/docs/NAVPatterns/patterns/journal-template-batch-line/index.md b/content/docs/NAVPatterns/patterns/journal-template-batch-line/index.md
new file mode 100644
index 00000000..99fe99af
--- /dev/null
+++ b/content/docs/NAVPatterns/patterns/journal-template-batch-line/index.md
@@ -0,0 +1,182 @@
++++
+title = "Journal Template Batch Line"
+weight = 720
+tags = ["C/AL"]
+categories = ["Pattern"]
++++
+_Originally by Bogdana Botez at Microsoft Development Center Copenhagen_
+
+## Abstract
+
+The role of a journal line is to temporarily hold transaction data until the transaction is posted. Before posting, the entries are in a draft state, which means that they are available for corrections and/or deletion. As soon as the entries are posted, they are converted to ledger entries.
+
+Journal templates are used to specify the underlying journal structure and to provide the default information for the journal batches. Journal batches usually serve to group journal lines, such as lines created by two different users.
+
+[![ ][image0]][anchor0]
+
+## Description
+
+Journal templates and journal batches are used if there is a need to create and post one or more entries. They are implemented in multiple areas of the application, like Sales, Purchases, Cash Receipts, Payments, Fixed Assets1.
+
+### Journal Templates
+
+The journal templates are located on the Journal Template page. A Journal Template definition contains a series of attributes, such as:
+
+* Name
+* Description
+* Type
+* Recurring
+* No. Series
+
+The Journal Template table stores the relevant attributes that define the nature and behavior of the journal templates, for example:
+
+Journal Template Table Field | Description
+-----|-----
+Test Report ID | The journals offer the possibility of running test reports3. The role of a test report is to simulate the posting process. The verification criteria for the journal lines is ran, and the report can be displayed, all without doing the actual posting. This helps finding and correcting any errors that might exist in the data. The name of the test report is the same with the name of the corresponding journal, plus the suffix " - Test". For example, the General Journal has the associated test report named General Journal - Test.
+Posting Report ID | This report is printed when a user selects Post and Print4.
+Page ID | For some journals, more UI objects are required. For example, the General Journals have a special page for bank and cash.
+Source Code | Here you can enter a Trail Code for all the postings done through this Journal4.
+Recurring | Whenever you post lines from a recurring journal, new lines are automatically created with a posting date defined in the recurring date formula.
+
+Each journal template defines a default value of those attributes. The values that are defined in a template will be inherited by the journal batches, which will be created from a journal template.
+
+Microsoft Dynamics NAV is released with a number of standard journal templates predefined in the Journal Templates page. More templates can be defined by the users.
+
+### Journal Batches
+
+Journal batches are created with the help of the journal templates.
+
+A journal batch is typically used to make a distinction between collections of logically grouped journal lines. A typical design is to have a journal batch for each user who enters lines. The batches are used during the posting process, in order to post one or multiple lines at once.
+
+### Journal Lines
+
+Journal lines contain the actual business data (posting dates, account numbers, amounts) that will be posted as ledger entries.
+
+During posting, only the information from the journal lines is needed. However, the information has been created with the help of the journal templates and grouped together using the journal batches.
+
+Posting creates ledger entries from the temporary content that is stored in the journal lines. Ledger entries are not created directly. Instead, they are posted from journal lines.
+
+[![ ][image1]][anchor1]
+
+### Aggregation
+
+There is a 1:n aggregation relationship between journal templates and journal batches, as well as between journal batches and journal lines. Deleting a template will cascade deletion of the related batches and lines. Deleting a batch will cascade into deletion of related lines.
+
+### Recurring Journals
+
+A recurring journal is used to post transactions that repeat periodically. In a recurring journal, the user enters only the variable data, such as posting date, amounts, and accounts to be used for posting.
+
+After posting a recurring journal, new journal lines are created containing the posting date for the next recurring period. The posting date recurrence pattern is previously defined in the Recurring Frequency field (for example, monthly recurrences are defined with the date formula 1M).
+
+A boolean field named Recurring is placed on both the journal templates and journal batches, giving the possibility of defining the type of the journal to be used.
+
+### Consistent User Experience
+
+To keep a consistent user interface experience, it is recommended that the the following guidelines are taken respected concerning navigation:
+
+* Journal template to journal batches:
+
+* On the Journal Templates page, create an action called "Batches" and place it in the Navigate tab of the ribbon. Link the action to the batches list page.
+
+* Journal batch to journal lines:
+
+* On the Batch page, create an action called "Edit Journal" in the Home ribbon tab. Link the action to the journal lines list page.
+
+To keep a consistent user interface experience, it is recommended that the the following guidelines are taken respected concerning posting:
+
+* Posting multiple batches
+
+* On the Journal Batches page, posting actions (Post, Post and Print) are available. When invoked, the batch posting will iterate through all related journal lines and trigger the posting routine for all of the lines.
+
+## Usage
+
+### General Journals
+
+The General Journal Templates page (101) uses the Gen. Journal Template table (80).
+
+Various template types are defined: General,Sales,Purchases,Cash Receipts,Payments,Assets,Intercompany,Jobs. Based on the journal type, two other attributes are automatically set on the template lines as follows:
+
+* Page ID: Defines which journal page relates to the current journal template
+* Source Code: Filled with the default codes that are defined in Source Code Setup table (242).
+
+The General Journals Batches page (251) is linked to the Gen. Journal Batch source table (232), which has a multiple-to-1 relationship with Gen. Journal Template table, based on the Journal Template Name field.
+
+Some of the fields in the Gen. Journal Batch table are not editable. Instead, their value is automatically calculated from the parent Gen. Journal Template table. For example, the Recurring field (22) is a FlowField with the following calculation formula:
+
+```al
+Lookup("Gen. Journal Template".Recurring WHERE (Name=FIELD(Journal Template Name)))
+```
+
+Similarly, the Template Type field is a FlowField that gets its value from the parent table:
+
+```al
+Lookup("Gen. Journal Template".Type WHERE (Name=FIELD(Journal Template Name)))
+```
+
+### Setting up a New Batch
+
+When the user creates a new batch, the following field values are transferred from the Gen. Journal Template table to the Gen. Journal Batch table:
+
+```al
+"Bal. Account Type" := GenJnlTemplate."Bal. Account Type";
+
+"Bal. Account No." := GenJnlTemplate."Bal. Account No.";
+
+"No. Series" := GenJnlTemplate."No. Series";
+
+"Posting No. Series" := GenJnlTemplate."Posting No. Series";
+
+"Reason Code" := GenJnlTemplate."Reason Code";
+
+"Copy VAT Setup to Jnl. Lines" := GenJnlTemplate."Copy VAT Setup to Jnl. Lines";
+
+"Allow VAT Difference" := GenJnlTemplate."Allow VAT Difference";
+```
+
+### Cascade record deletion
+
+When a record from the Gen. Journal Template table is deleted, the corresponding Gen. Journal Batch and Gen. Journal Line records are also deleted.
+
+### Cascade updates
+
+When the reason code or the posting number series change in the current batch, all linked Gen. Journal Line records are updated (see ModifyLines function on the Gen. Journal Batch table).
+
+The Gen. Journal Line table (814) stores a relation with the Journal Batch Name field (51) in the Gen. Journal Batch table. The Gen. Journal Line table also inherits the table relation with the Journal Template Name field (1) in the Gen. Journal Template table.
+
+## NAV Usages
+
+Implementations of this pattern in NAV include:
+
+* General Journal (see "Use the Pattern" above)
+* Item Journal
+* Resource Journal
+* Job Journal
+
+References
+
+1. [NAV Course 50534][anchor2] Finance Essentials in Microsoft Dynamics NAV 2013, Chapter 3: "General Journals".
+2. [NAV Course 50435][anchor3] Application Setup in Microsoft Dynamics NAV 2013, Chapter 5: "Set up Journal Templates and Batches"
+3. [Test reports][anchor4] Definition on MSDN.
+4. [Microsoft Dynamics NAV 2009: Using the journals and entries in a custom application][anchor5] Blog article by Mark Brummel
+5. [Search result for "Journal+NAV" ][anchor6]Various topics on MSDN
+
+## Related Pattern: Standard Journal
+
+For cases when most of the journal data can be used later (like monthly electricity payments, for example), the user has the possibility to save the current transaction details for later use. See the related pattern, Standard Journal.
+
+{{< youtube xtsZ5beNdZg>}}
+
+
+
+[anchor0]: 2438.Journal-Template-Batch-Line-1.jpg
+[anchor1]: 8103.Journal-Template-Batch-Line-2.jpg
+[anchor2]: https://mbs.microsoft.com/partnersource/communities/training/trainingmaterials/student/course80534.htm?printpage=false
+[anchor3]: https://mbs.microsoft.com/partnersource/communities/training/trainingmaterials/student/course80435.htm?printpage=false
+[anchor4]: http://msdn.microsoft.com/en-us/library/dd338776.aspx
+[anchor5]: http://www.packtpub.com/article/microsoft-dynamics-nav-2009-using-journals-and-entries-custom-application
+[anchor6]: http://social.msdn.microsoft.com/Search/en-US?query=journals%20nav&ac=3
+[anchor7]: https://www.youtube.com/watch?v=xtsZ5beNdZg&list=PLhZ3P-LY7CqmVszuvtJLujFyHpsVN0U_w&index=12
+
+
+[image0]: 2438.Journal-Template-Batch-Line-1.jpg
+[image1]: 8103.Journal-Template-Batch-Line-2.jpg
diff --git a/content/docs/NAVPatterns/patterns/master-data/PageCardDefinition.PNG b/content/docs/NAVPatterns/patterns/master-data/PageCardDefinition.PNG
new file mode 100644
index 00000000..c2d579d2
Binary files /dev/null and b/content/docs/NAVPatterns/patterns/master-data/PageCardDefinition.PNG differ
diff --git a/content/docs/NAVPatterns/patterns/master-data/PageCardPropertiesV2.PNG b/content/docs/NAVPatterns/patterns/master-data/PageCardPropertiesV2.PNG
new file mode 100644
index 00000000..66ba49ed
Binary files /dev/null and b/content/docs/NAVPatterns/patterns/master-data/PageCardPropertiesV2.PNG differ
diff --git a/content/docs/NAVPatterns/patterns/master-data/PageListDefinition.PNG b/content/docs/NAVPatterns/patterns/master-data/PageListDefinition.PNG
new file mode 100644
index 00000000..94859c14
Binary files /dev/null and b/content/docs/NAVPatterns/patterns/master-data/PageListDefinition.PNG differ
diff --git a/content/docs/NAVPatterns/patterns/master-data/PageListPropertiesV2.PNG b/content/docs/NAVPatterns/patterns/master-data/PageListPropertiesV2.PNG
new file mode 100644
index 00000000..439418ba
Binary files /dev/null and b/content/docs/NAVPatterns/patterns/master-data/PageListPropertiesV2.PNG differ
diff --git a/content/docs/NAVPatterns/patterns/master-data/Table.PNG b/content/docs/NAVPatterns/patterns/master-data/Table.PNG
new file mode 100644
index 00000000..822d573a
Binary files /dev/null and b/content/docs/NAVPatterns/patterns/master-data/Table.PNG differ
diff --git a/content/docs/NAVPatterns/patterns/master-data/TablePropertiesV2.PNG b/content/docs/NAVPatterns/patterns/master-data/TablePropertiesV2.PNG
new file mode 100644
index 00000000..cf5f5205
Binary files /dev/null and b/content/docs/NAVPatterns/patterns/master-data/TablePropertiesV2.PNG differ
diff --git a/content/docs/NAVPatterns/patterns/master-data/index.md b/content/docs/NAVPatterns/patterns/master-data/index.md
new file mode 100644
index 00000000..f42cbbd4
--- /dev/null
+++ b/content/docs/NAVPatterns/patterns/master-data/index.md
@@ -0,0 +1,168 @@
++++
+title = "Master Data"
+weight = 780
+tags = ["C/AL"]
+categories = ["Pattern"]
++++
+_By [Soren Klemmensen][anchor0], [_Partner-Ready-Software_ ][anchor1] & [360 Visibility][anchor2]_
+
+# Abstract
+
+The goal of this design pattern is to outline a standard way of creating master data, such as customers, vendors or items, in NAV.
+
+# Description
+
+This pattern creates a standard master data entity, listing all necessary prerequisites, attributes and methods to generate a basic version of the data entity.
+
+It is being used for creating a master data entity and can roughly be divided into 2 categories: Business and Product
+
+Examples of Master Data tables are:
+
+Business Data:
+
+* Table 18: Customer
+* Table 23: Vendor
+* Table 270: Bank Account
+* Table 5050: Contact
+
+Product Data:
+
+* Table 15 G/L Account
+* Table 27: Item
+* Table 156: Resource
+* Table 5600: Fixed Asset
+* Table 5718: Nonstock Item
+* Table 5913: Loaner
+
+Master Data has attributes such as Location, Countries & Item Vendors. These tables are closely related to Master Data tables and are called Supplemental and Subsidiary tables.
+
+Supplemental:
+
+* Table 9: Country/Region
+* Table 14: Location
+
+Subsidiary:
+
+* Table 99: Item Vendor
+
+Master Data is among other used in journals, posting routines and historic data like ledger entries.
+
+**Naming & Conventions**
+
+Table & Card Page
+
+* Singular
+
+* Examples: Customer, Vendor & Item
+
+List Page
+
+* Plural if Editable = TRUE.
+
+* Singular + "List" if Editable = FALSE.
+
+# Example
+
+The data entity has a single primary key field with the following properties:
+
+|
+----------|---------
+Field No. | 1
+Name | "No."
+Date Type | Code 20
+
+Each master data entity has a field which gives a name tag to the data entity carrying the natural name of the entity. This field is called Name if the data entity refers to a living person or an organization, such as a customer or a vendor; it is called Description instead if the data entity does not refer to a person or organization, such as an item. The field has the following properties:
+
+|
+----------|---------
+Name | Name or Description
+Date Type | Text 50
+
+An added benefit of using the Name or Description field naming is that this will be part of the dropdown when looking up based on the table relation.
+
+The table properties of a master data table contain the following entries:
+
+|
+------------------|---------
+LookupPageID | A ListType Page referring to the table which is not editable.
+DrillDownPageID | The same page as defined in the LookupPageID
+DataCaptionFields | The primary key plus the field which provides the primary name tag for the record. This will typically be the Name or Description field defined earlier.
+
+
+# NAV Specific Example
+
+We will create an Example table here with a Card and List Page as described above.
+
+First we create the Table with No. as the primary key.
+
+[![ ][image0]][anchor3]
+
+Than we create a non-editable Page called Example List
+
+[![ ][image1]][anchor4]
+
+Than we create an Example Card Page with the following definition
+
+[![ ][image2]][anchor5]
+
+Now we can set the properties of the 2 pages.
+
+First the Example Card Page Properties
+
+[![ ][image3]][anchor6]
+
+Than the Example List Page Properties
+
+[![ ][image4]][anchor7]
+
+Last but not least we set the Table Properties
+
+[![ ][image5]][anchor8]
+
+# NAV Usages
+
+You can see this pattern used in the following tables & pages:
+
+Business Data:
+
+* Table 18: Customer, Page 21 Customer Card & Page 22 Customer List
+* Table 23: Vendor, Page 26 Vendor Card & Page 27 Vendor List
+* Table 270: Bank Account, Page 370 Bank Account Card & Page 371 Bank Account List
+* Table 5050: Contact, Page 5050 Contact Card & Page 5052 Contact List
+
+Product Data:
+
+* Table 15 G/L Account, Page 17 G/L Account Card & Page 18 G/L Account List
+* Table 27: Item, Page 30 Item Card & Page 31 Item List
+* Table 156: Resource, Page 76 Resource Card & Page 77 Resource List
+* Table 5600: Fixed Asset, Page 5600 Fixed Asset Card & Page 5601 Fixed Asset List
+* Table 5718: Nonstock Item, Page 5725 Nonstock Item Card & Page 5726 Nonstock Item List
+* Table 5913: Loaner, Page 5922 Loaner Card & Page 5923 Loaner List
+
+# Consequences
+
+This pattern should not be used when this is not Master Data.
+
+# References
+
+Patterns that are typically used in connection with the Master Data Pattern could be the **"No. Series", "Address Integration"** and/or the **"Entity State"** design patterns., Master Data are central to almost everything we do, so most patterns connect in one way or another to the Master Data Pattern.
+
+
+
+[anchor0]: http://mvp.microsoft.com/en-us/mvp/Soren%20Klemmensen-5001002 "Soren Klemmensen"
+[anchor1]: http://partner-ready-software.com/ "Partner-Ready-Software"
+[anchor2]: http://www.360visibility.com/ "360 Visibility"
+[anchor3]: Table.PNG
+[anchor4]: PageListDefinition.PNG
+[anchor5]: PageCardDefinition.PNG
+[anchor6]: PageCardPropertiesV2.PNG
+[anchor7]: PageListPropertiesV2.PNG
+[anchor8]: TablePropertiesV2.PNG
+
+
+[image0]: Table.PNG
+[image1]: PageListDefinition.PNG
+[image2]: PageCardDefinition.PNG
+[image3]: PageCardPropertiesV2.PNG
+[image4]: PageListPropertiesV2.PNG
+[image5]: TablePropertiesV2.PNG
diff --git a/content/docs/NAVPatterns/patterns/multi-file-download/index.md b/content/docs/NAVPatterns/patterns/multi-file-download/index.md
new file mode 100644
index 00000000..fe7bb5f8
--- /dev/null
+++ b/content/docs/NAVPatterns/patterns/multi-file-download/index.md
@@ -0,0 +1,97 @@
++++
+title = "Multi-file Download"
+weight = 800
+tags = ["C/AL"]
+categories = ["Pattern"]
++++
+_By Martin Dam at Microsoft Development Center Copenhagen_
+
+## Abstract
+
+The goal of this pattern is to enable the users to download multiple files as a zip file instead of downloading one by one. On the Web Client this is preferred way of delivering multiple files since it is one of the web patterns and we cannot use File Management code unit to place files silently on the machine.
+
+## Description
+
+When generating reports that consists of multiple, and usually an unknown number of files, the developer will have to handle the download which also depends on the client the user is on. The problem is that the WinClient has access to the user's file system, whereas the web client does not. Following web guidelines, and the fact that client side .NET is not available in Web client, you can't initiate multiple downloads which requires the developer respond to the type of client. In some browsers it is possible to download files one-by-one in the Web client by using a confirm dialog, however this is a hack and should not be used.
+
+To solve this problem, a generic download mechanism is used that is client dependent event when multiple files need to be downloaded. For Web client the files are compressed using ZIP and for WinClient the files are downloaded directly to the file system.
+
+The pattern is usable for all objects that output multiple files and is available in both WinClient and Web client.
+
+## Usage
+
+The pattern consists of two steps: 1) Gathering the files and 2) downloading the file(s)
+
+For first step consists of a loop that goes through the files that needs to be downloaded. If it is on the Web client, the files are added to a ZIP archive server-side using a naming convention defined by the integration function GetSeriesFileName. This function takes a filename and number, and transforms it to unique names following a meaningful deterministic pattern e.g. prepend an integer before the file extension. The same function is used when the temporary files are created server side, so the files can be found deterministically later. This removes the need for storing filenames and consequently allows an arbitrary number of files. The second integration function: GetTotalNumberOfFiles, returns the total number of files generated during the data processing and makes the pattern able to handle an arbitrary number of files.
+
+The second step is the actually download of file(s). For the Web client this consists of closing the ZIP achieve and downloading via the standard download handler that works in the Web client. For the Win client, the files are saved directly to the client during the first step.
+
+Code 1: File loop shows an example implementation of this pattern. ServerFileName is generated at the beginning of the report/codeunit, and is the base for GetSeriesFilename. The file that is actually written to during data processing is stored in another variable which holds the output from GetSeriesFilename on the current file number. Note; the example code will only create a ZIP file if there in fact are multiple files to be downloaded.
+
+```al
+IF FileName = '' THEN
+ ERROR(SupplyFileNameErr);
+
+IF ZipMultipleFiles AND (GetTotalNumberOfFiles \> 1) AND FileManagement.IsWebClient
+THEN BEGIN
+ Basename := FileManagement.GetFileName(FileName);
+ ZipFileName := FileManagement.CreateZipArchiveObject;
+ FOR FileNo := 1 TO GetTotalNumberOfFiles DO
+ FileManagement.AddFileToZipArchive(
+ GetSeriesFilename(ServerFileName,FileNo),GetSeriesFilename(Basename,FileNo));
+ FileManagement.DownloadHandler(ZipFileName,'','','',
+ STRSUBSTNO('%1.zip',FileManagement.GetFileNameWithoutExtension(FileName)))
+END ELSE BEGIN
+ IF FileManagement.IsWebClient THEN BEGIN
+ IF GetTotalNumberOfFile > 1 THEN
+ ERROR(MultipleFilesWebClientErr);
+ FileManagementDownloadHandler(GetSeriesFilename(ServerFileName,1),'','','',
+ FileManagement.GetFileName(FileName));
+ END ELSE
+ FOR FileNo := 1 TO GetTotalNumberOfFiles DO
+ FileManagement.DownloadToFile(GetSeriesFilieName(ServerFileName,FileNo),
+ GetSeriesFilename(FileName,FileNo));
+END;
+```
+
+_Code 1: File loop_
+
+The following code is an example implementation of the GetSeriesFilename function. It needs to support the case where no directory is given, only a filename, in order to add files to the root of the ZIP archive. The example will add a file number right before the extension, e.g. C:\\directory\\file.txt will become C:\\directory\\file1.txt etc.
+
+```al
+LOCAL GetSeriesFilename(FileName : Text;FileNo : Integer) : Text
+IF STRPOS(FileName,'\\') <> 0 THEN
+ Directory := FileMgt.GetDirectoryName(FileName) + '\\';
+EXIT(Directory + FileMgt.GetFileNameWithoutExtension(FileName) + FORMAT(FileNo) + '.' +
+ FileMgt.GetExtension(FileName));
+```
+
+#### _Code 2: GetSeriesFilename_
+
+The pattern depends on .NET library System.IO.Compression.ZipFile, which from NAV 2015 is implemented in Codeunit 419 (File management). It consists of three functions:
+
+* CreateZipArchiveObject: Which creates a System.IO.Compression.ZipArchive on a server side file
+* AddFileToZipArchive: Which adds a server side file to the archive using System.IO.Compression.ZipFileExtensions.CreateEntryFromFile(). This function allows to use arbitrary naming and does not require to create a server directory before creating the ZIP achieve.
+* CloseZipArchive: Which simply closes the ZIP file and saves it to disk.
+
+The pattern is integrated into the report/codeunit in question by providing a filename field on the request page if it is on WinClient but hidden if it is in Web client. On the Web client, a meaningful default filename is used for the file to download, e.g. in Code 1, Filename is set by the user on the request page if it is WinClient, and set to a default value in Web client.
+
+## NAV Usages
+
+This pattern is used by VAT Report and Blacklist communication in the Italian localization in NAV 2015\. The ZIP archive functions are available on all localization from NAV 2015\.
+
+## Consequences
+
+This pattern can be used whenever you need to ZIP one or more files. The above example downloads the file, but it could have been transmitted, saved to a persistent storage etc. It could also be used to improve download speed between server and client where the WinClient would unzip the files locally and save individually to disk. This follows the normal procedure in NAV in the WinClient, which is to download files directly to disk, so a ZIP file should only be created when the user specifically needs it or the Web client is invoking the report/codeunit.
+
+## Related Topics
+
+File Management
+
+## References
+
+[System.IO.Compression.ZipFile][anchor0]
+
+
+
+[anchor0]: http://msdn.microsoft.com/en-us/library/vstudio/system.io.compression.zipfile
diff --git a/content/docs/NAVPatterns/patterns/multi-page-list/Multi-page-list-img-1.jpg b/content/docs/NAVPatterns/patterns/multi-page-list/Multi-page-list-img-1.jpg
new file mode 100644
index 00000000..8664e3f4
Binary files /dev/null and b/content/docs/NAVPatterns/patterns/multi-page-list/Multi-page-list-img-1.jpg differ
diff --git a/content/docs/NAVPatterns/patterns/multi-page-list/Multi-page-list-img-2.jpg b/content/docs/NAVPatterns/patterns/multi-page-list/Multi-page-list-img-2.jpg
new file mode 100644
index 00000000..fe8cbffc
Binary files /dev/null and b/content/docs/NAVPatterns/patterns/multi-page-list/Multi-page-list-img-2.jpg differ
diff --git a/content/docs/NAVPatterns/patterns/multi-page-list/index.md b/content/docs/NAVPatterns/patterns/multi-page-list/index.md
new file mode 100644
index 00000000..3fea0820
--- /dev/null
+++ b/content/docs/NAVPatterns/patterns/multi-page-list/index.md
@@ -0,0 +1,91 @@
++++
+title = "Multi-Page List"
+weight = 810
+tags = ["C/AL"]
+categories = ["Pattern"]
++++
+_By Bogdana Botez at Microsoft Development Center Copenhagen_
+
+### Abstract
+
+This pattern describes how to open a related document (or card) page from a list page, for the case when there can be more than one pages associated to the rows of the list page.
+
+### Description
+
+The example below illustrates the connection of a List Page with multiple Document Pages, while the second example links the List Page with Card Pages.
+
+[![ ][image0]][anchor0]
+
+The records contained in a list page have an associated page, which is specified in the CardPageID property of the list page. The NAV infrastructure assures the following scenarios are in place, without the need to add any explicit C/AL code:
+
+1. From the selected record in the list page, the user can double-click in order to open the related card page.
+2. The "Edit" action is available on the ribbon as well as in the right-click context menu of the list page rows. Invoking this action, opens the related card page.
+
+However, there are situations when rows of the list page can correspond to different pages each (either cards or documents). For example, consider a list page containing 3 rows, requiring the following behaviour:
+
+|
+-----|----------------
+Row1 | Opens page ID 1
+Row2 | Opens page ID 2
+Row3 | Opens page ID 3
+
+This situation is not handled automatically by NAV. There is no possibility to specify more than one CardPageID in the list page properties. Furthermore, there is no possibility to specify one or more document page IDs on the list page. Therefore, those cases need to be handled explicitly by the C/AL developer.
+
+### Usage
+
+The solution used in NAV implementations is at the list page level, as following:
+
+1. The property CardPageID of the list page remains undefined.
+2. An action named "Show Document" or "Card" is created on the Navigate tab, with the properties:
+
+ * Image = EditLines
+ * Promoted = Yes
+ * ShortCutKey = Shift+F7
+
+3. The OnAction trigger for the Card action, contains explicit logic to run the targeted card page. It can, for example, be a CASE statement, which invokes PAGE.RUN(...) based on an enumeration field of the current row.
+
+### NAV Specific Example
+
+For example, the NAV page Sales List (page ID 45), which displays the Sales Header Table (table ID 36), chooses which card to open, based on the Document Type field. This is an option field, which can have the following values: Quote, Order, Invoice, Credit Memo, Blanket Order, Return Order. For each document type, the related card page must be opened.
+
+[![ ][image1]][anchor1]
+
+For this purpose, a new action ("Card") is added to the Sales List page. The OnAction trigger of this new action contains the page selection logic:
+
+```al
+CASE "Document Type" OF
+ "Document Type"::Quote:
+ PAGE.RUN(PAGE::"Sales Quote",Rec);
+ "Document Type"::Order:
+ PAGE.RUN(PAGE::"Sales Order",Rec);
+ "Document Type"::Invoice:
+ PAGE.RUN(PAGE::"Sales Invoice",Rec);
+ "Document Type"::"Return Order":
+ PAGE.RUN(PAGE::"Sales Return Order",Rec);
+ "Document Type"::"Credit Memo":
+ PAGE.RUN(PAGE::"Sales Credit Memo",Rec);
+ "Document Type"::"Blanket Order":
+ PAGE.RUN(PAGE::"Blanket Sales Order",Rec);
+END;
+```
+
+### NAV Usages
+
+Some of the NAV implementations of this pattern can be found in the following pages:
+
+1. Sales List (45) and Sales List Archive (5159)
+2. Purchase List (53) and Purchase List Archive (5166)
+3. Available - Sales Lines (499)
+4. Sales Lines (516)
+5. Purchase Lines (518)
+
+
+
+[anchor0]: Multi-page-list-img-1.jpg
+[anchor1]: Multi-page-list-img-2.jpg
+[anchor2]: http://sharepointemea/sites/DynamicsNAV/Wiki/Nav%20Wiki%20Documents/NAV%20App%20Patterns/NAV%20App%20Patterns%20for%20Review/Multi-Page%20List.docx#_msocom_5
+[anchor3]: http://sharepointemea/sites/DynamicsNAV/Wiki/Nav%20Wiki%20Documents/NAV%20App%20Patterns/NAV%20App%20Patterns%20for%20Review/Multi-Page%20List.docx#_msocom_7
+
+
+[image0]: Multi-page-list-img-1.jpg
+[image1]: Multi-page-list-img-2.jpg
diff --git a/content/docs/NAVPatterns/patterns/multilanguage-application-data/2746.3.png b/content/docs/NAVPatterns/patterns/multilanguage-application-data/2746.3.png
new file mode 100644
index 00000000..37cb2fc3
Binary files /dev/null and b/content/docs/NAVPatterns/patterns/multilanguage-application-data/2746.3.png differ
diff --git a/content/docs/NAVPatterns/patterns/multilanguage-application-data/4812.4.png b/content/docs/NAVPatterns/patterns/multilanguage-application-data/4812.4.png
new file mode 100644
index 00000000..21657b93
Binary files /dev/null and b/content/docs/NAVPatterns/patterns/multilanguage-application-data/4812.4.png differ
diff --git a/content/docs/NAVPatterns/patterns/multilanguage-application-data/5670.1.png b/content/docs/NAVPatterns/patterns/multilanguage-application-data/5670.1.png
new file mode 100644
index 00000000..6f1df820
Binary files /dev/null and b/content/docs/NAVPatterns/patterns/multilanguage-application-data/5670.1.png differ
diff --git a/content/docs/NAVPatterns/patterns/multilanguage-application-data/6746.2.png b/content/docs/NAVPatterns/patterns/multilanguage-application-data/6746.2.png
new file mode 100644
index 00000000..c2b0dbbe
Binary files /dev/null and b/content/docs/NAVPatterns/patterns/multilanguage-application-data/6746.2.png differ
diff --git a/content/docs/NAVPatterns/patterns/multilanguage-application-data/index.md b/content/docs/NAVPatterns/patterns/multilanguage-application-data/index.md
new file mode 100644
index 00000000..79495c6f
--- /dev/null
+++ b/content/docs/NAVPatterns/patterns/multilanguage-application-data/index.md
@@ -0,0 +1,96 @@
++++
+title = "Multilanguage Application Data"
+weight = 820
+tags = ["C/AL"]
+categories = ["Pattern"]
++++
+_Originally by Bogdana Botez at Microsoft Development Center Copenhagen_
+
+## Abstract
+
+Generally, NAV translation refers to the translation of UI elements like captions and user texts (messages on dialogs, warnings, error messages). This translation is done by the Microsoft Dynamics NAV team before releasing the localized version of the product.
+
+But there is one more scenario. In this scenario, Cronus International Ltd., wants to sell a "Fiets" to a Dutch customer, a"Cykel" to a Danish one, and a "Bicicletta" to an Italian customer. All 3 are the same inventory item - and its default name is "Bicycle". But for reporting, Cronus International Ltd. wants to use the customer language preferences for translating the bicycle's name.
+
+Sometimes there's a need to support multiple languages for domestic transactions, too. For example, [Switzerland has 4 official languages][anchor0]: German, French, Italian and Romansh, the first 3 of them being supported by NAV.
+
+## Description
+
+The example below uses the Item Translation feature of NAV, however, implementations of the same pattern exist for other application areas.
+
+### How to use the pattern
+
+**Enter translations for "Bicycle"**
+
+In the Windows client, on the bicycle Item card, on the Home ribbon tab, choose Translations.
+
+[![ ][image0]][anchor1]
+
+On the opened page, enter the Danish (language code DAN), the Italian (ITA), and the Dutch (NLD) translations for "Bicycle".
+
+[![ ][image1]][anchor2]
+
+**Set the desired language for the Dutch, Danish, and Italian customers**
+
+On the customer card for your 3 customers, in the Foreign Trade FastTab, choose the preferred language for each customer. If no language is specified, then the default item description will be used for items sold or anyhow associated to that customer. If for example, the DAN (Danish) language is specified for the customer, and the "Bicycle" has a translation in Danish, then this translation "Cykel" will be used instead of the default name "Bicycle".
+
+[![ ][image2]][anchor3]
+
+**See the result**
+
+After those changes, when the customer (in this case the Danish "Lauritzen Kontormøbler A/S") transactions a bicycle, the translated description "Cykel" will be displayed on the documents and reports. For example, creating a sales order for this customer with 1 item No. 1000, shows:
+
+[![ ][image3]][anchor4]
+
+## Usage
+
+**Create the translation table**
+
+Named " Translation" table, where is replaced with the name of the actual object being translated. For the Item example above, this table will be named "Item Translation".
+
+The table definition contains at a minimum:
+Field | Description
+---------|---------
+Entity ID field | For example, Item No.
+Language Code | Identifies the language of this translation string (for example, "DAN" (Danish), "BGR"(Bulgarian). This is one of the language codes defined in the Languages table.
+Translation | The translated string.
+
+The table above has a key composed of the first two fields.
+
+**Create the user interface for entering new translations of **
+
+* Create a Translations page to present the table created above
+* On the Entity card - add a Translations menu option which will open the Translations page
+
+## NAV Usages
+
+Some of the NAV implementations of this pattern are:
+
+1. Item Translation
+2. Payment Term Translation
+3. Shipment Method Translation
+4. Unit of Measure Translation
+
+## Related Topics
+
+The **Extended Text** pattern is a more powerful version of the **Multilanguage application data** pattern presented in this section. The main differences are:
+
+Pattern | Multilanguage Application Data | Extended Text
+--------|--------------------------------|--------------
+Supports translation of application data | Yes | Yes
+Format | Single- or multi-line | Single-line
+Applies to document type | Can choose which document types are affected. | All document types are affected.
+
+
+
+[anchor0]: http://en.wikipedia.org/wiki/Languages_of_Switzerland "Switzerland has 4 official languages"
+[anchor1]: 5670.1.png
+[anchor2]: 6746.2.png
+[anchor3]: 2746.3.png
+[anchor4]: 4812.4.png
+
+
+[image0]: 5670.1.png
+[image1]: 6746.2.png
+[image2]: 2746.3.png
+[image3]: 4812.4.png
diff --git a/content/docs/NAVPatterns/patterns/net-exception-handling-in-cal/1856.DotNet_5F00_Exception_5F00_Handling_5F00_in_5F00_CAL_5F00_Diagram.png b/content/docs/NAVPatterns/patterns/net-exception-handling-in-cal/1856.DotNet_5F00_Exception_5F00_Handling_5F00_in_5F00_CAL_5F00_Diagram.png
new file mode 100644
index 00000000..464f1e2d
Binary files /dev/null and b/content/docs/NAVPatterns/patterns/net-exception-handling-in-cal/1856.DotNet_5F00_Exception_5F00_Handling_5F00_in_5F00_CAL_5F00_Diagram.png differ
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
new file mode 100644
index 00000000..9175a3e3
--- /dev/null
+++ b/content/docs/NAVPatterns/patterns/net-exception-handling-in-cal/_index.md
@@ -0,0 +1,207 @@
++++
+title = "NET Exception Handling in CAL"
+weight = 860
+tags = ["C/AL"]
+categories = ["Pattern"]
++++
+_Originally by Mostafa Balat, Microsoft Development Center Copenhagen_
+
+## Abstract
+
+When there is a need to use .NET classes within C/AL, one of the main challenges is to handle the exceptions the methods of these .NET classes may throw. Eventually, if not handled, they will basically bubble up as runtime errors, halting the current operation a user is doing without having a chance to properly display errors in a user-friendly format.
+
+## Description
+
+Using the .NET classes in order to extend NAV's functionality usually triggers the need to create an add-on assembly. This is a pretty powerful approach and opens the door for empowering NAV with new and extra functionality while harnessing the full power of .NET.
+
+For example, integration with a Web service into NAV can be done to extend NAV's functionality or benefit from a service model offered through a 3rd party. To do so, it is possible to write a .NET add-in to handle the required bi-directional communication between NAV and the Web service. Alternatively, the implementation itself can be done in C/AL, with no add-in dependency. The latter option simplifies customization, deployment and upgradeability. Additionally, it builds up on the knowledge NAV developers have with C/AL programming.
+
+On the other hand, not using an add-in exposes NAV to runtime errors due to unhandled exceptions that get thrown at different levels. The first is the communication layer, in which HTTP requests and responses are exchanged. The second is the business logic layer, at which the content of the requests and response is being prepared and groomed using XML Elements and being retrieved or edited based on the respective XPaths.
+
+### When to Use It
+
+When .NET classes are used, they may throw exceptions upon failure. Some of these exceptions cannot pre-checked (e.g. like existence of a file on disk) and will only be figured out at runtime. Eventually, to present the error reason to a user and explain what needs to be done to address it, the exception needs to be handled gracefully. This also protects the client for unexpected crashes that may deteriorate the user experience.
+
+### Diagram
+
+[![ ][image0]][anchor0]
+
+## Usage
+
+A Try-Catch-Finally statement does not exist in C/AL. The alternative is to run the code consuming .NET objects within a codeunit, and handle the runtime errors, as follows:
+
+1. Write the code that uses the .NET classes in a codeunit.
+2. Run the codeunit using **CODEUNIT.RUN** or the Run function on a codeunit variable.
+3. Handle the return value of the **OnRun** trigger for the codeunit within an **IF** statement.
+4. Write the exception handling code in a function, and call it when the return value for **OnRun** is **FALSE**.
+5. The exception handling function should determine which exception to handle, and pass this over to **COD1291 DotNet Exception Handler** codeunit.
+
+When an exception is thrown, it is already wrapped up within an NAV exception. This means the real exception to look for is retrievable through the **InnerException** property of the NAV exception. Then, next step would be to properly determine the type of that exception, and act accordingly. The **COD1291 DotNet Exception Handler** codeunit takes care of looping through the nested levels of inner exceptions, digging for an exception with the expected type. If found, it is retrieved; otherwise, the main (outer) exception's message is retrieved.
+
+## NAV Specific Example
+
+### Overview
+
+The exception handling pattern is implemented in the integration with a web service for bank file format conversion. Within the respective "External Data Handling Codeunit" for that web service, communication through SOAP requests is required. Eventually, the HTTP web request and response .NET classes are used for that purpose.
+
+For instance, if a failure to establish a connection to the web service due to lack of Internet access, a **WebException** is thrown with the relevant error details. **COD1290 Web Service Mgt.** codeunit wraps up the required functionality to interact with a web service in C/AL, handling all the required plumping work to send web requests, receive web responses, and extract valid and error details from the web response.
+
+### Code Sample
+
+The "External Data Handling Codeunit" is a consumer of **COD1290 Web Service Mgt.** codeunit. If a runtime exception occurs, it gets handled as follows:
+
+```al
+LOCAL PROCEDURE SendDataToConversionService@1(VAR PaymentFileTempBlob@1003 : Record 99008535;BodyTempBlob@1004 : Record 99008535;PostingExch@1007 : Record 1220);
+VAR
+ BankDataConvServiceSetup@1000 : Record 1260;
+ WebServiceRequestMgt@1001 : Codeunit 1290;
+ BodyInStream@1005 : InStream;
+ ResponseInStream@1002 : InStream;
+BEGIN
+ IF NOT BodyTempBlob.Blob.HASVALUE THEN
+ ERROR(NoRequestBodyErr);
+
+ PrepareSOAPRequestBody(BodyTempBlob);
+
+ COMMIT;
+
+ BankDataConvServiceSetup.GET;
+ BodyTempBlob.Blob.CREATEINSTREAM(BodyInStream);
+ WebServiceRequestMgt.SetGlobals(BodyInStream,
+ BankDataConvServiceSetup."Service URL",BankDataConvServiceSetup."User Name",BankDataConvServiceSetup.GetPassword);
+
+ IF NOT WebServiceRequestMgt.RUN THEN
+ WebServiceRequestMgt.ProcessFaultResponse;
+
+ WebServiceRequestMgt.GetResponseContent(ResponseInStream);
+
+ CheckIfErrorsOccurred(ResponseInStream,PostingExch);
+
+ ReadContentFromResponse(PaymentFileTempBlob,ResponseInStream);
+END;
+```
+
+```al
+PROCEDURE ProcessFaultResponse@15();
+VAR
+ XMLDOMMgt@1006 : Codeunit 6224;
+ DotNetExceptionHandler@1000 : Codeunit 1291;
+ WebException@1005 : DotNet "'System, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089'.System.Net.WebException";
+ WebExceptionStatus@1004 : DotNet "'System, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089'.System.Net.WebExceptionStatus";
+ XmlDoc@1003 : DotNet "'System.Xml, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089'.System.Xml.XmlDocument";
+ HttpWebResponseError@1007 : DotNet "'System, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089'.System.Net.HttpWebResponse";
+ HttpStatusCode@1008 : DotNet "'System, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089'.System.Net.HttpStatusCode";
+ ResponseInputStream@1002 : InStream;
+BEGIN
+ DotNetExceptionHandler.Collect;
+
+ IF NOT DotNetExceptionHandler.CastToType(WebException,GETDOTNETTYPE(WebException)) THEN
+ DotNetExceptionHandler.Rethrow;
+
+ IF NOT WebException.Status.Equals(WebExceptionStatus.ProtocolError) THEN
+ ERROR(WebException.Message);
+
+ ResponseInputStream := WebException.Response.GetResponseStream;
+ DebugLogStreamToTempFile(ResponseInputStream,'WebExceptionResponse',TempDebugLogTempBlob);
+
+ HttpWebResponseError := WebException.Response;
+ IF NOT (HttpWebResponseError.StatusCode.Equals(HttpStatusCode.Found) OR
+ HttpWebResponseError.StatusCode.Equals(HttpStatusCode.InternalServerError))
+ THEN
+ ERROR(WebException.Message);
+
+ XmlDoc := XmlDoc.XmlDocument;
+ XmlDoc.Load(ResponseInputStream);
+
+ ERROR(XMLDOMMgt.FindNodeTextWithNamespace(XmlDoc.DocumentElement,FaultStringXmlPathTxt,'soap',SoapNamespaceTxt));
+END;
+```
+
+```al
+OBJECT Codeunit 1291 DotNet Exception Handler
+{
+ OBJECT-PROPERTIES
+ {
+ Date=;
+ Time=;
+ Version List=;
+ }
+ PROPERTIES
+ {
+ OnRun=BEGIN
+ END;
+
+ }
+ CODE
+ {
+ VAR
+ OuterException@1000 : DotNet "'mscorlib'.System.Exception";
+
+ PROCEDURE Catch@3(VAR Exception@1002 : DotNet "'mscorlib'.System.FormatException";Type@1007 : DotNet "'mscorlib'.System.Type");
+ BEGIN
+ Collect;
+ IF NOT CastToType(Exception,Type) THEN
+ Rethrow;
+ END;
+
+ PROCEDURE Collect@1();
+ BEGIN
+ OuterException := GETLASTERROROBJECT;
+ END;
+
+ PROCEDURE TryCastToType@5(Type@1000 : DotNet "'mscorlib'.System.Type") : Boolean;
+ VAR
+ Exception@1001 : DotNet "'mscorlib'.System.FormatException";
+ BEGIN
+ EXIT(CastToType(Exception,Type));
+ END;
+
+ PROCEDURE CastToType@2(VAR Exception@1002 : DotNet "'mscorlib'.System.FormatException";Type@1007 : DotNet "'mscorlib'.System.Type") : Boolean;
+ BEGIN
+ Exception := OuterException;
+
+ REPEAT
+ IF Type.Equals(Exception.GetType()) THEN
+ EXIT(TRUE);
+ Exception := Exception.InnerException;
+ UNTIL ISNULL(Exception);
+
+ EXIT(FALSE);
+ END;
+
+ PROCEDURE Rethrow@4();
+ BEGIN
+ IF NOT ISNULL(OuterException.InnerException) THEN
+ ERROR(OuterException.InnerException.Message);
+
+ ERROR(OuterException.Message);
+ END;
+
+ BEGIN
+ END.
+ }
+}
+```
+
+## NAV Usages
+
+The DotNet Exception Handler codeunit has been used for the Web service integration required for:
+
+1. Payment Export from the Payment Journal for creating bank-specific payment files.
+2. Bank Statement Import on the Bank Acc. Reconciliation card for importing the content of bank-specific statements.
+3. Bank name lookup on the Bank Account card for dynamically identifying the format to use to generate a bank-specific payment file.
+
+## Ideas for Improvement
+
+Extend the language support in C/AL to provide a built-in Try-Catch-Finally statement, similar to .NET languages.
+
+## Consequences
+
+When this pattern should not be used: avoid nesting of codeunits. To properly handle the exceptions, you need to use the codeunit as an atomic piece of functionality that may pass or fail without using Codeunit.Run internally.
+
+
+
+[anchor0]: 1856.DotNet_5F00_Exception_5F00_Handling_5F00_in_5F00_CAL_5F00_Diagram.png
+
+
+[image0]: 1856.DotNet_5F00_Exception_5F00_Handling_5F00_in_5F00_CAL_5F00_Diagram.png
diff --git a/content/docs/NAVPatterns/patterns/net-exception-handling-in-cal/tryfunction-net-exception-handling-in-cal/TryFunction_5F00_DotNet_5F00_Exception_5F00_Handling_5F00_in_5F00_CAL.png b/content/docs/NAVPatterns/patterns/net-exception-handling-in-cal/tryfunction-net-exception-handling-in-cal/TryFunction_5F00_DotNet_5F00_Exception_5F00_Handling_5F00_in_5F00_CAL.png
new file mode 100644
index 00000000..fb8c66ed
Binary files /dev/null and b/content/docs/NAVPatterns/patterns/net-exception-handling-in-cal/tryfunction-net-exception-handling-in-cal/TryFunction_5F00_DotNet_5F00_Exception_5F00_Handling_5F00_in_5F00_CAL.png differ
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
new file mode 100644
index 00000000..209b5c70
--- /dev/null
+++ b/content/docs/NAVPatterns/patterns/net-exception-handling-in-cal/tryfunction-net-exception-handling-in-cal/index.md
@@ -0,0 +1,224 @@
++++
+title = "TryFunction NET Exception Handling in CAL"
+weight = 1240
+tags = ["C/AL"]
+categories = ["Pattern"]
++++
+_Originally by Mostafa Balat, Microsoft Development Center Copenhagen_
+
+## Abstract
+
+When there is a need to use .NET classes within C/AL, one of the main challenges is to handle the exceptions the methods of these .NET classes may throw. Eventually, if not handled, they will basically bubble up as runtime errors, halting the current operation a user is doing without having a chance to properly display errors in a user-friendly format.
+
+## Description
+
+Using the .NET classes in order to extend NAV's functionality usually triggers the need to create an add-on assembly. This is a pretty powerful approach and opens the door for empowering NAV with new and extra functionality while harnessing the full power of .NET.
+
+For example, integration with a Web service into NAV can be done to extend NAV's functionality or benefit from a service model offered through a 3rd party. To do so, it is possible to write a .NET add-in to handle the required bi-directional communication between NAV and the Web service. Alternatively, the implementation itself can be done in C/AL, with no add-in dependency. The latter option simplifies customization, deployment and upgradeability. Additionally, it builds up on the knowledge NAV developers have with C/AL programming.
+
+On the other hand, not using an add-in exposes NAV to runtime errors due to unhandled exceptions that get thrown at different levels. The first is the communication layer, in which HTTP requests and responses are exchanged. The second is the business logic layer, at which the content of the requests and response is being prepared and groomed using XML Elements and being retrieved or edited based on the respective XPaths.
+
+### When to Use It
+
+When .NET classes are used, they may throw exceptions upon failure. Some of these exceptions cannot pre-checked (e.g. like existence of a file on disk) and will only be figured out at runtime. Eventually, to present the error reason to a user and explain what needs to be done to address it, the exception needs to be handled gracefully. This also protects the client for unexpected crashes that may deteriorate the user experience.
+
+### Diagram
+
+[![ ][image0]][anchor0]
+
+## Usage
+
+A Try-Catch-Finally statement does not exist in C/AL. The alternative is to run the code consuming .NET objects within a TryFunction, and handle the runtime errors, as follows:
+
+1. Write the code that uses the .NET classes in a procedure whose TryFunction property is set to Yes.
+2. Invoke the TryFunction like any other procedure within the code.
+3. Handle the return value of the TryFunction within an **IF..THEN** statement.
+4. Write the exception handling code in a function, and call it when the return value for the TryFunction is **FALSE**.
+5. The exception handling function should determine which exception to handle, and pass this over to the **COD1291 DotNet Exception Handler** codeunit.
+
+When an exception is thrown, it is already wrapped up within an NAV exception. This means the real exception to look for is retrievable through the **InnerException** property of the NAV exception. Then, the next step would be to properly determine the type of that exception, and act accordingly. The **COD1291 DotNet Exception Handler** codeunit takes care of looping through the nested levels of inner exceptions, digging for an exception with the expected type. If found, it is retrieved; otherwise, the main (outer) exception's message is retrieved.
+
+## NAV Specific Example
+
+### Overview
+
+The exception handling pattern is implemented in the integration with a web service for bank file format conversion. Within the respective "External Data Handling Codeunit" for that web service, communication through SOAP requests is required. Eventually, the HTTP web request and response .NET classes are used for that purpose.
+
+For instance, if a failure to establish a connection to the web service due to lack of Internet access, a **WebException** is thrown with the relevant error details. **COD1290 Web Service Mgt.** codeunit wraps up the required functionality to interact with a web service in C/AL, handling all the required plumping work to send web requests, receive web responses, and extract valid and error details from the web response.
+
+### Code Sample
+
+The "External Data Handling Codeunit" is a consumer of **COD1290 Web Service Mgt.** codeunit. If a runtime exception occurs, it gets handled as follows:
+
+```al
+[TryFunction]
+PROCEDURE SendRequestToWebService@17();
+VAR
+ WebRequestHelper@1000 : Codeunit 1299;
+ HttpWebRequest@1007 : DotNet "'System, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089'.System.Net.HttpWebRequest";
+ HttpStatusCode@1002 : DotNet "'System, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089'.System.Net.HttpStatusCode";
+ ResponseHeaders@1001 : DotNet "'System, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089'.System.Collections.Specialized.NameValueCollection";
+ ResponseInStream@1006 : InStream;
+BEGIN
+ CheckGlobals;
+
+ BuildWebRequest(GlobalURL,HttpWebRequest);
+
+ ResponseInStreamTempBlob.INIT;
+ ResponseInStreamTempBlob.Blob.CREATEINSTREAM(ResponseInStream);
+
+ CreateSoapRequest(HttpWebRequest.GetRequestStream,GlobalRequestBodyInStream,GlobalUsername,GlobalPassword);
+
+ WebRequestHelper.GetWebResponse(HttpWebRequest,HttpWebResponse,ResponseInStream,
+ HttpStatusCode,ResponseHeaders,GlobalProgressDialogEnabled);
+
+ ExtractContentFromResponse(ResponseInStream,ResponseBodyTempBlob);
+END;
+```
+
+```al
+LOCAL PROCEDURE SendDataToConversionService@1(VAR PaymentFileTempBlob@1003 : Record 99008535;BodyTempBlob@1004 : Record 99008535;PostingExch@1007 : Record 1220);
+VAR
+ BankDataConvServiceSetup@1000 : Record 1260;
+ WebServiceRequestMgt@1001 : Codeunit 1290;
+ BodyInStream@1005 : InStream;
+ ResponseInStream@1002 : InStream;
+BEGIN
+ IF NOT BodyTempBlob.Blob.HASVALUE THEN
+ ERROR(NoRequestBodyErr);
+
+ PrepareSOAPRequestBody(BodyTempBlob);
+
+ COMMIT;
+
+ BankDataConvServiceSetup.GET;
+ BodyTempBlob.Blob.CREATEINSTREAM(BodyInStream);
+ WebServiceRequestMgt.SetGlobals(BodyInStream,BankDataConvServiceSetup."Service URL",
+ BankDataConvServiceSetup."User Name",BankDataConvServiceSetup.GetPassword);
+
+ IF NOT WebServiceRequestMgt.SendRequestToWebService THEN
+ WebServiceRequestMgt.ProcessFaultResponse;
+
+ WebServiceRequestMgt.GetResponseContent(ResponseInStream);
+
+ CheckIfErrorsOccurred(ResponseInStream,PostingExch);
+
+ ReadContentFromResponse(PaymentFileTempBlob,ResponseInStream);
+END;
+```
+
+```al
+PROCEDURE ProcessFaultResponse@15();
+VAR
+ XMLDOMMgt@1006 : Codeunit 6224;
+ DotNetExceptionHandler@1000 : Codeunit 1291;
+ WebException@1005 : DotNet "'System, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089'.System.Net.WebException";
+ WebExceptionStatus@1004 : DotNet "'System, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089'.System.Net.WebExceptionStatus";
+ XmlDoc@1003 : DotNet "'System.Xml, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089'.System.Xml.XmlDocument";
+ HttpWebResponseError@1007 : DotNet "'System, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089'.System.Net.HttpWebResponse";
+ HttpStatusCode@1008 : DotNet "'System, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089'.System.Net.HttpStatusCode";
+ ResponseInputStream@1002 : InStream;
+BEGIN
+ DotNetExceptionHandler.Collect;
+
+ IF NOT DotNetExceptionHandler.CastToType(WebException,GETDOTNETTYPE(WebException)) THEN
+ DotNetExceptionHandler.Rethrow;
+
+ IF NOT WebException.Status.Equals(WebExceptionStatus.ProtocolError) THEN
+ ERROR(WebException.Message);
+
+ ResponseInputStream := WebException.Response.GetResponseStream;
+ DebugLogStreamToTempFile(ResponseInputStream,'WebExceptionResponse',TempDebugLogTempBlob);
+
+ HttpWebResponseError := WebException.Response;
+ IF NOT (HttpWebResponseError.StatusCode.Equals(HttpStatusCode.Found) OR
+ HttpWebResponseError.StatusCode.Equals(HttpStatusCode.InternalServerError))
+ THEN
+ ERROR(WebException.Message);
+
+ XmlDoc := XmlDoc.XmlDocument;
+ XmlDoc.Load(ResponseInputStream);
+
+ ERROR(XMLDOMMgt.FindNodeTextWithNamespace(XmlDoc.DocumentElement,FaultStringXmlPathTxt,'soap',SoapNamespaceTxt));
+END;
+```
+
+```al
+OBJECT Codeunit 1291 DotNet Exception Handler
+{
+ OBJECT-PROPERTIES
+ {
+ Date=;
+ Time=;
+ Version List=;
+ }
+ PROPERTIES
+ {
+ OnRun=BEGIN
+ END;
+ }
+ CODE
+ {
+ VAR
+ OuterException@1000 : DotNet "'mscorlib'.System.Exception";
+
+ PROCEDURE Catch@3(VAR Exception@1002 : DotNet "'mscorlib'.System.FormatException";Type@1007 : DotNet "'mscorlib'.System.Type");
+ BEGIN
+ Collect;
+ IF NOT CastToType(Exception,Type) THEN
+ Rethrow;
+ END;
+
+ PROCEDURE Collect@1();
+ BEGIN
+ OuterException := GETLASTERROROBJECT;
+ END;
+
+ PROCEDURE TryCastToType@5(Type@1000 : DotNet "'mscorlib'.System.Type") : Boolean;
+ VAR
+ Exception@1001 : DotNet "'mscorlib'.System.FormatException";
+ BEGIN
+ EXIT(CastToType(Exception,Type));
+ END;
+
+ PROCEDURE CastToType@2(VAR Exception@1002 : DotNet "'mscorlib'.System.FormatException";Type@1007 : DotNet "'mscorlib'.System.Type") : Boolean;
+ BEGIN
+ Exception := OuterException;
+
+ REPEAT
+ IF Type.Equals(Exception.GetType()) THEN
+ EXIT(TRUE);
+ Exception := Exception.InnerException;
+ UNTIL ISNULL(Exception);
+
+ EXIT(FALSE);
+ END;
+
+ PROCEDURE Rethrow@4();
+ BEGIN
+ IF NOT ISNULL(OuterException.InnerException) THEN
+ ERROR(OuterException.InnerException.Message);
+
+ ERROR(OuterException.Message);
+ END;
+
+ BEGIN
+ END.
+ }
+}
+```
+
+## NAV Usages
+
+The **CO1291 DotNet Exception Handler** codeunit has been used for the Web service integration required for:
+
+1. Payment Export from the Payment Journal for creating bank-specific payment files.
+2. Bank Statement Import on the Bank Acc. Reconciliation card for importing the content of bank-specific statements.
+3. Bank name lookup on the Bank Account card for dynamically identifying the format to use to generate a bank-specific payment file.
+
+
+
+[anchor0]: TryFunction_5F00_DotNet_5F00_Exception_5F00_Handling_5F00_in_5F00_CAL.png
+
+
+[image0]: TryFunction_5F00_DotNet_5F00_Exception_5F00_Handling_5F00_in_5F00_CAL.png
diff --git a/content/docs/NAVPatterns/patterns/no-series/1452.2.png b/content/docs/NAVPatterns/patterns/no-series/1452.2.png
new file mode 100644
index 00000000..cc3af9e5
Binary files /dev/null and b/content/docs/NAVPatterns/patterns/no-series/1452.2.png differ
diff --git a/content/docs/NAVPatterns/patterns/no-series/3527.3.png b/content/docs/NAVPatterns/patterns/no-series/3527.3.png
new file mode 100644
index 00000000..04c3780e
Binary files /dev/null and b/content/docs/NAVPatterns/patterns/no-series/3527.3.png differ
diff --git a/content/docs/NAVPatterns/patterns/no-series/5661.1.png b/content/docs/NAVPatterns/patterns/no-series/5661.1.png
new file mode 100644
index 00000000..2b158bbe
Binary files /dev/null and b/content/docs/NAVPatterns/patterns/no-series/5661.1.png differ
diff --git a/content/docs/NAVPatterns/patterns/no-series/index.md b/content/docs/NAVPatterns/patterns/no-series/index.md
new file mode 100644
index 00000000..d59f2b3a
--- /dev/null
+++ b/content/docs/NAVPatterns/patterns/no-series/index.md
@@ -0,0 +1,208 @@
++++
+title = "No Series"
+weight = 870
+tags = ["C/AL"]
+categories = ["Pattern"]
++++
+_By Bogdana Botez, at Microsoft Development Center Copenhagen_
+
+## Abstract
+
+Number series assignment is widely used in Microsoft Dynamics NAV to automatically assign unique numbers to data entries.
+
+## Description
+
+Documents and entities of any type (invoices, orders, customers, inventory items etc) are usually assigned an unique number, which can be later referenced whenever that particular entry needs to be consulted. When a new data entry is created, NAV implements the possibility to auto-assign a new number for this entry. For example, whenever a new sales order is created, it can be auto-numbered. This number has a specific format which is set up previously by the NAV administrator. For example, sales orders could have numbers between SO00001 and SO99999\. When all the numbers in this series have been used, NAV will show an error and the administrator has to either extend the current number series, or create a new series to be used.
+
+## Usage
+
+The number series is implemented at the table level. Each time a new record is inserted, the user can either:
+
+* type a new number (if allowed by the setup), or
+* have an auto-generated number created.
+
+### Number Series definition
+
+From the implementation point of view, a number series is a record in the table 308 - "No. Series".
+
+[![ ][image0]][anchor0]
+
+The most relevant fields are:
+|
+----|----|----
+Code | Code 10 | Used to identify the number series in further places where it will be used.
+Default Nos. | Boolean | The boolean **Default Nos.** decides whether this number series can use automatic numbering. If false, then the user is expected to manually look up the No. field and select it from the number series.
+Manual Nos. | Boolean | If the **Manual Nos.** is Yes, then the used will be allowed to manually type the value of the next number ("No.") field.
+Date Order | Boolean | **Date Order** is used to decide whether or not the numbers from the range are assigned chronologically.
+
+### Number Series sequence
+
+The sequence definition is found in table 309 - No. Series Line. Contains the explicit definition of the series, for example a series called BANK, could start with BANK001 and increase incrementally by one, in the sequence BANK001, BANK002, BANK003, .... Until the last number BANK999\. When hitting the number BANK990, the user will receive a warning that the series is about to be exhausted and it must either be increased, or a new series should be assigned.
+
+[![ ][image1]][anchor1]
+
+[![ ][image2]][anchor2]
+
+The most relevant fields are:
+
+|
+----|----|----
+Series Code | Code 10 | Links it to the number series defined in table 308\.
+Starting No. | Integer | This is the first number in the sequence, for a book indexing application it can be BOOK0001\.
+Ending No. | Integer | The last number in the sequence, for example BOOK5000\.
+Warning No. | Integer | When this number is reached, the user will see a warning message stating that the number series is running out of assignable numbers.
+Increment-by No. | Integer | The value for incrementing the numeric part of the series.
+Last No. Used | Code 20 | The last number from the sequence that was assigned.
+Last Date Used | Date | Stating when the last number was assigned.
+
+### Add the default number series to the setup
+
+Various series of numbers can be defined as seen above. For example, a library can have a number series for indexing rental of each of the following: books, movies, music, video. The books series can be split by domain, for example a series BOOKSCIFI can have BOOK0001...BOOK1500, then BOOKTECH can use the sequence BOOK1501...BOOK4000 and so on.
+
+The default number series for a certain application area is typically stored in the domain setup table. For example, NAV Demo Database stores the default Item number series in the Inventory Setup table 313\. This means that you will need to add the Number Series fields in your setup table and ensure that before the customer starts using the solution, the number series is created and added to the setup defaults.
+
+### How to use the Number Series
+
+The table which will host the number from the number series, needs the following fields:
+
+|
+----|----|----
+No. | Code 20 | Contains the auto-generated sequential number.
+No. Series | Code 10 | The number series definition, which decides what the next No. will be.
+
+And the code to make the number series alive:
+
+**OnInsert**
+
+```al
+OnInsert()
+
+IF "No." = '' THEN
+ NoSeriesMgt.InitSeries(DefaultNoSeriesCode,OldNoSeriesCode,NewDate,NewNo,NewNoSeriesCode);
+```
+
+**Field "No."**
+
+```al
+No. - OnValidate()
+
+IF "No." <> xRec."No." THEN BEGIN // Validate that "No." corresponds to the current No. Series rules
+ NoSeriesMgt.TestManual(DefaultNoSeriesCode);
+ "No. Series" := '';
+END;
+```
+
+**AssistEdit**
+
+```al
+AssistEdit() : Boolean
+
+IF "No." = '' THEN
+ NoSeriesMgt.InitSeries(DefaultNoSeriesCode,OldNoSeriesCode,NewDate,NewNo,NewNoSeriesCode);
+```
+
+Where:
+
+* **DefaultNoSeriesCode** is typically found in the application domain setup table.
+* **OldNoSeriesCode** is typically the previous value of the "No. Series" code, which is found in **xRec."No. Series"**
+* **NewDate** is typically 0D (empty date)
+* NewNo and **NewNoSeriesCode** are the current values found in **"No."** and **"No. Series"**
+
+## NAV Usages
+
+See below an example of how NAV implements the number series pattern.
+
+### Sales and Receivables
+
+The setup table 311 (Sales & Receivables Setup) contains the default number series to be used in the whole application domain. In the demo data, some of the series defined here are: Customer Nos., Quote Nos., Order Nos., Invoice Nos., Posted Invoice Nos., Credit Memo Nos., Posted Credit Memo Nos., etc. Those number series are dimensioned for the needs of a typical small to medium sized company, however, various business have different patterns (for example, posting an unusual high number of invoices). In this case, the number series should be adjusted accordingly to make space for the customized company needs.
+
+The default number series defined in the setup is then used in the individual tables. For example, table 18 - Customer, has
+
+### Field "No."
+
+```al
+{ 1 ; ;No. ;Code20 ;AltSearchField=Search Name;
+
+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;
+}
+```
+
+### Field "No. Series"
+
+```al
+{ 107 ; ;No. Series ;Code10 ;TableRelation="No. Series"; Editable=No }
+```
+
+### AssistEdit
+
+```al
+PROCEDURE AssistEdit@2(OldCust@1000 : Record 18) : Boolean;
+VAR
+ Cust@1001 : Record 18;
+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;
+ EXIT(TRUE);
+ END;
+ END;
+END;
+```
+
+### OnInsert
+
+```al
+OnInsert=
+ BEGIN
+ IF "No." = '' THEN BEGIN
+ SalesSetup.GET;
+
+ SalesSetup.TESTFIELD("Customer Nos.");
+
+ NoSeriesMgt.InitSeries(SalesSetup."Customer Nos.",xRec."No. Series",0D,"No.","No. Series");
+ END;
+ ...
+ END
+```
+
+To run the AssistEdit procedure, include this code on the No. - OnAssistEdit() trigger of the Page:
+
+### No. - OnAssistEdit()
+
+```al
+IF AssistEdit(xRec) THEN
+ CurrPage.UPDATE;
+```
+
+{{< youtube 1lG9rY_dmM4>}}
+
+
+
+[anchor0]: 5661.1.png
+[anchor1]: 1452.2.png
+[anchor2]: 3527.3.png
+[anchor3]: https://www.youtube.com/watch?v=1lG9rY_dmM4&list=PLhZ3P-LY7CqmVszuvtJLujFyHpsVN0U_w&index=8
+
+
+[image0]: 5661.1.png
+[image1]: 1452.2.png
+[image2]: 3527.3.png
diff --git a/content/docs/NAVPatterns/patterns/notifications/_index.md b/content/docs/NAVPatterns/patterns/notifications/_index.md
new file mode 100644
index 00000000..52b7d965
--- /dev/null
+++ b/content/docs/NAVPatterns/patterns/notifications/_index.md
@@ -0,0 +1,7 @@
++++
+title = "Notifications"
+weight = 890
+tags = ["C/AL"]
+categories = ["Pattern"]
++++
+A collection of patterns about notifications.
diff --git a/content/docs/NAVPatterns/patterns/notifications/in-context-notifications/2514.Fig4.png b/content/docs/NAVPatterns/patterns/notifications/in-context-notifications/2514.Fig4.png
new file mode 100644
index 00000000..ff09925b
Binary files /dev/null and b/content/docs/NAVPatterns/patterns/notifications/in-context-notifications/2514.Fig4.png differ
diff --git a/content/docs/NAVPatterns/patterns/notifications/in-context-notifications/2526.Fig11.png b/content/docs/NAVPatterns/patterns/notifications/in-context-notifications/2526.Fig11.png
new file mode 100644
index 00000000..cc4a7869
Binary files /dev/null and b/content/docs/NAVPatterns/patterns/notifications/in-context-notifications/2526.Fig11.png differ
diff --git a/content/docs/NAVPatterns/patterns/notifications/in-context-notifications/6646.Fig9.png b/content/docs/NAVPatterns/patterns/notifications/in-context-notifications/6646.Fig9.png
new file mode 100644
index 00000000..ec3b5ead
Binary files /dev/null and b/content/docs/NAVPatterns/patterns/notifications/in-context-notifications/6646.Fig9.png differ
diff --git a/content/docs/NAVPatterns/patterns/notifications/in-context-notifications/6724.Fig2.png b/content/docs/NAVPatterns/patterns/notifications/in-context-notifications/6724.Fig2.png
new file mode 100644
index 00000000..3d3c9b8d
Binary files /dev/null and b/content/docs/NAVPatterns/patterns/notifications/in-context-notifications/6724.Fig2.png differ
diff --git a/content/docs/NAVPatterns/patterns/notifications/in-context-notifications/7701.Fig1.png b/content/docs/NAVPatterns/patterns/notifications/in-context-notifications/7701.Fig1.png
new file mode 100644
index 00000000..84fd9867
Binary files /dev/null and b/content/docs/NAVPatterns/patterns/notifications/in-context-notifications/7701.Fig1.png differ
diff --git a/content/docs/NAVPatterns/patterns/notifications/in-context-notifications/index.md b/content/docs/NAVPatterns/patterns/notifications/in-context-notifications/index.md
new file mode 100644
index 00000000..1efa1a09
--- /dev/null
+++ b/content/docs/NAVPatterns/patterns/notifications/in-context-notifications/index.md
@@ -0,0 +1,244 @@
++++
+title = "In-context Notifications"
+weight = 640
+tags = ["C/AL"]
+categories = ["Pattern"]
++++
+_By Soumya Dutta at Microsoft Development Center Copenhagen_
+
+## Context
+
+Application developers need to raise a notification on events that are not blocking but do require attention from users. Notifications alert users to issues or information, and let them decide whether to react immediately or finish what they're doing first.
+
+## Problem
+
+Application developers have started to use system calls like CONFIRM or MESSAGE to alert or inform users about a condition. These calls interrupt users by displaying a window in the middle of the screen and forcing an immediate response.
+
+## Solution
+
+Notifications display a message in a blue bar at the top of the workspace, as shown in Figure 1\.
+
+[![ ][image0]][anchor0]
+
+Figure 1 - Example of a notification
+
+Notifications alert users to something they probably should act on, but can choose to ignore. For example, a notification might alert someone who is about to invoice a customer for inventory that isn't available, as shown in Figure 1, but allow them to post the invoice anyway. This is different from an error that prevents someone from posting an invoice without specifying a required field.
+
+In this example, if you choose **Details** a page opens to show the status of the inventory, as shown in Figure 2\.
+
+[![ ][image1]][anchor1]
+
+Figure 2 - Clicking an action in a notification
+
+## Raising a notification
+
+The code in Figure 3 raises the notification in Figure 1\.
+
+**COD 311: Item-Check Avail.**
+
+**CreateAndSendNotification**
+
+AvailabilityCheckNotification.ID(GetItemAvailabilityNotificationId);
+
+AvailabilityCheckNotification.MESSAGE(STRSUBSTNO(NotificationMsg,ItemNo));
+
+AvailabilityCheckNotification.SCOPE(NOTIFICATIONSCOPE::LocalScope);
+
+AvailabilityCheckNotification.ADDACTION(DetailsTxt,CODEUNIT::"Item-Check Avail.",'ShowNotificationDetails');
+
+ItemAvailabilityCheck.PopulateDataOnNotification(AvailabilityCheckNotification,ItemNo,UnitOfMeasureCode
+
+,InventoryQty,GrossReq,ReservedReq,SchedRcpt,ReservedRcpt,CurrentQuantity,CurrentReservedQty,
+
+TotalQuantity,EarliestAvailDate);
+
+AvailabilityCheckNotification.SEND;
+
+...
+
+Figure 3 - Raising a notification
+
+The first thing to note is that a new Notification DataType object has been introduced to C/SIDE.
+
+[![ ][image2]][anchor2]
+
+Figure 4\. Notification is a new data type
+
+In the code sample in Figure 3, the first line defines the scope. Currently , only the LocalScope is supported.
+
+### Identifying a notification
+
+The ID is an optional parameter for the notification object that tracks the object in communications between the client and the server. Notifications have unique IDs that can be hard coded as GUIDs, as shown in Figure 5\. A GUID can be generated using [the CREATEGUID system function][anchor3].
+
+**COD 311: Item-Check Avail.**
+
+**GetItemAvailabilityNotificationId**
+
+EXIT('2712AD06-C48B-4C20-820E-347A60C9AD00');
+
+Figure 5\. Uniquely identifying a notification
+
+If the call to set ID is skipped, sending the notification creates a new notification with an ID that is generated at run-time instead of updating a notification that is already displayed (if present) with the ID.
+
+### Including messages notifications
+
+Notifications should display a message. This message is set by an assignment call to the **MESSAGE** parameter of the notification object.
+
+### Invoking actions on notifications
+
+Notifications can display action buttons, as shown in Figure 2 where a button named **Details** opens the inventory status for the item. To do that, when the button is clicked the **ADDACTION** method is invoked on the notification object using the following parameters:
+
+* The text for the button.
+* The code unit number that hosts the method to call.
+* The name of the invoked method in the above code unit to call.
+
+Figure shows the method that is called when the action is invoked- ShowNotificationDetails.
+
+**COD 311: Item-Check Avail.**
+
+**ShowNotificationDetails**
+
+ItemAvailabilityCheck.InitializeFromNotification(AvailabilityCheckNotification);
+
+ItemAvailabilityCheck.SetHeading(AvailabilityCheckNotification.MESSAGE);
+
+ItemAvailabilityCheck.RUNMODAL;****
+
+Figure 6 - Invoking an action
+
+To show the inventory status using the Availability check page, certain parameters must be initialized. For example, the item number, the unit of measure code, and so on. This is done in the call to InitializeFromNotification method on the page. Figure 7 shows the code for this call.
+
+**Page 1872: Item Availability Check**
+
+**InitializeFromNotification**
+
+GET(AvailabilityCheckNotification.GETDATA('ItemNo'));
+
+SETRANGE("No.",AvailabilityCheckNotification.GETDATA('ItemNo'));
+
+EVALUATE(TotalQuantity,AvailabilityCheckNotification.GETDATA('TotalQuantity'));
+
+EVALUATE(InventoryQty,AvailabilityCheckNotification.GETDATA('InventoryQty'));
+
+CurrPage.AvailabilityCheckDetails.PAGE.SetUnitOfMeasureCode(
+
+AvailabilityCheckNotification.GETDATA('UnitOfMeasureCode'));
+
+Figure 7 - Fetching parameters from a notification object
+
+Note how **GETDATA** uses a key to fetch values from the notification object, and how those values are used to initialize the page.
+
+Notifications can include zero, one, or more than one action buttons. More than one action buttons result in multiple **ADDACTION** calls to the notification object.
+
+### Populating parameters on notifications
+
+Actions use the values set on the notification objects. As shown in Figure 3 and Figure 7, the line that calls the method PopulateDataOnNotification does that. The result is shown in Figure 8\.
+
+**Page 1872: Item Availability Check**
+
+**PopulateDataOnNotification**
+
+AvailabilityCheckNotification.SETDATA('ItemNo',ItemNo);
+
+AvailabilityCheckNotification.SETDATA('UnitOfMeasureCode',UnitOfMeasureCode);
+
+AvailabilityCheckNotification.SETDATA('GrossReq',FORMAT(GrossReq));
+
+...
+
+Figure 8 - Populating parameters on notifications
+
+The invoked method must be stateless. Therefore, the context for creating the notification should be reproducible by using data that could be a part of the notification object. In this example, the SETDATA method on the notification object passes values for the item number, unit of measure code, and so on, as key value pairs.
+
+### Displaying the notification to the user
+
+The last line in Figure 3 calls **SEND** to display the notification. If you know the ID of the notification, you can also call **RECALL** to hide it. However, avoid updating a displayed notification, for example by changing the message, by calling both **RECALL** and **SEND**. This makes two server--client calls. Instead, call only **SEND**. Calling **SEND** for a notification that is already displayed updates the notification.
+
+## Turning notifications on or off, and controlling when they are sent
+
+By default, all notifications are turned on. However, you can specify the notifications you want to receive, and turn on or turn off some or all of them. For example, if you don't want to be disturbed or are willing to accept the consequences of ignoring the message. This is unique to notifications.
+
+[![ ][image3]][anchor4]
+
+Figure 9 - The My Notifications page
+
+Additionally, some notifications let you specify the conditions under which they are sent. For example, if you want to be notified when inventory is running low, but only for items you buy from a certain vendor.
+
+1. In the top right corner, choose the Search for Page or Report icon, enter my notifications, and then choose the related link.
+2. To turn on or turn off a notification, select or clear the Enabled check box.
+3. To specify conditions that trigger a notification, choose View filter details, and then fill in the fields.
+
+The **MyNotifications** object determines whether notifications are turned on or off. Notifications are isolated from each other by the hard-coded GUID, as discussed in the section titled Identifying a notification. A fixed ID is essential to turning off a notification. The following are ways to achieve this.
+
+1. **OnInitializingNotificationWithDefaultState** is a published method on the **MyNotifications** page that is called when the enabled state of all the notifications is initialized.
+
+**Codeunit 311: Item-Check Avail.**
+
+**OnInitializingNotificationWithDefaultState**
+
+MyNotifications.InsertDefaultWithTableNum(GetItemAvailabilityNotificationId,
+
+ItemAvailabilityNotificationTxt,
+
+ItemAvailabilityNotificationDescriptionTxt,
+
+DATABASE::Item);
+
+Figure 10 - Adding a notification to the My Notifications page
+
+You must subscribe to this method and call either **InsertDefault** or **InsertDefaultWithTableNum** on the **MyNotifications** table. Both of these take the ID of the notification, a short description of the notification, and text that provides details about the conditions for the notification. The difference is that the **InsertDefaultWithTableNum** method takes an additional argument representing the table number if there is specific criteria for when to turn on a notification for a certain table. In this case, the notification can be enabled only for items that the criteria specified in the FilterPage. The FIlterPage is opened from the **MyNotifications** page.
+
+[![ ][image4]][anchor5]
+
+Figure 11 - Defining filter criteria to turn on a notification
+
+1. **IsEnabled** or **IsEnabledForRecord** are used to query if the notification is turned on. It may make sense to call this as early as possible in the condition checks, so you don't make calculations that will not yield much if the notification is turned off. The second method takes the additional parameter that represents the record for which the enabled state is to be determined. In Figure 12, the check is for an item.
+
+**Codeunit 311: Item-Check Avail.**
+
+**IsItemAvailabilityNotificationEnabled**
+
+**EXIT**(MyNotifications.IsEnabledForRecord(GetItemAvailabilityNotificationId,Item));
+
+Figure 12 - Checking whether notifications are turned on
+
+You may check that the call to this function is made almost as the first step in checking for availability.
+
+1. **OnStateChanged** event should be subscribed to if the developer needs to do something additional when changing the state of a notification, such as turn on another notification.
+
+The ability to turn notifications on or off is not required. If skipped, the notification is always shown when the condition that triggers it is met, and a user cannot turn it off.
+
+## NAV specific usages
+
+For examples of how these objects are used in Dynamics NAV, look at the code for the following objects:
+
+1. Codeunit 1802 Data Migration Notifier
+2. Codeunit 311 Item-Check Avail.
+3. Codeunit 312 Cust-Check Cr. Limit
+4. Codeunit 1854 Item Sales Forecast Notifier (in SalesAndInventoryForecast extension)
+5. Codeunit 1852 Item Sales Forecast Scheduler (in SalesAndInventoryForecast extension)
+
+## Best practices
+
+The following list summarizes best practices for creating notifications:
+
+1. Do not set data on the notification that you will not use in the method invoked from the action button.
+2. Ensure that the **MyNotifications** table is accessed only as described above, and that the correct pairs of calls are made. For example, **InsertDefault**...**IsEnabled** and **InsertDefaultWithTableNum** ...**IsEnabledForRecord**.
+3. Do not call **RECALL** before **SEND** in a server call-back if you need to update a notification that is already displayed. Instead, call only **SEND** to update the notification. This reduces traffic on the network.
+4. Ensure that the method specified on the **ADDACTION** method for the notification is (a) exists, (b) is global and (c) follows the signature described above.
+
+
+
+[anchor0]: 7701.Fig1.png
+[anchor1]: 6724.Fig2.png
+[anchor2]: 2514.Fig4.png
+[anchor3]: https://msdn.microsoft.com/en-us/library/dd339033.aspx
+[anchor4]: 6646.Fig9.png
+[anchor5]: 2526.Fig11.png
+
+
+[image0]: 7701.Fig1.png
+[image1]: 6724.Fig2.png
+[image2]: 2514.Fig4.png
+[image3]: 6646.Fig9.png
+[image4]: 2526.Fig11.png
diff --git a/content/docs/NAVPatterns/patterns/notifications/notification-lifecycle-management-pattern/0677.3-notifications-smaller.PNG b/content/docs/NAVPatterns/patterns/notifications/notification-lifecycle-management-pattern/0677.3-notifications-smaller.PNG
new file mode 100644
index 00000000..2016fc1b
Binary files /dev/null and b/content/docs/NAVPatterns/patterns/notifications/notification-lifecycle-management-pattern/0677.3-notifications-smaller.PNG differ
diff --git a/content/docs/NAVPatterns/patterns/notifications/notification-lifecycle-management-pattern/4807.1st-notification.PNG b/content/docs/NAVPatterns/patterns/notifications/notification-lifecycle-management-pattern/4807.1st-notification.PNG
new file mode 100644
index 00000000..1819163f
Binary files /dev/null and b/content/docs/NAVPatterns/patterns/notifications/notification-lifecycle-management-pattern/4807.1st-notification.PNG differ
diff --git a/content/docs/NAVPatterns/patterns/notifications/notification-lifecycle-management-pattern/6138.logo.png b/content/docs/NAVPatterns/patterns/notifications/notification-lifecycle-management-pattern/6138.logo.png
new file mode 100644
index 00000000..ab1bcc95
Binary files /dev/null and b/content/docs/NAVPatterns/patterns/notifications/notification-lifecycle-management-pattern/6138.logo.png differ
diff --git a/content/docs/NAVPatterns/patterns/notifications/notification-lifecycle-management-pattern/8512.2nd-notification.PNG b/content/docs/NAVPatterns/patterns/notifications/notification-lifecycle-management-pattern/8512.2nd-notification.PNG
new file mode 100644
index 00000000..73ef5941
Binary files /dev/null and b/content/docs/NAVPatterns/patterns/notifications/notification-lifecycle-management-pattern/8512.2nd-notification.PNG differ
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
new file mode 100644
index 00000000..b8d3195d
--- /dev/null
+++ b/content/docs/NAVPatterns/patterns/notifications/notification-lifecycle-management-pattern/index.md
@@ -0,0 +1,198 @@
++++
+title = "Notification Lifecycle Management Pattern"
+weight = 880
+tags = ["C/AL"]
+categories = ["Pattern"]
++++
+_By David Bastide at Microsoft Development Center Copenhagen_
+
+[![ ][image0]][anchor0]
+
+
+
+**Context**
+
+This pattern is about sending notifications in Dynamics NAV, starting with version 2018, tracking them in the Notification Lifecycle Management framework, and recalling them when needed.
+
+
+
+**Description**
+
+Notifications are easy to use in a wide range of cases. Instead of using notifications in a fire-and-forget way, we need to track them so that we can recall them if we need to.
+
+If we can have only one notification on a given page, an easy and efficient solution is to use a predefined Notification ID, as suggested in the ["Using In-context Notifications"][anchor7] pattern.
+
+However, some cases can be more complicated. For example, when you are adding lines to a table, what if several lines raise individual notifications? Using the same notification ID for each notification will no longer work because the latest notification overwrites the previous ones. Only one notification for a given notification ID can exist, and only the notification message would be updated. This is illustrated in Figure 1\.
+
+[![ ][image1]][anchor1]
+
+_Figure 1: Notification that an item that is not in stock. The notification ID is a predefined GUID, 2712AD06-C48B-4C20-820E-347A60C9AD00, for example._
+
+[![ ][image2]][anchor2]
+
+_Figure 2: You add a second item that is not in stock. the notification is fired with the same GUID, 2712AD06-C48B-4C20-820E-347A60C9AD00, for example. The previous notification is overwritten._
+
+Here is the code for this behavior:
+
+```al
+LOCAL PROCEDURE CreateAndSendNotification@23(UnitOfMeasureCode@1010 : Code[20];InventoryQty@1009 : Decimal;GrossReq@1008 : Decimal;ReservedReq@1007 : Decimal;SchedRcpt@1006 : Decimal;ReservedRcpt@1005 : Decimal;CurrentQuantity@1004 : Decimal;CurrentReservedQty@1003 : Decimal;TotalQuantity@1002 : Decimal;EarliestAvailDate@1001 : Date) : Boolean;
+VAR
+ ItemAvailabilityCheck@1011 : Page 1872;
+ AvailabilityCheckNotification@1000 : Notification;
+BEGIN
+ AvailabilityCheckNotification.ID(GetItemAvailabilityNotificationId);
+ AvailabilityCheckNotification.MESSAGE(STRSUBSTNO(NotificationMsg,ItemNo));
+ AvailabilityCheckNotification.SCOPE(NOTIFICATIONSCOPE::LocalScope);
+ AvailabilityCheckNotification.ADDACTION(DetailsTxt,CODEUNIT::"Item-Check Avail.",'ShowNotificationDetails');
+
+ ItemAvailabilityCheck.PopulateDataOnNotification(AvailabilityCheckNotification,ItemNo,UnitOfMeasureCode,InventoryQty,GrossReq,ReservedReq,SchedRcpt,ReservedRcpt,CurrentQuantity,CurrentReservedQty,TotalQuantity,EarliestAvailDate);
+ AvailabilityCheckNotification.SEND;
+ EXIT(FALSE);
+END;
+
+LOCAL PROCEDURE **GetItemAvailabilityNotificationId**@27() : GUID;
+BEGIN
+ EXIT('2712AD06-C48B-4C20-820E-347A60C9AD00');
+END;
+```
+
+An easy fix would be to dynamically generate the notification ID. However, what if you fix the issue that triggered the notification?
+
+Here is the code for this possible fix:
+
+```al
+LOCAL PROCEDURE CreateAndSendNotification@23(UnitOfMeasureCode@1010 : Code[20];InventoryQty@1009 : Decimal;GrossReq@1008 : Decimal;ReservedReq@1007 : Decimal;SchedRcpt@1006 : Decimal;ReservedRcpt@1005 : Decimal;CurrentQuantity@1004 : Decimal;CurrentReservedQty@1003 : Decimal;TotalQuantity@1002 : Decimal;EarliestAvailDate@1001 : Date) : Boolean;
+VAR
+ ItemAvailabilityCheck@1011 : Page 1872;
+ AvailabilityCheckNotification@1000 : Notification;
+BEGIN
+ AvailabilityCheckNotification.ID(CREATEGUID);
+ AvailabilityCheckNotification.MESSAGE(STRSUBSTNO(NotificationMsg,ItemNo));
+ AvailabilityCheckNotification.SCOPE(NOTIFICATIONSCOPE::LocalScope);
+ AvailabilityCheckNotification.ADDACTION(DetailsTxt,CODEUNIT::"Item-Check Avail.",'ShowNotificationDetails');
+
+ ItemAvailabilityCheck.PopulateDataOnNotification(AvailabilityCheckNotification,ItemNo,UnitOfMeasureCode,InventoryQty,GrossReq,ReservedReq,SchedRcpt,ReservedRcpt,CurrentQuantity,CurrentReservedQty,TotalQuantity,EarliestAvailDate);
+ AvailabilityCheckNotification.SEND;
+ EXIT(FALSE);
+END;
+```
+
+Now, notifications do not replace each other, but we cannot recall them because we do not track each notification ID.
+
+[![ ][image3]][anchor3]
+
+_Figure 3: Three sales lines with a notification for each one_
+
+Imagine that you're adding several items to a sales order, and inventory is low for some of the items. Each sales line will send a notification for its item if the quantity to sell is higher than the available inventory. By using dynamically generated notification IDs (**CREATEGUID**), each notification will not be overwritten, which is what we want. This is shown in Figure 3\. But after you see the notification, you may decide to decrease the quantity in the sales line. At that point, the notification should be recalled. To do that, we need a way to track the notifications and their IDs.
+
+
+
+**Solution**
+
+The solution is to use the Notification Lifecycle Management framework.
+
+The framework allows you to keep track of notifications by saving notification IDs and with other useful information (record ID and optional additional context) that will allow you to easily find the notification IDs to recall. This can be seen as an internal dictionary to put and get notification IDs.
+
+
+
+This framework has three main components:
+
+* A temporary, in-memory table: **Notification Context** (1519). This table stores each notification GUID, the record ID of the record that caused each notification (on what object), and optionally, an additional GUID that represents an additional context: the cause of the notification (why). This lets you track and recall each notification. You can fire-and-forget the notification, but if you need to recall it you can find it by using the record ID of the cause and the optional additional context ID.
+* A singleton codeunit: **Notification Lifecycle Mgt.** (1511), that provides functions to create new notification context lines and recall them. This codeunit contains one instance of the temporary table **Notification Context**.
+
+* A helper codeunit:**Notification Lifecycle Helper** (1508), that subscribes to various events and makes the calls to the **Notification Lifecycle Mgt.** codeunit.
+
+
+
+The unit tests for this framework are in codeunit **Notification Lifecycle Tests** (139480).
+
+
+
+The main functions provided by codeunit 1511 are:
+
+* **SendNotification**(NotificationToSend : Notification;RecId : RecordID)
+ * Sends a notification and keeps track of it in the simplest way. We have a notification to send and the record ID of the object that triggered the notification.
+* **SendNotificationWithAdditionalContext**(NotificationToSend : Notification;RecId : RecordID;AdditionalContextId : GUID)
+ * Sends a notification and keeps track of it with additional information. For example, a GUID that represents the context in which the notification was sent, and an item with insufficient inventory.
+* **RecallNotificationsForRecord**(RecId : RecordID;HandleDelayedInsert : Boolean)
+
+ * Recalls all notifications that were sent by a given record ID. The HandleDelayedInsert flag should be TRUE if it is possible that the record ID provided is from a record that was not yet in the database (TRUE unless we recall notifications after deletion of a record).
+* **RecallNotificationsForRecordWithAdditionalContext**(RecId : RecordID;AdditionalContextId : GUID;HandleDelayedInsert : Boolean)
+ * Recalls the notification that was sent by a given Record ID in a particular context. The HandleDelayedInsert flag should be TRUE if it is possible that the Record ID provided is from a record that was not yet in the database (TRUE unless we recall notifications after deleting a record).
+* **SetRecordID**(RecId : RecordID)
+ * Sets the record ID after delayed insertion of a record. This will update the initially incomplete Record ID in the **Notification Context** table to a full Record ID.
+
+* **UpdateRecordID**(CurrentRecId : RecordID;NewRecId : RecordID)
+ * Replace CurrentRecId with NewRecId in the **Notification Context** table. This is called by **SetRecordId**.
+
+
+
+**Usage**
+
+
+
+The simple case is:
+
+1. We create a notification object.
+2. We call **SendNotification** or **SendNotificationWithAdditionalContext**.
+3. When we want to recall the notification, we call **RecallNotificationsForRecord** or **RecallNotificationsForRecordWithAdditionalContext**.
+
+[![ ][image4]][anchor4]
+
+_Figure 4: without additional context_
+
+[![ ][image5]][anchor5]
+
+_Figure 5: with additional context_
+
+However, delayed insert means that the simple case seen above doesn't happen very often. The issue is that when we call **SendNotification**, we provide the cause object's record ID. If this object has not been inserted yet, which is often the case when the user creates a new invoice, a new line, and so on, the record ID is incomplete. When the object is inserted the record ID is completed, but if we call **RecallNotificationsForRecord** at a later point, the record ID will be different from the incomplete record ID we used when sending the notification. The solution is to detect that the object is not yet inserted when we send the notification, and at a later point, set the record ID when the cause object is inserted.
+
+
+
+The realistic case is:
+
+1. We have a temporary object with a partially complete ID. Something like: Sales Line, 1000, "".
+2. We send a notification caused by this object (item out of stock).
+3. The temporary table receives NotificationId, empty record Id (Quote, ""), additional context (item out of stock).
+4. When the user leaves the field, the line is inserted. We replace the empty record ID (Quote, "") by the full record ID (Sales Line, 1000, 10000).
+5. We recall the notification (the user put a lower quantity for example).
+6. We search for records with the full record ID and the additional context (item out of stock).
+7. If found, they are recalled.
+
+[![ ][image6]][anchor6]
+
+_Figure 6: delayed insert, with additional context_
+
+**Usages in NAV:**
+
+COD311 (Item-Check Avail.)
+
+COD312 (Cust-Check Cr. Limit)
+
+COD1508 (Notification Lifecycle Handler)
+
+**Related Patterns:**
+
+[In-context notifications][anchor7]
+
+[Singleton codeunit][anchor8]
+
+
+[anchor0]: 6138.logo.png
+[anchor1]: 4807.1st-notification.PNG
+[anchor2]: 8512.2nd-notification.PNG
+[anchor3]: 0677.3-notifications-smaller.PNG
+[anchor4]: sequence1.png
+[anchor5]: sequence2.png
+[anchor6]: sequence3.png
+[anchor7]: /navpatterns/1-patterns/notifications/in-context-notifications/
+[anchor8]: /navpatterns/1-patterns/singleton/singleton-codeunit/
+
+
+[image0]: 6138.logo.png
+[image1]: 4807.1st-notification.PNG
+[image2]: 8512.2nd-notification.PNG
+[image3]: 0677.3-notifications-smaller.PNG
+[image4]: sequence1.png
+[image5]: sequence2.png
+[image6]: sequence3.png
diff --git a/content/docs/NAVPatterns/patterns/notifications/notification-lifecycle-management-pattern/sequence1.png b/content/docs/NAVPatterns/patterns/notifications/notification-lifecycle-management-pattern/sequence1.png
new file mode 100644
index 00000000..9d4ca9a8
Binary files /dev/null and b/content/docs/NAVPatterns/patterns/notifications/notification-lifecycle-management-pattern/sequence1.png differ
diff --git a/content/docs/NAVPatterns/patterns/notifications/notification-lifecycle-management-pattern/sequence2.png b/content/docs/NAVPatterns/patterns/notifications/notification-lifecycle-management-pattern/sequence2.png
new file mode 100644
index 00000000..fb37e35f
Binary files /dev/null and b/content/docs/NAVPatterns/patterns/notifications/notification-lifecycle-management-pattern/sequence2.png differ
diff --git a/content/docs/NAVPatterns/patterns/notifications/notification-lifecycle-management-pattern/sequence3.png b/content/docs/NAVPatterns/patterns/notifications/notification-lifecycle-management-pattern/sequence3.png
new file mode 100644
index 00000000..22f84cea
Binary files /dev/null and b/content/docs/NAVPatterns/patterns/notifications/notification-lifecycle-management-pattern/sequence3.png differ
diff --git a/content/docs/NAVPatterns/patterns/observer/index.md b/content/docs/NAVPatterns/patterns/observer/index.md
new file mode 100644
index 00000000..9be9315e
--- /dev/null
+++ b/content/docs/NAVPatterns/patterns/observer/index.md
@@ -0,0 +1,92 @@
++++
+title = "Observer"
+weight = 900
+tags = ["C/AL"]
+categories = ["Pattern"]
++++
+_By Nikolai L'Estrange, from TVision Technology Ltd. in the UK_
+
+### Abstract
+
+Track all record changes against a defined table or set of tables.
+
+### Problem
+
+Microsoft Dynamics NAV has the built in ability to track all record changes against any table, however it does not always fire the triggers for every table.
+
+### Solution
+
+Create a setup table to define which other tables you want to track changes for, and optionally what triggers you want to fire, then link this up to the standard triggers in Codeunit 1\.
+
+Codeunit 1 Application Management contains the triggers OnDatabaseInsert, OnDatabaseModify, OnDatabaseDelete and OnDatabaseRename which are what we need to subscribe to in order to track record changes. However these triggers are only fired sometimes. This is determined by the parameters set in the function GetTableTriggerSetup, which is called once per table per session.
+
+In order to define which tables we are interested in we can create a new table with the following fields:
+
+**Observable Table:**
+
+"Table ID" | Integer | Object.ID WHERE (Type=CONST(Table))
+----|----|----
+TrackInsert | Boolean | [optional]
+TrackModify | Boolean | [optional]
+TrackDelete | Boolean | [optional]
+TrackRename | Boolean | [optional]
+
+Then we can create a Codeunit that will set the Table Trigger Setup parameters and also subscribe to the OnDatabase triggers.
+
+```al
+LOCAL [EventSubscriber] GetTableTriggerSetup(TableId : Integer;VAR OnDatabaseInsert : Boolean;VAR OnDatabaseModify : Boolean;VAR OnDatabaseDelete : Boolean;VAR OnDatabaseRename : Boolean)
+IF Observable.GET(TableId) THEN BEGIN
+ IF Observable.TrackInsert THEN
+ OnDatabaseInsert := TRUE;
+ IF Observable.TrackModify THEN
+ OnDatabaseModify := TRUE;
+ IF Observable.TrackDelete THEN
+ OnDatabaseDelete := TRUE;
+ IF Observable.TrackRename THEN
+ OnDatabaseRename := TRUE;
+END;
+
+LOCAL [EventSubscriber] OnDatabaseInsert(RecRef : RecordRef)
+IF Observable.Get(RecRef.NUMBER) AND Observable.TrackInsert THEN
+ //do something
+
+LOCAL [EventSubscriber] OnDatabaseModify(RecRef : RecordRef)
+IF Observable.Get(RecRef.NUMBER) AND Observable.TrackModify THEN
+ //do something
+
+LOCAL [EventSubscriber] OnDatabaseDelete(RecRef : RecordRef)
+IF Observable.Get(RecRef.NUMBER) AND Observable.TrackDelete THEN
+ //do something
+
+LOCAL [EventSubscriber] OnDatabaseRename(RecRef : RecordRef;xRecRef : RecordRef)
+IF Observable.Get(RecRef.NUMBER) AND Observable.TrackRename THEN
+ //do something
+```
+
+_**Note:**_ In NAV2016 all these functions can be EventSubscribers that subscribe to the functions in Codeunit 1 as per above, in earlier versions of NAV these functions will need to be Global and called explicitly from within the Codeunit 1 functions.
+
+### NAV Usages
+
+Variations of this pattern exists in the standard product in:
+
+* Codeunit 423 Change Log Management
+* Codeunit 5150 Integration Management. In this Codeunit the tables that fire triggers are hardcoded in C/AL.
+
+### Consequences
+
+It is important that in our GetTableTriggerSetup function we only ever set the parameters to **TRUE**, and **never** set them to **FALSE**. This is because there may be other Codeunits listening to the triggers for that table, e.g. Change Log. This is also why we check the setup again within each trigger.
+
+### Related Topics
+
+This pattern was originally described in the following blog:
+
+[https://geeknikolai.wordpress.com/2015/10/30/observer-pattern-in-dynamics-nav-2016/][anchor0]
+
+### NAV Versions
+
+* From NAV 2016 use the code as shown
+* For earlier versions see _Note_ above
+
+
+
+[anchor0]: https://geeknikolai.wordpress.com/2015/10/30/observer-pattern-in-dynamics-nav-2016/
diff --git a/content/docs/NAVPatterns/patterns/posting-routine-select-behavior/1563.PostingRoutineSelectBehaviour2.png b/content/docs/NAVPatterns/patterns/posting-routine-select-behavior/1563.PostingRoutineSelectBehaviour2.png
new file mode 100644
index 00000000..968490be
Binary files /dev/null and b/content/docs/NAVPatterns/patterns/posting-routine-select-behavior/1563.PostingRoutineSelectBehaviour2.png differ
diff --git a/content/docs/NAVPatterns/patterns/posting-routine-select-behavior/5428.PostingRoutineSelectBehaviour3.png b/content/docs/NAVPatterns/patterns/posting-routine-select-behavior/5428.PostingRoutineSelectBehaviour3.png
new file mode 100644
index 00000000..7594bc94
Binary files /dev/null and b/content/docs/NAVPatterns/patterns/posting-routine-select-behavior/5428.PostingRoutineSelectBehaviour3.png differ
diff --git a/content/docs/NAVPatterns/patterns/posting-routine-select-behavior/6170.PostingRoutineSelectBehaviour4.png b/content/docs/NAVPatterns/patterns/posting-routine-select-behavior/6170.PostingRoutineSelectBehaviour4.png
new file mode 100644
index 00000000..3c49f16e
Binary files /dev/null and b/content/docs/NAVPatterns/patterns/posting-routine-select-behavior/6170.PostingRoutineSelectBehaviour4.png differ
diff --git a/content/docs/NAVPatterns/patterns/posting-routine-select-behavior/7585.PostingRoutineSelectBehaviour1.png b/content/docs/NAVPatterns/patterns/posting-routine-select-behavior/7585.PostingRoutineSelectBehaviour1.png
new file mode 100644
index 00000000..0263a778
Binary files /dev/null and b/content/docs/NAVPatterns/patterns/posting-routine-select-behavior/7585.PostingRoutineSelectBehaviour1.png differ
diff --git a/content/docs/NAVPatterns/patterns/posting-routine-select-behavior/index.md b/content/docs/NAVPatterns/patterns/posting-routine-select-behavior/index.md
new file mode 100644
index 00000000..79bc2d92
--- /dev/null
+++ b/content/docs/NAVPatterns/patterns/posting-routine-select-behavior/index.md
@@ -0,0 +1,86 @@
++++
+title = "Posting Routine - Select Behavior"
+weight = 940
+tags = ["C/AL"]
+categories = ["Pattern"]
++++
+_By waldo_
+
+## Abstract
+
+Send information (parameters) to a processing framework/routine so that it knows what to do, how to behave.
+
+## Description
+
+For a processing routine to behave correctly, it needs sometimes input of a user to know what it has to do, check or avoid doing. To do this, usually a piece of UI is getting called (STRMENU) with the question what to do. These input needs to get to the routine.
+
+1. The user selects the option on how he wants the process to behave
+2. There are extra fields on the processing table, that are used internally and act like parameters. These Fields get filled according to the selections that the user has made
+3. The processing codeunit receives the processing table, and therefor also the parameters
+
+[![ ][image0]][anchor0]
+
+## Usage
+
+Example: Sales Post.
+
+On Sales Header, there were fields created which act like internal parameter-fields for the "Sales Post" routine:
+
+[![ ][image1]][anchor1]
+
+When pressing "Post", the Selection Codeunit is getting called:
+
+[![ ][image2]][anchor2]
+
+Or in case of the Post&Print, the selection codeunit is different:
+
+[![ ][image3]][anchor3]
+
+Next, the user is able to select the options with an STRMENU, which results in filling in the fields above, like:
+
+```al
+Selection := STRMENU(Text000,3);
+IF Selection = 0 THEN
+ EXIT;
+Ship := Selection IN [1,3];
+Invoice := Selection IN [2,3];
+```
+
+The processing codeunit is being called after these options were set.
+
+## NAV Usages
+
+This is also being used in the Purchase Post.
+
+However, the Service Post works different. In Short:
+
+* There were no parameter fields added to the processing table (Service Header)
+* The processing routine isn't called by CODEUNIT.RUN, but a function in a declared codeunit, where you pass the Invoice and Ship parameter separately.
+
+## Ideas for improvement
+
+Implement it consequently. There is (in my knowledge) no reason to do Service Posting differently from Sales and Purchase.
+
+Furthermore, one might argue if adding fields to a table (which also means adding fields to the SQL Table) is the right solution to pass parameters to processing methods.
+
+On the other hand, as we are handling tables as being "classes" in many cases, it does make sense to add "properties" to those "classes" to change the behavior of the method (SalesHeader.Post).
+
+## Related Topics
+
+I would like to add a pattern like "Using Argument tables" (as a sub-pattern for the facade-pattern). It somewhat is related to this way of handling parameters: using tablefields to pass a flexible amount of parameters to functions/codeunits.
+
+{{< youtube SxywT2XSpcI>}}
+
+
+
+[anchor0]: 7585.PostingRoutineSelectBehaviour1.png
+[anchor1]: 1563.PostingRoutineSelectBehaviour2.png
+[anchor2]: 5428.PostingRoutineSelectBehaviour3.png
+[anchor3]: 6170.PostingRoutineSelectBehaviour4.png
+[anchor4]: https://www.youtube.com/watch?v=SxywT2XSpcI&list=PLhZ3P-LY7CqmVszuvtJLujFyHpsVN0U_w&index=7
+
+
+[image0]: 7585.PostingRoutineSelectBehaviour1.png
+[image1]: 1563.PostingRoutineSelectBehaviour2.png
+[image2]: 5428.PostingRoutineSelectBehaviour3.png
+[image3]: 6170.PostingRoutineSelectBehaviour4.png
diff --git a/content/docs/NAVPatterns/patterns/product-name/ProductName-Logo.png b/content/docs/NAVPatterns/patterns/product-name/ProductName-Logo.png
new file mode 100644
index 00000000..db667504
Binary files /dev/null and b/content/docs/NAVPatterns/patterns/product-name/ProductName-Logo.png differ
diff --git a/content/docs/NAVPatterns/patterns/product-name/ProductName-Sample.PNG b/content/docs/NAVPatterns/patterns/product-name/ProductName-Sample.PNG
new file mode 100644
index 00000000..cfec526a
Binary files /dev/null and b/content/docs/NAVPatterns/patterns/product-name/ProductName-Sample.PNG differ
diff --git a/content/docs/NAVPatterns/patterns/product-name/ProductName-output.png b/content/docs/NAVPatterns/patterns/product-name/ProductName-output.png
new file mode 100644
index 00000000..40a3888a
Binary files /dev/null and b/content/docs/NAVPatterns/patterns/product-name/ProductName-output.png differ
diff --git a/content/docs/NAVPatterns/patterns/product-name/index.md b/content/docs/NAVPatterns/patterns/product-name/index.md
new file mode 100644
index 00000000..f5f5d49c
--- /dev/null
+++ b/content/docs/NAVPatterns/patterns/product-name/index.md
@@ -0,0 +1,52 @@
++++
+title = "Product Name"
+weight = 950
+tags = ["C/AL"]
+categories = ["Pattern"]
++++
+#### **Abstract**
+
+On many occasions, it's needed to refer to the product name in messages or errors. It's not a good practice to hardcode the brand name like Dynamics NAV 2017, and change this value with each rebranding. You can use client **ProductName** System Object instead to refer to the product name.
+
+[![ ][image0]][anchor0]
+
+#### **Problem**
+
+When you want to refer to product name in informational messages or errors, it's not a good practice to hardcode the brand name, as this requires changing this text when a rebranding happens.
+
+#### **Solution**
+
+Instead of hardcoding the product name like "Dynamics NAV 2017", it's recommended to use **ProductName** System Object that platform provides.
+
+You can use **ProductName** to refer to the product name, this you can use in text messages. **ProductName** has 3 values **ProductName.Short**, **ProductName.Full**, and **ProductName.Marketing**, each one should be used according to the context of your message.
+
+It's recommended to use **ProductName.Short** for most in-product texts , **ProductName.Full** when other dynamics apps are present in the message context (like CRM), and **ProductName.Marketing** only when absolutely necessary.
+
+**Usage**: You should make a placeholder in your text constant and substitute this placeholder with **ProductName** as shown below.
+
+[![ ][image1]][anchor1]
+
+**Output**
+
+[![ ][image2]][anchor2]
+
+#### **Benefits**
+
+* This decouples your messages from the application name
+* This removes the effort of maintaining these messages and keeping them up to date with the brand name.
+
+#### **Limitations**
+
+* **ProductName** can't be used for tooltips and captions, it can only be used for text constants (Labels).
+* **ProductName** System object doesn't exist in NAV 2016 and earlier version.
+
+
+
+[anchor0]: ProductName-Logo.png
+[anchor1]: ProductName-Sample.PNG
+[anchor2]: ProductName-output.png
+
+
+[image0]: ProductName-Logo.png
+[image1]: ProductName-Sample.PNG
+[image2]: ProductName-output.png
diff --git a/content/docs/NAVPatterns/patterns/queries/_index.md b/content/docs/NAVPatterns/patterns/queries/_index.md
new file mode 100644
index 00000000..30ab3a09
--- /dev/null
+++ b/content/docs/NAVPatterns/patterns/queries/_index.md
@@ -0,0 +1,7 @@
++++
+title = "Queries"
+weight = 960
+tags = ["C/AL"]
+categories = ["Pattern"]
++++
+Expand to see NAV design patterns which use queries.
diff --git a/content/docs/NAVPatterns/patterns/queries/select-distinct-with-queries/6521.clip_5F00_image001.png b/content/docs/NAVPatterns/patterns/queries/select-distinct-with-queries/6521.clip_5F00_image001.png
new file mode 100644
index 00000000..7352078f
Binary files /dev/null and b/content/docs/NAVPatterns/patterns/queries/select-distinct-with-queries/6521.clip_5F00_image001.png differ
diff --git a/content/docs/NAVPatterns/patterns/queries/select-distinct-with-queries/Untitled-picture.png b/content/docs/NAVPatterns/patterns/queries/select-distinct-with-queries/Untitled-picture.png
new file mode 100644
index 00000000..a187e9d4
Binary files /dev/null and b/content/docs/NAVPatterns/patterns/queries/select-distinct-with-queries/Untitled-picture.png differ
diff --git a/content/docs/NAVPatterns/patterns/queries/select-distinct-with-queries/clip_5F00_image002.png b/content/docs/NAVPatterns/patterns/queries/select-distinct-with-queries/clip_5F00_image002.png
new file mode 100644
index 00000000..557e5e39
Binary files /dev/null and b/content/docs/NAVPatterns/patterns/queries/select-distinct-with-queries/clip_5F00_image002.png differ
diff --git a/content/docs/NAVPatterns/patterns/queries/select-distinct-with-queries/clip_5F00_image003.png b/content/docs/NAVPatterns/patterns/queries/select-distinct-with-queries/clip_5F00_image003.png
new file mode 100644
index 00000000..0a6f5b35
Binary files /dev/null and b/content/docs/NAVPatterns/patterns/queries/select-distinct-with-queries/clip_5F00_image003.png differ
diff --git a/content/docs/NAVPatterns/patterns/queries/select-distinct-with-queries/clip_5F00_image004.png b/content/docs/NAVPatterns/patterns/queries/select-distinct-with-queries/clip_5F00_image004.png
new file mode 100644
index 00000000..2bb4257a
Binary files /dev/null and b/content/docs/NAVPatterns/patterns/queries/select-distinct-with-queries/clip_5F00_image004.png differ
diff --git a/content/docs/NAVPatterns/patterns/queries/select-distinct-with-queries/clip_5F00_image005.png b/content/docs/NAVPatterns/patterns/queries/select-distinct-with-queries/clip_5F00_image005.png
new file mode 100644
index 00000000..25e7bab9
Binary files /dev/null and b/content/docs/NAVPatterns/patterns/queries/select-distinct-with-queries/clip_5F00_image005.png differ
diff --git a/content/docs/NAVPatterns/patterns/queries/select-distinct-with-queries/clip_5F00_image006.png b/content/docs/NAVPatterns/patterns/queries/select-distinct-with-queries/clip_5F00_image006.png
new file mode 100644
index 00000000..c60328e7
Binary files /dev/null and b/content/docs/NAVPatterns/patterns/queries/select-distinct-with-queries/clip_5F00_image006.png differ
diff --git a/content/docs/NAVPatterns/patterns/queries/select-distinct-with-queries/clip_5F00_image007.png b/content/docs/NAVPatterns/patterns/queries/select-distinct-with-queries/clip_5F00_image007.png
new file mode 100644
index 00000000..58ffaf5b
Binary files /dev/null and b/content/docs/NAVPatterns/patterns/queries/select-distinct-with-queries/clip_5F00_image007.png differ
diff --git a/content/docs/NAVPatterns/patterns/queries/select-distinct-with-queries/clip_5F00_image008.png b/content/docs/NAVPatterns/patterns/queries/select-distinct-with-queries/clip_5F00_image008.png
new file mode 100644
index 00000000..12bc812c
Binary files /dev/null and b/content/docs/NAVPatterns/patterns/queries/select-distinct-with-queries/clip_5F00_image008.png differ
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
new file mode 100644
index 00000000..b0fe885a
--- /dev/null
+++ b/content/docs/NAVPatterns/patterns/queries/select-distinct-with-queries/index.md
@@ -0,0 +1,98 @@
++++
+title = "SELECT DISTINCT with Queries"
+weight = 1040
+tags = ["C/AL"]
+categories = ["Pattern"]
++++
+_By Bogdana Botez, at Microsoft Development Center Copenhagen_
+
+### Abstract
+
+This pattern explains how to perform SELECT DISTINCT by using queries in Dynamics NAV.
+
+**Description**
+
+When working with tables, sometimes a developer needs to perform a SELECT DISTINCT (also known as SELECT UNIQUE) from a table. As NAV does not provide this out of the box, we present below a way to select unique records by using queries.
+
+**Problem statement**
+
+Let's consider the **VAT Entry** table as below: [ ][anchor0]
+
+[![ ][image0]][anchor1]
+
+The goal is to select one line for each separate document that produced VAT Entries. In other words, we want records grouped by **Type, Document Type** and **Document No.**. However, if there are multiple lines with the same value of the triad **Type, Document Type** and **Document No.** in the **VAT Entry** table, we only want to see one of them.
+
+#### Solution
+
+Create a new query object **VAT Entry Distinct Document No.**, with a single DataItem sourced from **VAT Entry** table. Add the three desired group-by fields **Type, Document Type** and **Document No.** as columns.
+
+[![ ][image1]][anchor2]
+
+To enable grouping, add one more column, with **Method Type** = **Totals**. This will automatically set the **Group By** checkbox to TRUE on the three precedent fields.
+
+Note that the **Group By** field is read-only and trying to set it by hand will clarify that:
+
+[![ ][image2]][anchor3]
+
+Running the query yields a single record per document. You can notice in the second line below for example, how the sales invoice number 103001 had 2 VAT Entries, but it shows up only once in the query:
+
+[![ ][image3]][anchor4]
+
+### Example
+
+One thing is to be noted: there is a limitation to how much information you can take out from the records. For example, if we need to extract more information than just the one we already have in the columns, then the following apply: adding one more column of **Method Type** = **None** will indeed show more information, but it _might_ affect the grouping. More details below.
+
+**The grouping is affected** for example when the additional column is the **VAT Entry No**. In this case, this column brings up additional grouping criteria and one group for each entry number will end up being created.
+
+[![ ][image4]][anchor5]
+
+You can notice that both lines of document 103001 are not visible, which contradicts the goal of SELECT DISTINCT.
+
+[![ ][image5]][anchor6]
+
+**The grouping is not affected** when the additional column does not influence grouping (has variation identical with one of the other existing columns). For example, adding **Posting Date** as a new column, does not change anything because posting date is the same for all lines of a document, so the query result is identical with the initial one:
+
+[![ ][image6]][anchor7]
+
+Below is the result of this query, where you can notice that the initial grouping is preserved and correct. Bonus, we can now read the **Posting Date** of the document too.
+
+[![ ][image7]][anchor8]
+
+**The grouping is also not affected** when adding more columns of **Method Type** = **Totals**. However, this can only be done with columns of Decimal data type.
+
+### **NAV Usages**
+
+This pattern is used in Query 19: **VAT Entries Base Amt. Sum**. This query is used in Report 19: **VAT- VIES Declaration Tax Auth**.
+
+[![ ][image8]][anchor9]
+
+The pattern is also used in Query 1511: **User IDs by Notification Type**.
+
+
+### **Related Topics**
+
+A variation using loops could be described, for C/AL development on NAV 6 where query objects are not available.
+
+
+
+[anchor0]: https://microsoft.sharepoint.com/teams/DynamicsNAV/Wiki/Nav%20Wiki%20Documents/NAV%20App%20Patterns/NAV%20App%20Patterns%20for%20Review/Table%20Select%20Distinct.docx#_msocom_1
+[anchor1]: 6521.clip_5F00_image001.png
+[anchor2]: clip_5F00_image002.png
+[anchor3]: clip_5F00_image003.png
+[anchor4]: clip_5F00_image004.png
+[anchor5]: clip_5F00_image005.png
+[anchor6]: clip_5F00_image006.png
+[anchor7]: clip_5F00_image007.png
+[anchor8]: clip_5F00_image008.png
+[anchor9]: Untitled-picture.png
+
+
+[image0]: 6521.clip_5F00_image001.png
+[image1]: clip_5F00_image002.png
+[image2]: clip_5F00_image003.png
+[image3]: clip_5F00_image004.png
+[image4]: clip_5F00_image005.png
+[image5]: clip_5F00_image006.png
+[image6]: clip_5F00_image007.png
+[image7]: clip_5F00_image008.png
+[image8]: Untitled-picture.png
diff --git a/content/docs/NAVPatterns/patterns/queries/use-queries-to-detect-duplicate-records/clip_5F00_image002.jpg b/content/docs/NAVPatterns/patterns/queries/use-queries-to-detect-duplicate-records/clip_5F00_image002.jpg
new file mode 100644
index 00000000..8e2daebb
Binary files /dev/null and b/content/docs/NAVPatterns/patterns/queries/use-queries-to-detect-duplicate-records/clip_5F00_image002.jpg differ
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
new file mode 100644
index 00000000..b2681324
--- /dev/null
+++ b/content/docs/NAVPatterns/patterns/queries/use-queries-to-detect-duplicate-records/index.md
@@ -0,0 +1,99 @@
++++
+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_
+
+## Abstract
+
+This pattern uses queries to create an efficient way to detect duplicate entries in a table. This is, for example, useful when trying to find out which customers or contacts have the same names, so we can merge them later.
+
+## Description
+
+Duplicate detection has several requirements in Microsoft Dynamics NAV. One method to eliminate duplication is by defining the relevant field as the primary key. However, this method is not always practical either due to the size of the field or due to business requirements that dictate how duplicates are detected but not necessarily how they are eliminated. An example of this method is to detect contacts with the same name and take action to merge them if they are.
+
+Before Dynamics NAV 2013, the only possibility was to iterate through the table in a loop and then create a sub-loop where another instance of the same table is filtered to check for duplicates. For example, to check for duplicate names in the Customer table, the code would look like this:
+
+```al
+PROCEDURE HasDuplicateCustomers@26() : Boolean;
+VAR
+ Customer@1000 : Record 18;
+ Customer2@1001 : Record 18;
+BEGIN
+ IF Customer.FINDSET THEN
+ REPEAT
+ Customer2.SETRANGE(Name,Customer.Name);
+ IF Customer2.COUNT \> 1 THEN
+ EXIT(TRUE);
+ UNTIL Customer.NEXT = 0;
+ EXIT(FALSE);
+END;
+```
+
+This code would involve setting filters on the **Customer** table as many times as there are records in the table. This is an expensive operation.
+
+Starting with Dynamics NAV 2013, we can use queries to create a more efficient implementation of the same logic.
+
+## Usage
+
+The solution involves that you create a query to return duplicates, and then invoke it from a method that would test the value of the query to identify if duplicates were found.
+
+**Step 1** -- **Creating the Query**
+
+* The query must be created with the table we want to search in as the dataitem.
+* The field we want to search for must be created as a grouped field.
+* Create a totaling field on the count, and add a filter for Count > 1. This ensures that only records with more than one instance of the field that we selected in the previous step are included in the query result.
+
+Continuing with our Customer Name example, here is how the query would look:
+
+[![ ][image0]][anchor0]
+
+####
+
+ ELEMENTS
+ {
+ { 1 ; ;DataItem; ;
+ DataItemTable=Table18 }
+ { 2 ;1 ;Column ; ;
+ DataSource=Name }
+ { 3 ;1 ;Column ; ;
+ ColumnFilter=Count_=FILTER(\>1);
+ MethodType=Totals;
+ Method=Count }
+ }
+
+**Step 2 -- Invoking the Query to Check for Duplicates**
+
+Now that the query is created, all we need to do is to invoke the query and check if any records are returned, which would mean that there are duplicates.
+
+Here is an alternate implementation of the **HasDuplicateCustomers** method using the query that we created:
+
+```al
+PROCEDURE HasDuplicateCustomersWithQuery@27() : Boolean;
+VAR
+ CustomerDuplicate@1000 : Query 70000;
+BEGIN
+ CustomerDuplicate.OPEN;
+ EXIT(CustomerDuplicate.READ);
+END;
+```
+
+**Examples**
+
+* **Acc. Sched. Chart Management** codeunit (762),
+methods CheckDuplicateAccScheduleLineDescription and CheckDuplicateColumnLayoutColumnHeader
+* **Analysis Report Chart Mgt.** codeunit (770),
+methods CheckDuplicateAnalysisLineDescription and CheckDuplicateAnalysisColumnHeader
+
+## **Consequences**
+
+A new query object is needed for every type of duplicate check. This could easily explode and create a maintenance problem.
+
+
+
+[anchor0]: clip_5F00_image002.jpg
+
+
+[image0]: clip_5F00_image002.jpg
diff --git a/content/docs/NAVPatterns/patterns/queries/use-queries-to-replace-nested-loops/5040.clip_5F00_image002.png b/content/docs/NAVPatterns/patterns/queries/use-queries-to-replace-nested-loops/5040.clip_5F00_image002.png
new file mode 100644
index 00000000..cdcf2ee1
Binary files /dev/null and b/content/docs/NAVPatterns/patterns/queries/use-queries-to-replace-nested-loops/5040.clip_5F00_image002.png differ
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
new file mode 100644
index 00000000..a61d2583
--- /dev/null
+++ b/content/docs/NAVPatterns/patterns/queries/use-queries-to-replace-nested-loops/index.md
@@ -0,0 +1,194 @@
++++
+title = "Use Queries to Replace Nested Loops"
+weight = 1350
+tags = ["C/AL"]
+categories = ["Pattern"]
++++
+_Originally by Bogdan Sturzoiu, Microsoft Development Center Copenhagen_
+
+## Abstract
+
+This pattern shows how the new query object type introduced in NAV 2013 allows you to replace costly loops when inspecting data from two or more tables.
+
+### Description
+
+One of the core operations in a relational database is joining two or more tables. For example, you might need to extract all sales lines in the database together with information regarding the related sales header. This requires joining the Sales Header and Sales Line tables using Sales Header No. as the connecting field.;
+
+The join operation has traditionally been done in C/AL by record looping. When NAV 2013 introduced the query object, it allowed us to produce a data set that is the result of a join operation between two or more tables. This simplifies the problem of finding related records in two tables linked through a foreign key.
+
+#### Pattern Elements
+
+1. Two or more tables that contain records linked through a foreign key: Table 1, Table 2, Table n.
+2. A query object Query X, that joins Table 1, Table 2, etc. based on the connecting key.
+3. A processing codeunit that loops through the query records (or any other code-bearing object).
+
+#### Pattern Steps
+
+1. Run the query on the connected tables.
+2. Loop through the records returned by the query.
+3. Process the records.
+
+[![ ][image0]][anchor0]
+
+_Figure 1\. The pattern elements_
+
+### Usage
+
+#### Sample Problem
+
+The Bank Acc. Reconciliation Line table (274) and the Bank Account Ledger Entry table (271) are connected through the Bank Account No. field. Identify the matching pairs of records based on having the same remaining amount and transaction date.
+
+#### Solution Using Nested Loops
+
+The classic C/AL approach is to:
+
+1. Set the necessary filters on the left table, i.e. table 274\.
+2. Loop through the filtered records.
+3. For each record in the filter, find the related records in the right table (table 271) and set the required filters on it.
+4. For each pair of records from the left and right table, decide if they are a solution and if so, apply them to each other.
+
+```al
+PROCEDURE MatchSingle@5(BankAccReconciliation@1003 : Record 273);
+VAR
+ BankAccRecLine@1005 : Record 274;
+ BankAccLedgerEntry@1006 : Record 271;
+ BankAccEntrySetReconNo@1007 : Codeunit 375;
+BEGIN
+ BankAccRecLine.SETRANGE("Bank Account No.",BankAccReconciliation."Bank Account No.");
+ BankAccRecLine.SETRANGE("Statement No.",BankAccReconciliation."Statement No.");
+ BankAccRecLine.SETFILTER(Difference,'<>%1',0);
+ BankAccRecLine.SETRANGE(Type,BankAccRecLine.Type::"Bank Account Ledger Entry");
+
+ IF BankAccRecLine.FINDSET THEN
+ REPEAT
+ BankAccLedgerEntry.SETRANGE("Bank Account No.",BankAccRecLine."Bank Account No.");
+ BankAccLedgerEntry.SETRANGE(Open,TRUE);
+ BankAccLedgerEntry.SETRANGE("Statement Status",BankAccLedgerEntry."Statement Status"::Open);
+ BankAccLedgerEntry.SETFILTER("Remaining Amount",'<>%1',0);
+ IF BankAccLedgerEntry.FINDSET THEN
+ REPEAT
+ IF (BankAccRecLine.Difference = BankAccLedgerEntry."Remaining Amount") AND
+ (BankAccRecLine."Transaction Date" = BankAccLedgerEntry."Posting Date") THEN
+ BankAccEntrySetReconNo.ApplyEntries(
+ BankAccRecLine,BankAccLedgerEntry, Relation::"One-to-One");
+ UNTIL BankAccLedgerEntry.NEXT = 0;
+ UNTIL BankAccRecLine.NEXT = 0;
+END;
+```
+
+**Solution Using Query**
+
+The new query-based approach involves:
+
+1. Define a query that returns the full filtered join of tables 271 and 274\.
+2. Loop through the records returned by the query.
+3. For each query record, decide if it represents a solution and then connect the two table records that formed it through an application.
+
+```al
+PROCEDURE MatchSingle@5(BankAccReconciliation@1003 : Record 273);
+VAR
+ BankRecMatchCandidates@1001 : Query 1252;
+ BankAccEntrySetReconNo@1007 : Codeunit 375;
+BEGIN
+ BankRecMatchCandidates.SETRANGE(Rec_Line_Bank_Account_No,
+ BankAccReconciliation."Bank Account No.");
+ BankRecMatchCandidates.SETRANGE(Rec_Line_Statement_No,
+ BankAccReconciliation."Statement No.");
+
+ IF NOT BankRecMatchCandidates.OPEN THEN
+ EXIT;
+
+ WHILE BankRecMatchCandidates.READ DO BEGIN
+ BankAccLedgerEntry.GET(BankRecMatchCandidates.Entry_No);
+ BankAccRecLine.GET(BankAccRecLine."Statement Type"::"Bank Reconciliation",
+ BankRecMatchCandidates.Rec_Line_Bank_Account_No,
+ BankRecMatchCandidates.Rec_Line_Statement_No,
+ BankRecMatchCandidates.Rec_Line_Statement_Line_No);
+ BankAccEntrySetReconNo.ApplyEntries(BankAccRecLine,BankAccLedgerEntry,
+ Relation::"One-to-One");
+ END;
+END;
+```
+
+where the query 1252 is defined as:
+
+```al
+OBJECT Query 1252 Bank Rec. Match Candidates
+{
+ OBJECT-PROPERTIES
+ {
+ Date=;
+ Time=;
+ Version List=;
+ }
+ PROPERTIES
+ {
+ }
+ ELEMENTS
+ {
+ { 1 ; ;DataItem; ;
+ DataItemTable=Table274;
+ DataItemTableFilter=Difference=FILTER(<>0),
+ Type=FILTER(=Bank Account Ledger Entry) }
+ { 2 ;1 ;Column ;Rec_Line_Bank_Account_No;
+ DataSource=Bank Account No. }
+ { 3 ;1 ;Column ;Rec_Line_Statement_No;
+ DataSource=Statement No. }
+ { 4 ;1 ;Column ;Rec_Line_Statement_Line_No;
+ DataSource=Statement Line No. }
+ { 5 ;1 ;Column ;Rec_Line_Transaction_Date;
+ DataSource=Transaction Date }
+ { 6 ;1 ;Column ;Rec_Line_Difference ;
+ DataSource=Difference }
+ { 7 ;1 ;DataItem; ;
+ DataItemTable=Table271;
+ DataItemTableFilter=Remaining Amount=FILTER(<>0),
+ Open=CONST(Yes),
+ Statement Status=FILTER(Open);
+ DataItemLink=Bank Account No.=Bank_Acc_Reconciliation_Line."Bank Account o.",
+ Remaining Amount=Bank_Acc_Reconciliation_Line.Difference,
+ Posting Date=Bank_Acc_Reconciliation_Line."Transaction Date" }
+ { 8 ;2 ;Column ; ;
+ DataSource=Entry No. }
+ { 9 ;2 ;Column ;Bank_Account_No ;
+ DataSource=Bank Account No. }
+ { 10 ;2 ;Column ; ;
+ DataSource=Posting Date }
+ { 11 ;2 ;Column ; ;
+ DataSource=Remaining Amount }
+ { 12 ;2 ;Column ;Bank_Ledger_Entry_Open;
+ DataSource=Open }
+ { 13 ;2 ;Column ; ;
+ DataSource=Statement Status }
+ }
+ CODE
+ {
+ BEGIN
+ END.
+ }
+```
+
+When comparing the two implementations, we notice the following advantages of using a query instead of two loops:Comparison
+
+1. A query produces the Cartesian product of tables 1 and 2 faster than by looping through both of them. The advantage grows as there are more tables linked.
+
+The query object leverages the power of SQL Server (as it basically executes a SQL query directly against the database server), and as such it only requires one trip to the database to fetch the data, instead of getting the related records on the right side of the join one by one.
+
+1. A query is scalable in the sense that it allows reusing its definition by applying filters when looping through its dataset. So a generic query can be reused for a variety of purposes just by adapting the filters to the programming need. Duplicating traditional record loops in different functions, on the other side, requires extensive code copy-pasting, which can introduce code defects.
+2. Using a query only requires one loop, whereas joining two or more tables requires multiple code loops that quickly become difficult to read and follow.
+3. Queries are easy to create and maintain and generally provide a cleaner design.
+
+### NAV Specific Examples
+
+In Microsoft Dynamics NAV 2013 R2, we can see the query object used in the bank account reconciliation matching algorithm. The object is query Bank Rec. Match Candidates query (1252), and it is called by the matching engine in the Match Bank Rec. Lines codeunit (1252).
+
+### Ideas for Improvement
+
+The query object type could be improved to allow the passing of parameters at runtime, or, in general, being built dynamically at runtime. This will remove the need for multiple static definitions of the same base query used in slightly different contexts.
+
+
+
+[anchor0]: 5040.clip_5F00_image002.png
+
+
+[image0]: 5040.clip_5F00_image002.png
diff --git a/content/docs/NAVPatterns/patterns/read-once-initialization-and-validation/2465.5.png b/content/docs/NAVPatterns/patterns/read-once-initialization-and-validation/2465.5.png
new file mode 100644
index 00000000..f5e3339c
Binary files /dev/null and b/content/docs/NAVPatterns/patterns/read-once-initialization-and-validation/2465.5.png differ
diff --git a/content/docs/NAVPatterns/patterns/read-once-initialization-and-validation/4.png b/content/docs/NAVPatterns/patterns/read-once-initialization-and-validation/4.png
new file mode 100644
index 00000000..5c3c48fb
Binary files /dev/null and b/content/docs/NAVPatterns/patterns/read-once-initialization-and-validation/4.png differ
diff --git a/content/docs/NAVPatterns/patterns/read-once-initialization-and-validation/4314.3.png b/content/docs/NAVPatterns/patterns/read-once-initialization-and-validation/4314.3.png
new file mode 100644
index 00000000..f95b8b4b
Binary files /dev/null and b/content/docs/NAVPatterns/patterns/read-once-initialization-and-validation/4314.3.png differ
diff --git a/content/docs/NAVPatterns/patterns/read-once-initialization-and-validation/4477.2.png b/content/docs/NAVPatterns/patterns/read-once-initialization-and-validation/4477.2.png
new file mode 100644
index 00000000..7e7637bc
Binary files /dev/null and b/content/docs/NAVPatterns/patterns/read-once-initialization-and-validation/4477.2.png differ
diff --git a/content/docs/NAVPatterns/patterns/read-once-initialization-and-validation/5025.2.png b/content/docs/NAVPatterns/patterns/read-once-initialization-and-validation/5025.2.png
new file mode 100644
index 00000000..b7f9732f
Binary files /dev/null and b/content/docs/NAVPatterns/patterns/read-once-initialization-and-validation/5025.2.png differ
diff --git a/content/docs/NAVPatterns/patterns/read-once-initialization-and-validation/5672.8.png b/content/docs/NAVPatterns/patterns/read-once-initialization-and-validation/5672.8.png
new file mode 100644
index 00000000..baa9cf7a
Binary files /dev/null and b/content/docs/NAVPatterns/patterns/read-once-initialization-and-validation/5672.8.png differ
diff --git a/content/docs/NAVPatterns/patterns/read-once-initialization-and-validation/5857.1.png b/content/docs/NAVPatterns/patterns/read-once-initialization-and-validation/5857.1.png
new file mode 100644
index 00000000..db9bbbff
Binary files /dev/null and b/content/docs/NAVPatterns/patterns/read-once-initialization-and-validation/5857.1.png differ
diff --git a/content/docs/NAVPatterns/patterns/read-once-initialization-and-validation/6332.1.png b/content/docs/NAVPatterns/patterns/read-once-initialization-and-validation/6332.1.png
new file mode 100644
index 00000000..2978fd71
Binary files /dev/null and b/content/docs/NAVPatterns/patterns/read-once-initialization-and-validation/6332.1.png differ
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
new file mode 100644
index 00000000..580a7489
--- /dev/null
+++ b/content/docs/NAVPatterns/patterns/read-once-initialization-and-validation/index.md
@@ -0,0 +1,82 @@
++++
+title = "Read-once Initialization and Validation"
+weight = 970
+tags = ["C/AL"]
+categories = ["Pattern"]
++++
+_By Tim Grant_
+
+## Abstract
+
+One time query and validation of a setup table.
+
+## Description
+
+Firstly identifies if a setup table record is in memory, removing the need to execute and validate the re-query again within a code unit. (From NAV 2013+ service tier database caching exists).
+
+If you need to query a setup table, but not sure if the table is yet in memory from earlier code, this helps to determine this call (see Singleton Table pattern).
+
+Allows a central function to validate critical fields before processing automated code. **TESTFIELD** calls are added to the function and not the INIT call in order to ensure that critical functions do not run without this human check in place as these may rely on key configuration. If you have a design where specific field values are critical (custom or standard) for integrity, then INIT INSERT is a dangerous default which could result in the corruption of processes. This is separate automated INIT INSERT from a setup page.
+
+## NAV Specific Example
+
+A global variable record and a global status indicator, but within the context of a local function.
+
+[![ ][image0]][anchor0]
+
+[![ ][image1]][anchor1]
+
+![ ][image2]
+
+## NAV Usages
+
+A similar example of this is in Codeunit 80, but without any validation.
+
+[![ ][image3]][anchor2]
+
+E.g. If there is a bespoke change, then this existing **GetGLSetup** can be called earlier to accommodate for an earlier bespoke change:
+
+[][anchor3][![ ][image4]][anchor4]
+
+This means that by the time the code reaches the original **GetGLSetup** code requirement, this record is already in memory as the status flag is set to True, eliminating another check. For such Patterns the setup record should never be cleared, it should be called once globally, initiated and left in memory for future checks. It should **not** be declared locally at the same time, review your code for matching local variable usage before implementing this.
+
+## Ideas for improvement
+
+Merge **Singleton Table** with dynamic validation field meta configuration. This removes the coding of **TESTFIELD** to a central setup check.
+
+Example with the **Sales & Receivables Setup**
+
+[![ ][image5]][anchor5]
+
+[![ ][image6]][anchor6]
+
+[![ ][image7]][anchor7]
+
+## Consequences
+
+If in the circumstance that a field value in the setup table is expected to change within the life of the code component instance. e.g. If a CHANGECOMPANY is required to cross reference another Setup table, use a separate variable.
+
+## Related Topics
+
+This is related to the **Singleton Table** pattern.
+
+
+
+[anchor0]: 6332.1.png
+[anchor1]: 5025.2.png
+[anchor2]: 4.png
+[anchor3]: 5.png
+[anchor4]: 2465.5.png
+[anchor5]: 5857.1.png
+[anchor6]: 4477.2.png
+[anchor7]: 4314.3.png
+
+
+[image0]: 6332.1.png
+[image1]: 5025.2.png
+[image2]: 5672.8.png
+[image3]: 4.png
+[image4]: 2465.5.png
+[image5]: 5857.1.png
+[image6]: 4477.2.png
+[image7]: 4314.3.png
diff --git a/content/docs/NAVPatterns/patterns/released-entity/index.md b/content/docs/NAVPatterns/patterns/released-entity/index.md
new file mode 100644
index 00000000..f4b2eeb8
--- /dev/null
+++ b/content/docs/NAVPatterns/patterns/released-entity/index.md
@@ -0,0 +1,88 @@
++++
+title = "Released Entity"
+weight = 1000
+tags = ["C/AL"]
+categories = ["Pattern"]
++++
+_Authors: Henrik Langbak and Kim Ginnerup, Bording Data_
+
+## Abstract
+
+This pattern prevent data from being used elsewhere before it is in a system consistent state.
+NAV inserts a record as soon as the primary key has been set. But the record may not be in a valid state at this point in time. How do you know if a newly inserted record is ready for use?
+
+## Description
+
+Whenever you need to stall the release of data, you can use this pattern.
+
+Because NAV decides when a record is written to the database, it may not be in a system consistent state. Nobody should use the record before everything is in place and the record is valid. An inserted record may even have data in other tables that needs to be inserted and in a valid state before other parts of the system can use the data without running into a problem.
+
+Data entered into the system may have to be approved by a second person before it can be used.
+
+Data requires different parties (e.g. Departments) to add information before data is valid.
+
+The solution is an Option Field with two or three values:
+(Open, Released) or (Open, Pending, Released)
+
+The states should be interpreted as:
+
+State | Description
+------|-----
+Open | Not all data is in place. The record is system inconsistent. The record or record hierarchy is invisible for all other parts of the system.
+Pending | The record is system consistent. But is awaiting someone to manually raise the state to Released. The record is still invisible.
+Released | All data is in place and the record is system consistent, and ready for use. It is now visible for the rest of the system. The state can never be reversed.
+
+The option field name: Release State.
+
+This pattern is very similar to the Blocked Entity pattern, but it has one significant difference.
+The record is not visible to any part of the system, before it is in the Released state.
+There is no going back. When the Released state is reached, it will stay that way for the life of the record. In case of a tri-state, it is ok to bypass Pending seen from a system state perspective.
+
+If there is a hierarchy, e.g. Header and Lines, then the Release State field resides on the Header. As long as the Header remains unreleased, the lines are considered inconsistent and must not be used.
+
+The important and critical part of this pattern is that the whole application needs to obey the "Release State"-contract or the system will fail.
+
+## **Usage**
+
+To use this pattern you need to create an Option Field named: "Release State" with at least the two states: Open, Released.
+
+### Automatic pending or release
+
+If it is feasible to set the Release State automatically, create a local function named: "CheckAndSetReleaseState" that validates the record and sets Release State to Pending or Released, when all system requirements are met.
+
+The function only works one way. It can only increase the state. If the function is called when release state is Pending or Release, the function will do nothing.
+
+If the function is implemented, then the user should not be able to change the value from Open to Pending or Released, only the function can do that.
+
+The function should be called on every change, this way it will be visible to the end user immediately when all requirements are met.
+If the Pattern is implemented without this function, then field is maintained by the end user.
+
+## **NAV Specific Example**
+
+NAV has Table 36 Sales Header, Field 120 Status.
+In this example, it is called: Status.
+Status has 4 values: Open, Released, Pending Approval, Pending Prepayment.
+
+## **NAV Usages**
+
+The example above, taken from NAV, shows similarity with this pattern, but in the Sales Header example it is possible to modify the Release state to an earlier state, through a function in NAV. This is not recommended in the pattern.
+
+## **Ideas for improvement**
+
+In case of a Pending State, you may need an Approved Date and Approved By. Depending on how formal it needs to be.
+
+## **Consequences**
+
+This pattern is only used on data creation.
+If there is a need for shuffling between states back and forth, then this is not the pattern to use.
+
+## **Related Topics**
+
+Blocked Entity is a variant of this pattern but Blocked Entity is used later in the life-cycle.
+
+An alternative to the pattern is temporary tables. But the temporary table is an all or nothing approach. If all data in the Record is valid, the temporary record can update the real data. If not, abandon the process. The Release State pattern is simpler and requires less code.
+
+## **References**
+
+There is a term called: Long Term Lock. This pattern has some resemblance.
+When talking about hierarchical data structures and the Release State is held on the root node, there is a pattern called Hierarchical Locking that has some resemblance.
diff --git a/content/docs/NAVPatterns/patterns/report-selection/index.md b/content/docs/NAVPatterns/patterns/report-selection/index.md
new file mode 100644
index 00000000..e5b2ff95
--- /dev/null
+++ b/content/docs/NAVPatterns/patterns/report-selection/index.md
@@ -0,0 +1,120 @@
++++
+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
+
+Group 1: Dale Gauci & Kimberly Congleton
+
+Group 2: Jens Winberg, Tim Grant, Alen Tot
+
+Thanks to Tim Grant who had merged the two patterns, corrected, sent for review and finalized them.
+
+## Meet the Pattern
+
+It should be possible to change which document report object should be used when printing. This configuration can be stored in one place (Report Selection) and available at configuration time. In this situation, the code for printing records can remain the same.
+
+The purpose of this pattern is to describe the process to implement a reports selection by retrieving the specific **Document Header**, determining which type of document is related to it in the **Report Selection Screen**, and call the report which needs to be printed by passing the specific document header. This specifies the reports that you can print when you work with the various documents for sales and purchases, such as orders, quotes, invoices, and credit memos.
+
+## Know the Pattern
+
+The program can preselect which report will be printed when you print from various types of purchase and sales headers. For example, on an order, the **Order Confirmation** report is automatically printed.
+
+The **Report Selection** table contains the specification of which report will be printed in different situations. The **Report Selection** table also contains the report ID and report name for the report that will be printed when the user works with a given document type.
+
+The user can, of course, choose to have the program print a different report than the preselected one. You can also add reports to the **Report Selection** table to have the program print more than one report per document type.
+
+This pattern should be used when the user needs to print a new type of document which can have different report layouts, or when the user needs to print several different reports in sequence. This can also be used during a new post and print routine, a test report for journal posting or for re-printing a posted document. Using this pattern will minimize code by allowing a flexible means of specifying different reports to print, instead of hard-coding a report id when printing a document.
+
+## Use the Pattern
+
+The Report Selection Pattern involves the **Report Selection** table as the central configuration element also with the **Report Selection** Page. The table is used in 4 generic functional workflows:
+
+1. Test Print un-posted
+
+If the Report Selection is related to a Test Report relating to a Document:
+
+ * The Document's Page including Printing Actions
+ * Test Report-Print Codeunit 228
+
+2. Print un-posted. If the Report Selection is related to an existing Document (un-posted):
+
+ * The Document's Page (Document / List) including the Printing Actions
+ * The Document-Print Codeunit 229 is used atomically to use the document type in the generic Sales Header table before the report selection is found.
+
+3. Print while posting. If the Document is printed at the point of posting then:
+
+ * The Document's Post + Print Codeunit (82 or 92), with the related **GetReport** and **PrintReport** functions.
+
+4. Print after posting. If the Report Selection is related to an existing Document (posted):
+
+ * The specific document related posted header table is used along with a typical function: **PrintRecords** within the respective table.
+
+## Example
+
+Sales Document (Invoice)
+
+Pre-conditions
+
+* The document header/lines table is present and there is a report which has a parent DataItem linked to this document.
+* A document and list page is present and related to the document.
+* A post and print Codeunit exists for the document.
+
+Implementation:
+
+* Add a new **Option String** to the **Report Selection** table, **Usage** field (including any ML Captions).
+* Calling of **GetReport** function, passing the document header.
+
+```al
+SalesSetup.GET;
+IF SalesSetup."Post & Print with ob Queue" THEN
+ SalesPostViaobQueue.EnqueueSalesDoc(SalesHeader)
+ELSE BEGIN
+ CODEUNIT.RUN(CODEUNIT::"Sales-Post",SalesHeader);
+ GetReport(SalesHeader);
+END;
+COMMIT;
+```
+
+* Implementing the new document type within the **GetReport** Function, and calling **PrintReport** with the New OptionString
+
+```al
+"Document Type"::Invoice:
+ BEGIN
+ IF "Last Posting No." = '' THEN
+ SalesInvHeader."No.":= "No."
+ ELSE
+ SalesInvHeader."No." := "Last Posting No.";
+ SalesInvHeader.SETRECFILTER;
+ PrintReport(ReportSelection.Usage::"S.Invoice");
+ END;
+```
+
+* Implement the new Document Type and calling the associated report ID, passing the document header
+
+## NAV Usages
+
+* Codeunits 82, 92, for post and printing purchase and sales documents
+* Codeunit 229 for Document printing
+* Header Tables 110, 112, 114, 120, 122, 124, 295, 297, 302, 304\. 5744, 5746, 6650, 6660
+
+This pattern is already used for printing documents like sales invoices, proforma, waybills, Finance Charge Memos, Receipt Documents.
+
+## Ideas for improvement
+
+The "Usage" Field could be more dynamic through a setup field linked to a document type table mapped to the document header Table ID.
+
+Report Selection table is currently based on the Usage of the report to be defined. A more generic way could be to only select what record id the printing selection is based on could also be implemented. (I think this means Recordref and Table Number could be used to identify the posted tables)
+
+The Report Selection matrix could be evolved to accommodate the Journal post & print configuration. This setup currently resides in the Gen. Journal Template table.
+
+Merge the **PrintReport** functions in the purchase and sales post printing functions (82, 92) into a more atomic print selection component.
+
+Merge the **PrintRecords** functions in the separate posted header tables into a more atomic print selection component.
+
+## Related Patterns
+
+* Posting Routine Pattern (Journal/Document)
+* Post Batch Routine Pattern
diff --git a/content/docs/NAVPatterns/patterns/security/1-sensitive-data-encapsulation/Data-Encapsulation-_2D00_-figure-1.png b/content/docs/NAVPatterns/patterns/security/1-sensitive-data-encapsulation/Data-Encapsulation-_2D00_-figure-1.png
new file mode 100644
index 00000000..a0c7b342
Binary files /dev/null and b/content/docs/NAVPatterns/patterns/security/1-sensitive-data-encapsulation/Data-Encapsulation-_2D00_-figure-1.png differ
diff --git a/content/docs/NAVPatterns/patterns/security/1-sensitive-data-encapsulation/Logo-_2D00_-Protected-Data-Encapsulation.png b/content/docs/NAVPatterns/patterns/security/1-sensitive-data-encapsulation/Logo-_2D00_-Protected-Data-Encapsulation.png
new file mode 100644
index 00000000..65ebd0ff
Binary files /dev/null and b/content/docs/NAVPatterns/patterns/security/1-sensitive-data-encapsulation/Logo-_2D00_-Protected-Data-Encapsulation.png differ
diff --git a/content/docs/NAVPatterns/patterns/security/1-sensitive-data-encapsulation/Multi-_2D00_-1-2.JPG b/content/docs/NAVPatterns/patterns/security/1-sensitive-data-encapsulation/Multi-_2D00_-1-2.JPG
new file mode 100644
index 00000000..0ad6ad70
Binary files /dev/null and b/content/docs/NAVPatterns/patterns/security/1-sensitive-data-encapsulation/Multi-_2D00_-1-2.JPG differ
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
new file mode 100644
index 00000000..89adf3be
--- /dev/null
+++ b/content/docs/NAVPatterns/patterns/security/1-sensitive-data-encapsulation/index.md
@@ -0,0 +1,107 @@
++++
+title = "Sensitive Data Encapsulation"
+weight = 120
+tags = ["C/AL"]
+categories = ["Pattern"]
++++
+_By Bogdana Botez at Microsoft Development Center Copenhagen_
+
+[![ ][image0]][anchor0]
+
+**Aliases:** Encapsulation, Separation of Concerns [1]
+
+
+**Context**: You want to store and protect sensitive data which already exists in a system, but it is not clear which data needs protection and how to store it.
+
+**Problem**: Sensitive data is scattered and mixed with other data in various parts of the system (passwords residing in the same table with non-sensitive data, part of the private information might be stored in files, hardcoded text constants, hardcoded info as part of the code etc.).
+
+**Forces:**
+
+* **Obscurity:** sensitive data mixed with other data and code makes it hard to have an overview of what needs to be protected.
+* **Effort:** the protection mechanisms needs to be applied multiple times, once for each location of the sensitive data.
+* **Diversity:** different data storage mediums can require different protection solutions.
+* **Cohesiveness:** code, low importance data, highly sensitive data are all monolithically mixed which might make it hard to select and protect only what is important.
+* **Low** **Performance**: protecting all data can slow down the system.
+
+**Solution:** Extract and separate sensitive data into a single known repository.
+
+To apply this pattern in Dynamics NAV, a table structure similar to Table 1261 Service Password can be used. The Service Password table also contains additional functionality which will help to further apply related patterns like [**Access Control**][anchor1] and [**Encryption**][anchor2].
+
+Figure 1 describes the definition of a table which is already available in Dynamics NAV. This table can be used for storing sensitive data. As a minimum, the table only needs two fields:
+
+1. The key is of type GUID (Globally Unique Identifier), which is a 128-bit value consisting of multiple groups of hexadecimal digits [2]. Each key needs to be unique and will be used for storing and retrieving the protected information.
+2. The value (the actual data to be encapsulated) is of type BLOB (Binary Large OBject) [3], which contains the encrypted or un-encrypted data (for encryption, see the related [**Encryption**][anchor2] pattern).
+
+[![Figure 1- Example definition, table used for Data Encapsulation][image1]][anchor3]
+
+_Figure 1- Example definition, table used for **Sensitive** **Data** **Encapsulation**_
+
+Let's use a software system (which can be a Dynamics NAV extension or customization). Chances are that the system will look similar to the one described in part 1 of Figure 2: there is data handled in various places of the system, on various storage solutions. Intertwined with normal data, there is sensitive data. For example: Dynamics CRM (Customer Relationship Management) connection information could be all stored in one table, and consists of the connection URI (normal data), enabled/disabled status (normal data), and connection password (sensitive data).
+
+Figure 2 illustrates the system before and after applying the **Sensitive Data Encapsulation** pattern.
+
+* Before: picture part 1 shows the initial system, where most likely the data is scattered across the system. Furthermore, it is stored in different mediums, like
+ * In a table (possible together with low-importance data)
+ * Hardcoded (in Dynamics NAV, it can be a text constant, or just plainly typed in the code)
+ * In files
+
+Furthermore, actors which have to interact with the data, need to remember where to find each piece of information and how to retrieve it.
+
+* After: the part 2 of Figure 2 shows the same system after application of **Sensitive Data Encapsulation**. Now, all data is stored in one place, and all actors which interact with it can read it from the unique location.
+
+[![ ][image2]][anchor4]
+
+_Figure 2 - Data access before and after **Sensitive** **Data** **Encapsulation.**_
+
+**Benefits:**
+
+* **Clarity:** when all sensitive data is encapsulated in one place, it is clear which is the information that needs to be protected.
+* **Simplicity:** easier to protect just a limited number of known resources when they are grouped.
+* **Homogeneity:** the same protection can be applied to all data, since it is stored in the same place.
+* **Separation of concerns:** treat each section of the computer program differently, by separating it and clearly addressing its own requirements and limitations. [1]
+* **Performance**: data protection techniques like **Access Control** and **Encryption** can now be applied only to the sensitive data (not to everything), which improves the performance of the system.
+
+**Drawbacks:**
+
+* **Single point of failure:** a maliciously intended actor has no longer a need to reverse engineer the places where important data is stored. In the unwanted situation when this actor would have already obtained access to the system, they can more easily locate the sensitive information. This is a step towards information disclosure, but mechanisms like [**Encryption**][anchor2] and logging can provide further protection.
+* **Limited Text Length**: there is a limit on how long the encrypted text can be. This limit is imposed by the OS encryption service and it depends on the composition of the text as well as on the system specifics. In NAV, we had implemented the Encrypted Text for text values of max 250 chars, which is enough to cover passwords, person ID numbers, credit card info, but it might turn insufficient in other future scenarios.
+* **Nomenclature**: the name of the table **Service Password** is too specific, since it started by being used for passwords, but it has the capability and it now contains other sensitive data like API Keys, credit card numbers etc.
+
+**References**
+
+[1] "Wikipedia," [Online]. Available: https://en.wikipedia.org/wiki/Separation_of_concerns.
+
+[2] "GUID Structure," [Online]. Available: https://msdn.microsoft.com/en-us/library/aa373931(VS.85).aspx.
+
+[3] "Wikipedia," [Online]. Available: https://en.wikipedia.org/wiki/Binary_large_object.
+
+[4] waldo, "How Do I: Manage Companies in Microsoft Dynamics NAV 2013 R2".
+
+[5] Microsoft, "Multitenant Deployment Architecture," Microsoft, [Online]. Available: https://msdn.microsoft.com/en-us/library/dn271675(v=nav.90).aspx.
+
+[6] B. Botez, "Setup Table design pattern," Microsoft, 2013\. [Online]. Available: https://community.dynamics.com/nav/w/designpatterns/76.setup-table. [Accessed 31 07 2016].
+
+[7] "Shotgun Surgery," [Online]. Available: https://en.wikipedia.org/wiki/Shotgun_surgery. [Accessed 31 07 2016].
+
+[8] M. Fowler, Refactoring: Improving the design of existing code, Addison Wesley, 1999\.
+
+[9] "Masking out," [Online]. Available: https://en.wikipedia.org/wiki/Data_masking#Masking_out. [Accessed 29 7 2016].
+
+[10] "Key Vault," Microsoft, [Online]. Available: https://azure.microsoft.com/en-us/services/key-vault/.
+
+[11] "How to: Configure SSL to Secure the Connection to Microsoft Dynamics NAV Web Client," Microsoft, [Online]. Available: https://msdn.microsoft.com/en-us/library/hh167264(v=nav.90).aspx. [Accessed 2 8 2016].
+
+[12] "sniffer," [Online]. Available: http://compnetworking.about.com/od/networksecurityprivacy/g/bldef_sniffer.htm. [Accessed 02 08 2016].
+
+
+
+[anchor0]: Logo-_2D00_-Protected-Data-Encapsulation.png
+[anchor1]: /navpatterns/1-patterns/security/3-single-point-of-access/
+[anchor2]: /navpatterns/1-patterns/security/2-data-encryption/
+[anchor3]: Data-Encapsulation-_2D00_-figure-1.png
+[anchor4]: Multi-_2D00_-1-2.JPG
+
+
+[image0]: Logo-_2D00_-Protected-Data-Encapsulation.png
+[image1]: Data-Encapsulation-_2D00_-figure-1.png
+[image2]: Multi-_2D00_-1-2.JPG
diff --git a/content/docs/NAVPatterns/patterns/security/2-data-encryption/Encryption-_2D00_-1.JPG b/content/docs/NAVPatterns/patterns/security/2-data-encryption/Encryption-_2D00_-1.JPG
new file mode 100644
index 00000000..212c606a
Binary files /dev/null and b/content/docs/NAVPatterns/patterns/security/2-data-encryption/Encryption-_2D00_-1.JPG differ
diff --git a/content/docs/NAVPatterns/patterns/security/2-data-encryption/Encryption-_2D00_-2.png b/content/docs/NAVPatterns/patterns/security/2-data-encryption/Encryption-_2D00_-2.png
new file mode 100644
index 00000000..82a747a9
Binary files /dev/null and b/content/docs/NAVPatterns/patterns/security/2-data-encryption/Encryption-_2D00_-2.png differ
diff --git a/content/docs/NAVPatterns/patterns/security/2-data-encryption/Encryption-_2D00_-3.png b/content/docs/NAVPatterns/patterns/security/2-data-encryption/Encryption-_2D00_-3.png
new file mode 100644
index 00000000..f8827028
Binary files /dev/null and b/content/docs/NAVPatterns/patterns/security/2-data-encryption/Encryption-_2D00_-3.png differ
diff --git a/content/docs/NAVPatterns/patterns/security/2-data-encryption/Encryption-_2D00_-4.png b/content/docs/NAVPatterns/patterns/security/2-data-encryption/Encryption-_2D00_-4.png
new file mode 100644
index 00000000..54928d28
Binary files /dev/null and b/content/docs/NAVPatterns/patterns/security/2-data-encryption/Encryption-_2D00_-4.png differ
diff --git a/content/docs/NAVPatterns/patterns/security/2-data-encryption/Encryption-_2D00_-5.png b/content/docs/NAVPatterns/patterns/security/2-data-encryption/Encryption-_2D00_-5.png
new file mode 100644
index 00000000..545780b6
Binary files /dev/null and b/content/docs/NAVPatterns/patterns/security/2-data-encryption/Encryption-_2D00_-5.png differ
diff --git a/content/docs/NAVPatterns/patterns/security/2-data-encryption/Logo-_2D00_-Encryption.png b/content/docs/NAVPatterns/patterns/security/2-data-encryption/Logo-_2D00_-Encryption.png
new file mode 100644
index 00000000..f4a79ce3
Binary files /dev/null and b/content/docs/NAVPatterns/patterns/security/2-data-encryption/Logo-_2D00_-Encryption.png differ
diff --git a/content/docs/NAVPatterns/patterns/security/2-data-encryption/Multi-_2D00_-1-2-3.JPG b/content/docs/NAVPatterns/patterns/security/2-data-encryption/Multi-_2D00_-1-2-3.JPG
new file mode 100644
index 00000000..b3c706d1
Binary files /dev/null and b/content/docs/NAVPatterns/patterns/security/2-data-encryption/Multi-_2D00_-1-2-3.JPG differ
diff --git a/content/docs/NAVPatterns/patterns/security/2-data-encryption/index.md b/content/docs/NAVPatterns/patterns/security/2-data-encryption/index.md
new file mode 100644
index 00000000..20c4d26b
--- /dev/null
+++ b/content/docs/NAVPatterns/patterns/security/2-data-encryption/index.md
@@ -0,0 +1,159 @@
++++
+title = "Data Encryption"
+weight = 140
+tags = ["C/AL"]
+categories = ["Pattern"]
++++
+_By Bogdana Botez at Microsoft Development Center Copenhagen_
+
+[![ ][image0]][anchor0]
+
+**Context**: After applying [**Sensitive Data Encapsulation**][anchor1], all sensitive data is gathered in a known place in the database. This makes it possible to apply further protection best practices.
+
+**Problem**: If any non-authorized actor manages to get access to a copy of the database, the sensitive data is immediately available in clear-text.
+
+**Forces:**
+
+* **Accessibility:** anyone who managed to steal a copy of the database can at once read the sensitive information.
+
+**Solution:** Encrypt sensitive data. Dynamics NAV offers a simple mechanism for data encryption, to be used by NAV developers.
+
+Figure 1 adds one more step (in continuation of the figure in pattern **[Sensitive Data Encapsulation][anchor2]**), in which the first two panels describe how sensitive data has all been gathered in one known place. This makes the current pattern, **Encryption**, much easier -- since now data needs to be encrypted in only one place. The last panel of Figure 1 shows the system after the next step, **Encryption**, has been implemented. This shows the iterative process of applying patterns and making the code better step by step.
+
+[![ ][image1]][anchor3]
+
+_Figure 1- **Encryption** becomes easier after first applying [**Sensitive Data Encapsulation**][anchor1]._
+
+**Usage**: in Dynamics NAV, codeunit 1266 Encryption Management offers an API for encryption of data. The available functionality is described in Table 1\.
+
+Table 1- Encryption functionality in Dynamics NAV (found in codeunit 1266 Encryption Management)
+
+Procedure | Description
+----------|------------
+EnableEncryption | Confirms with the user before enabling encryption in all companies of the current database.
+EnableEncryptionSilently | Enables encryption without UI interaction (to be used for web services).
+DisableEncryption | Disables encryption. Has a boolean "Silent" parameter which enables/disables UI interaction.
+Encrypt | Checks that encryption is possible before encrypting the given text.
+Decrypt | Checks that encryption is possible before decrypting the given text.
+ExportKey | If encryption is possible, it shows a confirmation dialog to confirm the export of the encryption key, and proceeds to saving it to a location chosen by the user.
+ImportKey | Imports the encryption key from a user chosen location.
+ChangeKey | Changes the encryption key.
+IsEncryptionEnabled | Returns TRUE is encryption is enabled.
+IsEncryptionPossible | Check is the correct key is present, which only works if encryption is enabled.
+DeleteEncryptedDataInAllCompanies | Confirms through UI and if the user agrees, deletes all data stored in the Service Password table for all companies in the current database. At the end, it deletes the encryption key.
+
+**User confirmation of encryption**
+
+Ideally, encryption should be enabled by default.
+
+However, in some versions of Dynamics NAV, there are places where any user who happens to hit a sensitive field (like a password) is asked if they want to encrypt or not. If user confirmation is needed, then let only the security administrators decide (not any random user).
+
+To ask the security administrator if they want to enable encryption, plug the code below into the OnValidate trigger of the data field which is to be encrypted. This trigger will then get executed each time there's a change on the password field. For example, if the user is entering a password, then add a call to this procedure to Password -- OnValidate() trigger:
+
+```al
+EncryptionIsNotActivatedQst@1001 : TextConst 'ENU=Data encryption is not activated. It is recommended that you encrypt data. \\Do you want to open the Data Encryption Management window?';
+
+LOCAL PROCEDURE CheckEncryption@6();
+BEGIN
+ IF NOT ENCRYPTIONENABLED THEN
+ IF CONFIRM(EncryptionIsNotActivatedQst) THEN
+ PAGE.RUN(PAGE::"Data Encryption Management");
+END;
+```
+
+The code above will guide the user on encrypting, storing the encryption key and choosing a safe password, as follows. Consider for example that NAV is used by a small business, where Stan is the business owner. Hence, Stan has access to all NAV setup and security decisions. Stan wants to enable a connection between NAV and CRM. In NAV, he opens page CRM Connection Setup and enters the URL, user name and password for CRM (Figure 10). When Stan leaves the password field, if data encryption is not enabled, then he sees the confirmation message in Figure 2\.
+
+[![ ][image2]][anchor4]
+
+_Figure 2- The administrator is advised to enable encryption._
+
+Now Stan becomes aware that there is no encryption and has the opportunity to enable it. If he clicks on "Yes", Stan will be taken to the Data Encryption Management page (Figure 3).
+
+[![ ][image3]][anchor5]
+
+_Figure 3- Data Encryption Management page_
+
+Stan can now choose "Enable Encryption" from the ribbon which informs him that an encryption key will be created (Figure 4).
+
+[![ ][image4]][anchor6]
+
+_Figure 4- The administrator is encouraged to save a copy of the encryption key._
+
+Stan has the option to save the encryption key in a safe location. He invokes the action Enable Encryption and is guided forward (Figure 5).
+
+[![ ][image5]][anchor7]
+
+_Figure 5- The encryption key needs to be protected by a password._
+
+Stan chooses a password, which in this implementation requires minimum 8 characters with at least one uppercase character, one lowercase character and one digit. If the chosen password is too weak, Stan is given information on the expected complexity (Figure 6).
+
+[![ ][image6]][anchor8]
+
+_Figure 6- The administrator chooses a strong password._
+
+In the end, Stan chooses a location for the encryption key on disk, in a standard file save dialog. The layout of the file save dialog depends on the display target used (web browser, rich client etc.).
+
+**NAV Usages.** Encryption examples can be found in NAV in the following places:
+
+* Page 1260 Bank Data Conv. Service Setup
+* Page 1270 OCR Service Setup
+* Page 5330 CRM Connection Setup
+* Table 1275 Doc. Exch. Service Setup
+
+**Benefits:**
+
+* **Protection**: even if the database has been compromised, the sensitive data is encrypted hence not immediately accessible. Combined with best user practices like setting strong passwords, encryption can prove to be a very strong protection mechanism.
+
+**Anti-patterns:**
+
+* **Performance:** Do not encrypt everything, because it will have a performance impact on the system. Only important information should be encrypted.
+* **System calls and "Do It Yourself" solutions**: Although system calls for encryptions are available in NAV (ENCRYPT, DECRYPT and ENCRYPTIONKEY), unless there is no other way, refrain from using them directly. Use instead he API in codeunit 1266 Encryption Management, which is safer to use, because it protects against common mistakes (like attempting to encrypt an already encrypted string, enabling encryption in only one company which makes data not usable in another on the same tenant etc.). Use the [**Single Point of Access**][anchor9] pattern to handle sensitive data which needs to be encrypted.
+* **System-level encryption.** The similar codeunit API 1803 Encrypted Key/Value Management is not intended to be reused by partner NAV developers. Normal NAV users do not have permission to access this resource. This stores sensitive data to be accessed by system NAV functionality.
+
+**References**
+
+[1] "Wikipedia," [Online]. Available: https://en.wikipedia.org/wiki/Separation_of_concerns.
+
+[2] "GUID Structure," [Online]. Available: https://msdn.microsoft.com/en-us/library/aa373931(VS.85).aspx.
+
+[3] "Wikipedia," [Online]. Available: https://en.wikipedia.org/wiki/Binary_large_object.
+
+[4] waldo, "How Do I: Manage Companies in Microsoft Dynamics NAV 2013 R2".
+
+[5] Microsoft, "Multitenant Deployment Architecture," Microsoft, [Online]. Available: https://msdn.microsoft.com/en-us/library/dn271675(v=nav.90).aspx.
+
+[6] B. Botez, "Setup Table design pattern," Microsoft, 2013\. [Online]. Available: https://community.dynamics.com/nav/w/designpatterns/76.setup-table. [Accessed 31 07 2016].
+
+[7] "Shotgun Surgery," [Online]. Available: https://en.wikipedia.org/wiki/Shotgun_surgery. [Accessed 31 07 2016].
+
+[8] M. Fowler, Refactoring: Improving the design of existing code, Addison Wesley, 1999\.
+
+[9] "Masking out," [Online]. Available: https://en.wikipedia.org/wiki/Data_masking#Masking_out. [Accessed 29 7 2016].
+
+[10] "Key Vault," Microsoft, [Online]. Available: https://azure.microsoft.com/en-us/services/key-vault/.
+
+[11] "How to: Configure SSL to Secure the Connection to Microsoft Dynamics NAV Web Client," Microsoft, [Online]. Available: https://msdn.microsoft.com/en-us/library/hh167264(v=nav.90).aspx. [Accessed 2 8 2016].
+
+[12] "sniffer," [Online]. Available: http://compnetworking.about.com/od/networksecurityprivacy/g/bldef_sniffer.htm. [Accessed 02 08 2016].
+
+
+
+[anchor0]: Logo-_2D00_-Encryption.png
+[anchor1]: /navpatterns/1-patterns/security/1-sensitive-data-encapsulation/
+[anchor2]: /navpatterns/1-patterns/security/1-sensitive-data-encapsulation/
+[anchor3]: Multi-_2D00_-1-2-3.JPG
+[anchor4]: Encryption-_2D00_-1.JPG
+[anchor5]: Encryption-_2D00_-2.png
+[anchor6]: Encryption-_2D00_-3.png
+[anchor7]: Encryption-_2D00_-4.png
+[anchor8]: Encryption-_2D00_-5.png
+[anchor9]: /navpatterns/1-patterns/security/3-single-point-of-access/
+
+
+[image0]: Logo-_2D00_-Encryption.png
+[image1]: Multi-_2D00_-1-2-3.JPG
+[image2]: Encryption-_2D00_-1.JPG
+[image3]: Encryption-_2D00_-2.png
+[image4]: Encryption-_2D00_-3.png
+[image5]: Encryption-_2D00_-4.png
+[image6]: Encryption-_2D00_-5.png
diff --git a/content/docs/NAVPatterns/patterns/security/3-single-point-of-access/Logo-_2D00_-Single-Point-of-Access.png b/content/docs/NAVPatterns/patterns/security/3-single-point-of-access/Logo-_2D00_-Single-Point-of-Access.png
new file mode 100644
index 00000000..571894e3
Binary files /dev/null and b/content/docs/NAVPatterns/patterns/security/3-single-point-of-access/Logo-_2D00_-Single-Point-of-Access.png differ
diff --git a/content/docs/NAVPatterns/patterns/security/3-single-point-of-access/Multi-_2D00_-1-2-3-4.JPG b/content/docs/NAVPatterns/patterns/security/3-single-point-of-access/Multi-_2D00_-1-2-3-4.JPG
new file mode 100644
index 00000000..7c18166f
Binary files /dev/null and b/content/docs/NAVPatterns/patterns/security/3-single-point-of-access/Multi-_2D00_-1-2-3-4.JPG differ
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
new file mode 100644
index 00000000..e2c42eb8
--- /dev/null
+++ b/content/docs/NAVPatterns/patterns/security/3-single-point-of-access/index.md
@@ -0,0 +1,85 @@
++++
+title = "Single Point of Access"
+weight = 160
+tags = ["C/AL"]
+categories = ["Pattern"]
++++
+_By Bogdana Botez at Microsoft Development Center Copenhagen_
+
+[![ ][image0]][anchor0]
+
+**Context**: Protected data needs to be used. There are many types of entities which might attempt to use the data.
+
+**Problem**: If no standard way of accessing data exists, then each entity might attempt to build its own system for handing the sensitive data. The data access layer might be implemented over and over again by each entity, without reuse of known best practices and with a lot of code duplication.
+
+**Forces:**
+
+* **Code duplication:** if each entity attempts to write its own routines for data access, invariably this will bring duplication.
+* **No knowledge reuse:** if a bug is found and fixed in one of the implementations, there is no guarantee that all the other implementations will be updated. For example
+ * **Double-Encryption**: lack of care or knowledge could lead a NAV developer to attempt to double-encrypt strings, which would render them unusable.
+ * **Multi-company configuration [4]**: in NAV, it is possible to store multiple companies on the same tenant [5] database. A developer who has not investigated such a configuration and its consequences on encryption, can attempt to encrypt data in (for example) a Setup Table [6] of only one company, which would make this table unusable from any unencrypted company (since the server will observe that encryption is enabled, and try to retrieve it as it were enabled for all companies and fails).
+ * **"Shotgun surgery" [7]:** one change in the data access technique (like a new requirement to validate the user's identity before viewing any protected data), calls for updates in every single implementation of data access, if multiple implementations exist. Hence, one change of requirement triggers multiple efforts to update the product.
+
+**Solution:** In Dynamics NAV the same library which offers [**Encryption**][anchor1], is also intended to be used as a **Single Point of Access**. Write code to create, read and remove sensitive data only through codeunit 1266 Encryption Management, and never directly.
+
+In Figure 9 (figure numbering is continued from pattern **[Encryption][anchor2]**), the panels 1, 2 and 3 have applied the [**Sensitive Data Encapsulation**][anchor3] and [**Encryption**][anchor1] patterns. Once data is encapsulated, no matter if it has been encrypted or not, the next pattern, **Single Point of Access**, becomes available as seen in the panels 2 and 3 of Figure 1\.
+
+You can observe that, in panel 3, each usage needs to access separately the encrypted data. This means that each usage needs to implement again the encryption capabilities. This is resolved in panel 4, where all users access one common API which encrypts, hence the workload of encryption is moved from the usages, to the API, and needs to be implemented only once.
+
+[![ ][image1]][anchor4]
+
+_Figure 1- **Single Point of Access** pattern applied._
+
+This shows how pattern application is an iterative process, where one step follows another. Refactoring [8] the code to apply one pattern, cleans and clarifies the code and in some cases, makes clear the possibility of further refactoring.
+
+**Usage**: call the procedures available in codeunit 1266 Encryption Management to store and access sensitive data.
+
+**Benefits:**
+
+* **Simplicity**: only one implementation exists.
+* **Knowledge reuse**: if a bug is found and fixed, there is just one place which needs to be repaired. Therefore, there is no risk that some of the usages would still be flawed by the same bug. For example:
+ * **Double encryption**: The **Single Point of Access** API already implements knowledge to avoid encryption of already encrypted strings.
+ * **Multi-company**: The **Single Point of Access** API already implements knowledge for handling encryption in a multi-company setup.
+ * **Easy maintenance**: a change in specification needs only one code update.
+
+**Consequences:**
+
+* **Single point of failure:** A defect in the access library will affect all usages until it is found and fixed.
+
+**References**
+
+[1] "Wikipedia," [Online]. Available: https://en.wikipedia.org/wiki/Separation_of_concerns.
+
+[2] "GUID Structure," [Online]. Available: https://msdn.microsoft.com/en-us/library/aa373931(VS.85).aspx.
+
+[3] "Wikipedia," [Online]. Available: https://en.wikipedia.org/wiki/Binary_large_object.
+
+[4] waldo, "How Do I: Manage Companies in Microsoft Dynamics NAV 2013 R2".
+
+[5] Microsoft, "Multitenant Deployment Architecture," Microsoft, [Online]. Available: https://msdn.microsoft.com/en-us/library/dn271675(v=nav.90).aspx.
+
+[6] B. Botez, "Setup Table design pattern," Microsoft, 2013\. [Online]. Available: https://community.dynamics.com/nav/w/designpatterns/76.setup-table. [Accessed 31 07 2016].
+
+[7] "Shotgun Surgery," [Online]. Available: https://en.wikipedia.org/wiki/Shotgun_surgery. [Accessed 31 07 2016].
+
+[8] M. Fowler, Refactoring: Improving the design of existing code, Addison Wesley, 1999\.
+
+[9] "Masking out," [Online]. Available: https://en.wikipedia.org/wiki/Data_masking#Masking_out. [Accessed 29 7 2016].
+
+[10] "Key Vault," Microsoft, [Online]. Available: https://azure.microsoft.com/en-us/services/key-vault/.
+
+[11] "How to: Configure SSL to Secure the Connection to Microsoft Dynamics NAV Web Client," Microsoft, [Online]. Available: https://msdn.microsoft.com/en-us/library/hh167264(v=nav.90).aspx. [Accessed 2 8 2016].
+
+[12] "sniffer," [Online]. Available: http://compnetworking.about.com/od/networksecurityprivacy/g/bldef_sniffer.htm. [Accessed 02 08 2016].
+
+
+
+[anchor0]: Logo-_2D00_-Single-Point-of-Access.png
+[anchor1]: /navpatterns/1-patterns/security/2-data-encryption/
+[anchor2]: /navpatterns/1-patterns/security/2-data-encryption/
+[anchor3]: /navpatterns/1-patterns/security/1-sensitive-data-encapsulation/
+[anchor4]: Multi-_2D00_-1-2-3-4.JPG
+
+
+[image0]: Logo-_2D00_-Single-Point-of-Access.png
+[image1]: Multi-_2D00_-1-2-3-4.JPG
diff --git a/content/docs/NAVPatterns/patterns/security/4-masked-text/Logo-_2D00_-Masked-Text.png b/content/docs/NAVPatterns/patterns/security/4-masked-text/Logo-_2D00_-Masked-Text.png
new file mode 100644
index 00000000..77e74923
Binary files /dev/null and b/content/docs/NAVPatterns/patterns/security/4-masked-text/Logo-_2D00_-Masked-Text.png differ
diff --git a/content/docs/NAVPatterns/patterns/security/4-masked-text/Masking-_2D00_-CRM-Connection-Setup-page.PNG b/content/docs/NAVPatterns/patterns/security/4-masked-text/Masking-_2D00_-CRM-Connection-Setup-page.PNG
new file mode 100644
index 00000000..d1378d6b
Binary files /dev/null and b/content/docs/NAVPatterns/patterns/security/4-masked-text/Masking-_2D00_-CRM-Connection-Setup-page.PNG differ
diff --git a/content/docs/NAVPatterns/patterns/security/4-masked-text/index.md b/content/docs/NAVPatterns/patterns/security/4-masked-text/index.md
new file mode 100644
index 00000000..9f08fce5
--- /dev/null
+++ b/content/docs/NAVPatterns/patterns/security/4-masked-text/index.md
@@ -0,0 +1,80 @@
++++
+title = "Masked Text"
+weight = 180
+tags = ["C/AL"]
+categories = ["Pattern"]
++++
+_By Bogdana Botez at Microsoft Development Center Copenhagen_
+
+[![ ][image0]][anchor0]
+
+**Aliases:** Masking out
+
+**Context**: In the user interface (UI) of a software system, the user enters protected information such as a password, an access key, a credit card number etc.
+
+**Problem**: The entered information is visible during data entry and whenever any user (the one who entered the data, or a foreign user) opens the UI.
+
+**Forces:**
+
+* **Information disclosure:** sensitive data is visible in the UI.
+
+**Solution:** Use the "Masked" field property to display dots instead of characters on the sensitive text field in the UI.
+
+**Usage:** Figure 1 shows how an unmasked and a masked field look in Dynamics NAV. On page Microsoft Dynamics CRM Connection Setup, the first two fields (Dynamics CRM URL and User Name) are not masked. The next field (Password) is masked. As the user types text into the Password field, the characters are one by one replaced with dots. When the user had finished typing and had left the Password field (moved focus to another page element), then a pre-defined number of dots is showed in the field, no matter what the real length of the password is. This is done so that the length of the text is not disclosed. Hence, no matter if the text had 5, 10 or 20 characters, as soon as the user leaves the masked field, 10 dots will be visible.
+
+[![ ][image1]][anchor1]
+
+_Figure 1 - The field "Password" is masked._
+
+To apply this pattern in Dynamics NAV, the developer has two choices:
+
+1. **Mask everywhere**
+
+Mask the field in all pages which expose it or will expose it in the future. In this case, masking needs to be set at the table level, by opening the table in design mode and setting the field's property ExtendedDatatype=Masked.
+
+2. **Mask only in selected pages**
+
+Mask the field in only a subset of pages. Open the pages where the field should be masked in design mode, open the property page for the field in question, and set ExtendedDatatype=Masked. This option can be used for example when a field should be hidden from most users (in most usual pages), but still visible to administrators in specific pages.
+
+**Benefits:**
+
+* **Information protection**: sensitive data is stored and can be used by the system, but once entered it is not visible in the UI anymore.
+
+**Drawbacks:**
+
+* **"Forgotten password":** the text in a masked field is hidden from all users, including users who theoretically have the right to see it. In this case, if the developer wishes to disclose the text only to certain users, they have to write extra code (like a lookup trigger) which will verify the user's permissions and if permitted, show or send the clear-text value to the user who requested it.
+
+**References**
+
+[1] "Wikipedia," [Online]. Available: https://en.wikipedia.org/wiki/Separation_of_concerns.
+
+[2] "GUID Structure," [Online]. Available: https://msdn.microsoft.com/en-us/library/aa373931(VS.85).aspx.
+
+[3] "Wikipedia," [Online]. Available: https://en.wikipedia.org/wiki/Binary_large_object.
+
+[4] waldo, "How Do I: Manage Companies in Microsoft Dynamics NAV 2013 R2".
+
+[5] Microsoft, "Multitenant Deployment Architecture," Microsoft, [Online]. Available: https://msdn.microsoft.com/en-us/library/dn271675(v=nav.90).aspx.
+
+[6] B. Botez, "Setup Table design pattern," Microsoft, 2013\. [Online]. Available: https://community.dynamics.com/nav/w/designpatterns/76.setup-table. [Accessed 31 07 2016].
+
+[7] "Shotgun Surgery," [Online]. Available: https://en.wikipedia.org/wiki/Shotgun_surgery. [Accessed 31 07 2016].
+
+[8] M. Fowler, Refactoring: Improving the design of existing code, Addison Wesley, 1999\.
+
+[9] "Masking out," [Online]. Available: https://en.wikipedia.org/wiki/Data_masking\#Masking_out. [Accessed 29 7 2016].
+
+[10] "Key Vault," Microsoft, [Online]. Available: https://azure.microsoft.com/en-us/services/key-vault/.
+
+[11] "How to: Configure SSL to Secure the Connection to Microsoft Dynamics NAV Web Client," Microsoft, [Online]. Available: https://msdn.microsoft.com/en-us/library/hh167264(v=nav.90).aspx. [Accessed 2 8 2016].
+
+[12] "sniffer," [Online]. Available: http://compnetworking.about.com/od/networksecurityprivacy/g/bldef_sniffer.htm. [Accessed 02 08 2016].
+
+
+
+[anchor0]: Logo-_2D00_-Masked-Text.png
+[anchor1]: Masking-_2D00_-CRM-Connection-Setup-page.PNG
+
+
+[image0]: Logo-_2D00_-Masked-Text.png
+[image1]: Masking-_2D00_-CRM-Connection-Setup-page.PNG
diff --git a/content/docs/NAVPatterns/patterns/security/5-ssl-in-nav/Logo-_2D00_-SSL.JPG b/content/docs/NAVPatterns/patterns/security/5-ssl-in-nav/Logo-_2D00_-SSL.JPG
new file mode 100644
index 00000000..e05a60db
Binary files /dev/null and b/content/docs/NAVPatterns/patterns/security/5-ssl-in-nav/Logo-_2D00_-SSL.JPG differ
diff --git a/content/docs/NAVPatterns/patterns/security/5-ssl-in-nav/SSL-_2D00_-before-and-after.PNG b/content/docs/NAVPatterns/patterns/security/5-ssl-in-nav/SSL-_2D00_-before-and-after.PNG
new file mode 100644
index 00000000..ee9359b9
Binary files /dev/null and b/content/docs/NAVPatterns/patterns/security/5-ssl-in-nav/SSL-_2D00_-before-and-after.PNG differ
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
new file mode 100644
index 00000000..0c8eaf6e
--- /dev/null
+++ b/content/docs/NAVPatterns/patterns/security/5-ssl-in-nav/index.md
@@ -0,0 +1,76 @@
++++
+title = "SSL in NAV"
+weight = 190
+tags = ["C/AL"]
+categories = ["Pattern"]
++++
+_By Bogdana Botez at Microsoft Development Center Copenhagen_
+
+[![ ][image0]][anchor0]
+
+**Context**: The security of data transmission is just as important as the security of data storage. When data is transmitted over the web, Secure Sockets Layer (SSL) is available to be used with the web client in Dynamics NAV. Microsoft's NAV cloud solution has SSL enabled by default. However, if a partner company chooses to deploy their own NAV, then they need to handle SSL explicitly.
+
+**Problem**: Although data is stored securely, before it even gets to be stored, it needs to travel the web on a client-server connection, where it is vulnerable.
+
+**Forces:**
+
+* **Insecure communication:** When the user enters a password, if unprotected, a network sniffer [12] could catch and read it. A sniffer is either a software program or hardware device which examine network traffic. Years ago, sniffers were tools used exclusively by professional network engineers, but nowadays, they are also popular with Internet hackers and people just curious about networking. A public Wi-Fi network could easily be eavesdropped by an unwanted actor.
+
+By using data storage patterns like **Sensitive Data Encapsulation**, **Encryption, Single Point of Access** or **Azure Key Vault**, the sensitive data is preserved securely in the implementation of Dynamics NAV. But before it gets into a secure store, this data needs to be transmitted from the user, through a user interface, on a client-server connection and all the way to the database. Is the data safe while being transmitted?
+
+**Solution:** To protect the data before it reaches the server, remember to configure SSL (Secure Sockets Layer) in Dynamics NAV.
+
+_SSL_ is a web protocol that encrypts data that is transmitted over a network to make the data and the network more secure and reliable. A website that is enabled with SSL uses Hypertext Transfer Protocol Secure (HTTPS) instead of Hypertext Transfer Protocol (HTTP) as a communication protocol.
+
+Figure 1 shows data communication between the client (where the user enters data) and the server (which connects further to the database). Without encryption (left side), data is available in clear text over the wire. A person equipped with a network sniffer can easily intercept and read it. On the right side of the picture, SSL is used to encrypt the user's data. Sniffing the traffic would capture the encrypted stream, but access to the real content would be impeded by encryption.
+
+[![ ][image1]][anchor1]
+
+_Figure 1 - Data transmission before (http://...) and after SSL encryption (https://...)._
+
+**Usage**: the latest information about how to configure SSL for the web client in Dynamics NAV is found online at on MSDN at [https://msdn.microsoft.com/en-us/library/hh167264(v=nav.90).aspx][anchor2].
+
+**Benefits:**
+
+* **Secure communication**: encryption protects the data transmitted over a network, so the data is safe all the way on its journey from the user to the database.
+
+**Consequences:**
+
+* **Awareness:** a NAV system administrator might not be aware that they need to enable SSL when using the web client.
+* **Extra work:** there is extra effort to enable SSL on a self-administered NAV system. The good news is that Microsoft's cloud NAV solution has SSL by default and no extra work is required from the developer on that aspect.
+
+**References**
+
+[1] "Wikipedia," [Online]. Available: https://en.wikipedia.org/wiki/Separation_of_concerns.
+
+[2] "GUID Structure," [Online]. Available: https://msdn.microsoft.com/en-us/library/aa373931(VS.85).aspx.
+
+[3] "Wikipedia," [Online]. Available: https://en.wikipedia.org/wiki/Binary_large_object.
+
+[4] waldo, "How Do I: Manage Companies in Microsoft Dynamics NAV 2013 R2".
+
+[5] Microsoft, "Multitenant Deployment Architecture," Microsoft, [Online]. Available: https://msdn.microsoft.com/en-us/library/dn271675(v=nav.90).aspx.
+
+[6] B. Botez, "Setup Table design pattern," Microsoft, 2013\. [Online]. Available: https://community.dynamics.com/nav/w/designpatterns/76.setup-table. [Accessed 31 07 2016].
+
+[7] "Shotgun Surgery," [Online]. Available: https://en.wikipedia.org/wiki/Shotgun_surgery. [Accessed 31 07 2016].
+
+[8] M. Fowler, Refactoring: Improving the design of existing code, Addison Wesley, 1999\.
+
+[9] "Masking out," [Online]. Available: https://en.wikipedia.org/wiki/Data_masking#Masking_out. [Accessed 29 7 2016].
+
+[10] "Key Vault," Microsoft, [Online]. Available: https://azure.microsoft.com/en-us/services/key-vault/.
+
+[11] "How to: Configure SSL to Secure the Connection to Microsoft Dynamics NAV Web Client," Microsoft, [Online]. Available: https://msdn.microsoft.com/en-us/library/hh167264(v=nav.90).aspx. [Accessed 2 8 2016].
+
+[12] "sniffer," [Online]. Available: http://compnetworking.about.com/od/networksecurityprivacy/g/bldef_sniffer.htm. [Accessed 02 08 2016].
+
+
+
+[anchor0]: Logo-_2D00_-SSL.JPG
+[anchor1]: SSL-_2D00_-before-and-after.PNG
+[anchor2]: https://msdn.microsoft.com/en-us/library/hh167264(v=nav.90).aspx
+
+
+[image0]: Logo-_2D00_-SSL.JPG
+[image1]: SSL-_2D00_-before-and-after.PNG
diff --git a/content/docs/NAVPatterns/patterns/security/_index.md b/content/docs/NAVPatterns/patterns/security/_index.md
new file mode 100644
index 00000000..19fab25e
--- /dev/null
+++ b/content/docs/NAVPatterns/patterns/security/_index.md
@@ -0,0 +1,29 @@
++++
+title = "Security"
+weight = 1030
+tags = ["C/AL"]
+categories = ["Pattern"]
++++
+_By Bogdana Botez at Microsoft Development Center Copenhagen_
+
+## **Security patterns for NAV**
+
+### **Problem statement**
+
+When sensitive data is stored inside the Dynamics NAV database, if negligently handled, it can become vulnerable. Particularly, the location, access and the state of the data are critical.
+
+Use the following patterns to enhance security on your NAV system.
+
+1. [Sensitive Data Encapsulation][anchor0]
+2. [Data Encryption][anchor1]
+3. [Single Point of Access][anchor2]
+4. [Masked Text][anchor3]
+5. [SSL in NAV][anchor4]
+
+
+
+[anchor0]: /navpatterns/1-patterns/security/1-sensitive-data-encapsulation/
+[anchor1]: /navpatterns/1-patterns/security/2-data-encryption/
+[anchor2]: /navpatterns/1-patterns/security/3-single-point-of-access/
+[anchor3]: /navpatterns/1-patterns/security/4-masked-text/
+[anchor4]: /navpatterns/1-patterns/security/5-ssl-in-nav/
diff --git a/content/docs/NAVPatterns/patterns/setup-specificity-fallback/0724.Printer-Selection.png b/content/docs/NAVPatterns/patterns/setup-specificity-fallback/0724.Printer-Selection.png
new file mode 100644
index 00000000..95a2e449
Binary files /dev/null and b/content/docs/NAVPatterns/patterns/setup-specificity-fallback/0724.Printer-Selection.png differ
diff --git a/content/docs/NAVPatterns/patterns/setup-specificity-fallback/2335.FindPrinter.png b/content/docs/NAVPatterns/patterns/setup-specificity-fallback/2335.FindPrinter.png
new file mode 100644
index 00000000..9e4abc71
Binary files /dev/null and b/content/docs/NAVPatterns/patterns/setup-specificity-fallback/2335.FindPrinter.png differ
diff --git a/content/docs/NAVPatterns/patterns/setup-specificity-fallback/index.md b/content/docs/NAVPatterns/patterns/setup-specificity-fallback/index.md
new file mode 100644
index 00000000..d5ca5897
--- /dev/null
+++ b/content/docs/NAVPatterns/patterns/setup-specificity-fallback/index.md
@@ -0,0 +1,53 @@
++++
+title = "Setup Specificity Fallback"
+weight = 1060
+tags = ["C/AL"]
+categories = ["Pattern"]
++++
+_By Jan Hoek at IDYN_
+
+## Abstract
+
+The Setup Specificity Fallback pattern allows users to efficiently define a potentially complex setup in terms of rules and exceptions to these rules, exceptions to the exceptions, etc.
+
+## Description
+
+The pattern involves a setup table with a compound (i.e. consisting of more than one field) primary key, where each record in the table maps a combination of primary key values to a particular setup value. However, setting up and maintaining each and every combination could prove to be rather labour-intensive.
+
+With the Setup Specificity Pattern in place, primary key fields in the setup table can have a special value (typically: zero or an empty string) that acts as a wildcard, meaning that the setup record in question applies to a combination of primary key fields with any value in the field containing that wildcard. According to the pattern, when querying the setup table, the application attempts to find the appropriate setup record by searching less and less specific, starting with only specified primary key values, and possibly ending with only wildcard values, replacing specific values with wildcards in a predefined order until a setup record is found, e.g.:
+
+
+|
+---|---|---
+Step 1. | Try to find record with: Field A = value "a" and Field B = value "b" | If found, return record; if not, go to step 2.
+Step 2. | Try to find record with: Field A = value "a" and Field B = wildcard value | If found, return record; if not, go to step 3\.
+Step 3. | Try to find record with: Field A = wildcard value and Field B = value "b" | If found, return record; if not, go to step 4.
+Step 4. | Try to find record with: Field A = wildcard value and Field B = wildcard value | If found, return record; if not, optionally return hard-coded value
+
+The least specific record can be thought of as the general rule, and each of the subsequent records is an exception to that rule. By defining only exceptions to the rule, instead of each and every combination, the burden of setting up and maintaining the setup records is strongly reduced, and the overall setup is much easier to read and understand.
+
+## Usage
+
+1. Create your setup table. Remember that this pattern applies only to setup tables with a compound primary key;
+2. Create a function that will return the required value from the setup;
+3. For each level of specificity, the function should test if the corresponding record exists. If it does, the setup value should be returned. If it doesn't, the logic should fall-back to a lower level of specificity.
+4. (Optional) If even the least specific setup record can not be found, the function may return a blank value or hard-coded default value.
+
+## NAV Usages
+
+The Printer Selection table in NAV is an example of the Setup Specificity Fallback pattern. Its primary key consists of two fields. A blank value in either field means that the record applies to any user or report respectively.
+
+[![ ][image0]][anchor0]
+
+The FindPrinter function in codeunit 1 searches for the printer to use, with a decreasing level of specificity.
+
+[![ ][image1]][anchor1]
+
+
+
+[anchor0]: 0724.Printer-Selection.png
+[anchor1]: 2335.FindPrinter.png
+
+
+[image0]: 0724.Printer-Selection.png
+[image1]: 2335.FindPrinter.png
diff --git a/content/docs/NAVPatterns/patterns/silent-file-upload-and-download/6428.Silent-File-download-design-pattern.png b/content/docs/NAVPatterns/patterns/silent-file-upload-and-download/6428.Silent-File-download-design-pattern.png
new file mode 100644
index 00000000..bddbb4ac
Binary files /dev/null and b/content/docs/NAVPatterns/patterns/silent-file-upload-and-download/6428.Silent-File-download-design-pattern.png differ
diff --git a/content/docs/NAVPatterns/patterns/silent-file-upload-and-download/8688.Silent-file-upload-and-download-NAV-design-pattern.png b/content/docs/NAVPatterns/patterns/silent-file-upload-and-download/8688.Silent-file-upload-and-download-NAV-design-pattern.png
new file mode 100644
index 00000000..6be4bdaf
Binary files /dev/null and b/content/docs/NAVPatterns/patterns/silent-file-upload-and-download/8688.Silent-file-upload-and-download-NAV-design-pattern.png differ
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
new file mode 100644
index 00000000..d6b21254
--- /dev/null
+++ b/content/docs/NAVPatterns/patterns/silent-file-upload-and-download/index.md
@@ -0,0 +1,101 @@
++++
+title = "Silent File Upload and Download"
+weight = 1080
+tags = ["C/AL"]
+categories = ["Pattern"]
++++
+_By Bogdana Botez, at Microsoft Development Center Copenhagen_
+
+## Abstract
+
+This pattern is about silently handing file transfers between NAV Service Tier and the NAV client. By "silently" we mean: without showing a dialog box at upload or download time.
+
+## Description
+
+As a terminology clarification [1], note that both "upload" and "download" are named as seen from the client's point of view:
+
+* Download" defines transferring a file from the server to the client.
+* "Upload" transfers the file from the client to the server.
+
+[![ ][image0]][anchor0]
+
+Sometimes, files must be transferred to or from known locations without triggering file-save or file-load dialogs.
+
+In the following, both the historical and the recommended ways of silently transferring files are described. Since we keep both implementations possible for the sake of backward compatibility, we strongly recommend that you use the file-transfer API provided that is provided with the File Management codeunit (419).
+
+The legacy API for file transfers [2]:
+
+```al
+[Ok :=] UPLOAD(DialogTitle, FromFolder, FromFilter, FromFile, ToFile)
+
+[Ok :=] DOWNLOAD(FromFile, DialogTitle, ToFolder, ToFilter, ToFile)
+```
+
+As you can see, this historical API leaves no place for turning off the functionality for showing a dialog. Historically, NAV offered a remedy to this, namely by using the "Magicpath" string, which is the constant ''. Under this condition, the way to invoke silent file upload or download becomes:
+
+```al
+[Ok :=] UPLOAD(DialogTitle, Magicpath, FromFilter, FromFile, ToFile)
+
+[Ok :=] DOWNLOAD(FromFile, DialogTitle, Magicpath, ToFilter, ToFile)
+```
+
+This remedy introduced an issue: If we use "Magicpath" instead of **FromFolder** and **ToFolder** specifications, then where do we upload from and where do we download to? The answer is that they are uploaded to and downloaded from the NAV server's temporary folder. The path to the temporary file can be obtained when this file is created, by using the following function in **File Management: := ServerTempFileName()**.
+
+The new API for file transfers in the **File Management** codeunit:
+
+[Text :=] UploadFileSilent(ClientFilePath)
+
+[Text :=] DownloadTempFile(ServerFileName)
+
+Using the API in the **File Management** codeunit instead of the historical API is recommended for all file transferring and file management in NAV implementations.
+
+## Usage
+
+The following describes a scenario for the silent file upload/download pattern, both from the user's point of view and from the NAV developer's point of view.
+
+### The NAV User
+
+The production manager at CRONUS needs an XML file in a specific format containing his latest product list with description, prices, and quantities. He wants to import the list into his web shop to keep product information updated with data in NAV.
+
+[![ ][image1]][anchor1]
+
+The production manager wants to have the file in a predefined location on his hard drive. The location has been defined in a NAV setup table.
+
+### The NAV Developer
+
+The NAV developer has written a module to query the CRONUS database and to export the product list in the pre-described XML format required by the web shop. He saves the data in a temporary server file created with this code:
+
+```al
+ServerFileName := FileManagement.ServerTempFileName('xml');
+```
+
+When the file has been populated with the latest product data, the NAV developer uses the following call to download the file from the temporary location on the server to the predefined location on the client:
+
+```al
+FileManagement.DownloadToFile(ServerFileName,ClientFileName);
+```
+
+The call to **DownloadToFile** is part of the **File Management** codeunit, and it embeds the silent download offered by **DownloadTempFile**:
+
+```al
+PROCEDURE DownloadToFile@13(ServerFileName@1002 : Text;ClientFileName@1000 : Text);
+VAR
+ TempClientFileName@1001 : Text;
+BEGIN
+ ValidateFileNames(ServerFileName,ClientFileName); TempClientFileName := DownloadTempFile(ServerFileName); MoveFile(TempClientFileName,ClientFileName);
+END
+```
+
+## Consequences
+
+1. Even today, there is no fully silent up or download. The user will get a warning stating the system is trying to run a client side .NET component. However when dismissing this warning, the user can show to not see it again.
+2. Due to client side .NET interop and of security concerns, silently downloading/uploading files on the web is disabled. Therefore, this pattern is recommended for implementation with Windows clients only.
+
+
+
+[anchor0]: 8688.Silent-file-upload-and-download-NAV-design-pattern.png
+[anchor1]: 6428.Silent-File-download-design-pattern.png
+
+
+[image0]: 8688.Silent-file-upload-and-download-NAV-design-pattern.png
+[image1]: 6428.Silent-File-download-design-pattern.png
diff --git a/content/docs/NAVPatterns/patterns/singleton/0535.Singleton.png b/content/docs/NAVPatterns/patterns/singleton/0535.Singleton.png
new file mode 100644
index 00000000..149bf691
Binary files /dev/null and b/content/docs/NAVPatterns/patterns/singleton/0535.Singleton.png differ
diff --git a/content/docs/NAVPatterns/patterns/singleton/_index.md b/content/docs/NAVPatterns/patterns/singleton/_index.md
new file mode 100644
index 00000000..8c61390d
--- /dev/null
+++ b/content/docs/NAVPatterns/patterns/singleton/_index.md
@@ -0,0 +1,39 @@
++++
+title = "Singleton"
+weight = 1090
+tags = ["C/AL"]
+categories = ["Pattern"]
++++
+_By Bogdana Botez at Microsoft Development Center Copenhagen_
+
+[![ ][image0]][anchor0]
+
+**Context:** The pattern described in this article applies to Dynamics NAV only. For the general definition of the **Singleton** pattern, see for example [this link][anchor1].
+
+**Problem**: As a C/AL developer, you need to coordinate action (through a codeunit) or store information (in a table) that is unique across the system.
+
+**Forces:**
+
+* **Lost reference to centralizer:** An instance of a relevant object could attempt to centralize control or data in the system. However, once this object is no longer in scope, the reference to it is lost and cannot be retrieved for later coordination
+* **Cannot rebuild the initial state:** A new instance of the same object could be created, however the last known state of the lost instance cannot be known anymore.
+
+**Solution:** create an object which resides in memory in a single copy (instance). Have a way to retrieve this unique object from code. This object can either be a [**Singleton Table**][anchor2], or a [**Singleton Codeunit**][anchor3].
+
+The implementation varies depending on the technology and language used. In object-oriented (OO) languages like C\#, C++ or Java, the **Singleton** uses static classes and class members to instantiate. There is a mechanism to ensure only one instance of the object can exist. This mechanism is many times explicit (like in OO languages) and sometimes implicit as part of the compiler or platform (as it is in NAV).
+
+**Benefits:**
+
+* **Centralization**: the setup information is stored in a single well-known place and easily retrievable from anywhere in the application code, by invoking a Record.GET. In case of a single-instance codeunit, any reference to it will retrieve the same instance, so the context will be preserved.
+* **Persistence**: information remains even after the instance goes out of scope, because it continues to live in memory.
+
+**Limitation:** The generic object-oriented **Singleton** pattern permits instantiation of a limited number n of objects (where usually n=1, but it can have other positive values too). However, in Dynamics NAV, the Singleton patterns are limited: n is always 1\.
+
+
+
+[anchor0]: 0535.Singleton.png
+[anchor1]: https://en.wikipedia.org/wiki/Singleton_pattern
+[anchor2]: /navpatterns/1-patterns/singleton/singleton-table/
+[anchor3]: /navpatterns/1-patterns/singleton/singleton-codeunit/
+
+
+[image0]: 0535.Singleton.png
diff --git a/content/docs/NAVPatterns/patterns/singleton/singleton-codeunit/2313.Singleton-Codeunit-_2D00_-CSIDE-SingleInstance-property.PNG b/content/docs/NAVPatterns/patterns/singleton/singleton-codeunit/2313.Singleton-Codeunit-_2D00_-CSIDE-SingleInstance-property.PNG
new file mode 100644
index 00000000..6b575f68
Binary files /dev/null and b/content/docs/NAVPatterns/patterns/singleton/singleton-codeunit/2313.Singleton-Codeunit-_2D00_-CSIDE-SingleInstance-property.PNG differ
diff --git a/content/docs/NAVPatterns/patterns/singleton/singleton-codeunit/Singleton-Codeunit-_2D00_-example-_2D00_-bad.PNG b/content/docs/NAVPatterns/patterns/singleton/singleton-codeunit/Singleton-Codeunit-_2D00_-example-_2D00_-bad.PNG
new file mode 100644
index 00000000..3c9c05da
Binary files /dev/null and b/content/docs/NAVPatterns/patterns/singleton/singleton-codeunit/Singleton-Codeunit-_2D00_-example-_2D00_-bad.PNG differ
diff --git a/content/docs/NAVPatterns/patterns/singleton/singleton-codeunit/Singleton-Codeunit-_2D00_-example-_2D00_-good.PNG b/content/docs/NAVPatterns/patterns/singleton/singleton-codeunit/Singleton-Codeunit-_2D00_-example-_2D00_-good.PNG
new file mode 100644
index 00000000..2657b6a1
Binary files /dev/null and b/content/docs/NAVPatterns/patterns/singleton/singleton-codeunit/Singleton-Codeunit-_2D00_-example-_2D00_-good.PNG differ
diff --git a/content/docs/NAVPatterns/patterns/singleton/singleton-codeunit/Singleton-Codeunit.png b/content/docs/NAVPatterns/patterns/singleton/singleton-codeunit/Singleton-Codeunit.png
new file mode 100644
index 00000000..c785fb0d
Binary files /dev/null and b/content/docs/NAVPatterns/patterns/singleton/singleton-codeunit/Singleton-Codeunit.png differ
diff --git a/content/docs/NAVPatterns/patterns/singleton/singleton-codeunit/index.md b/content/docs/NAVPatterns/patterns/singleton/singleton-codeunit/index.md
new file mode 100644
index 00000000..1d8a7a83
--- /dev/null
+++ b/content/docs/NAVPatterns/patterns/singleton/singleton-codeunit/index.md
@@ -0,0 +1,104 @@
++++
+title = "Singleton Codeunit"
+weight = 1100
+tags = ["C/AL"]
+categories = ["Pattern"]
++++
+_By Bogdana Botez at Microsoft Development Center Copenhagen_
+
+[![ ][image0]][anchor0]
+
+
+**Problem**: In some situations, global state needs to be preserved at runtime throughout a session.
+
+There are functionality areas in NAV where centralized application management code is needed, like for example managing permissions, notifications, the debugger etc. State needs to be preserved across calls to the management codeunit.
+
+For example:
+
+* The debugger needs to remember the session which is being debugged
+* The permission manager has a testability function, where it can be set to emulate that it's running as a SaaS (Software As A Service -- the cloud) platform, even when the tests run in a "on-premise" local lab.
+
+Take for example the following test of Azure ML (Azure Machine Learning) integration with NAV.
+
+Context: Azure Machine Learning services are paid. However, Dynamics NAV includes a monthly pre-paid quota of Azure ML, which can be used for free by the users. There is an upper limit to this quota, and when it is reached, Azure ML services are turned off until the next month starts and a new quota becomes available for consumption.
+
+The test below checks that, when the monthly quota is exceeded, then the function IsAzureMLLimitReached returns TRUE.
+
+```al
+[Test]
+PROCEDURE AzureMLProcessingTimeExceedsLimit@11();
+VAR
+ AzureMachineLearningUsage@1004 : Record 2002;
+ ProcessingTime@1000 : Decimal;
+BEGIN
+ // [SCENARIO] Azure Machine Learning Processing time exceeds AzureML limit
+
+ // [GIVEN] AzureMachineLearningUsage > 0
+ Initialize; // calls PermissionManager.SetTestabilitySoftwareAsAService(TRUE);
+ ProcessingTime := LibraryRandom.RandDec(1000,2);
+ AzureMachineLearningUsage.IncrementTotalProcessingTime(ProcessingTime);
+
+ // [WHEN] When IsAzureMLLimitReached is invoked with Limit more than Processing time
+ // [THEN] HasAzureLimitReached returns TRUE
+ Assert.IsTrue(AzureMachineLearningUsage.IsAzureMLLimitReached(ProcessingTime - 1),
+ 'HasAzureLimitReached returns wrong value when Processing time exceeds Limit.');
+ PermissionManager.SetTestabilitySoftwareAsAService(FALSE);
+END;
+```
+
+The figure below explains what happens when the Permission Manager is not a singleton. When it is invoked from different places (first from the test, second from the production code), then different instances of the Permission Manager will fire up and answer. In detail:
+
+1. The test calls Initialize which sets SaaS=TRUE in codeunit Permission Manager
+2. The test calls into production code to validate it works as expected. It calls AzureMachineLearningUsage codeunit to find out if the monthly quota has been reached. The function IsAzureMLLimitReached in AzureMachineLearningUsage codeunit is designed only for SaaS. If the code doesn't run in SaaS, then it always returns FALSE.
+3. Therefore, a call to PermissionManager is made, to find out if the environment is SaaS.
+4. However, a different instance of Permission Manager is reached -- and instance where SaaS was never set to TRUE. This is a mistake -- the test intended to simulate SaaS, but the state it set in the beginning is not reachable from production code.
+5. The production code will assess (wrongly) that it's not running SaaS, and say that the Azure ML limit has not been reached (incorrect -- and the test fails).
+
+[![ ][image1]][anchor1]
+
+**Solution:** restrict the number of instantiations of a codeunit to only one, by setting the codeunit property **SingleInstance** to **Yes**.
+
+Returning to the previous example, let's analyze the case when the codeunit Permission Manager is a singleton codeunit:
+
+[![ ][image2]][anchor2]
+
+When the codeunit Permission Manager is a singleton, then no matter from where it is invoked, the same instance will be reached. Therefore, the status set by the test (SaaS = TRUE) will be reachable from the production code, and the test will pass, as seen in the figure below.
+
+[![ ][image3]][anchor3]
+
+**Consequences**
+
+1\. Use Singleton Codeunit with care and only when there is no other solution. Preserving a global state could often enough be more harmful than useful. One risk is that tests might fail apparently non-deterministically.
+
+For example, a problem we have met in the development team for Madeira release, was that the singleton codeunit function PermissionManager.SetSoftwareAsAService(TRUE) is often used to emulate and test SaaS conditions. However, if a test 'forgets' to reset the state to default (FALSE), then another codeunit which is not supposed to emulate SaaS, will suddenly run as SaaS and will fail. Even if the test has code that resets the state to FALSE, this code might never be reached because of an earlier failure or other error in the test which would stop execution.
+
+2\. The singleton codeunit is only "alive" for the current session. If the user logs out, the old session is closed and the singleton cleared out so any values stored in the old session's singleton will be lost when the session was closed. When the user logs in again, a new session (with a new fresh instance of the singleton) will be created.
+
+**NAV Usages**
+
+Most of the usages in NAV refer to the so-called "management codeunits". The management codeunits are needed to run, in a centralized way, various modular parts of the application (features), like the CRM integration, Permissions, Workflows etc. Some of the **Singleton Codeunits** in NAV are listed below:
+
+* Codeunit 423 Change Log Management
+* Codeunit 1503 Workflow Record Management
+* Codeunit 1511 Notification Lifecycle Mgt.
+* Codeunit 1629 Office Attachment Manager
+* Codeunit 1632 Office Error Engine
+* Codeunit 5150 Integration Management
+* Codeunit 9002 Permission Manager
+* Etc.
+
+**Note:** while the object-oriented **Singleton** pattern can restrict the number of instantiations of the singleton to an integer n > 0, in Dynamics NAV the **Singleton Codeunit** can only have n=1\.
+
+
+
+
+[anchor0]: Singleton-Codeunit.png
+[anchor1]: Singleton-Codeunit-_2D00_-example-_2D00_-bad.PNG
+[anchor2]: 2313.Singleton-Codeunit-_2D00_-CSIDE-SingleInstance-property.PNG
+[anchor3]: Singleton-Codeunit-_2D00_-example-_2D00_-good.PNG
+
+
+[image0]: Singleton-Codeunit.png
+[image1]: Singleton-Codeunit-_2D00_-example-_2D00_-bad.PNG
+[image2]: 2313.Singleton-Codeunit-_2D00_-CSIDE-SingleInstance-property.PNG
+[image3]: Singleton-Codeunit-_2D00_-example-_2D00_-good.PNG
diff --git a/content/docs/NAVPatterns/patterns/singleton/singleton-table/5554.Singleton-Table.png b/content/docs/NAVPatterns/patterns/singleton/singleton-table/5554.Singleton-Table.png
new file mode 100644
index 00000000..56a98554
Binary files /dev/null and b/content/docs/NAVPatterns/patterns/singleton/singleton-table/5554.Singleton-Table.png differ
diff --git a/content/docs/NAVPatterns/patterns/singleton/singleton-table/_index.md b/content/docs/NAVPatterns/patterns/singleton/singleton-table/_index.md
new file mode 100644
index 00000000..31bbebb7
--- /dev/null
+++ b/content/docs/NAVPatterns/patterns/singleton/singleton-table/_index.md
@@ -0,0 +1,103 @@
++++
+title = "Singleton Table"
+weight = 1110
+tags = ["C/AL"]
+categories = ["Pattern"]
++++
+## Singleton Table
+
+_By Elly Nkya at Microsoft Development Center Copenhagen_
+
+[![ ][image0]][anchor0]
+
+**Problem**: The developer needs to define a single record that can contain a set of rules and behavior (optional, mandatory, or defaulting mechanisms), that apply to a functionality, and can be configured by a user.
+
+**Forces**
+
+* You want a central place to define the address and logo of your company (see Company Information table).
+* You want to define the no. series that should be used for your sales documents (see Sales & Receivables Setup table).
+* You want to know whether your sales documents should be archived (see Sales & Receivables Setup table).
+* You want to define the rounding accuracy your system should (see General Ledger Setup table).
+
+**Solution:** Define a single record that can contain a set of rules and behavior (optional, mandatory, or defaulting mechanisms), that apply to a functionality, and can be configured by a user.
+
+In a functionality that is large enough (such as sales, inventory, fixed) you may want to define a global set of rules, that are configurable by the user.
+
+**Implementation**
+
+**1\. Define:** Create a Setup Table with Dummy a Primary Key. Typically with type Code=10\. Then add fields to define the global rules.
+
+**2\. Instantiate:** Place the instantiation code in a central place where it is guaranteed to be invoked before the functionality uses it. This is done in Codeunit 2\.
+
+**3\. Enforce:** Give the user access to the record so that he can change the default setup, by creating a Card page. On the page, enforce the singleton to prevent deletion of the record or insertion of a new record
+
+**4\. Use:** Access the rule in code and use it
+
+**NAV Usages**
+
+Rounding rules for Unit-Amounts and Amounts are implemented using the Singleton pattern.
+
+**1\. Define:** The General Ledger Setup is used for this.
+
+**2\. Instantiate:** In codeunit 2, the following code is invoked
+
+```al
+WITH GLSetup DO
+ IF NOT FINDFIRST THEN BEGIN
+ INIT;
+ INSERT;
+ END;
+```
+
+**3\. Enforce:** On the General Ledger Setup. The following properties are setup:
+
+```al
+ DeleteAllowed=false,
+ InsertAllowed=false
+```
+
+**4\. Use:** Access the rounding rules are used
+
+```al
+...
+GLSetup.GET;
+UnitCostCurrency := ROUND(...,GLSetup."Unit-Amount Rounding Precision");
+...
+```
+
+Or if accessing the rule multiple times and performance is a consideration, use lazy instantiation:
+
+```al
+...
+GetGLSetup;
+UnitCostCurrency := ROUND(...,GLSetup."Unit-Amount Rounding Precision");
+...
+
+LOCAL GetGLSetup()
+IF NOT GLSetupRead THEN
+ GLSetup.GET;
+GLSetupRead := TRUE;
+```
+
+**Related topics**
+
+[Singleton design pattern][anchor1].
+
+The **Singleton Table** has two established applications in Dynamics NAV:
+
+1. [**Setup Tables**][anchor2] -- which are commonly storing user setup data in NAV,
+2. **Cue Tables** -- used to calculate values for the visual representation of Cues on the NAV role center pages.
+
+YouTube Video of NAV Singleton:
+
+{{< youtube aQPu-s9FkYI>}}
+
+
+
+[anchor0]: 5554.Singleton-Table.png
+[anchor1]: https://en.wikipedia.org/wiki/Singleton_pattern
+[anchor2]: /navpatterns/1-patterns/singleton/singleton-table/setup-table/
+[anchor3]: https://www.youtube.com/watch?v=aQPu-s9FkYI&list=PLhZ3P-LY7CqmVszuvtJLujFyHpsVN0U_w&index=13
+
+
+[image0]: 5554.Singleton-Table.png
diff --git a/content/docs/NAVPatterns/patterns/singleton/singleton-table/cue-table/Cue-Table-Figure-1.JPG b/content/docs/NAVPatterns/patterns/singleton/singleton-table/cue-table/Cue-Table-Figure-1.JPG
new file mode 100644
index 00000000..15a3dfab
Binary files /dev/null and b/content/docs/NAVPatterns/patterns/singleton/singleton-table/cue-table/Cue-Table-Figure-1.JPG differ
diff --git a/content/docs/NAVPatterns/patterns/singleton/singleton-table/cue-table/Cue-Table.png b/content/docs/NAVPatterns/patterns/singleton/singleton-table/cue-table/Cue-Table.png
new file mode 100644
index 00000000..44f81deb
Binary files /dev/null and b/content/docs/NAVPatterns/patterns/singleton/singleton-table/cue-table/Cue-Table.png differ
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
new file mode 100644
index 00000000..eff99fd9
--- /dev/null
+++ b/content/docs/NAVPatterns/patterns/singleton/singleton-table/cue-table/index.md
@@ -0,0 +1,74 @@
++++
+title = "Cue Table"
+weight = 440
+tags = ["C/AL"]
+categories = ["Pattern"]
++++
+_By Bogdana Botez at Microsoft Development Center Copenhagen_
+
+[![ ][image0]][anchor0]
+
+
+Cues are the second usual application of the [**Singleton Table**][anchor1] pattern in Dynamics NAV, after [**Setup Tables**][anchor2].
+
+**Context**: The user gets overview information about the business on the Dynamics NAV Role Center page.
+
+Figure 1 - Cue information in Dynamics NAV shows cue information seen by the user on the **Sales Order Processor** role center.
+
+[![ ][image1]][anchor3]
+
+The overview information consists of summed-up numbers, calculated from business data, like for example how many sales orders are still open, how many shipments are ready to go, or partially shipped, how many documents are waiting for approval etc.
+
+**Problem**: NAV stores data in tables. By definition, a table is a repetitive structure containing multiple lines, each line having a different piece of the information. But sometimes this repetitive information needs to be summed-up or otherwise synthetized, and presented as an overview.
+
+**Solution:** Store overview information in a singleton table.
+
+There are two ways of calculating overview information in NAV.
+
+1. By using a FlowField. This applies for simpler calculations, like filtered or unfiltered counts, sums etc.
+2. By writing C/AL code to perform custom calculations. Use this when:
+
+ * The way to calculate the overview is too complex for flow fields, or
+ * The data needs to be pulled from an external system (like Dynamics CRM, QuickBooks or any external integration).
+
+The implementation of Cues is already described in detail on MSDN, in [Creating and Customizing Cues][anchor4] and in [Walkthrough: Creating a Cue Based on a FlowField][anchor5].
+
+**NAV Usages**
+
+_Table 1 - Cue tables in Dynamics NAV_ shows some examples of singleton tables used for creating Cues.
+
+Table ID | Table Name
+---------|-----------
+1313 | Activities Cue
+5370 | CRM Synch. Job Status Cue
+9042 | Team Member Cue
+9050 | Warehouse Basic Cue
+9051 | Warehouse WMS Cue
+9052 | Service Cue
+9053 | Sales Cue
+9054 | Finance Cue
+9055 | Purchase Cue
+9056 | Manufacturing Cue
+9057 | Job Cue
+9058 | Warehouse Worker WMS Cue
+9059 | Administration Cue
+9060 | SB Owner Cue
+9061 | RapidStart Services Cue
+9063 | Relationship Mgmt. Cue
+9069 | O365 Sales Cue
+9070 | Accounting Services Cue
+
+Table 1 - Cue tables in Dynamics NAV
+
+
+
+[anchor0]: Cue-Table.png
+[anchor1]: /navpatterns/1-patterns/singleton/singleton-table/
+[anchor2]: /navpatterns/1-patterns/singleton/singleton-table/setup-table/
+[anchor3]: Cue-Table-Figure-1.JPG
+[anchor4]: https://msdn.microsoft.com/en-us/library/dn789553(v=nav.90).aspx
+[anchor5]: https://msdn.microsoft.com/en-us/library/ff477101(v=nav.90).aspx
+
+
+[image0]: Cue-Table.png
+[image1]: Cue-Table-Figure-1.JPG
diff --git a/content/docs/NAVPatterns/patterns/singleton/singleton-table/setup-table/6675.NAVSetupTablePattern2.png b/content/docs/NAVPatterns/patterns/singleton/singleton-table/setup-table/6675.NAVSetupTablePattern2.png
new file mode 100644
index 00000000..486d3d35
Binary files /dev/null and b/content/docs/NAVPatterns/patterns/singleton/singleton-table/setup-table/6675.NAVSetupTablePattern2.png differ
diff --git a/content/docs/NAVPatterns/patterns/singleton/singleton-table/setup-table/Setup-Table.png b/content/docs/NAVPatterns/patterns/singleton/singleton-table/setup-table/Setup-Table.png
new file mode 100644
index 00000000..018b9a11
Binary files /dev/null and b/content/docs/NAVPatterns/patterns/singleton/singleton-table/setup-table/Setup-Table.png differ
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
new file mode 100644
index 00000000..11ec5be1
--- /dev/null
+++ b/content/docs/NAVPatterns/patterns/singleton/singleton-table/setup-table/index.md
@@ -0,0 +1,75 @@
++++
+title = "Setup Table"
+weight = 1070
+tags = ["C/AL"]
+categories = ["Pattern"]
++++
+_By Abhishek Ghosh, at Microsoft Development Center Copenhagen_
+
+[![ ][image0]][anchor0]
+
+This is the first and most well-known of the two usual applications of the **Singleton Table** pattern in Dynamics NAV.
+
+**Problem:** the developer needs to store information about the operating setup or environment in the database, in a way that can be persisted across sessions.
+
+**Solution:** The information is stored in a table with one record only. The user is subsequently able to modify, but not add or delete records in the table.
+
+The implementation of the pattern involves several considerations:
+
+* Suffixing the table name with Setup (ex: General Ledger Setup).
+* Defining a suitable primary key
+* Creating a page where the user can view and edit a record, but not add new records or delete an existing one
+* Optionally, updating the Company - Initialize codeunit.
+
+**Defining a Primary Key**
+
+Since this kind of tables is a collection of several environment or setup parameters, the primary key does not refer to any business attributes for this kind of tables. However, for maintaining the integrity of the database, it is necessary to define a primary key.
+
+So, the most common implementation is to have a field "Primary Key" of Code[10]. This is populated with a blank value when the record is inserted. This field is not added to the page, so that the user cannot be modify it later.
+
+**Creating a Page**
+
+The **CardPage** type is most suitable for representing this kind of tables. In addition, the **InsertAllowed** and **DeleteAllowed** properties in the page should be set to false to prevent the user from adding or deleting records in the table.
+
+In the **OnOpenPage** trigger, the following code should be added to insert a record when the user opens the page for the first time, if a record does not exist already.
+
+```al
+OnOpenPage()
+ RESET;
+ IF NOT GET THEN BEGIN
+ INIT;
+ INSERT;
+ END;
+```
+
+The following diagram describes the flow of the program, once the user tries to access the setup information. The user opens the page. If the record containing setup information already exists, then the page opens on the existing record. Else, a new empty record is created and the page opens on it.
+
+[![ ][image1]][anchor1]
+
+**Company-Initialize Codeunit**
+
+The Company-Initialize codeunit (codeunit 2) is executed when a new company is created. We recommended that you add records to the single-record tables in this codeunit. If some of the fields are expected to have default values, they can also be populated here.
+
+**NAV Usages**
+
+Several Setup tables in NAV implement this pattern. Some of those are:
+
+* Table 98 General Ledger Setup
+* Table 311 Sales & Receivables Setup
+* Table 312 Purchases & Payables Setup
+* Table 313 Inventory Setup
+* Table 242 Source Code Setup
+
+**Variation:** While most tables just insert a record with empty primary key in codeunit 2, table 242 ("Source Code Setup") offers an example of inserting default values into all fields of the table (method "InitSourceCodeSetup"). This practice, wherever feasible, is likely to reduce the effort during implementation.
+
+**Related resources:** [Considerations on optimizing the Singleton Table, by Søren Klemmensen][anchor2].
+
+
+
+[anchor0]: Setup-Table.png
+[anchor1]: 6675.NAVSetupTablePattern2.png
+[anchor2]: http://www.klemmensen.ca/Blog/Post/35/Initialize-Setup-Tables
+
+
+[image0]: Setup-Table.png
+[image1]: 6675.NAVSetupTablePattern2.png
diff --git a/content/docs/NAVPatterns/patterns/standard-journal/0143.Standard-Document-Pattern-2.png b/content/docs/NAVPatterns/patterns/standard-journal/0143.Standard-Document-Pattern-2.png
new file mode 100644
index 00000000..fb34ee13
Binary files /dev/null and b/content/docs/NAVPatterns/patterns/standard-journal/0143.Standard-Document-Pattern-2.png differ
diff --git a/content/docs/NAVPatterns/patterns/standard-journal/0456.Standard-Journal.png b/content/docs/NAVPatterns/patterns/standard-journal/0456.Standard-Journal.png
new file mode 100644
index 00000000..87828729
Binary files /dev/null and b/content/docs/NAVPatterns/patterns/standard-journal/0456.Standard-Journal.png differ
diff --git a/content/docs/NAVPatterns/patterns/standard-journal/0820.Standard-Document-Pattern-1.png b/content/docs/NAVPatterns/patterns/standard-journal/0820.Standard-Document-Pattern-1.png
new file mode 100644
index 00000000..60e5c217
Binary files /dev/null and b/content/docs/NAVPatterns/patterns/standard-journal/0820.Standard-Document-Pattern-1.png differ
diff --git a/content/docs/NAVPatterns/patterns/standard-journal/5327.Standard-Document-Pattern-3.png b/content/docs/NAVPatterns/patterns/standard-journal/5327.Standard-Document-Pattern-3.png
new file mode 100644
index 00000000..992ab55e
Binary files /dev/null and b/content/docs/NAVPatterns/patterns/standard-journal/5327.Standard-Document-Pattern-3.png differ
diff --git a/content/docs/NAVPatterns/patterns/standard-journal/index.md b/content/docs/NAVPatterns/patterns/standard-journal/index.md
new file mode 100644
index 00000000..43f4d4fd
--- /dev/null
+++ b/content/docs/NAVPatterns/patterns/standard-journal/index.md
@@ -0,0 +1,76 @@
++++
+title = "Standard Journal"
+weight = 1150
+tags = ["C/AL"]
+categories = ["Pattern"]
++++
+_By Bogdana Botez, at Microsoft Development Center Copenhagen_
+
+Meet the **Standard Journal** pattern, which gives the NAV user the advantage of storing transaction details and reusing them multiple times at later dates. For example, this is how to pay a monthly bill.
+
+## Abstract
+
+If the journal data can be reused later, the user has the possibility to save the current temporary transaction details. One example can be the case of monthly electricity payments. The user will manually enter the details for the first payment, of the current month. Next month, a part of the data will be the same, such as the vendor and transaction details. If the user has saved the initial monthly payment as a standard journal, then they can now reuse it to create the draft of the next monthly payment. Once the draft journal lines are created, they can be updated with the current month information.
+
+## Description
+
+When a journal is created, the user can invoke the Save as Standard Journal action to save the current journal for later use. When saving the journal as a standard journal, the user is required to choose a code, which is later used to identify the saved journal. The journal lines are stored in a separate table. There can be one standard journal saved per journal type and code.
+
+Later, the user can create new journal lines by using the Get Standard Journals action. This action restores the saved journal into the new journal lines.
+
+### Scenario
+
+**Step 1**: The data entered by the user through the Journal page is stored temporarily in the Journal Line table. The data is available for editing or deleting. The journal line data will be stored in this table until it is either deleted or posted.
+
+**Step 2**: The user decides to save the current journal line entries for later use. If this is the monthly rent, the user may want to use similar entries next month when a new payment is due. On the Journal page, the user invokes the Save as Standard Journal action. This triggers the Save as Standard Journal report, which copies the entries from the Journal Line table to the Standard Journal Line table. When saving, the user will be asked for an identifier, a code, which will be used to later uniquely identify the saved entries.
+
+**Step 3**: When the user invokes the Get Standard Journal action, a list of codes are presented to the user so that they can decide which standard journal to restore and copy in the Journal Line table.
+
+[][anchor0][![ ][image0]][anchor1]
+
+[![ ][image1]][anchor2][][anchor3]
+
+The sequence flow of the three steps is described in the following diagram.
+
+[![ ][image2]][anchor4][][anchor5][][anchor6]
+
+## NAV Specific Example
+
+In the standard version of NAV, the Standard Journal functionality is implemented in the following journals:
+
+* Item Journal, which saves data to the Standard Item Journal
+* General Journal, which saves data to the Standard General Journal
+
+### General Journal
+
+The user enters data in the General Journal page (39) and invokes the Save/Get actions as illustrated in the following screenshot:
+
+[][anchor7][![ ][image3]][anchor8]
+
+When saving the journal lines, the Save as Standard Gen. Journal report (750) is invoked. The report saves the entries in the Standard General Journal Line table (751).
+
+## NAV Usages
+
+1. Standard General Journal
+2. Standard Item Journal
+
+{{< youtube XeTKmO2Eqgw>}}
+
+
+
+[anchor0]: 3201.NAVPatternStdJournal1.png
+[anchor1]: 0820.Standard-Document-Pattern-1.png
+[anchor2]: 0143.Standard-Document-Pattern-2.png
+[anchor3]: 6521.NAVPatternStdJournal2.png
+[anchor4]: 5327.Standard-Document-Pattern-3.png
+[anchor5]: 3618.Standard-Document-Pattern-3.png
+[anchor6]: 1104.NAVPatternStdJournal3.png
+[anchor7]: 8585.NAVPatternStdJournal3.jpg
+[anchor8]: 0456.Standard-Journal.png
+[anchor9]: https://www.youtube.com/watch?v=XeTKmO2Eqgw&list=PLhZ3P-LY7CqmVszuvtJLujFyHpsVN0U_w&index=21
+
+
+[image0]: 0820.Standard-Document-Pattern-1.png
+[image1]: 0143.Standard-Document-Pattern-2.png
+[image2]: 5327.Standard-Document-Pattern-3.png
+[image3]: 0456.Standard-Journal.png
diff --git a/content/docs/NAVPatterns/patterns/temporary-dataset-report/0250.Temporary-Dataset-Report-1.png b/content/docs/NAVPatterns/patterns/temporary-dataset-report/0250.Temporary-Dataset-Report-1.png
new file mode 100644
index 00000000..67260f5c
Binary files /dev/null and b/content/docs/NAVPatterns/patterns/temporary-dataset-report/0250.Temporary-Dataset-Report-1.png differ
diff --git a/content/docs/NAVPatterns/patterns/temporary-dataset-report/2376.Temporary-Dataset-Report-2.png b/content/docs/NAVPatterns/patterns/temporary-dataset-report/2376.Temporary-Dataset-Report-2.png
new file mode 100644
index 00000000..d536ba97
Binary files /dev/null and b/content/docs/NAVPatterns/patterns/temporary-dataset-report/2376.Temporary-Dataset-Report-2.png differ
diff --git a/content/docs/NAVPatterns/patterns/temporary-dataset-report/4010.Temporary-Dataset-Report-6.jpg b/content/docs/NAVPatterns/patterns/temporary-dataset-report/4010.Temporary-Dataset-Report-6.jpg
new file mode 100644
index 00000000..fc8b9563
Binary files /dev/null and b/content/docs/NAVPatterns/patterns/temporary-dataset-report/4010.Temporary-Dataset-Report-6.jpg differ
diff --git a/content/docs/NAVPatterns/patterns/temporary-dataset-report/4118.Temporary-Dataset-Report-5.jpg b/content/docs/NAVPatterns/patterns/temporary-dataset-report/4118.Temporary-Dataset-Report-5.jpg
new file mode 100644
index 00000000..4ac6fa94
Binary files /dev/null and b/content/docs/NAVPatterns/patterns/temporary-dataset-report/4118.Temporary-Dataset-Report-5.jpg differ
diff --git a/content/docs/NAVPatterns/patterns/temporary-dataset-report/6523.Temporary-Dataset-Report-4.jpg b/content/docs/NAVPatterns/patterns/temporary-dataset-report/6523.Temporary-Dataset-Report-4.jpg
new file mode 100644
index 00000000..9cbcb1db
Binary files /dev/null and b/content/docs/NAVPatterns/patterns/temporary-dataset-report/6523.Temporary-Dataset-Report-4.jpg differ
diff --git a/content/docs/NAVPatterns/patterns/temporary-dataset-report/7607.Temporary-Dataset-Report-3.png b/content/docs/NAVPatterns/patterns/temporary-dataset-report/7607.Temporary-Dataset-Report-3.png
new file mode 100644
index 00000000..8aa6399b
Binary files /dev/null and b/content/docs/NAVPatterns/patterns/temporary-dataset-report/7607.Temporary-Dataset-Report-3.png differ
diff --git a/content/docs/NAVPatterns/patterns/temporary-dataset-report/index.md b/content/docs/NAVPatterns/patterns/temporary-dataset-report/index.md
new file mode 100644
index 00000000..b54a4c68
--- /dev/null
+++ b/content/docs/NAVPatterns/patterns/temporary-dataset-report/index.md
@@ -0,0 +1,159 @@
++++
+title = "Temporary Dataset Report"
+weight = 1190
+tags = ["C/AL"]
+categories = ["Pattern"]
++++
+_Originally by Abhishek Ghosh, at Microsoft Development Center Copenhagen_
+
+## Abstract
+
+This pattern generates the data to be displayed dynamically by combing/processing several data sources. It then displays the resulting dataset without writing to the database.
+
+[![ ][image6]][anchor9]
+
+## Description
+
+While writing reports in NAV, we have the luxury of using a built-in iterator. So, once we define the dataitem and the ordering, the runtime takes care of the iteration.
+
+The iterator has one shortcoming: It can only run through records written into the database. There are situations, however, where we want to display temporary datasets created at runtime by processing data from different sources. That is where the Temporary Dataset Report pattern can be used.
+
+## Usage
+
+This pattern takes a two-step approach to displaying the data:
+
+* Parse the data sources to create a record buffer in a temporary record variable.
+
+* Iterate through a dataitem of the Integer table and display one record from the temporary recordset in each iteration.
+
+#### Step 1: Combining data sources to create a dataset
+
+In this step, we would process the existing data to create a temporary recordset. The three most common techniques to do this are discussed in the following paragraphs.
+
+The first technique is mostly used when we want to build the report based on one or more source tables. A lot of processing is required, and we therefore want to store and display information from a temporary recordset. With this technique, we create a dataitem of the source record and then iterate through this dataitem to create the temporary recordset. An advantage of this technique is that it allows the user to perform additional filtering on the data source tables since they are added as additional dataitems and therefore will have their tabs on the request page by default.
+
+[![ ][image7]][anchor10]
+
+The second technique was made available with NAV 2013 when queries were introduced as a tool to help us combine data from different sources. Instead of writing data into a temporary record variable, we can create a query to combine the data from different sources. This offers better performance than the first technique in almost every situation. However, with a query, we sacrifice the luxury of getting a flexible filtering on the request page for the source dataitem.
+
+The third technique is to write a function (or a codeunit, if the complexity demands so) that will crunch the data and create the temporary record variable. This function must be invoked from the OnPreReport trigger (or the OnPreDataItem trigger of the Integer dataitem).
+
+[![ ][image8]][anchor11]
+
+The following table summarizes when to use each of the three techniques:
+
+Technique | When to Use
+----------|------------
+Source Record DataItem | When it is important to offer flexible filtering possibilities on the source data.
+Query | When performance is critical.
+Populating temporary table in a function | Only when the source dataset is too complex to use either of the other techniques.
+
+#### Step 2: Iterating through the Integer dataitem
+
+When you have created the dataset as a temporary record variable or a query, the next step is to iterate through them to display the information. However, the report controller in NAV cannot iterate through temporary records or through the results of a query. This is where the Integer table comes into use.
+
+The Integer table is a virtual table with only one field, Number, which is an integer field. For all practical purposes, we can assume that this table has pre-inserted records with value of Number, starting from 1 and ending with the highest integer value. So, if we keep iterating through this dataitem, the value of the Number field will be 1 for the first iteration and will keep increasing by 1 for every iteration.
+
+There are two ways to use the Integer dataitem to iterate through the temporary recordset:
+
+* Loop for an indeterminate number of times until you reach the end of the recordset.
+
+* Calculate the number of records in the temporary recordset in advance and then iterate that many number of times.
+
+With the first approach, in the Integer-OnAfterGetRecord trigger, you must make the temporary record variable move to the next record, follow that up with a check if you have reached the end of the recordset, and then use CurrReport.BREAK if you have reached the end.
+
+With the second approach, you must limit the number of times the report iterates through the Integer dataitem. The Number field in the Integer table will have a value of 1 in the first iteration and will increment automatically by 1 on every subsequent iteration. To ensure that the report iterates through the Integer dataitem "n" times, you must, therefore, filter the Integer dataitem on Number = 1..n. You must also move the temporary record variable to the next record in every iteration of the Integer-OnAfterGetRecord trigger. NOTE: This technique cannot be used if you use queries as the temporary data source, since queries do not (in NAV 2013) return the number of records.
+
+## Example
+
+**Goal:** To print a report that lists all contacts that have open documents (both sales and purchase) and displays those documents in chronological order
+
+A contact can be connected to a vendor or a customer using the Create As Customer/Vendor function on the Contact Card. When a sales/purchase document is created for this customer/vendor, the Contact No. is stored in the Sell-to Contact No. or Buy-from Contact No. fields of the sales and purchase documents. The obvious choice would be to display the data directly from the Sales Header and Purchase Header records. However, the requirement is to display all of them together chronologically, which means they will need to be stored in, and be read from, one common dataset.
+
+**Step 1: Design a new table that will hold the temporary dataset.**
+
+The UI must display the Document Type, Document No., Document Date, Amount Incl. VAT and the name of the salesperson or purchaser. The table is designed as follows.
+
+[![ ][image3]][anchor5]
+
+Since this table is used to sort the data by Document Date, a "Document Date" key is added to the table.
+
+[![ ][image4]][anchor6]
+
+Some additional properties to note:
+
+* Sales Header dataitem should be linked to the Contact dataitem through the DataItemLink property -- "Sell-to Contact No.=FIELD(No.)"
+
+* Purchase Header dataitem should be linked to the Contact dataitem through the DataItemLink property -- "Buy-from Contact No.=FIELD(No.)"
+
+**Step 2: In the Sales Header** -- OnAfterGetRecord, write the following code to populate the data buffer:
+
+[![ ][image5]][anchor7]
+
+**Where:**
+
+* ContactDocumentBuffer is a temporary global variable of the buffer table created.
+
+* SalesPerson is a local record variable for table 13\.
+* SalesTxt is a text constant denoting "Sales" in ENU.
+
+Similar code must be written in Purchase Header -- OnAfterGetRecord to store data from the purchase documents into the buffer.
+
+**Step 3:** Iterate through the temporary records in the Integer dataitem.
+
+Since the requirement is to sort the data by Document Date, we must sort the ContactDocumentBuffer table by this key. Also, by the time the execution of Integer -- OnPreDataItem begins, the number of records in the temporary buffer should be known already. Therefore, we can limit the number of times to repeat the Integer dataitem to the number of records in the buffer.
+
+We meet both requirements with the following lines of code in Integer - OnPreDataItem:
+
+```al
+ContactDocumentBuffer.SETCURRENTKEY("Document Date");
+
+SETRANGE(Number,1,ContactDocumentBuffer.COUNT);
+```
+
+Lastly, we must move the record pointer by 1 record every time we loop through the Integer dataitem. So, in Integer -- OnAfterGetRecord, we add the following lines of code:
+
+```al
+IF Number = 1 THEN
+ ContactDocumentBuffer.FINDFIRST
+ELSE
+ ContactDocumentBuffer.NEXT;
+```
+
+Now, the only task that remains is to design the RDLC layout. (Not part of this application pattern description.)
+
+NAV Usages
+
+This is a commonly used pattern in several reports, such as:
+
+* Report 204 -- Sales -- Quote
+
+* Report 205 -- Order Confirmation
+
+* Report 206 -- Sales -- Invoice
+
+{{< youtube QHn5oEOJv0Q>}}
+
+
+[anchor0]: /cfs-file.ashx/__key/communityserver-wikis-components-files/00-00-00-00-42/0250.Temporary-Dataset-Report-1.gif
+[anchor1]: /cfs-file.ashx/__key/communityserver-wikis-components-files/00-00-00-00-42/8424.Temporary-Dataset-Report-1.gif
+[anchor2]: /cfs-file.ashx/__key/communityserver-wikis-components-files/00-00-00-00-42/2376.Temporary-Dataset-Report-2.gif
+[anchor3]: /cfs-file.ashx/__key/communityserver-wikis-components-files/00-00-00-00-42/8461.Temporary-Dataset-Report-2.gif
+[anchor4]: /cfs-file.ashx/__key/communityserver-wikis-components-files/00-00-00-00-42/7607.Temporary-Dataset-Report-3.gif
+[anchor5]: 6523.Temporary-Dataset-Report-4.jpg
+[anchor6]: 4118.Temporary-Dataset-Report-5.jpg
+[anchor7]: 4010.Temporary-Dataset-Report-6.jpg
+[anchor8]: https://www.youtube.com/watch?v=QHn5oEOJv0Q&list=PLhZ3P-LY7CqmVszuvtJLujFyHpsVN0U_w&index=10
+[anchor9]: 0250.Temporary-Dataset-Report-1.png
+[anchor10]: 2376.Temporary-Dataset-Report-2.png
+[anchor11]: 7607.Temporary-Dataset-Report-3.png
+
+[image0]: /resized-image.ashx/__size/550x0/__key/communityserver-wikis-components-files/00-00-00-00-42/0250.Temporary-Dataset-Report-1.gif
+[image1]: /resized-image.ashx/__size/550x0/__key/communityserver-wikis-components-files/00-00-00-00-42/2376.Temporary-Dataset-Report-2.gif
+[image2]: /resized-image.ashx/__size/550x0/__key/communityserver-wikis-components-files/00-00-00-00-42/7607.Temporary-Dataset-Report-3.gif
+[image3]: 6523.Temporary-Dataset-Report-4.jpg
+[image4]: 4118.Temporary-Dataset-Report-5.jpg
+[image5]: 4010.Temporary-Dataset-Report-6.jpg
+[image6]: 0250.Temporary-Dataset-Report-1.png
+[image7]: 2376.Temporary-Dataset-Report-2.png
+[image8]: 7607.Temporary-Dataset-Report-3.png
diff --git a/content/docs/NAVPatterns/patterns/totals-and-discounts-on-subpages-sales-and-purchases/4300.Fig-1.jpg b/content/docs/NAVPatterns/patterns/totals-and-discounts-on-subpages-sales-and-purchases/4300.Fig-1.jpg
new file mode 100644
index 00000000..ced5c6c4
Binary files /dev/null and b/content/docs/NAVPatterns/patterns/totals-and-discounts-on-subpages-sales-and-purchases/4300.Fig-1.jpg differ
diff --git a/content/docs/NAVPatterns/patterns/totals-and-discounts-on-subpages-sales-and-purchases/6305.Fig-4.jpg b/content/docs/NAVPatterns/patterns/totals-and-discounts-on-subpages-sales-and-purchases/6305.Fig-4.jpg
new file mode 100644
index 00000000..3a1e1cae
Binary files /dev/null and b/content/docs/NAVPatterns/patterns/totals-and-discounts-on-subpages-sales-and-purchases/6305.Fig-4.jpg differ
diff --git a/content/docs/NAVPatterns/patterns/totals-and-discounts-on-subpages-sales-and-purchases/Fig-10.jpg b/content/docs/NAVPatterns/patterns/totals-and-discounts-on-subpages-sales-and-purchases/Fig-10.jpg
new file mode 100644
index 00000000..396b3e38
Binary files /dev/null and b/content/docs/NAVPatterns/patterns/totals-and-discounts-on-subpages-sales-and-purchases/Fig-10.jpg differ
diff --git a/content/docs/NAVPatterns/patterns/totals-and-discounts-on-subpages-sales-and-purchases/Fig-11.jpg b/content/docs/NAVPatterns/patterns/totals-and-discounts-on-subpages-sales-and-purchases/Fig-11.jpg
new file mode 100644
index 00000000..0bc23569
Binary files /dev/null and b/content/docs/NAVPatterns/patterns/totals-and-discounts-on-subpages-sales-and-purchases/Fig-11.jpg differ
diff --git a/content/docs/NAVPatterns/patterns/totals-and-discounts-on-subpages-sales-and-purchases/Fig-12.jpg b/content/docs/NAVPatterns/patterns/totals-and-discounts-on-subpages-sales-and-purchases/Fig-12.jpg
new file mode 100644
index 00000000..903eaa16
Binary files /dev/null and b/content/docs/NAVPatterns/patterns/totals-and-discounts-on-subpages-sales-and-purchases/Fig-12.jpg differ
diff --git a/content/docs/NAVPatterns/patterns/totals-and-discounts-on-subpages-sales-and-purchases/Fig-2.jpg b/content/docs/NAVPatterns/patterns/totals-and-discounts-on-subpages-sales-and-purchases/Fig-2.jpg
new file mode 100644
index 00000000..73cb186e
Binary files /dev/null and b/content/docs/NAVPatterns/patterns/totals-and-discounts-on-subpages-sales-and-purchases/Fig-2.jpg differ
diff --git a/content/docs/NAVPatterns/patterns/totals-and-discounts-on-subpages-sales-and-purchases/Fig-3.jpg b/content/docs/NAVPatterns/patterns/totals-and-discounts-on-subpages-sales-and-purchases/Fig-3.jpg
new file mode 100644
index 00000000..bd6ed8d7
Binary files /dev/null and b/content/docs/NAVPatterns/patterns/totals-and-discounts-on-subpages-sales-and-purchases/Fig-3.jpg differ
diff --git a/content/docs/NAVPatterns/patterns/totals-and-discounts-on-subpages-sales-and-purchases/Fig-5.jpg b/content/docs/NAVPatterns/patterns/totals-and-discounts-on-subpages-sales-and-purchases/Fig-5.jpg
new file mode 100644
index 00000000..9486f205
Binary files /dev/null and b/content/docs/NAVPatterns/patterns/totals-and-discounts-on-subpages-sales-and-purchases/Fig-5.jpg differ
diff --git a/content/docs/NAVPatterns/patterns/totals-and-discounts-on-subpages-sales-and-purchases/Fig-6.jpg b/content/docs/NAVPatterns/patterns/totals-and-discounts-on-subpages-sales-and-purchases/Fig-6.jpg
new file mode 100644
index 00000000..d2dd890c
Binary files /dev/null and b/content/docs/NAVPatterns/patterns/totals-and-discounts-on-subpages-sales-and-purchases/Fig-6.jpg differ
diff --git a/content/docs/NAVPatterns/patterns/totals-and-discounts-on-subpages-sales-and-purchases/Fig-7.jpg b/content/docs/NAVPatterns/patterns/totals-and-discounts-on-subpages-sales-and-purchases/Fig-7.jpg
new file mode 100644
index 00000000..0268d59f
Binary files /dev/null and b/content/docs/NAVPatterns/patterns/totals-and-discounts-on-subpages-sales-and-purchases/Fig-7.jpg differ
diff --git a/content/docs/NAVPatterns/patterns/totals-and-discounts-on-subpages-sales-and-purchases/Fig-8.jpg b/content/docs/NAVPatterns/patterns/totals-and-discounts-on-subpages-sales-and-purchases/Fig-8.jpg
new file mode 100644
index 00000000..ef327703
Binary files /dev/null and b/content/docs/NAVPatterns/patterns/totals-and-discounts-on-subpages-sales-and-purchases/Fig-8.jpg differ
diff --git a/content/docs/NAVPatterns/patterns/totals-and-discounts-on-subpages-sales-and-purchases/Fig-9.jpg b/content/docs/NAVPatterns/patterns/totals-and-discounts-on-subpages-sales-and-purchases/Fig-9.jpg
new file mode 100644
index 00000000..2088aed4
Binary files /dev/null and b/content/docs/NAVPatterns/patterns/totals-and-discounts-on-subpages-sales-and-purchases/Fig-9.jpg differ
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
new file mode 100644
index 00000000..88d0d8a4
--- /dev/null
+++ b/content/docs/NAVPatterns/patterns/totals-and-discounts-on-subpages-sales-and-purchases/index.md
@@ -0,0 +1,190 @@
++++
+title = "Totals and Discounts on Subpages Sales and Purchases"
+weight = 1220
+tags = ["C/AL"]
+categories = ["Pattern"]
++++
+_By Nikola Kukrika at Microsoft Development Center Copenhagen_
+
+## Abstract
+
+To increase discoverability and productivity, critical information, such as statistics, can be moved from separate pages onto the document where it is needed.
+
+For example, documents should clearly display totals and discounts information to provide quick overview, make it easy to relate the amounts to the document currency, and to allow the user to see and apply discounts directly on the document.
+
+## Description
+
+Before Microsoft Dynamics NAV 2015, totals and discount information was scattered between the document and the Statistics page, which made it less discover-able and less usable for new users. It was difficult to see the discount amounts, relate the amounts to the currency of the invoice, and it was not-intuitive that you had to update the Statistics page to see updates on the document. In addition, the result of the update was not visible on the main document, which added to the confusion.
+
+Totals and discount information logically belongs on the document, as is the case on many competitor products.
+
+The Statistics page contains too much information for most common scenarios, and only a part of the information is visible on the related document. See the following example for sales order statistics.
+
+[![ ][image0]][anchor0]
+
+With the pattern implemented, selected statistics fields are placed on the sales order, as in the following example.
+
+[![ ][image1]][anchor1]
+
+This new layout gives precise and complete information about totals and discounts, with discounts on the left side and totals on the right side. The currency is clearly shown, and the layout resembles a printed document. The values are always updated (with some exceptions that are explained in the next following sections), and the user can apply a discount directly on the document.
+
+Options considered when deciding on the layout:
+
+* A new FactBox
+* A new FastTab
+* On the Lines subpage
+
+The Lines subpage option was chosen because:
+
+* Can be made with cleaner code
+* Is always visible, not collapsible, as opposed to FactBox and FastTab options
+* Less chrome in the UI
+
+## Usages
+
+Concerning update of totals and discounts, the following were considered:
+
+* All code should be in one place
+* Existing VAT, invoice discount calculation, etc. should be reused and not re-implemented.
+* The presentation layer should be separated from the logic.
+* Presentation layer should have simple (one liner) calls to the logic
+* System responsiveness should not be significantly affected
+* Totals and discounts should be updated "on-the-fly" on any change that affects amounts
+
+The following artifacts were implemented to achieve the desired functionality.
+
+### Flags That Totals Are Not Updated
+
+In order to always update the totals, the system must have a way to mark that a specific change should trigger a recalculation of the totals. To achieve this, the following was implemented.
+
+* On the lines:
+ * A new field, Recalculate Invoice Discount, was added and set to FALSE by default.
+
+The _UpdateAmounts_ function sets the new field to TRUE for any execution. This function was chosen as it is a central entry point for any amount-related change.
+
+[![ ][image2]][anchor2]
+
+* On the header:
+ * A new FlowField, Recalculate Invoice Discount was added. This field is TRUE if at least one line (that relates to the header) has been marked as "dirty", meaning that totals must be recalculated.
+
+[![ ][image3]][anchor3]
+
+Update diagram
+
+[![ ][image4]][anchor4]
+
+### Total Calculation and Update Logic
+
+The following new codeunits were added:
+
+* COD56 and COD66 (one for sales and one for purchase) are responsible for the "heavy" tasks of calculating/applying totals and discounts by calling into existing code, similarly to how the Statistics page works.
+* After recalculation, the flag field is reset on all the lines related to a specific header.
+* These calculations, in the existing implementation, are triggered ONLY in case of UI scenarios. Non-UI scenarios are not affected.
+
+**Note:** In case of web-service implementations that do not use the existing pages, the total calculation and the invoice discount logic should be called specifically to display similar functionality.
+
+### UI-Related Updates
+
+A new CU, COD57 has been added and it contains the logic used to update the fields that will, in the end, be displayed in the UI. Decision was to merge both the sales and purchase functionality into one place.
+
+### Update Controls
+
+The pattern is applied to both non-posted documents (orders, invoices, etc.) and posted documents (posted Invoice, etc.). Because the posted document is a document that does not change, the implementation is simplified as follows.
+
+#### Subpage - Non-posted documents
+
+The _OnAfterGetCurrRecord_ function call is responsible for updating the totals- and discount-related controls.
+
+To avoid duplicating lots of code in many places, a single function with more parameters "pattern" was chosen. The function returns all the controls updated in a single call, as follows.
+
+[![ ][image5]][anchor5]
+
+Manual invoice discount. One function call when applying a manual invoice discount, as follows.
+
+[![ ][image6]][anchor6]
+
+**Note:** After applying the manual discount, the lines should be refreshed as they will be changed in batch mode.
+
+The following "decorators" were added to recalculate amounts on the fly:
+
+* Totals should always be updated as soon as the user leaves the field.
+
+Code should be added to the validate triggers of the controls that are changing the amounts in order to refresh the total controls.[![ ][image7]][anchor7]
+
+**Note:** Amounts will not always be up to date because of the following:
+
+* Performance. The update must be triggered manually if the number of lines is above 100\. (100 was chosen because performance measurements have demonstrated that with more than 100 lines, the performance was worse than 0.5-1s in update time.)
+* Missed/customized triggers or header changes - cannot trigger lines update from header. Likewise, lines cannot trigger the header to update.
+* In the above cases, a link next to the totals will be displayed informing user that totals/discounts might not be updated and they can be updated by selecting the link
+
+#### Subpage -- Posted Documents
+
+Only the _OnAfterGetCurrRecord _function will have code because posted documents cannot be changed. Therefore, totals do not need to be updated.
+
+[![ ][image8]][anchor8]
+
+### UI -- Presentation Layer:
+
+After Platform added support for this specific case, the layout is very simple, as follows.
+
+[![ ][image9]][anchor9]
+
+This will produce the layout we wanted:
+
+[![ ][image12]][anchor13]
+
+### UI -- Manual Totals Update
+
+As mentioned before, there are cases when the user is informed that totals/discounts might not be up to date and that it should select a link in order to get the updated values.
+
+The link is part of the sub-part where totals and discounts are added. The visibility is controlled by actually controlling the text (message, or empty), because Platform does not support making only one visible/invisible (it must be a group). When the link is displayed, the styling is also changed for the total/discount controls, and the values are set to 0\.
+
+[![ ][image13]][anchor14]
+
+## NAV Usages
+
+We have implemented these scenarios for sales and purchase documents (order, invoice, quote, credit memos, return orders, including posted documents). Note that the implementation in NAV 2015 is a bit different than the one in NAV 2014 C5 objects (1300 range). It was decided not to merge the two implementations, among other reasons because the NAV 2014 C5 implementation does not fit the full application.
+
+The pattern can easily be extended to new sales and purchase documents. If the pattern is followed, it can also be extended to other types of documents, such as in services, jobs, etc.
+
+**Note:** This new pattern is not implemented in the North America and India versions. This is due to special local features in the two versions regions and the multitude of sales tax/vat options that would make the solution non-user friendly and not optimized for performance.
+
+## Ideas for Improvement
+
+Platform support to be able to trigger full-page update from the lines. Support does exist today, but it is not programmatically controlled at design time. This means that it cannot be used in all cases.
+
+{{< youtube U3noU-WT8Xk>}}
+
+
+
+[anchor0]: 4300.Fig-1.jpg
+[anchor1]: Fig-2.jpg
+[anchor2]: Fig-3.jpg
+[anchor3]: 6305.Fig-4.jpg
+[anchor4]: Fig-5.jpg
+[anchor5]: Fig-6.jpg
+[anchor6]: Fig-7.jpg
+[anchor7]: Fig-8.jpg
+[anchor8]: Fig-9.jpg
+[anchor9]: Fig-10.jpg
+[anchor10]: /cfs-file.ashx/__key/communityserver-wikis-components-files/00-00-00-00-42/Fig-11.jpg
+[anchor11]: /cfs-file.ashx/__key/communityserver-wikis-components-files/00-00-00-00-42/Fig-12.jpg
+[anchor12]: https://www.youtube.com/watch?v=U3noU-WT8Xk&list=PLhZ3P-LY7CqmVszuvtJLujFyHpsVN0U_w&index=22
+[anchor13]: Fig-11.jpg
+[anchor14]: Fig-12.jpg
+
+
+[image0]: 4300.Fig-1.jpg
+[image1]: Fig-2.jpg
+[image2]: Fig-3.jpg
+[image3]: 6305.Fig-4.jpg
+[image4]: Fig-5.jpg
+[image5]: Fig-6.jpg
+[image6]: Fig-7.jpg
+[image7]: Fig-8.jpg
+[image8]: Fig-9.jpg
+[image9]: Fig-10.jpg
+[image10]: /resized-image.ashx/__size/550x0/__key/communityserver-wikis-components-files/00-00-00-00-42/Fig-11.jpg
+[image11]: /resized-image.ashx/__size/550x0/__key/communityserver-wikis-components-files/00-00-00-00-42/Fig-12.jpg
+[image12]: Fig-11.jpg
+[image13]: Fig-12.jpg
diff --git a/content/docs/NAVPatterns/patterns/transfer-custom-fields/index.md b/content/docs/NAVPatterns/patterns/transfer-custom-fields/index.md
new file mode 100644
index 00000000..7745afe0
--- /dev/null
+++ b/content/docs/NAVPatterns/patterns/transfer-custom-fields/index.md
@@ -0,0 +1,11 @@
++++
+title = "Transfer Custom Fields"
+weight = 1230
+tags = ["C/AL"]
+categories = ["Pattern"]
++++
+{{< youtube cGaBqwfGCws>}}
+
+
+
+[anchor0]: https://www.youtube.com/watch?v=cGaBqwfGCws&list=PLhZ3P-LY7CqmVszuvtJLujFyHpsVN0U_w&index=9
diff --git a/content/docs/NAVPatterns/patterns/variant-facade/Picture2.png b/content/docs/NAVPatterns/patterns/variant-facade/Picture2.png
new file mode 100644
index 00000000..c4d2ce88
Binary files /dev/null and b/content/docs/NAVPatterns/patterns/variant-facade/Picture2.png differ
diff --git a/content/docs/NAVPatterns/patterns/variant-facade/Picture3.png b/content/docs/NAVPatterns/patterns/variant-facade/Picture3.png
new file mode 100644
index 00000000..10163b37
Binary files /dev/null and b/content/docs/NAVPatterns/patterns/variant-facade/Picture3.png differ
diff --git a/content/docs/NAVPatterns/patterns/variant-facade/Picture4.png b/content/docs/NAVPatterns/patterns/variant-facade/Picture4.png
new file mode 100644
index 00000000..102a869a
Binary files /dev/null and b/content/docs/NAVPatterns/patterns/variant-facade/Picture4.png differ
diff --git a/content/docs/NAVPatterns/patterns/variant-facade/Picture5.png b/content/docs/NAVPatterns/patterns/variant-facade/Picture5.png
new file mode 100644
index 00000000..4358fbeb
Binary files /dev/null and b/content/docs/NAVPatterns/patterns/variant-facade/Picture5.png differ
diff --git a/content/docs/NAVPatterns/patterns/variant-facade/Picture6.png b/content/docs/NAVPatterns/patterns/variant-facade/Picture6.png
new file mode 100644
index 00000000..b7eb6255
Binary files /dev/null and b/content/docs/NAVPatterns/patterns/variant-facade/Picture6.png differ
diff --git a/content/docs/NAVPatterns/patterns/variant-facade/Picture7.png b/content/docs/NAVPatterns/patterns/variant-facade/Picture7.png
new file mode 100644
index 00000000..5bdf7025
Binary files /dev/null and b/content/docs/NAVPatterns/patterns/variant-facade/Picture7.png differ
diff --git a/content/docs/NAVPatterns/patterns/variant-facade/Picture8.png b/content/docs/NAVPatterns/patterns/variant-facade/Picture8.png
new file mode 100644
index 00000000..310317fa
Binary files /dev/null and b/content/docs/NAVPatterns/patterns/variant-facade/Picture8.png differ
diff --git a/content/docs/NAVPatterns/patterns/variant-facade/index.md b/content/docs/NAVPatterns/patterns/variant-facade/index.md
new file mode 100644
index 00000000..cb969300
--- /dev/null
+++ b/content/docs/NAVPatterns/patterns/variant-facade/index.md
@@ -0,0 +1,282 @@
++++
+title = "Variant Facade"
+weight = 1440
+tags = ["C/AL"]
+categories = ["Pattern"]
++++
+_By Nikola Kukrika, waldo and Gary Winter_
+
+## Abstract
+
+The Variant façade provides a single interface that can take any Record, RecordRef or RecordID as an argument. With this pattern the code is encapsulated within the single object, with clear separation between common and table specific code.
+
+[![ ][image0]][anchor0]
+
+## Problem
+
+Since NAV is strongly typed, developers often need to duplicate functionality in order to add support for a new table. The developer would typically start by duplicating the function, changing the record type and implement slight modifications to the code if needed.
+
+The problem with this approach is that code duplication is one of the worst things in software development -- it makes code harder to understand, maintain, extend and test.
+
+One of the good examples of this approach and the resulting duplication is codeunit 229, Document-Print:
+
+[![ ][image1]][anchor1]
+
+[![ ][image2]][anchor2]
+
+After reading the code from the two code snippets above, it is very hard to see the differences. The most of the code is duplicated (more than 95%). To make the matters worse the codeunit itself has 10 additional methods that are used to print different records, some are similar to the ones above while others are completely different.
+
+[![ ][image3]][anchor3]
+
+[![ ][image4]][anchor4]
+
+Figure: the entire code of the codeunit 229, Document Print, 12 methods
+
+[![ ][image5]][anchor5]
+
+Figure: Visualization how it would look like with 20 methods
+
+Problems arising from this way of implementing code are:
+
+**Understanding of the code** is very hard, since the developer needs to read blocks of code that look similar and try to understand what exactly the differences are.
+
+**Maintaining and Extending** the code is hard, since every fix or a new behavior that needs to be implemented multiple times (in this case probably 12 times).
+
+**Upgradability is low** -- each conflict needs to be resolved many times. The hook pattern is hard to implement since it needs to be implemented for every function. Evening suffers the same problem - cannot use the evening easily since it needs to be raised from multiple places in the code.
+
+**Testing the code** is hard since the tests need to be replicated.
+
+**Constant cost of adding support for new tables** -- when the new record type needs to be supported, it has a constant cost. Adding a support for a new record will increase the Maintenance tax and it will make the code harder to understand and extend.
+
+**Conclusion** - **If the functionality needs to be used for many records the approach of duplicating the functions should be avoided.**
+
+## Solution
+
+The Variant façade pattern provides a single interface that would not need to be changed in the future. It will be able to take any record as a parameter. Common code should be kept separately from record specific code and both must be very visible so the developers can easily see what the differences are.
+
+The Key components of the pattern are:
+
+### Signature
+
+Instead of hardcoding a record type a variant is used as an argument. A Variant Façade function can receive three types of data: Record, RecordID, or RecordRef. This way, it can be reused anywhere in the product and the code will still work.
+
+A good practice is to combine this pattern with the Argument Table pattern to make sure that the signature does not change (Additional parameters can be added to the argument table without impacting existing code.). If the Argument Table is not needed, it should not be placed in the signature - it is always possible to add it at a latter point and have two public functions (one with and one without arguments).
+
+Example - For codeunit 229, Document-Print all of the public methods can be simply replaced with a single public method like this:
+
+With this approach the façade function can serve all of the record types and will not need to change in the future.
+
+### Casting to a record ref
+
+After the signature it is necessary to decide if the function will support passing of the Record ID and the RecordRef. Code tends to be easier to understand and maintain if only the records are passed as the arguments, however in some cases it is needed to support the other two types.
+
+* **Support for Record, Record ID and RecordRef:**
+
+After the signature if the function supports passing of the RecordID or RecordRef as a parameter it is a good practice to cast them to a RecordRef. Codeunit Data Type Management is used to do this:
+
+DataTypeManagement.GetRecordRef(RecRelatedVariant,RecordRef)
+
+* **Support Records only** - If the function supports only passing in the record, it is a good practice to check if the variant is a record:
+
+```al
+IF NOT RecordVariant.ISRECORD THEN
+ ERROR(NotARecordErr);
+```
+
+### Using the variant
+
+The variant can be passed instead of record when calling the Page.RUN, Codeunit.RUN or Report.RUN statically:
+
+For example:
+
+```al
+Page.RUN(PageID,Variant);
+
+Codeunit.RUN(CodeunitID,Variant);
+
+Report.RUN(ReportID,Variant);
+```
+
+These calls are identical to using an actual instance of the record, since the variant will be casted to the record automatically, with all filters, markings and values preserved.
+
+In case the variant was casted to the RecordRef (by using DataTypeManagement.GetRecordRef(RecRelatedVariant,RecordRef)), it is still possible to invoke the functions statically.
+
+The RecordRef simply needs to be casted into a variant and passed as a parameter, for example:
+
+```al
+VariantArgument := RecordRef;
+
+Page.RUN(PageID,VariantArgument);
+```
+
+Variant can always casted back to the original record in the table specific code, exact process is described below.
+
+### Table specific code
+
+To do table-specific processing, it is necessary to get the RecordRef first, since NUMBER parameter will tell us which table it is. In the table-specific code, it is possible to cast the variant back to the original record type, so data is accessible and it is possible to invoke functions.
+
+To do this, the best practice is to use the COPY function to preserve filters:
+
+```al
+SalesHeader.COPY(RecordVariant);
+```
+
+Assigning directly such as SalesHeader := RecordVariant, is possible, however all filters will be lost.
+
+Example of table specific code:
+
+```al
+CASE RecordRef.NUMBER OF
+ DATABASE::"Sales Header":
+ BEGIN
+ SalesHeader.COPY(RecordVariant);
+ SalesHeader.PrintDocument;
+ END;
+ DATABASE::"Purchase Header":
+ BEGIN
+ PurchaseHeader.COPY(RecordVariant)
+ ...
+ END;
+ ....
+```
+
+It is a good practice to try to avoid the table specific code if possible.
+
+One of the issues with table specific code is that the CASE statement can easily explode when large number of records are supported.
+
+There are two possible solutions:
+
+ 1. Move the calculations outside of the façade code unit and pass it in as part of the argument table.
+
+For example, instead of having a case like this within DocumentPrint codeunit:
+
+```al
+CASE RecordRef.NUMBER OF
+ DATABASE::"Sales Header":
+ BEGIN
+ SalesHeader.COPY(RecordVariant);
+ CASE SalesHeader."Document Type" OF
+ SalesHeader."Document Type"::Quote:
+ ReportSelections.SETRANGE(Usage,ReportSelections.Usage::"S.Quote");
+ SalesHeader."Document Type"::"Blanket Order":
+ ReportSelections.SETRANGE(Usage,ReportSelections.Usage::"S.Blanket");
+ SalesHeader."Document Type"::Order:
+ ReportSelections.SETRANGE(Usage,ReportSelections.Usage::"S.Order");
+ SalesHeader."Document Type"::"Return Order":
+ ReportSelections.SETRANGE(Usage,ReportSelections.Usage::"S.Return");
+ SalesHeader."Document Type"::Invoice:
+ ReportSelections.SETRANGE(Usage,ReportSelections.Usage::"S.Invoice");
+ SalesHeader."Document Type"::"Credit Memo":
+ ReportSelections.SETRANGE(Usage,ReportSelections.Usage::"S.Cr.Memo");
+ END;
+```
+
+If the ReportSelections.Usage is simply passed into the function from outside, then the case statement is not needed at all. For example:
+
+```al
+PrintDocumentArguments."Report Selection Usage" := SalesHeader.GetReportSelectionUsage;
+
+DocumentPrint.PrintDocument(SalesHeader,PrintDocumentArguments);
+```
+
+Where GetReportSelection usage is coded like this:
+
+```al
+CASE SalesHeader."Document Type" OF
+ SalesHeader."Document Type"::Quote:
+ EXIT (ReportSelections.Usage::"S.Quote");
+ SalesHeader."Document Type"::"Blanket Order":
+ EXIT(ReportSelections.Usage::"S.Blanket");
+ SalesHeader."Document Type"::Order:
+ EXIT(ReportSelections.Usage::"S.Order");
+ SalesHeader."Document Type"::"Return Order":
+ EXIT(ReportSelections.Usage::"S.Return");
+ SalesHeader."Document Type"::Invoice:
+ EXIT(ReportSelections.Usage::"S.Invoice");
+ SalesHeader."Document Type"::"Credit Memo":
+ EXIT(ReportSelections.Usage::"S.Cr.Memo");
+```
+
+This way the code is much more reusable and simpler to read.
+
+ 1. Use Rules Table to replace the code with data-driven approach.
+
+Setup table would contain the list of the reports and their usages. Based on Table ID and usage it is possible to set filters on the setup table and run the object ID from the result.
+
+For example:
+
+```al
+ReportSelectionSetup.SETRANGE("Table ID", RecordRef.NUMBER);
+
+ReportSelectionSetup.SETRANGE("Usage Type", RecordRef.FieldValue(ArgumentTable."Usage Type");
+
+ReportSelectionSetup.FINDFIRST;
+
+REPORT.RUN(ReportSelectionSetup."Report ID",VariantRecord);
+```
+
+## Example
+
+The following code illustrates how the Variant Façade pattern can be used to implement the Document-Print Codeunit.
+
+[![ ][image6]][anchor6]
+
+[![ ][image7]][anchor7]
+
+From the PrintDocument signature it is clear that it will not need to be changed in the future.
+
+Code duplication is avoided, specific code is isolated in the PrepareRecord function, there is clear extension point to add support for new records in the future. Since code is not duplicated and there is a single flow through the method, inserting hooks and events in the future will be straightforward.
+
+When adding the support for new records in most cases it is not need to change any code within the method, thus the cost of extending the usage is minimal.
+
+Note - PrepareRecord function is placed for the illustrational purposes. An improvement would be to move all of the code from the PrepareRecord function before calling the function. So for the SalesHeader and PurchaseHeaders discounts should be calculated before invoking the function. For passing of the argument it should be one of the fields in the Argument Table, thus the entire specific code would be eliminated.
+
+## Consequences
+
+* Not needed if the functionality needs to support few tables. Don't use it as a hammer
+* Strongly typing the records has it benefits since it is easier to find usages, errors will be visible at the compilation time.
+* Code becomes harder to debug within the variant façade
+* Be careful with filters and marks, if the function needs to support multiple records. Test these cases thoughtfully because with bad placement of code the filters can easily be lost.
+* Case statements can explode if there are too many tables that require specific processing. Then it is a must to find a way to keep the number of options in the CASE statement low. The Rules Table pattern and adding specifics to the Argument Table before invoking the code could help with keep the list shorter.
+
+## NAV usages
+
+* Codeunit 452 - Report Distribution Management
+* Codeunit 700 - Page Management
+* Codeunit 701 - Data Type Management
+* Codeunit 1268 - Export Launcher
+* Codeunit 1410 - Doc. Exch. Service Mgt.
+* Codeunit 1501 - Workflow Management
+* Codeunit 1521 - Workflow Response Handling
+* Codeunit 1531 - Workflow Change Rec Mgt.
+* Codeunit 1535 - Approvals Mgmt.
+
+## Related Topics
+
+OO Facade [https://en.wikipedia.org/wiki/Facade_pattern][anchor8]
+
+Argument Table pattern - [https://community.dynamics.com/nav/w/designpatterns/245.argument-table-pattern][anchor9]
+
+Rules Table pattern
+
+
+[anchor0]: picture1.png
+[anchor1]: Picture2.png
+[anchor2]: Picture3.png
+[anchor3]: Picture4.png
+[anchor4]: Picture5.png
+[anchor5]: Picture6.png
+[anchor6]: Picture7.png
+[anchor7]: Picture8.png
+[anchor8]: https://en.wikipedia.org/wiki/Facade_pattern
+[anchor9]: /nav/w/designpatterns/245.argument-table-pattern
+
+
+[image0]: picture1.png
+[image1]: Picture2.png
+[image2]: Picture3.png
+[image3]: Picture4.png
+[image4]: Picture5.png
+[image5]: Picture6.png
+[image6]: Picture7.png
+[image7]: Picture8.png
diff --git a/content/docs/NAVPatterns/patterns/variant-facade/picture1.png b/content/docs/NAVPatterns/patterns/variant-facade/picture1.png
new file mode 100644
index 00000000..e13943b5
Binary files /dev/null and b/content/docs/NAVPatterns/patterns/variant-facade/picture1.png differ
diff --git a/content/docs/NAVPatterns/related-links/index.md b/content/docs/NAVPatterns/related-links/index.md
new file mode 100644
index 00000000..dbcd675d
--- /dev/null
+++ b/content/docs/NAVPatterns/related-links/index.md
@@ -0,0 +1,21 @@
++++
+title = "Related Links"
+weight = 990
+tags = ["C/AL"]
++++
+Find below some related NAV Design Patterns links.
+
+[MSDN NAV Team Blog - posts about patterns][anchor0]
+
+[NAV Application Design slides ][anchor1]from NAV TechDays conference in Antwerp, 2013
+
+[NAV Application Design movie][anchor2] from NAV TechDays conference in Antwerp, 2013
+
+[NAV's Secret Code: Design Patterns of Today and Tomorrow][anchor3] slides from NAV TechDays conference in Antwerp, 2013
+
+
+
+[anchor0]: http://blogs.msdn.com/b/nav/archive/tags/patterns/ "MSDN NAV Team Blog - posts about patterns"
+[anchor1]: http://mibuso.com/dlinfo.asp?FileID=1573 "NAV Application Design slides "
+[anchor2]: http://mibuso.com/dlinfo.asp?FileID=1556 "NAV Application Design movie"
+[anchor3]: http://mibuso.com/dlinfo.asp?FileID=1568
diff --git a/content/docs/_index.md b/content/docs/_index.md
new file mode 100644
index 00000000..e48539bd
--- /dev/null
+++ b/content/docs/_index.md
@@ -0,0 +1,43 @@
+---
+title: "AL Guidelines"
+linkTitle: Docs
+weight: 20
+description: >
+ Patterns and Best Practices for AL Development
+---
+
+## Business Central Design Patterns & Best Practices
+
+This site is meant to house some of the community's knowledge about Microsoft Dynamics 365 Business Central AL Development best practices, particularly around hosting Design Patterns.
+
+### What are Design Patterns?
+
+To quote the original NAV Team blog post about the NAV Design Patterns:
+
+> A team of people interested in NAV application design has come together to work on naming and surfacing design solutions to common NAV business needs. When those solutions are generic enough to be applied in various places of the NAV application, with slight variations on implementation but mainly respecting the same base concepts, we can describe them as NAV design patterns.
+
+A design pattern is a repeatable template of how to solve a common development challenge.
+
+### What are Development Best Practices?
+
+(more text coming soon)
+
+### History of "NAV Design Patterns"
+
+In 2013, Microsoft NAV Dev Team and prominent members of the community collaborated on a Community and Microsoft collection of commonly needed / used Design Patterns.
+
+For some history on this, waldo's posts [Code is Poetry](https://www.waldo.be/2013/06/14/code-is-poetry/) and [Design is Philosophy](https://www.waldo.be/2013/08/28/design-is-philosophy-2/) are a great read.
+
+
+#### Behind This Project
+
+This project is a Microsoft Business Central Community initiative with support from the Microsoft Business Central team. The founding community members are
+* waldo ([Twitter](https://twitter.com/waldo1001), [Blog](https://www.waldo.be), [GitHub](https://github.com/waldo1001))
+* Arend-Jan Kauffmann ([Twitter](https://twitter.com/ajkauffmann), [Blog](https://www.kauffmann.nl/), [GitHub](https://github.com/ajkauffmann))
+* Henrik Helgesen ([Twitter](https://twitter.com/TheDoubleH), [Blog](https://thedoubleh.dev/), [GitHub](https://github.com/thedoubleh))
+* Jeremy Vyska ([Twitter](https://twitter.com/JeremyVyska),[Blog](https://jeremy.vyska.info/articles), [GitHub](https://github.com/JeremyVyska))
+
+#### Contributing
+
+To find out more about contributing, read up here:
+[Contributing](/docs/contributing/)
diff --git a/content/docs/agentic-coding/CommunityResources/Agents/_index.md b/content/docs/agentic-coding/CommunityResources/Agents/_index.md
new file mode 100644
index 00000000..b463099f
--- /dev/null
+++ b/content/docs/agentic-coding/CommunityResources/Agents/_index.md
@@ -0,0 +1,217 @@
+---
+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
new file mode 100644
index 00000000..828c5674
--- /dev/null
+++ b/content/docs/agentic-coding/CommunityResources/Agents/claude-agent.md
@@ -0,0 +1,757 @@
+---
+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
new file mode 100644
index 00000000..4e5fdfd1
--- /dev/null
+++ b/content/docs/agentic-coding/CommunityResources/Agents/cursor-agent.md
@@ -0,0 +1,675 @@
+---
+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
new file mode 100644
index 00000000..d7f62dce
--- /dev/null
+++ b/content/docs/agentic-coding/CommunityResources/Agents/github-copilot-agent.md
@@ -0,0 +1,552 @@
+---
+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
new file mode 100644
index 00000000..378bbd5f
--- /dev/null
+++ b/content/docs/agentic-coding/CommunityResources/Agents/github-copilot-chat-agent.md
@@ -0,0 +1,627 @@
+---
+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
new file mode 100644
index 00000000..8bddcda3
--- /dev/null
+++ b/content/docs/agentic-coding/CommunityResources/Tools/_index.md
@@ -0,0 +1,151 @@
+---
+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
new file mode 100644
index 00000000..7f467316
--- /dev/null
+++ b/content/docs/agentic-coding/CommunityResources/Tools/al-dependency-mcp-server.md
@@ -0,0 +1,45 @@
+---
+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
new file mode 100644
index 00000000..7312b533
--- /dev/null
+++ b/content/docs/agentic-coding/CommunityResources/Tools/al-development-collection.md
@@ -0,0 +1,49 @@
+---
+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
new file mode 100644
index 00000000..a520f6ed
--- /dev/null
+++ b/content/docs/agentic-coding/CommunityResources/Tools/al-objid-mcp-server.md
@@ -0,0 +1,44 @@
+---
+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
new file mode 100644
index 00000000..1ab55260
--- /dev/null
+++ b/content/docs/agentic-coding/CommunityResources/Tools/azure-devops-mcp.md
@@ -0,0 +1,46 @@
+---
+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
new file mode 100644
index 00000000..9597cc61
--- /dev/null
+++ b/content/docs/agentic-coding/CommunityResources/Tools/bc-code-intelligence-mcp.md
@@ -0,0 +1,43 @@
+---
+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
new file mode 100644
index 00000000..dd805816
--- /dev/null
+++ b/content/docs/agentic-coding/CommunityResources/Tools/clockify-mcp.md
@@ -0,0 +1,44 @@
+---
+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
new file mode 100644
index 00000000..a80c09c6
--- /dev/null
+++ b/content/docs/agentic-coding/CommunityResources/Tools/nab-al-tools-mcp.md
@@ -0,0 +1,79 @@
+---
+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
new file mode 100644
index 00000000..2ead1ed0
--- /dev/null
+++ b/content/docs/agentic-coding/CommunityResources/Tools/serena-mcp.md
@@ -0,0 +1,46 @@
+---
+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
new file mode 100644
index 00000000..56ae5d8c
--- /dev/null
+++ b/content/docs/agentic-coding/CommunityResources/Tools/waldo-bctelemetrybuddy.md
@@ -0,0 +1,29 @@
+---
+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
new file mode 100644
index 00000000..c98ceb4c
--- /dev/null
+++ b/content/docs/agentic-coding/CommunityResources/_index.md
@@ -0,0 +1,131 @@
+---
+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
new file mode 100644
index 00000000..44ec92a6
--- /dev/null
+++ b/content/docs/agentic-coding/CommunityResources/articles.md
@@ -0,0 +1,112 @@
+---
+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
new file mode 100644
index 00000000..41e86422
--- /dev/null
+++ b/content/docs/agentic-coding/CommunityResources/tools.md
@@ -0,0 +1,569 @@
+---
+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
new file mode 100644
index 00000000..dc114325
--- /dev/null
+++ b/content/docs/agentic-coding/CommunityResources/videos.md
@@ -0,0 +1,66 @@
+---
+---
+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
new file mode 100644
index 00000000..432eea27
--- /dev/null
+++ b/content/docs/agentic-coding/GettingMore/_index.md
@@ -0,0 +1,47 @@
+---
+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
new file mode 100644
index 00000000..0010d847
--- /dev/null
+++ b/content/docs/agentic-coding/GettingMore/code-review.md
@@ -0,0 +1,368 @@
+---
+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
new file mode 100644
index 00000000..d853917b
--- /dev/null
+++ b/content/docs/agentic-coding/GettingMore/documentation.md
@@ -0,0 +1,661 @@
+---
+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
new file mode 100644
index 00000000..6aaac1c6
--- /dev/null
+++ b/content/docs/agentic-coding/GettingMore/refactoring.md
@@ -0,0 +1,1041 @@
+---
+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
new file mode 100644
index 00000000..c4d0c696
--- /dev/null
+++ b/content/docs/agentic-coding/GettingMore/telemetry.md
@@ -0,0 +1,599 @@
+---
+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
new file mode 100644
index 00000000..3c9cfae7
--- /dev/null
+++ b/content/docs/agentic-coding/GettingMore/testing.md
@@ -0,0 +1,902 @@
+---
+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
new file mode 100644
index 00000000..2d092a76
--- /dev/null
+++ b/content/docs/agentic-coding/GettingStarted/_index.md
@@ -0,0 +1,20 @@
+---
+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
new file mode 100644
index 00000000..28698b30
--- /dev/null
+++ b/content/docs/agentic-coding/GettingStarted/best-practices.md
@@ -0,0 +1,415 @@
+---
+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
new file mode 100644
index 00000000..d91ba1ab
--- /dev/null
+++ b/content/docs/agentic-coding/GettingStarted/effective-prompting.md
@@ -0,0 +1,358 @@
+---
+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
new file mode 100644
index 00000000..ebd356af
--- /dev/null
+++ b/content/docs/agentic-coding/GettingStarted/glossary.md
@@ -0,0 +1,213 @@
+---
+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
new file mode 100644
index 00000000..3d78b238
--- /dev/null
+++ b/content/docs/agentic-coding/GettingStarted/limitations.md
@@ -0,0 +1,473 @@
+---
+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
new file mode 100644
index 00000000..cd5b007f
--- /dev/null
+++ b/content/docs/agentic-coding/GettingStarted/setup.md
@@ -0,0 +1,226 @@
+---
+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
new file mode 100644
index 00000000..aecf4e19
--- /dev/null
+++ b/content/docs/agentic-coding/GettingStarted/what-is-agentic-coding.md
@@ -0,0 +1,128 @@
+---
+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
new file mode 100644
index 00000000..44002af2
--- /dev/null
+++ b/content/docs/agentic-coding/_index.md
@@ -0,0 +1,65 @@
+---
+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
new file mode 100644
index 00000000..48ce0477
--- /dev/null
+++ b/content/docs/agentic-coding/vibe-coding-rules/README.md
@@ -0,0 +1,102 @@
+# Vibe Coding Rules - Contribution Guide
+
+This directory contains AI-optimized coding rules for AL development. Each rule set is organized into markdown files that can be easily consumed by AI coding assistants.
+
+## Directory Structure
+
+```
+vibe-coding/
+├── _index.md # Main landing page
+├── README.md # This file - contribution guide
+├── al-guidelines-rules.md # Complete rules file with references to all categories
+├── al-code-style.md # Code style, formatting, and documentation rules
+├── al-naming-conventions.md # File naming, object naming, and variable naming
+├── al-performance.md # Performance optimization and query guidelines
+├── al-error-handling.md # Error handling patterns and troubleshooting
+├── al-events.md # Event-driven development and extensibility
+├── al-testing.md # AL-Go workspace structure and testing guidelines
+```
+
+## How to Add New Rules
+
+### 1. Choose the Right Category
+Select the appropriate category for your rules, or create a new category if needed.
+
+### 2. Follow the Standard Format
+Each rule file should follow this structure:
+
+```markdown
+---
+title: "AL [Category Name] Rules"
+description: >
+ Brief description of the rule category for AL development
+globs: ["*.al", "*.json"] # File types this rule applies to
+alwaysApply: true|false # Whether to always apply these rules
+---
+
+# AL [Category Name] Rules
+
+Brief introduction to the category and its importance in AL development.
+
+## Rule 1: [Descriptive Title]
+
+### Intent
+What this rule aims to achieve, including implementation details and guidance for AI assistants.
+
+### Examples
+
+```al
+// Good example
+[code example]
+```
+
+You can also add a bad example to show what to avoid, but this is optional.
+```al
+// Bad example (avoid)
+[code example]
+```
+
+## Rule 2: [Next Rule]
+[Continue with same format...]
+```
+
+### 3. Update the Index
+After adding new rule files:
+- Update `al-guidelines-rules.md` to include `@your-new-file.md` reference
+- Update `_index.md` to include links to new categories
+- Ensure all cross-references are updated
+
+### 4. Test Your Rules
+Before submitting, test your rules with AI assistants to ensure they:
+- Are clearly understood by AI agents
+- Produce the expected code patterns
+- Don't conflict with existing rules
+
+## Contribution Workflow
+
+1. **Fork** the alguidelines repository
+2. **Create** your rule files in the appropriate category
+3. **Test** the rules with your preferred AI assistant
+4. **Submit** a pull request with:
+ - Clear description of the rules added
+ - Examples of how the rules improve code quality
+ - Any testing results with AI assistants (optional)
+
+## Best Practices for Rule Writing
+
+### Make Rules AI-Friendly
+- Use clear, unambiguous language specific to AL development
+- Provide specific AL code examples with proper syntax
+- Include both positive and negative examples, when applicable
+- Structure content consistently
+
+## Questions?
+
+If you have questions about contributing rules, please:
+- Open a discussion in the GitHub repository
+- Join the Business Central community Discord
+- Contact the initiative maintainers
+
+---
+
+*This README is part of the Vibe Coding for AL initiative - enhancing AL development through AI-optimized guidelines.*
\ No newline at end of file
diff --git a/content/docs/agentic-coding/vibe-coding-rules/_index.md b/content/docs/agentic-coding/vibe-coding-rules/_index.md
new file mode 100644
index 00000000..4e4060e4
--- /dev/null
+++ b/content/docs/agentic-coding/vibe-coding-rules/_index.md
@@ -0,0 +1,101 @@
+---
+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
new file mode 100644
index 00000000..a8f429ef
--- /dev/null
+++ b/content/docs/agentic-coding/vibe-coding-rules/al-code-style.md
@@ -0,0 +1,167 @@
+---
+title: "AL Code Style & Formatting Rules"
+description: >
+ AL Code structure, formatting, and folder organization guidelines for AL development
+globs: ["*.al"]
+alwaysApply: true
+---
+
+# AL Code Style & Formatting Rules
+
+These rules ensure consistent code structure and organization across AL projects, making code more maintainable and AI-assistant friendly.
+
+## Style guidelines for AL code
+ - Always use PascalCase for variable and function names.
+ - Use PascalCase for object names (e.g., tables, pages, reports).
+ - Maintain a consistent indentation style (2 spaces preferred).
+
+## Commonly used methods and patterns
+ - Temporary tables for performance optimization
+ - Use of events for extensibility
+
+## Rule 1: Consistent Indentation and Formatting
+
+### Intent
+Maintain consistent code formatting to improve readability and enable better AI understanding of code structure. Use indentation with two spaces consistently throughout your project and maintain consistent formatting within functions and procedures.
+
+### Examples
+
+```al
+// Good example
+procedure CalculateDiscount(Amount: Decimal; DiscountPct: Decimal): Decimal
+begin
+ if DiscountPct > 0 then
+ exit(Amount * DiscountPct / 100);
+
+ exit(0);
+end;
+```
+
+## Rule 2: Feature-Based Folder Organization
+
+### Intent
+Organize code by business features rather than object types to improve maintainability and logical grouping. Use feature-based organization with `src/feature/subfeature/` structure and place shared components in `Common` or `Shared` folders.
+
+### Examples
+
+```
+// Good example - Feature-based organization
+src/
+├── NoSeries/
+│ ├── NoSeries.Table.al
+│ ├── NoSeries.Page.al
+│ └── NoSeriesSetup.Codeunit.al
+├── Sales/
+│ ├── Invoice/
+│ │ ├── SalesInvoice.Page.al
+│ │ └── SalesInvoicePosting.Codeunit.al
+│ └── Order/
+│ └── SalesOrder.Page.al
+└── Common/
+ ├── Helpers/
+ │ └── DateHelper.Codeunit.al
+ └── Interfaces/
+ └── IPostable.Interface.al
+```
+
+```
+// Bad example (avoid object-type segregation)
+src/
+├── Tables/
+│ ├── NoSeries.Table.al
+│ └── SalesHeader.Table.al
+├── Pages/
+│ ├── NoSeries.Page.al
+│ └── SalesInvoice.Page.al
+└── Codeunits/
+ ├── NoSeriesSetup.Codeunit.al
+ └── SalesInvoicePosting.Codeunit.al
+```
+
+## Rule 3: Code Documentation and Comments
+
+### Intent
+Provide clear documentation for global functions using XML documentation comments. Code should be self-documenting through clear naming, but global functions in codeunits require proper documentation for API clarity.
+
+### Examples
+
+```al
+// Good example - XML documentation for global functions
+codeunit 50100 "Base64 Convert"
+{
+ ///
+ /// Converts the value of the input string to its equivalent string representation that is encoded with base-64 digits.
+ ///
+ /// The string to convert.
+ /// The string representation, in base-64, of the input string.
+ procedure ToBase64(String: Text): Text
+ begin
+ exit(Base64ConvertImpl.ToBase64(String));
+ end;
+
+ ///
+ /// Validates discount percentage against business rules.
+ ///
+ /// The discount percentage to validate.
+ procedure ValidateDiscountPercentage(DiscountPct: Decimal)
+ begin
+ if DiscountPct > 50 then
+ Error('Discount cannot exceed 50% due to company policy');
+
+ if DiscountPct < 0 then
+ Error('Discount percentage cannot be negative');
+ end;
+}
+```
+
+```al
+// Bad example (avoid inline comments for obvious operations)
+procedure ValidateDiscountPercentage(DiscountPct: Decimal)
+begin
+ // Check if discount is greater than 50
+ if DiscountPct > 50 then
+ Error('Discount cannot exceed 50%');
+
+ // Check if discount is less than 0
+ if DiscountPct < 0 then
+ Error('Discount percentage cannot be negative');
+end;
+```
+
+## Rule 4: Modular and Reusable Code Structure
+
+### Intent
+Keep code modular and reusable to enhance maintainability and reduce duplication. Write small, focused procedures that do one thing well and use interfaces and patterns where appropriate.
+
+### Examples
+
+```al
+// Good example - Modular approach
+procedure PostDocument(var DocumentHeader: Record "Sales Header")
+begin
+ ValidateDocument(DocumentHeader);
+ CalculateTotals(DocumentHeader);
+ CreateLedgerEntries(DocumentHeader);
+ UpdateStatus(DocumentHeader);
+end;
+
+local procedure ValidateDocument(var DocumentHeader: Record "Sales Header")
+begin
+ if DocumentHeader."No." = '' then
+ Error('Document number cannot be empty');
+end;
+
+local procedure CalculateTotals(var DocumentHeader: Record "Sales Header")
+begin
+ DocumentHeader.CalcFields(Amount);
+end;
+```
+
+```al
+// Bad example (avoid monolithic procedures)
+procedure PostDocument(var DocumentHeader: Record "Sales Header")
+begin
+ // All validation, calculation, and posting logic in one procedure
+ // ... 200+ lines of mixed concerns
+end;
+```
\ No newline at end of file
diff --git a/content/docs/agentic-coding/vibe-coding-rules/al-error-handling.md b/content/docs/agentic-coding/vibe-coding-rules/al-error-handling.md
new file mode 100644
index 00000000..dcc84411
--- /dev/null
+++ b/content/docs/agentic-coding/vibe-coding-rules/al-error-handling.md
@@ -0,0 +1,167 @@
+---
+title: "AL Error Handling & Troubleshooting Rules"
+description: >
+ AL Error handling patterns, debugging techniques, and troubleshooting guidelines for AL development
+globs: ["*.al"]
+alwaysApply: false
+---
+
+# AL Error Handling & Troubleshooting Rules
+
+Robust error handling and effective troubleshooting practices are essential for maintaining reliable Business Central applications.
+
+## Rule 1: Use TryFunctions for Error Handling
+
+### Intent
+Implement proper error handling using TryFunctions to manage exceptions gracefully and provide meaningful user feedback. Use TryFunctions for error handling in scenarios where rollback is required, implement proper exception handling for external service calls, provide meaningful error messages to users, and log errors appropriately for debugging purposes. When generating code that might fail (external calls, data operations, calculations), implement appropriate TryFunction error handling and provide clear error messages.
+
+### Examples
+
+```al
+// Good example - TryFunction with proper error handling and error labels
+procedure ProcessPayment(Amount: Decimal): Boolean
+var
+ PaymentService: Codeunit "Payment Service";
+ ErrorText: Text;
+ PaymentProcessingFailedLbl: Label 'Payment processing failed: %1', Comment = '%1 = Error message';
+ PaymentProcessingFailedTelemetryLbl: Label 'Payment processing failed', Locked = true;
+begin
+ if not TryProcessPaymentInternal(Amount) then begin
+ ErrorText := GetLastErrorText();
+ LogError(PaymentProcessingFailedTelemetryLbl, ErrorText);
+ Message(PaymentProcessingFailedLbl, ErrorText);
+ exit(false);
+ end;
+
+ exit(true);
+end;
+
+[TryFunction]
+local procedure TryProcessPaymentInternal(Amount: Decimal)
+var
+ PaymentService: Codeunit "Payment Service";
+begin
+ PaymentService.ProcessPayment(Amount);
+end;
+```
+
+```al
+// Bad example (avoid hardcoded error messages and unhandled errors)
+procedure ProcessPayment(Amount: Decimal)
+var
+ PaymentService: Codeunit "Payment Service";
+begin
+ // No error handling - will cause unhandled exceptions
+ // Also avoid hardcoded messages like this:
+ // Message('Payment could not be processed');
+ PaymentService.ProcessPayment(Amount);
+end;
+```
+
+## Rule 2: Use Error Labels for All Messages
+
+### Intent
+All error messages, warnings, and user messages must use label variables instead of hardcoded text. This ensures proper localization support and maintainability. Define labels with appropriate comments for translators and use Locked = true for technical messages that should not be translated.
+
+### Examples
+
+```al
+// Good example - Using error labels
+procedure ValidateBusinessLogic(SalesHeader: Record "Sales Header")
+var
+ Customer: Record Customer;
+ CustomerNotFoundErr: Label 'Customer %1 does not exist for sales document %2.', Comment = '%1 = Customer No., %2 = Sales Header No.';
+ CustomerBlockedErr: Label 'Customer %1 is blocked (%2). Cannot process sales document %3.', Comment = '%1 = Customer No., %2 = Blocked reason, %3 = Sales Header No.';
+ EmptyHeaderNoErr: Label 'Sales header number cannot be empty.';
+begin
+ if SalesHeader."No." = '' then
+ Error(EmptyHeaderNoErr);
+
+ if not Customer.Get(SalesHeader."Sell-to Customer No.") then
+ Error(CustomerNotFoundErr, SalesHeader."Sell-to Customer No.", SalesHeader."No.");
+
+ if Customer.Blocked <> Customer.Blocked::" " then
+ Error(CustomerBlockedErr, Customer."No.", Customer.Blocked, SalesHeader."No.");
+end;
+```
+
+```al
+// Bad example (avoid hardcoded error messages)
+procedure ValidateBusinessLogic(SalesHeader: Record "Sales Header")
+var
+ Customer: Record Customer;
+begin
+ if not Customer.Get(SalesHeader."Sell-to Customer No.") then
+ Error('Customer not found'); // Hardcoded - avoid this
+
+ if Customer.Blocked <> Customer.Blocked::" " then
+ Error('Customer blocked'); // Hardcoded - avoid this
+end;
+```
+
+## Rule 3: Code Compilation and Correctness Priority
+
+### Intent
+Generated AL code should prioritize correctness over immediate compilation. Code can fail to compile if AI suggests base functions or events that don't exist, or if variables in event subscriptions are incorrect. When this happens, leave space for manual fixes rather than changing the intended behavior. If you're confident the logic should work as suggested but there are naming or parameter issues, leave it for user correction rather than altering the business logic.
+
+### Examples
+
+```al
+// Good example - Correct logic even if function names need verification
+procedure HandleCustomerModification(var Customer: Record Customer)
+var
+ CustomerValidation: Codeunit "Customer Validation"; // May need verification
+begin
+ // Correct business logic - even if codeunit name needs adjustment
+ if not CustomerValidation.ValidateCustomerData(Customer) then
+ Error(ValidationFailedErr);
+
+ Customer.Modify(true);
+end;
+```
+
+```al
+// Good example - Event subscription with correct intent
+[EventSubscriber(ObjectType::Table, Database::Customer, OnAfterModifyEvent, '', false, false)]
+local procedure OnAfterCustomerModify(var Rec: Record Customer; var xRec: Record Customer; RunTrigger: Boolean)
+var
+ CustomerChangeLog: Codeunit "Customer Change Log"; // Function may need verification
+begin
+ // Correct logic - even if codeunit or method names need adjustment
+ CustomerChangeLog.LogCustomerChange(Rec, xRec);
+end;
+```
+
+## Rule 4: Custom Telemetry Implementation
+
+### Intent
+Add custom telemetry for tracking business-critical operations, but only when explicitly requested by the user. Use Session.LogMessage for custom telemetry with appropriate verbosity levels and data classification. Include relevant custom dimensions for context and use proper telemetry scope for extension publishers.
+
+### Examples
+
+```al
+// Good example - Custom telemetry (only when user explicitly requests it)
+procedure PostSalesDocument(var SalesHeader: Record "Sales Header")
+var
+ TelemetryCustomDimensions: Dictionary of [Text, Text];
+ SalesDocPostedMsg: Label 'Sales document posted successfully', Locked = true;
+ SalesDocPostFailedMsg: Label 'Sales document posting failed', Locked = true;
+begin
+ // Add context for telemetry
+ TelemetryCustomDimensions.Add('DocumentType', Format(SalesHeader."Document Type"));
+ TelemetryCustomDimensions.Add('CustomerNo', SalesHeader."Sell-to Customer No.");
+
+ if TryPostSalesDocument(SalesHeader) then begin
+ // Log successful operation
+ Session.LogMessage('SAL001', SalesDocPostedMsg,
+ Verbosity::Normal, DataClassification::SystemMetadata,
+ TelemetryScope::ExtensionPublisher, TelemetryCustomDimensions);
+ end else begin
+ // Log failed operation with error details
+ TelemetryCustomDimensions.Add('ErrorText', GetLastErrorText());
+ Session.LogMessage('SAL002', SalesDocPostFailedMsg,
+ Verbosity::Error, DataClassification::SystemMetadata,
+ TelemetryScope::ExtensionPublisher, TelemetryCustomDimensions);
+ end;
+end;
+```
\ No newline at end of file
diff --git a/content/docs/agentic-coding/vibe-coding-rules/al-events.md b/content/docs/agentic-coding/vibe-coding-rules/al-events.md
new file mode 100644
index 00000000..8eab527c
--- /dev/null
+++ b/content/docs/agentic-coding/vibe-coding-rules/al-events.md
@@ -0,0 +1,120 @@
+---
+title: "Event-Driven Development Rules"
+description: >
+ Guidelines for implementing event-driven patterns and extensibility in AL development
+globs: ["*.al"]
+alwaysApply: false
+---
+
+# Event-Driven Development Rules
+
+Event-driven development is fundamental to creating extensible and maintainable Business Central applications that follow the platform's architecture principles.
+
+## Rule 1: Use Events for Extensibility
+
+### Intent
+Implement proper event patterns to enable extensibility without modifying base application code. Subscribe to relevant Business Central events (OnBeforeInsert, OnAfterModify, etc.), create integration events in your code for future extensibility, use extension objects or events for all changes to standard application objects. When implementing business logic, prioritize event subscribers and suggest appropriate event subscription patterns and integration event creation.
+
+### Examples
+
+```al
+// Good example - Event subscriber implementation with Handler suffix
+codeunit 50100 "Sales Document Events Handler"
+{
+ [EventSubscriber(ObjectType::Table, Database::"Sales Header", OnBeforeInsert, '', false, false)]
+ local procedure OnBeforeInsertSalesHeader(var SalesHeader: Record "Sales Header"; RunTrigger: Boolean)
+ begin
+ // Custom validation logic
+ ValidateCustomFields(SalesHeader);
+ end;
+}
+```
+
+## Rule 2: Add Integration Events for Extensibility
+
+### Intent
+Use integration events to provide better extensibility points and clearer API contracts for other developers. Create integration events at logical business process points, document integration event parameters and expected behavior, provide meaningful event names that describe the business context, and implement handled patterns to allow subscribers to control execution flow. When designing extensible code, suggest integration events at appropriate business logic points with clear documentation and meaningful names.
+
+### Examples
+
+```al
+// Good example - Integration events with handled pattern
+codeunit 50101 "Customer Management"
+{
+ procedure CreateCustomer(var Customer: Record Customer): Boolean
+ var
+ IsHandled: Boolean;
+ begin
+ OnBeforeCreateCustomer(Customer, IsHandled);
+ if IsHandled then
+ exit(true);
+
+ if not Customer.Insert(true) then
+ exit(false);
+
+ OnAfterCreateCustomer(Customer);
+ exit(true);
+ end;
+
+ [IntegrationEvent(false, false)]
+ procedure OnBeforeCreateCustomer(var Customer: Record Customer; var IsHandled: Boolean)
+ begin
+ // Allow extensions to modify customer data before creation
+ // Set IsHandled to true to skip default processing
+ end;
+
+ [IntegrationEvent(false, false)]
+ procedure OnAfterCreateCustomer(var Customer: Record Customer)
+ begin
+ // Allow extensions to perform additional actions after customer creation
+ end;
+}
+```
+
+```al
+// Extension subscribing to integration events with Handler suffix
+codeunit 50102 "Customer Validation Handler"
+{
+ [EventSubscriber(ObjectType::Codeunit, Codeunit::"Customer Management", OnBeforeCreateCustomer, '', false, false)]
+ local procedure ValidateCustomerOnBeforeCreate(var Customer: Record Customer; var IsHandled: Boolean)
+ begin
+ // Custom validation logic
+ ValidateCustomerCreditLimit(Customer);
+
+ // Optionally handle the event to skip default processing
+ if ShouldSkipDefaultProcessing(Customer) then
+ IsHandled := true;
+ end;
+}
+```
+
+## Rule 3: Event Parameter Best Practices
+
+### Intent
+Design event parameters that provide sufficient context while maintaining performance and usability. Pass record variables by reference when possible, include relevant context parameters, use meaningful parameter names, consider performance implications of parameter passing, and implement handled patterns where appropriate. When creating events, ensure parameters provide sufficient context for subscribers while maintaining good performance and use descriptive parameter names that clearly indicate their purpose.
+
+### Examples
+
+```al
+// Good example - Well-designed event parameters with handled pattern
+codeunit 50103 "Document Posting Events"
+{
+ [IntegrationEvent(false, false)]
+ procedure OnBeforePostDocument(var DocumentHeader: Record "Sales Header"; var DocumentLines: Record "Sales Line"; PostingDate: Date; var IsHandled: Boolean)
+ begin
+ // Comprehensive context for document posting
+ // - Document header and lines for full context
+ // - Posting date for temporal context
+ // - IsHandled flag for control flow
+ end;
+
+ [IntegrationEvent(false, false)]
+ procedure OnAfterPostDocument(DocumentHeader: Record "Sales Header"; PostedDocumentNo: Code[20]; PostingResult: Boolean)
+ begin
+ // Results context after posting
+ // - Original document for reference
+ // - Posted document number for tracking
+ // - Success/failure indication
+ end;
+}
+```
\ No newline at end of file
diff --git a/content/docs/agentic-coding/vibe-coding-rules/al-guidelines-rules.md b/content/docs/agentic-coding/vibe-coding-rules/al-guidelines-rules.md
new file mode 100644
index 00000000..f0caf625
--- /dev/null
+++ b/content/docs/agentic-coding/vibe-coding-rules/al-guidelines-rules.md
@@ -0,0 +1,58 @@
+---
+description: AL Guidelines - Comprehensive AI-optimized coding rules for Microsoft Dynamics 365 Business Central development
+globs: ["*.al", "*.json", "app.json", "launch.json"]
+alwaysApply: true
+---
+
+# AL Guidelines - Vibe Coding Rules
+
+You are an AI assistant designed to aid in AL development, particularly for Microsoft Dynamics 365 Business Central. Your role is to assist developers in writing efficient, maintainable code following established patterns and best practices.
+
+## Core Principles
+
+- Follow event-driven programming model; never modify standard application objects
+- Use clear, meaningful names and maintain consistent code structure
+- Prioritize performance optimization and proper error handling
+- Focus on main application implementation by default
+- Only generate test code when explicitly requested
+- Maintain proper AL-Go workspace structure separation
+
+## Rule Categories
+
+The following rule sets provide comprehensive guidance for AL development:
+
+@al-code-style.md
+
+@al-naming-conventions.md
+
+@al-performance.md
+
+@al-error-handling.md
+
+@al-events.md
+
+@al-testing.md
+
+## Key Guidelines Summary
+
+- **File Naming**: Use `..al` pattern consistently
+- **Code Style**: Use two space indentation and PascalCase for variables, PascalCase for objects
+- **Folder Structure**: Organize by feature (`src/feature/subfeature/`) not by object type
+- **Performance**: Filter data early, use temporary tables, avoid unnecessary loops
+- **Events**: Prefer integration events over direct modifications for extensibility
+- **Testing**: Separate App and Test projects, generate tests only when requested
+- **Error Handling**: Use TryFunctions, provide meaningful error messages, implement telemetry
+
+## AL-Go Workspace Structure
+
+When working in AL-Go environments:
+- **App project**: Contains all application logic (tables, pages, codeunits, reports)
+- **Test project**: Contains all test code and references App project as dependency
+- **Never mix**: Application code stays in App, test code stays in Test project
+
+## AI Response Behavior
+
+- Provide concise, actionable advice with specific AL method references
+- Always explain the reasoning behind recommendations
+- Reference Business Central architecture patterns and established best practices
+- Focus on practical implementation guidance that can be immediately applied
\ No newline at end of file
diff --git a/content/docs/agentic-coding/vibe-coding-rules/al-naming-conventions.md b/content/docs/agentic-coding/vibe-coding-rules/al-naming-conventions.md
new file mode 100644
index 00000000..f5bed480
--- /dev/null
+++ b/content/docs/agentic-coding/vibe-coding-rules/al-naming-conventions.md
@@ -0,0 +1,133 @@
+---
+title: "Naming Conventions Rules"
+description: >
+ Comprehensive naming conventions for AL files, objects, variables, and functions
+globs: ["*.al"]
+alwaysApply: true
+---
+
+# Naming Conventions Rules
+
+Consistent naming conventions improve code readability, maintainability, and help AI assistants understand code structure and intent.
+
+## Rule 1: Object Naming Conventions
+
+### Intent
+Use consistent naming patterns for all AL objects to improve discoverability and maintain professional standards. Use PascalCase for object names (tables, pages, reports, codeunits) and meaningful, descriptive names that clearly indicate the object's purpose. Object names must not exceed 30 characters total, with a maximum of 26 characters for the name itself to reserve space for prefixes/affixes (3 characters + 1 space).
+
+### Examples
+
+```al
+// Good examples (within 26 character limit)
+table 50100 "Customer Ledger Entry" // 20 chars
+page 50101 "Sales Invoice" // 13 chars
+codeunit 50102 "Sales Invoice Posting" // 21 chars
+report 50103 "Customer Statement" // 18 chars
+```
+
+```al
+// Bad examples (avoid abbreviations, unclear names, or length violations)
+table 50100 "CustLE" // Too abbreviated
+page 50101 "SalesInv" // Too abbreviated
+table 50104 "Very Long Customer Ledger Entry" // 32 chars - exceeds limit
+codeunit 50102 "SIPoster" // Unclear abbreviation
+```
+
+## Rule 2: File Naming Conventions
+
+### Intent
+Establish consistent file naming patterns that clearly identify object types and facilitate organized development. Use pattern `..al` and maintain consistency across all file names. Ensure file names are descriptive and match the AL object name within the files.
+
+### Examples
+
+```al
+// Good examples
+NoSeries.Page.al
+NoSeries.Table.al
+NoSeriesErrorsImpl.Codeunit.al
+NoSeriesSetup.Codeunit.al
+CustomerCard.Page.al
+SalesHeader.Table.al
+PostSalesInvoice.Codeunit.al
+ItemLedgerEntry.Report.al
+InventorySetup.PageExt.al
+SalesHeader.TableExt.al
+
+// For implementations and interfaces
+INoSeries.Interface.al
+NoSeriesImpl.Codeunit.al
+
+// For test files
+NoSeriesTests.Codeunit.al
+SalesPostingTests.Codeunit.al
+```
+
+## Rule 3: Variable and Function Naming
+
+### Intent
+Use consistent naming conventions for variables and functions to improve code readability. Use PascalCase for variable and function names, descriptive names that clearly indicate purpose, and avoid abbreviations unless they are well-known business terms. Use consistent parameter naming in procedures.
+
+### Examples
+
+```al
+// Good examples - Variables
+var
+ CustomerLedgerEntry: Record "Cust. Ledger Entry";
+ TotalAmount: Decimal;
+ DiscountPercentage: Decimal;
+ IsValidTransaction: Boolean;
+```
+
+```al
+// Good examples - Functions
+procedure CalculateCustomerBalance(CustomerNo: Code[20]): Decimal
+procedure ValidateSalesDocument(var SalesHeader: Record "Sales Header")
+procedure UpdateInventoryQuantity(ItemNo: Code[20]; Quantity: Decimal)
+```
+
+## Rule 4: Parameter Naming in Event Subscribers
+
+### Intent
+Use meaningful parameter names in event subscribers to improve code clarity and maintainability. Use descriptive parameter names that clearly indicate their purpose, follow Business Central conventions for common parameter types, and maintain consistency across similar event subscribers. Avoid unclear generic names like "Rec" - use specific descriptive names.
+
+### Examples
+
+```al
+// Good example - Descriptive parameter names
+[EventSubscriber(ObjectType::Table, Database::"Sales Header", OnBeforeInsert, '', false, false)]
+local procedure AddDefaultValuesOnBeforeInsertSalesHeader(var SalesHeader: Record "Sales Header"; RunTrigger: Boolean)
+begin
+ // Event handling logic
+end;
+
+[EventSubscriber(ObjectType::Table, Database::Customer, OnBeforeModify, '', false, false)]
+local procedure CheckBalanceOnBeforeModifyCustomer(var Customer: Record Customer; var xCustomer: Record Customer)
+begin
+ // Event handling logic
+end;
+```
+
+## Rule 5: Interface and Implementation Naming
+
+### Intent
+Clearly distinguish between interfaces and their implementations using consistent naming patterns. Prefix interfaces with "I" (e.g., `INoSeries`), use "Impl" suffix for implementation codeunits, and keep interface and implementation names closely related. Ensure names stay within the 26-character limit.
+
+### Examples
+
+```al
+// Good examples (within character limits)
+// Interface file: ICustomerService.Interface.al
+interface ICustomerService
+{
+ procedure GetCustomerBalance(CustomerNo: Code[20]): Decimal;
+}
+
+// Implementation file: CustomerServiceImpl.Codeunit.al
+codeunit 50100 "Customer Service Impl" implements ICustomerService
+{
+ procedure GetCustomerBalance(CustomerNo: Code[20]): Decimal
+ begin
+ // Implementation logic
+ end;
+}
+```
\ No newline at end of file
diff --git a/content/docs/agentic-coding/vibe-coding-rules/al-performance.md b/content/docs/agentic-coding/vibe-coding-rules/al-performance.md
new file mode 100644
index 00000000..9e4aa7aa
--- /dev/null
+++ b/content/docs/agentic-coding/vibe-coding-rules/al-performance.md
@@ -0,0 +1,225 @@
+---
+title: "AL Performance Optimization Rules"
+description: >
+ Performance optimization guidelines and best practices for AL development
+globs: ["*.al"]
+alwaysApply: true
+---
+
+# AL Performance Optimization Rules
+
+These rules focus on writing performant AL code that scales well and provides optimal user experience in Business Central environments.
+
+## AL Performance Guidelines Summary
+- Always analyze performance impact when adding new features
+- Optimize queries by filtering data as early as possible
+- Avoid unnecessary loops; use set-based operations when possible
+- Use SetLoadFields to minimize data retrieval
+- Use temporary tables, dictionaries, or lists for temporary data storage
+
+## Rule 1: Early Data Filtering and Query Optimization
+
+### Intent
+Optimize queries by filtering data as early as possible to reduce data transfer and processing overhead. Apply filters before processing records, use appropriate table keys and sorting, minimize the amount of data retrieved from the database, and use SetRange and SetFilter methods effectively.
+
+### Examples
+
+```al
+// Good example - Early filtering
+procedure GetNumberOfCustomersByCity(CityFilter: Text): Integer
+var
+ Customer: Record Customer;
+begin
+ Customer.SetRange(City, CityFilter);
+ Customer.SetRange(Blocked, Customer.Blocked::" ");
+ if Customer.FindSet() then
+ repeat
+ // Process only filtered customers
+ until Customer.Next() = 0;
+
+ exit(Customer.Count);
+end;
+```
+
+```al
+// Bad example (avoid processing all records)
+procedure GetNumberOfCustomersByCity(CityFilter: Text): Integer
+var
+ Customer: Record Customer;
+ Count: Integer;
+begin
+ if Customer.FindSet() then
+ repeat
+ // Processing all customers then filtering
+ if (Customer.City = CityFilter) and (Customer.Blocked = Customer.Blocked::" ") then
+ Count += 1;
+ until Customer.Next() = 0;
+
+ exit(Count);
+end;
+```
+
+## Rule 2: Use SetLoadFields for Optimal Data Retrieval
+
+### Intent
+Use SetLoadFields to minimize data retrieval from the database by loading only the fields you need. Place SetLoadFields before the Get or Find operation, and include only the fields that will be used in your code.
+
+### Examples
+
+```al
+// Good example - SetLoadFields with filtering
+Item.SetRange("Third Party Item Exists", false);
+Item.SetLoadFields("Item Category Code");
+Item.FindFirst();
+```
+
+```al
+// Bad example (avoid SetLoadFields after filtering)
+Item.SetLoadFields("Item Category Code");
+Item.SetRange("Third Party Item Exists", false);
+Item.FindFirst();
+```
+
+## Rule 3: Use Temporary Tables, Dictionaries, and Lists for Performance
+
+### Intent
+Leverage temporary tables, dictionaries, and lists to improve performance in read-heavy scenarios and complex data processing. Use temporary tables for structured record data, dictionaries for key-value pairs, and lists for simple collections that are only temporarily needed.
+
+### Examples
+
+```al
+// Good example - Using temporary tables for structured data
+procedure ProcessSalesData(var TempSalesLine: Record "Sales Line" temporary)
+var
+ SalesLine: Record "Sales Line";
+begin
+ // Load data into temporary table once
+ if SalesLine.FindSet() then
+ repeat
+ TempSalesLine := SalesLine;
+ TempSalesLine.Insert();
+ until SalesLine.Next() = 0;
+
+ // Process temporary data multiple times without database hits
+ ProcessDiscounts(TempSalesLine);
+ CalculateTotals(TempSalesLine);
+ ValidateInventory(TempSalesLine);
+end;
+```
+
+```al
+// Good example - Using dictionaries for key-value temporary data
+procedure CacheCustomerData()
+var
+ Customer: Record Customer;
+ CustomerCache: Dictionary of [Code[20], Text];
+begin
+ if Customer.FindSet() then
+ repeat
+ CustomerCache.Add(Customer."No.", Customer.Name);
+ until Customer.Next() = 0;
+
+ // Use cached data for lookups
+ ProcessOrdersWithCache(CustomerCache);
+end;
+```
+
+```al
+// Good example - Using lists for simple collections
+procedure GetBlockedCustomers(): List of [Code[20]]
+var
+ Customer: Record Customer;
+ BlockedCustomers: List of [Code[20]];
+begin
+ Customer.SetRange(Blocked, Customer.Blocked::All);
+ if Customer.FindSet() then
+ repeat
+ BlockedCustomers.Add(Customer."No.");
+ until Customer.Next() = 0;
+
+ exit(BlockedCustomers);
+end;
+```
+
+## Rule 4: Avoid Unnecessary Loops - Use Set-Based Operations
+
+### Intent
+Minimize looping operations and favor set-based approaches when possible to improve performance. Use built-in aggregation methods (CalcSums, CalcFields), leverage SQL-based operations through AL, avoid nested loops when possible, and use batch operations for multiple record updates.
+
+### Examples
+
+```al
+// Good example - Set-based operation
+procedure GetTotalSalesAmount(CustomerNo: Code[20]): Decimal
+var
+ CustLedgerEntry: Record "Cust. Ledger Entry";
+begin
+ CustLedgerEntry.SetRange("Customer No.", CustomerNo);
+ CustLedgerEntry.CalcSums(Amount);
+ exit(CustLedgerEntry.Amount);
+end;
+```
+
+```al
+// Bad example (avoid manual loops for aggregation)
+procedure GetTotalSalesAmount(CustomerNo: Code[20]): Decimal
+var
+ CustLedgerEntry: Record "Cust. Ledger Entry";
+ TotalAmount: Decimal;
+begin
+ CustLedgerEntry.SetRange("Customer No.", CustomerNo);
+ if CustLedgerEntry.FindSet() then
+ repeat
+ TotalAmount += CustLedgerEntry.Amount;
+ until CustLedgerEntry.Next() = 0;
+
+ exit(TotalAmount);
+end;
+```
+
+## Rule 5: Performance Impact Analysis
+
+### Intent
+Always analyze and consider performance impact when adding new features or modifying existing code. While the AL compiler does not have direct access to performance profilers, you should implement performance-optimal code patterns from the start and consider scalability implications of code changes.
+
+### Examples
+
+```al
+// Good example - Performance-conscious implementation
+procedure UpdatePricesForItems(var Item: Record Item)
+var
+ ItemCount: Integer;
+begin
+ // Check data volume before processing
+ ItemCount := Item.Count();
+
+ if ItemCount > 1000 then begin
+ // Use batch processing for large datasets
+ UpdatePricesInBatches(Item);
+ end else begin
+ // Direct processing for smaller datasets
+ UpdatePricesDirectly(Item);
+ end;
+end;
+```
+
+```al
+// Good example - Batched modifications to minimize database writes
+procedure UpdateCustomerStatistics(CustomerNo: Code[20])
+var
+ Customer: Record Customer;
+ TotalBalance: Decimal;
+ LastPaymentDate: Date;
+begin
+ // Calculate all values first
+ CalculateCustomerTotals(CustomerNo, TotalBalance, LastPaymentDate);
+
+ // Single database write with all changes
+ Customer.SetLoadFields("Balance (LCY)", "Last Payment Date");
+ if Customer.Get(CustomerNo) then begin
+ Customer."Balance (LCY)" := TotalBalance;
+ Customer."Last Payment Date" := LastPaymentDate;
+ Customer.Modify(true);
+ end;
+end;
+```
\ No newline at end of file
diff --git a/content/docs/agentic-coding/vibe-coding-rules/al-testing.md b/content/docs/agentic-coding/vibe-coding-rules/al-testing.md
new file mode 100644
index 00000000..2c09aff0
--- /dev/null
+++ b/content/docs/agentic-coding/vibe-coding-rules/al-testing.md
@@ -0,0 +1,194 @@
+---
+title: "AL Testing & Project Structure Rules"
+description: >
+ AL-Go workspace structure, test generation guidelines, and project organization rules
+globs: ["*.al", "app.json"]
+alwaysApply: false
+---
+
+# AL Testing & Project Structure Rules
+
+These rules ensure proper project organization, test implementation, and workspace structure in AL-Go based development environments.
+
+## Rule 1: AL-Go Workspace Structure Guidelines
+
+### Intent
+Establish clear separation between application code and test code in AL-Go workspace environments.
+
+- App project contains Tables, Pages, Codeunits, Reports, APIs, Enums, etc.
+- Test project contains Test Codeunits, Test Pages, Mock objects, Test data.
+- Each project has its own app.json with appropriate dependencies.
+- Test project references the App project as a dependency.
+- Use App project ONLY for main application implementation, use Test project ONLY for test implementation, never include test files in the main App folder, and never include application logic in the Test folder.
+- When working in AL-Go workspace, always place files in the correct project based on their purpose.
+
+### Examples
+
+```
+// Good example - Proper AL-Go workspace structure
+Repository/
+├── .AL-Go/
+├── .github/
+├── App/
+│ ├── src/
+│ │ ├── Setup/
+│ │ ├── Feature1/
+│ │ ├── Feature2/
+│ │ ├── APIs/
+│ ├── app.json
+│ └── launch.json
+├── Test/
+│ ├── src/
+│ │ ├── SetupTests/
+│ │ ├── Feature1Tests/
+│ │ ├── Feature2Tests/
+│ │ ├── IntegrationTests/
+│ ├── app.json
+│ └── launch.json
+└── al.code-workspace
+```
+
+## Rule 2: Test Generation Guidelines
+
+### Intent
+Control when and how test code is generated to maintain focus on main application implementation.
+
+- DO NOT automatically generate test code unless explicitly requested
+- Focus on main application implementation by default
+- When user asks for implementation create only the main application objects
+- Only generate test files when user specifically requests "Create tests for...", "Generate unit tests...", "Add test coverage...", or "Write tests..."
+- If tests are requested, place them in the Test project following the folder structure where test files should mirror the App project structure but in the Test project
+- Unless the user explicitly requests tests, focus only on main application implementation
+
+## Rule 3: Project Dependencies Configuration
+
+### Intent
+Establish correct dependency relationships between App and Test projects.
+
+- App project app.json should NOT reference Test project
+- Test project app.json MUST reference App project as dependency
+- Test project should include testing frameworks (e.g., "Any", "Library Assert"), and each project maintains its own dependencies.
+- When configuring project dependencies, ensure Test project references App project but never the reverse and include appropriate testing frameworks in Test project.
+
+### Examples
+
+```json
+// Good example - Test project app.json
+{
+ "dependencies": [
+ {
+ "id": "your-app-id",
+ "name": "Your App Name",
+ "publisher": "Your Publisher",
+ "version": "1.0.0.0"
+ },
+ {
+ "id": "dd0be2ea-f733-4d65-bb34-a28f4624fb14",
+ "name": "Library Assert",
+ "publisher": "Microsoft",
+ "version": "20.0.0.0"
+ }
+ ]
+}
+```
+
+## Rule 4: Unit Testing Best Practices
+
+### Intent
+Write comprehensive unit tests that ensure reliability of business logic.
+
+- Write unit tests for all business logic
+- Follow given/when/then structure for test naming
+- Use Assert statements for validating critical conditions
+- Create test data factories for consistent test setup
+- Always try to use standard library codeunits to create data and post documents
+- When creating tests, use descriptive names that follow given/when/then pattern and include comprehensive assertions to validate expected behavior.
+
+### Examples
+
+```al
+// Good example - Well-structured unit test with standard library codeunits
+codeunit 50200 "Customer Management Tests"
+{
+ Subtype = Test;
+
+ var
+ Assert: Codeunit Assert;
+ LibrarySales: Codeunit "Library - Sales";
+ LibraryInventory: Codeunit "Library - Inventory";
+ LibraryRandom: Codeunit "Library - Random";
+ LibraryERM: Codeunit "Library - ERM";
+
+ [Test]
+ procedure GivenValidCustomer_WhenCreatingCustomer_ThenCustomerIsCreated()
+ var
+ Customer: Record Customer;
+ CustomerManagement: Codeunit "Customer Management";
+ CustomerNo: Code[20];
+ begin
+ // Given - Valid customer data using library
+ LibrarySales.CreateCustomer(Customer);
+ Customer."Credit Limit (LCY)" := LibraryRandom.RandDec(10000, 2);
+
+ // When - Creating customer
+ CustomerNo := CustomerManagement.CreateCustomer(Customer);
+
+ // Then - Customer is created successfully
+ Assert.IsTrue(Customer.Get(CustomerNo), 'Customer should be created');
+ Assert.AreEqual(Customer.Name, Customer.Name, 'Customer name should match');
+ end;
+
+ [Test]
+ procedure GivenSalesOrder_WhenPostingOrder_ThenInvoiceIsCreated()
+ var
+ SalesHeader: Record "Sales Header";
+ SalesLine: Record "Sales Line";
+ Item: Record Item;
+ PostedInvoiceNo: Code[20];
+ begin
+ // Given - Sales order with library-created data
+ LibraryInventory.CreateItem(Item);
+ LibrarySales.CreateSalesHeader(SalesHeader, SalesHeader."Document Type"::Order, '');
+ LibrarySales.CreateSalesLine(SalesLine, SalesHeader, SalesLine.Type::Item, Item."No.", LibraryRandom.RandInt(10));
+
+ // When - Posting sales order
+ PostedInvoiceNo := LibrarySales.PostSalesDocument(SalesHeader, true, true);
+
+ // Then - Posted invoice exists
+ Assert.AreNotEqual('', PostedInvoiceNo, 'Posted invoice should be created');
+ end;
+}
+```
+
+## Rule 5: Feature-Based Test Organization
+
+### Intent
+Organize test files to mirror the application structure while maintaining clear separation.
+
+- Test files should mirror App project structure in Test project
+- Use same feature-based organization for tests
+- Place shared test utilities in Common folder
+- Maintain consistent naming patterns
+
+### Examples
+
+```
+// Good example - Mirrored test structure
+App/src/
+├── NoSeries/
+│ ├── NoSeries.Table.al
+│ └── NoSeries.Page.al
+└── Sales/
+ └── Invoice/
+ └── SalesInvoice.Page.al
+
+Test/src/
+├── NoSeries/
+│ └── NoSeriesTests.Codeunit.al
+├── Sales/
+│ └── Invoice/
+│ └── SalesInvoiceTests.Codeunit.al
+└── Common/
+ └── TestHelpers/
+ └── TestDataFactory.Codeunit.al
+```
\ No newline at end of file
diff --git a/content/docs/agentic-coding/vibe-coding-rules/al-upgrade.md b/content/docs/agentic-coding/vibe-coding-rules/al-upgrade.md
new file mode 100644
index 00000000..3113ca8b
--- /dev/null
+++ b/content/docs/agentic-coding/vibe-coding-rules/al-upgrade.md
@@ -0,0 +1,457 @@
+---
+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
new file mode 100644
index 00000000..61275700
--- /dev/null
+++ b/content/docs/patterns/_index.md
@@ -0,0 +1,18 @@
+---
+title: "Design Patterns"
+weight: 2
+description: >
+ AL Code Design Patterns
+---
+
+# Business Central Design Patterns
+
+This section will cover patterns that solve certain design challenges in Business Central.
+
+From wikipedia:
+
+_In Computer science, a Design pattern is an abstract solution to a certain problem. Design patterns are used in object oriented programming. They give a possible solution to a problem of designing software ... They also simplify the language between computer scientists. Ideally, a design pattern should be reusable many times. It is like a brick of a house, it can be used for many different problems. One can also build bridges with bricks, not only houses._
+
+## Discussion
+
+All discussion related to Best Practice are to be found on the Github Repo's Discussion pages, found [here](https://github.com/microsoft/alguidelines/discussions/categories/bc-patterns)
diff --git a/content/docs/patterns/api-delegate-operation/index.md b/content/docs/patterns/api-delegate-operation/index.md
new file mode 100644
index 00000000..5b3f2a92
--- /dev/null
+++ b/content/docs/patterns/api-delegate-operation/index.md
@@ -0,0 +1,73 @@
+---
+title: "Delegate API Operation"
+tags: ["AL","API"]
+categories: ["Pattern"]
+---
+
+_Created by Arend-Jan Kauffmann, Described by Arend-Jan Kauffmann_
+
+## Abstract
+The goal of this pattern is to delegate data operations from the API page to a codeunit. The codeunit can implement its own logic for inserting, modifying or deleting data.
+
+## Context
+API pages implement a specific pattern for insert, modify and delete operations. An API page uses delayed insert behavior, which means that all fields will be validated before the record is inserted or modified.
+This is different from the standard behavior on a UI card page, where a record is first inserted, followed by a modify when field values are updated.
+
+## Problem
+The standard behavior can result in a number of challenges:
+
+* Code behind the OnValidate triggers and its event subscribers may expect that a record already exists when a field is being validated.
+* Usage of temporary buffer tables can become really complex when fully handled from the API page.
+* Applying default data to records before they are created does not work well with delayed inserts.
+
+## Description
+To mitigate these problems, we can delegate the data operation to a codeunit while canceling the data operation inside the API page.
+
+```al
+codeunit 50000 "Item API Operations"
+{
+ internal procedure InsertItem(var Item: Record Item)
+ begin
+ end;
+
+ internal procedure ModifyItem(var Item: Record Item)
+ begin
+ end;
+
+ internal procedure DeleteItem(var Item: Record Item)
+ begin
+ end;
+}
+```
+
+It is important that the record parameter is updated with the final result. This allows the API page to return the result of the API operation to the caller.
+
+The API page implements this in the page triggers as follows:
+
+```al
+ var
+ ItemAPIOperations: Codeunit "Item API Operations";
+
+ trigger OnInsertRecord(BelowxRec: Boolean): Boolean
+ begin
+ ItemAPIOperations.InsertItem(Rec);
+ exit(false);
+ end;
+
+ trigger OnModifyRecord(): Boolean
+ begin
+ ItemAPIOperations.ModifyItem(Rec);
+ exit(false);
+ end;
+
+ trigger OnDeleteRecord(): Boolean
+ begin
+ ItemAPIOperations.DeleteItem(Rec);
+ exit(false);
+ end;
+```
+
+The triggers must return false in order to cancel the operation in the page.
+
+## Benefits
+Inside the functions in the codeunit you have full control over the steps that are performed for the specific operation.
diff --git a/content/docs/patterns/api-register-fieldset/index.md b/content/docs/patterns/api-register-fieldset/index.md
new file mode 100644
index 00000000..b091e808
--- /dev/null
+++ b/content/docs/patterns/api-register-fieldset/index.md
@@ -0,0 +1,108 @@
+---
+title: "API Register Fieldset"
+tags: ["AL","API"]
+categories: ["Pattern"]
+---
+
+_Created by Arend-Jan Kauffmann, Described by Arend-Jan Kauffmann_
+
+## Abstract
+The goal of this pattern is to register the fields that are part of the request body of an API call.
+
+## Context
+A request to an API page to insert or modify a record requires a JSON body with the fields and values. It is not required to specify all fields that are exposed by the API page. Only those fields that are specified in the JSON body will be validated with a value. A call to insert a record (http POST) will leave the unspecified fields at their default value. A call to modify a record (http PATCH) will only update the specified fields and leave the other fields to their current value.
+
+## Problem
+The standard behavior can result in a number of challenges:
+
+* It's not possible to implement mandatory fields. Especially for fields with an initial value like integers, decimals and booleans the code doesn't know if they are their initial value or were included in the API request.
+* In the OnValidate trigger of a field it's not possible to verify if the API request is an insert or modify operation. Fields can't be protected to be modified during a specific operation, e.g. do not allow to modify during an insert.
+* When implementing the Delegated API Operation pattern, for example to implement a template with default values, the template values should not overwrite the provided values in the API request while non-specified fields should get a value from the template.
+
+## Description
+To mitigate these problems, we can register the fields during the OnValidate trigger in a temporary table. During the delayed insert or modify operation the code knows which fields were part of the API request.
+
+```al
+ field(displayName; Rec.Description)
+ {
+ trigger OnValidate()
+ begin
+ RegisterFieldSet(Rec.FieldNo(Description));
+ end;
+ }
+
+ ....
+
+ var
+ TempFieldSet: Record Field temporary;
+
+ local procedure RegisterFieldSet(FieldNumber: Integer)
+ begin
+ if TempFieldSet.Get(Database::Item, FieldNumber) then
+ exit;
+
+ TempFieldSet.Init();
+ TempFieldSet.TableNo := Database::Item;
+ TempFieldSet."No." := FieldNumber;
+ TempFieldSet.Insert();
+ end;
+```
+
+Now that we have the list of fields that are part of the request, they can be checked during the insert or modify operation. Or they can be handed over to a delegated operation.
+
+Some examples to work with the list of fields:
+
+```al
+ trigger OnInsertRecord(BelowxRec: Boolean): Boolean
+ begin
+ if TempFieldSet.Get(Database::Item, Rec.FieldNo(Inventory)) then
+ Error(InventoryCannotBeChangedInAPostRequestErr);
+
+ ItemAPIOperations.InsertItem(Rec, TempFieldSet)
+ exit(false);
+ end;
+
+ trigger OnModifyRecord(): Boolean
+ begin
+ ItemAPIOperations.ModifyItem(Rec, TempFieldSet);
+ exit(false);
+ end;
+
+ trigger OnDeleteRecord(): Boolean
+ begin
+ ItemAPIOperations.DeleteItem(Rec);
+ exit(false);
+ end;
+```
+
+The example code combines this with the Delegated API Operation pattern. The codeunit for the delegated operation can use the fieldset to apply a template while keeping the original values from the request.
+
+```al
+codeunit 50000 "Item API Operations"
+{
+ internal procedure InsertItem(var Item: Record Item; var TempFieldSet: Record "Field"; ModifiedDateTime: DateTime)
+ var
+ ConfigTemplateHeader: Record "Config. Template Header";
+ ConfigTemplateManagement: Codeunit "Config. Template Management";
+ RecRef: RecordRef;
+ begin
+ if not FindConfigTemplateHeader(Item, ConfigTemplateHeader) then
+ exit;
+ RecRef.GetTable(Item);
+
+ if ConfigTemplateManagement.ApplyTemplate(RecRef, TempFieldSet, RecRef, ConfigTemplateHeader) then
+ RecRef.SetTable(Item);
+ end;
+
+ internal procedure ModifyItem(var Item: Record Item)
+ begin
+ end;
+
+ internal procedure DeleteItem(var Item: Record Item)
+ begin
+ end;
+}
+```
+
+## Benefits
+Having a list of fields that are part of the API request provides more information during to the insert or modify operation. This helps to implement specific behavior, based on which fields were specified in the API request.
diff --git a/content/docs/patterns/command-queue/index.md b/content/docs/patterns/command-queue/index.md
new file mode 100644
index 00000000..a414765f
--- /dev/null
+++ b/content/docs/patterns/command-queue/index.md
@@ -0,0 +1,220 @@
+---
+title: "Command Queue"
+tags: ["AL"]
+categories: ["Pattern"]
+---
+
+_Created by Patrick Schiefer, Described by Patrick Schiefer_
+
+## Abstract
+The goal of this pattern is to control the flow of multiple processes
+
+
+## Problem
+Sometimes its neccassary to perform multiple processes in Business Central, for example you want to post more than one order or before you post an Order you also have to post a purchase order this often leads to spaghetti code with big if else structures, which is not easy to read
+
+## Description
+The pattern is ideal for executing several independent processes in succession. Since the processes are independent, each process must take care of error handling itself.
+The command queue should not be used to control a single process. Also it is important to know that the queue is just in the memory so if the service gets restarted the queue is gone and has to be rebuilt.
+
+## The Pattern
+To structure this problem we can use the "Command Queue" pattern. The pattern consist of two main parts the queue and the command interface
+
+
+
+First the command interface, it only has one procedure to execute the command
+```al
+interface ICommand
+{
+ procedure Execute()
+}
+```
+
+
+And then the Queue which consist of two codeunits, the Queue itself and a Queue Entry
+
+```al
+codeunit 50100 "Queue"
+{
+ procedure Push(var value: Interface ICommand)
+ var
+ Entry: Codeunit QueueEntry;
+ begin
+ Entry.SetValue(value);
+ if count = 0 then begin
+ first := Entry;
+ last := Entry;
+ end
+ else begin
+ last.SetNextEntry(Entry);
+ last := Entry;
+ end;
+ count += 1;
+ end;
+
+ procedure Pop() value: Interface ICommand
+ begin
+ if count > 0 then begin
+ value := first.GetValue();
+ first := first.GetNextEntry();
+ count -= 1;
+ end
+ else
+ Error('The Queue is empty!');
+ end;
+
+ procedure GetSize(): Integer
+ begin
+ exit(count);
+ end;
+
+ var
+ first: Codeunit QueueEntry;
+ last: Codeunit QueueEntry;
+ count: Integer;
+}
+
+
+
+codeunit 50102 "QueueEntry"
+{
+ procedure SetValue(var v: Interface ICommand)
+ begin
+ value := v;
+ end;
+
+ procedure GetValue(): Interface ICommand
+ begin
+ exit(value);
+ end;
+
+ procedure GetNextEntry(): Codeunit QueueEntry
+ begin
+ exit(NextEntry);
+ end;
+
+ procedure SetNextEntry(var Entry: Codeunit QueueEntry)
+ begin
+ NextEntry := Entry;
+ end;
+
+ var
+ value: Interface ICommand;
+ NextEntry: Codeunit QueueEntry;
+}
+```
+
+As we see the queue entry stores a command, since the command is an interface we can hide each business logic behind.
+
+## Benefits
+The logical flow is very easy to adopt, it is even possible to add entries to the queue while it is processed.
+
+## Example
+
+In this short example I show you how to post multiple sales orders and display message after finishing the last post.
+
+
+We have two commands in this example, the "SalesOrderPostCommander" is used to post a sales order and the "MessageCommander" displays a message.
+```al
+codeunit 50104 "SalesOrderPostCommander" implements ICommand
+{
+ procedure SetSalesOrderNumber(value : Code[20])
+ begin
+ No := value;
+ end;
+
+ procedure Execute()
+ begin
+ // TODO Post Sales Header
+ end;
+
+
+ var
+ No : Code[20];
+}
+
+
+codeunit 50103 "MessageCommander" implements ICommand
+{
+ procedure SetText(value: Text)
+ begin
+ t := value;
+ end;
+
+ procedure Execute()
+ begin
+ Message(t);
+ end;
+
+ var
+ t: Text;
+}
+```
+
+Using this two codeunits we can now implement a patch posting
+```al
+
+codeunit 50105 PatchPostQueue
+{
+ procedure PatchPost()
+ begin
+ FilterSalesOrdersToPost();
+ if not SalesOrders.Findset(false) then
+ exit(); // Nothing to post
+
+ repeat
+ AddSalesOrderToQueue(SalesOrder."No.");
+ until SalesOrders.Next() = 0;
+
+ AddMessageToQueue('Posting Complete');
+ ExecuteQueue();
+ end;
+
+ local procedure ExecuteQueue()
+ var
+ object : interface "ICommand";
+ begin
+ repeat
+ object := queue.Pop();
+ object.Execute();
+ until queue.GetSize() = 0;
+ end;
+
+ local procedure FilterSalesOrdersToPost()
+ begin
+ // Filter Sales Orders here
+ end;
+
+ local procedure AddMessageToQueue(message : Text)
+ var
+ t: Codeunit MessageCommander;
+ object: Interface ICommand;
+ begin
+ t.SetText(message);
+ object := t;
+ queue.Push(object);
+ end;
+
+ local procedure AddSalesOrderToQueue(No : Text)
+ var
+ SaleOrderCommander: Codeunit SalesOrderPostCommander;
+ object: Interface ICommand;
+ begin
+ SaleOrderCommander.SetSalesOrderNumber(No);
+ object := SaleOrderCommander;
+ queue.Push(object);
+ end;
+
+
+ var
+ SalesOrders : Record "Sales Header";
+ queue: Codeunit Queue;
+}
+
+```
+
+
+
+## References
+[Detailed Explanation of the pattern](https://patrickschiefer.wordpress.com/2022/02/24/part-2-how-to-implement-a-command-queue-in-pure-al/)
+
diff --git a/content/docs/patterns/command-queue/queue.png b/content/docs/patterns/command-queue/queue.png
new file mode 100644
index 00000000..a7411ca6
Binary files /dev/null and b/content/docs/patterns/command-queue/queue.png differ
diff --git a/content/docs/patterns/error-handling/index.md b/content/docs/patterns/error-handling/index.md
new file mode 100644
index 00000000..ecd3635a
--- /dev/null
+++ b/content/docs/patterns/error-handling/index.md
@@ -0,0 +1,24 @@
++++
+title = "Error Handling"
+tags = ["AL"]
+categories = ["Pattern"]
++++
+
+_Created by Microsoft, Described by Luuk Busschers (Dysel)_
+
+## Abstract
+
+The "Error Handling" system is used extensively to provide information to users about missing information in the system or other issues because of which the started process cannot be completed through Microsoft Dynamics 365 Business Central.
+
+## Description
+Because of there is already a lot written about how to use the error handling the best in several scenario's in this page you will find a link to documentation on learn.microsoft.com and a link to a video about this subject on youtube. These links can be found in the list of references.
+
+The Microsoft Learn part is about collecting errors which means that the process you did start will not be interupted when one error is given, it will collect the errors in the process an you are able to show the user afterwards which errors where given in the process.
+
+The youtube video shows more about errors presented in such way that the user will be informed about how to solve the error.
+
+## List of references
+
+For error handling, there is more information available on:
+- [Microsoft Learn: Error collections](https://learn.microsoft.com/en-us/dynamics365/business-central/dev-itpro/developer/devenv-error-collection/)
+- [Youtube: Microsoft Presents: User friendly error handling in AL](https://www.youtube.com/watch?v=D8233xMjVog&list=PLI1l3dMI8xlDM9onioMWUyMSCiFs_mMWw&index=27)
\ No newline at end of file
diff --git a/content/docs/patterns/event-bridge-pattern/index.md b/content/docs/patterns/event-bridge-pattern/index.md
new file mode 100644
index 00000000..e05deadc
--- /dev/null
+++ b/content/docs/patterns/event-bridge-pattern/index.md
@@ -0,0 +1,111 @@
+---
+title: "Event Bridge"
+tags: ["AL","Interface","Extendability"]
+categories: ["Pattern"]
+---
+
+_Created by waldo & Arend-Jan Kauffmann, Described by waldo_
+
+## Abstract
+
+In the world of interfaces, it is important to preserve (certain) events over multiple implementation of the interface.
+
+## Context
+
+An app can have interfaces.
+It makes it possible for other apps to extend/change the implementations of a certain part of the business logic.
+
+Like in this example, we have an interface, to implement different ways for getting weights from scales:
+
+```AL
+interface "IScale"
+{
+ procedure GetWeight(): Decimal
+ procedure Tare()
+}
+```
+
+## Problem
+
+Multiple apps can subscribe to certain events of the app.
+When a new implementation is created, we need to make sure that these events are raised at the right times. If those events were published on the implementation codeunit, it might very well be that those events will not be raised, hard to find, or whatever.
+
+So, if we would implement it like this, it isn't really extensible, as a different implemention would implement different events .. and it's not possible to subscribe to all of them (including future implementations)
+
+```AL
+codeunit 50407 "Scale Wrong" implements IScale
+{
+ procedure GetWeight() Result: Decimal
+ begin
+ //TODO: Implement Bar GetWeight
+ OnAfterGetWeight(Result);
+ end;
+
+ procedure Tare()
+ begin
+ //TODO: Implement Bar Tare
+ OnAfterTare();
+ end;
+
+ [IntegrationEvent(false, false)]
+ procedure OnAfterGetWeight(var Result: Decimal)
+ begin
+ end;
+
+ [IntegrationEvent(false, false)]
+ procedure OnAfterTare()
+ begin
+ end;
+}
+```
+
+## Description
+
+To mitigate this problem, we can work with a new, dedicated and isolated codeunit, with publisher events to be able to raise them from different places.
+
+```AL
+codeunit 50406 "IScale Triggers"
+{
+ [IntegrationEvent(false, false)]
+ procedure OnAfterGetWeight(var Result: Decimal)
+ begin
+ end;
+
+ [IntegrationEvent(false, false)]
+ procedure OnAfterTare()
+ begin
+ end;
+}
+```
+
+This way, it's possible to raise the events in all the implementations:
+
+```AL
+codeunit 50405 "Scale Bar" implements IScale
+{
+ var
+ IScaleTriggers: Codeunit "IScale Triggers";
+
+ procedure GetWeight() Result: Decimal
+ begin
+ //TODO: Implement Bar GetWeight
+ IScaleTriggers.OnAfterGetWeight(Result);
+ end;
+
+ procedure Tare()
+ begin
+ //TODO: Implement Bar Tare
+ IScaleTriggers.OnAfterTare();
+ end;
+}
+```
+
+## Benefits
+
+This new codeunit, with public events, makes the events accessible from all places, including new apps that are dependent from this app.
+
+The naming convention (both starting with "IScale") also makes it very easy to find that corresponding events for the interface.
+
+## When not to use
+
+Obviously, the events should be carefully considered: only the events that make sense to "share" over all implementations, need this approach.
diff --git a/content/docs/patterns/facade-pattern/diagram.jpg b/content/docs/patterns/facade-pattern/diagram.jpg
new file mode 100644
index 00000000..a64ccd8a
Binary files /dev/null and b/content/docs/patterns/facade-pattern/diagram.jpg differ
diff --git a/content/docs/patterns/facade-pattern/index.md b/content/docs/patterns/facade-pattern/index.md
new file mode 100644
index 00000000..965a4adb
--- /dev/null
+++ b/content/docs/patterns/facade-pattern/index.md
@@ -0,0 +1,187 @@
+---
+title: "Façade"
+tags: ["AL","Decoupling","Readability","Testability","Extendability"]
+categories: ["Pattern"]
+---
+
+_Created by Erich Gamma, Richard Helm, Ralph Johnson, John Vlissides (Gang of Four), Described by Jesper Schulz-Wedde (Microsoft)_
+
+## 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
+- Testability
+- Maintainability
+
+## Context
+
+Whenever you want to write an isolated piece of business logic, from now on referred to as subsystem, this pattern should be applicable.
+
+## 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.
+
+## Description
+
+The facade pattern is one of the most commonly used and known patterns, first described the book [Design Patterns: Elements of Reusable Object-Oriented Software](https://archive.org/details/designpatternsel00gamm/page/185). It is classified as a structural pattern. While it originates from the object-oriented world, it for sure also applies to AL. The power of a facade is that it:
+
+- Improves the readability of your subsystem's API, as it acts as an entry point to your subsystem. This also allows for easier documentation of your API, as you can focus it to a few public objects.
+- Improves the maintainability of your subsystem, as the internal subsystem can be modified or even completely replaced freely, without risking to break the public API - as long as the facade remains intact.
+- Ensures that your code remains loosely coupled and encapsulated, as no other subsystem can take dependencies on your internal implementation details. This also fosters the reusability of your code.
+- Suggests what needs most attention in your tests, as you would want to make sure that your subsystem's public API behaves as designed.
+
+Whenever you develop a functional group or an independent system, which has a clear API, you should add a facade to achieve the above-mentioned benefits.
+
+### What is a subsystem?
+
+A subsystem is a group of objects, which together provide a set of capabilities. Libraries are excellent examples of such subsystems.
+In Business Central, all modules in the System Application are subsystems. Examples are hence every module you will find in the system application, such as:
+
+- Azure Blob Services API
+- Barcode
+- Cryptography Management
+- Encoding
+- Image
+- RegEx
+- ...
+
+If you can describe a set of capabilities out of context of the rest of the system, chances are you are looking at a subsystem.
+
+### The Pattern
+
+This is arguably one of easiest patterns to understand and implement. Loosely speaking, you simply need to smack a wrapper on top of your implementation and hide away your implementation details from the outside world.
+
+
+
+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_
+
+```AL
+///
+/// Codeunit to extract image information.
+///
+codeunit 3971 Image
+{
+ Access = Public;
+
+ var
+ ImageImpl: Codeunit "Image Impl.";
+
+ ///
+ /// Creates an image from the specified data stream.
+ ///
+ /// A Stream that contains the image data.
+ procedure FromStream(InStream: InStream)
+ begin
+ ImageImpl.FromStream(InStream);
+ end;
+
+ ///
+ /// Gets the width in pixels.
+ ///
+ /// The width in pixels.
+ procedure GetWidth(): Integer
+ begin
+ exit(ImageImpl.GetWidth());
+ end;
+}
+```
+
+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.
+- No methods contain any logic. They merely point to implementation details.
+- The object naming suggests that it will get referenced from the outside.
+
+Anyone who wants to access the subsystem, will only have to take a dependency on the facade; the implementation details are not needed nor accessible.
+
+Anyone who wants to access the subsystem, will only have to relate to this one public facade; the documentation makes it easy-to-understand the subsystem's capabilities.
+
+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_
+
+```AL
+codeunit 3970 "Image Impl."
+{
+ Access = Internal;
+
+ var
+ TempBlob: Codeunit "Temp Blob";
+
+ procedure FromStream(InStream: InStream)
+ var
+ OutStream: OutStream;
+ begin
+ TempBlob.CreateOutStream(OutStream);
+ CopyStream(OutStream, InStream);
+ end;
+
+ procedure GetWidth(): Integer
+ var
+ Image: DotNet Image;
+ InStream: InStream;
+ begin
+ TempBlob.CreateInStream(InStream);
+ Image := Image.FromStream(InStream);
+ exit(Image.Width());
+ end;
+}
+```
+
+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
+
+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
+
+This is a very applicable pattern, which can be used in most cases. While it may appear to be overkill at the beginning of the design of your subsystem, chances are your subsystem will evolve in complexity over time. The pattern may complicate implementation of extensibility, as you hide away all implementation details and hence also take away the opportunity to extend those, but that does not mean that extensibility cannot be achieved - but it requires more careful designing for extensibility. Extensibility with facades could be a topic of its own, which for now isn't covered by this pattern description. As always, use your common sense when to use and when not to use this pattern.
+
+## Snippets
+
+Given the simplicity of this pattern, there are no snippets available at the moment.
+
+## List of references
+
+This is one of the most commonly used and discussed, initially described here:
+
+[Design Patterns: Elements of Reusable Object-Oriented Software](https://archive.org/details/designpatternsel00gamm/page/185). Addison Wesley. pp. 185ff. ISBN 0-201-63361-2.
+
+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)
diff --git a/content/docs/patterns/generic-method-pattern/index.md b/content/docs/patterns/generic-method-pattern/index.md
new file mode 100644
index 00000000..a16e2b64
--- /dev/null
+++ b/content/docs/patterns/generic-method-pattern/index.md
@@ -0,0 +1,356 @@
+---
+title: "Generic Method"
+tags: ["AL","Decoupling","Readability","Testability","Extendability"]
+categories: ["Pattern"]
+---
+
+_Created by Gary Winter (Cloud Ready Software), Described by waldo (iFacto Business Solutions | Dynex)_
+
+## 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
+- Testability
+- Encapsulation
+
+## Context
+
+Whenever you have to write an isolated piece of business logic, this pattern should be applicable.
+
+## Problem
+
+Usually, when you ask people where to place code, they all have their own opinion: on a table or page, or in some kind of codeunit library where lots of functions will be placed, or .. . In fact, does it matter? It sure does, because in many cases, the code simply isn't maintainable, let alone extendable or testable.
+
+## 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 is a method?
+
+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
+- a batch run to send emails
+- ...
+
+In a way, except "data validation", most of the things we write in our daily life, are methods.
+
+### 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.
+
+Let me start by showing an example, so you can refer to this complete example during the rest of the article:
+
+```AL
+codeunit 53100 "WLD BlockCustomer Meth"
+{
+ internal procedure BlockCustomer(var Cust: Record Customer; HideDialog: Boolean)
+ var
+ IsHandled: Boolean;
+ begin
+ 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)
+ begin
+ if IsHandled then
+ exit;
+
+ Cust.Blocked := Cust.Blocked::All;
+ Cust.Modify(true);
+ end;
+
+ local procedure ConfirmBlockCustomer(HideDialog: Boolean): Boolean
+ var
+ ConfirmManagement: Codeunit "Confirm Management";
+ ConfirmQst: Label 'Are you sure?';
+ DefaultAnswer: Boolean;
+ begin
+ DefaultAnswer := true;
+
+ if HideDialog then
+ exit(DefaultAnswer);
+ exit(ConfirmManagement.GetResponseOrDefault(ConfirmQst, DefaultAnswer));
+ end;
+
+ local procedure AcknowledgeBlockCustomer(HideDialog: Boolean)
+ var
+ AcknowledgeMsg: Label 'You successfully executed "BlockCustomer"';
+ begin
+ if not GuiAllowed or HideDialog then
+ exit;
+ Message(AcknowledgeMsg);
+ end;
+
+ [IntegrationEvent(false, false)]
+ local procedure OnBeforeBlockCustomer(var Cust: Record Customer; var IsHandled: Boolean)
+ begin
+ end;
+
+ [IntegrationEvent(false, false)]
+ 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 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").
+
+```AL
+codeunit 53100 "WLD BlockCustomer Meth"
+{
+ internal procedure BlockCustomer(var Cust: Record Customer; HideDialog: Boolean)
+ var
+ IsHandled: Boolean;
+ begin
+ if not ConfirmBlockCustomer(HideDialog) then
+ exit;
+ ...
+ AcknowledgeBlockCustomer(HideDialog)
+ end;
+...
+
+ local procedure ConfirmBlockCustomer(HideDialog: Boolean): Boolean
+ var
+ ConfirmManagement: Codeunit "Confirm Management";
+ ConfirmQst: Label 'Are you sure?';
+ DefaultAnswer: Boolean;
+ begin
+ DefaultAnswer := true;
+
+ if HideDialog then
+ exit(DefaultAnswer);
+ exit(ConfirmManagement.GetResponseOrDefault(ConfirmQst, DefaultAnswer));
+ end;
+
+ local procedure AcknowledgeBlockCustomer(HideDialog: Boolean)
+ var
+ AcknowledgeMsg: Label 'You successfully executed "BlockCustomer"';
+ begin
+ if not GuiAllowed or HideDialog then
+ exit;
+ Message(AcknowledgeMsg);
+ end;
+...
+}
+```
+
+_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:
+
+```AL
+codeunit 53100 "WLD BlockCustomer Meth"
+{
+ internal procedure BlockCustomer(var Cust: Record Customer; HideDialog: Boolean)
+ var
+ IsHandled: Boolean;
+ begin
+ ...
+ OnBeforeBlockCustomer(Cust, IsHandled);
+ ...
+ OnAfterBlockCustomer(Cust);
+ ...
+ end;
+...
+ [IntegrationEvent(false, false)]
+ local procedure OnBeforeBlockCustomer(var Cust: Record Customer; var IsHandled: Boolean)
+ begin
+ end;
+
+ [IntegrationEvent(false, false)]
+ local procedure OnAfterBlockCustomer(var Cust: Record Customer)
+ begin
+ end;
+}
+```
+
+_The method layer_
+The last layer is obviously where the business logic will be written.
+
+The relevant part is:
+
+```AL
+codeunit 53100 "WLD BlockCustomer Meth"
+{
+ internal procedure BlockCustomer(var Cust: Record Customer; HideDialog: Boolean)
+ var
+ IsHandled: Boolean;
+ begin
+ ...
+ DoBlockCustomer(Cust, IsHandled);
+ ...
+ end;
+
+ local procedure DoBlockCustomer(var Cust: Record Customer; IsHandled: Boolean)
+ begin
+ if IsHandled then
+ exit;
+
+ Cust.Blocked := Cust.Blocked::All;
+ Cust.Modify(true);
+ end;
+...
+}
+```
+
+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
+- readable function calls
+- ...
+
+**Call out to the method from a table**
+
+Now, there is a reason that the one global procedure in the method-codeunit is `internal`. We shouldn't be calling this procedure directly. We should be calling it through it's "class" (usually, a table can be seen as a class). That means, we would have to create a public (not internal) procedure on table-level, which can be used in the business logic.
+
+In our example above, this could be that table extension:
+
+```AL
+tableextension 53100 "Customer Ext BASE" extends Customer
+{
+ procedure BlockCustomer(HideDialog: Boolean)
+ var
+ WLDBlockCustomerMeth: Codeunit "WLD BlockCustomer Meth";
+ begin
+ WLDBlockCustomerMeth.BlockCustomer(Rec, HideDialog);
+ end;
+
+ procedure BlockCustomer()
+ begin
+ BlockCustomer(false);
+ end;
+}
+```
+
+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._
+
+**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`
+
+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.
+
+## Benefits
+
+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).
+
+### 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.
+
+Maybe one more example:
+if you would apply this pattern to your product, at the customer, you'll be able to hotfix your methods simply by decoupling them and fixing the method instead of waiting for a hotfix from the hotfix-departement.
+
+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_
+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_
+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?_".
+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.
+
+## When not to use
+
+However, there are occasions where you can't use this pattern. Just imagine if you'd put EVERY SINGE LINE of code in a method, including the simple validation code on a table, or library-functions like in helper codeunits. That would obviously not make any sense.
+
+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.
+
+## 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`
+
+## List of references
+
+There have been a number of occasions where people have been sharing this pattern. Here is one:
+
+{{< youtube id="CWpaD9RUa6U" yt_start="1516" >}}
diff --git a/content/docs/patterns/no-series/index.md b/content/docs/patterns/no-series/index.md
new file mode 100644
index 00000000..538a7eb9
--- /dev/null
+++ b/content/docs/patterns/no-series/index.md
@@ -0,0 +1,348 @@
++++
+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
new file mode 100644
index 00000000..95570ecd
--- /dev/null
+++ b/content/docs/patterns/template-method-pattern/index.md
@@ -0,0 +1,153 @@
+---
+title: "Template Method Pattern"
+tags: ["AL", "Interface", "Readability"]
+categories: ["Pattern"]
+---
+
+_Created by Patrick Schiefer, Described by Patrick Schiefer_
+
+## Abstract
+The goal of this pattern is to simplify the solution of similar problems and make your code more readable.
+
+## Problem
+In nearly every app you sometimes have to solve similar problems for different cases. Mostly not the same developer will solve every case. This results in different solutions.
+
+## Description
+The pattern is used when you have problems which are independent but require the same logical flow. Examples which occur very often are: _Posting Documents_, _Printing Reports_ or _Exporting Data_.
+
+## Bad Code Example
+```al
+codeunit 50010 ExportSalesLines
+{
+ procedure ExportData(SalesHeader: Record "Sales Header", SalesLine : Record "Sales Line")
+ begin
+ if not SalesHeader.CheckData() then
+ exit;
+ repeat
+ case SalesHeader.ExportType of
+ Enum::ExportType::A:
+ GenerateLineTypeA(SalesLine);
+ Enum::ExportType::B:
+ GenerateLineTypeB(SalesLine);
+ end;
+ until SalesLine.Next() = 0;
+
+ case SalesHeader.ExportType of
+ Enum::ExportType::A:
+ WriteToFile();
+ Enum::ExportType::B:
+ SendToWebService();
+ end;
+ end;
+
+ //TODO Implementation of procedures used in example
+}
+```
+As you can see in this example the readability gets worse with every new case.
+
+## The Pattern
+To implement the Pattern you need at least 3 objects:
+- A template codeunit
+- An Interface which provides the needed procedures
+- A codeunit which Implements the interrface
+
+In my example I show how to implement a data export with templating.
+
+We start with the template
+```al
+codeunit 50000 ExportTemplate
+{
+ procedure ExportData(Export: Interface IDataExport)
+ begin
+ if not Export.CheckData() then
+ exit;
+ if Export.GetLinesToExport() then
+ repeat
+ Export.ExportLine();
+ until not Export.NextLine();
+ Export.Finish();
+ end;
+}
+```
+As you can see the template just calls procedures via an interface and just defines the flow of the export without really implementing it.
+
+As the second part we need an interface for the export functions
+```al
+interface IDataExport
+{
+ procedure CheckData(): Boolean
+ procedure GetLinesToExport(): Boolean
+ procedure ExportLine()
+ procedure NextLine(): Boolean
+ procedure Finish()
+}
+```
+
+And now we need an implementation. For my example I wrote a export Codeunit for Sales Headers
+```al
+codeunit 50001 SalesHeaderExport implements IDataExport
+{
+ procedure SetSalesHeader(DocType: Enum "Sales Document Type"; No: Code[10])
+ begin
+ SalesHeader.Get(DocType, No);
+ end;
+
+ procedure CheckData(): Boolean
+ begin
+ SalesHeader.TestField(Status, Enum::"Sales Document Status"::Released);
+ end;
+
+ procedure GetLinesToExport(): Boolean
+ begin
+ SalesLines.SetRange("Document Type", SalesHeader."Document Type");
+ SalesLines.SetRange("Document No.", SalesHeader."No.");
+ exit(SalesLines.FindSet());
+ end;
+
+ procedure ExportLine()
+ begin
+ //Generate Exportdata here
+ end;
+
+ procedure NextLine(Steps : integer): Boolean
+ begin
+ exit(SalesLines.Next(Steps) <> 0);
+ end;
+
+ procedure Finish()
+ begin
+ // Send or Save data here
+ end;
+
+ var
+ SalesHeader: Record "Sales Header";
+ SalesLines: Record "Sales Line";
+}
+```
+
+Now lets have a look how to use the pattern
+```al
+codeunit 50002 ExportOrders
+{
+ procedure ExportOrder(DocType: Enum "Sales Document Type"; No: Code[10])
+ var
+ Export: Codeunit ExportTemplate;
+ ExportImpl: Codeunit SalesHeaderExport;
+ ExportInt: Interface IDataExport;
+ begin
+ ExportImpl.SetSalesHeader(DocType, No);
+ ExportInt := exportImpl;
+ Export.ExportData(exportInt);
+ end;
+}
+```
+
+## Benefits
+Your code gains readability and it is very easy to add new cases for the template. You don't always have to think about the whole logic. You just have to implement the details.
+
+## When not to use
+The Pattern should not be used for problems which differ too much. For example, if you have two data exports in your app, one is exporting header and lines and the second one only export header. In this case I would suggest to not use the pattern or to make two templates out of it.
+
+## References
+[Detailed Explanation of the pattern](https://patrickschiefer.com/2022/04/08/template-method-pattern/)
+
diff --git a/content/search.md b/content/search.md
new file mode 100644
index 00000000..394feea5
--- /dev/null
+++ b/content/search.md
@@ -0,0 +1,4 @@
+---
+title: Search Results
+layout: search
+---
diff --git a/go.mod b/go.mod
new file mode 100644
index 00000000..318448e4
--- /dev/null
+++ b/go.mod
@@ -0,0 +1,5 @@
+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
new file mode 100644
index 00000000..963e3a5b
--- /dev/null
+++ b/go.sum
@@ -0,0 +1,8 @@
+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/hugo.toml b/hugo.toml
new file mode 100644
index 00000000..0163dbbc
--- /dev/null
+++ b/hugo.toml
@@ -0,0 +1,224 @@
+baseURL = 'https://alguidelines.dev/'
+enableRobotsTXT = true
+languageCode = 'en-us'
+title = 'AL Guidelines'
+
+# Hugo allows theme composition (and inheritance). The precedence is from left to right.
+theme = ["github.com/google/docsy"]
+
+# Will give values to .Lastmod etc.
+enableGitInfo = true
+
+# Comment out to disable taxonomies in Docsy
+# disableKinds = ["taxonomy", "taxonomyTerm"]
+
+# You can add your own taxonomies
+[taxonomies]
+author = "author"
+category = "categories"
+tag = "tags"
+
+[params.taxonomy]
+# set taxonomyCloud = [] to hide taxonomy clouds
+taxonomyCloud = ["tags", "categories"]
+
+# If used, must have same lang as taxonomyCloud
+taxonomyCloudTitle = ["Tag Cloud", "Categories", "Authors"]
+
+# set taxonomyPageHeader = [] to hide taxonomies on the page headers
+taxonomyPageHeader = ["tags", "categories", "authors"]
+
+# Highlighting config
+pygmentsCodeFences = true
+pygmentsUseClasses = false
+# Use the new Chroma Go highlighter in Hugo.
+pygmentsUseClassic = false
+#pygmentsOptions = "linenos=table"
+# See https://help.farbox.com/pygments.html
+pygmentsStyle = "tango"
+
+# First one is picked as the Twitter card image if not set on page.
+#images = ["images/project-illustration.png"]
+
+# Configure how URLs look like per section.
+[permalinks]
+blog = "/blog/:slug/"
+
+[markup.goldmark.renderer]
+unsafe = true
+
+[markup.highlight]
+style = "tango"
+
+## Configuration for BlackFriday markdown parser: https://github.com/russross/blackfriday
+[blackfriday]
+angledQuotes = false
+hrefTargetBlank = false
+latexDashes = true
+plainIDAnchors = true
+
+# Image processing configuration.
+[imaging]
+anchor = "smart"
+quality = 75
+resampleFilter = "CatmullRom"
+
+[services]
+[services.googleAnalytics]
+# Comment out the next line to disable GA tracking. Also disables the feature described in [params.ui.feedback].
+id = ""
+
+# Language configuration
+
+[languages]
+[languages.en]
+title = "alguidelines.dev - Business Central Design Patterns"
+languageName = "English"
+# Weight used for sorting.
+weight = 1
+[languages.en.params]
+description = "Guidelines for when Developing AL for Microsoft Dynamics 365 Business Central"
+
+# Everything below this are Site Params
+
+[params]
+copyright = "alguidelines.dev Project"
+github_repo = "https://github.com/microsoft/alguidelines"
+github_project_repo = "https://github.com/microsoft/alguidelines"
+github_branch = "main"
+privacy_policy = ""
+shorttitle = "alguidelines.dev"
+
+# Google Custom Search Engine ID. Remove or comment out to disable search.
+# gcs_engine_id = "c632b781ffe71b197"
+#algolia_docsearch = true
+offlineSearch = true
+
+# current release branch - could be rc
+release_branch = "master"
+# the main version. Never is rc.
+release_version = "v1.15.1"
+
+slackurl = ""
+
+installurl = ''
+learnmoreurl = ''
+#twitterurl = ''
+
+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 = ""
+#twitter = ''
+
+[[params.whatsnew]]
+badge = 'FISSION DZONE REFCARD'
+body = 'Get started with Serverless on Kubernetes in 5 minutes! Learn how to set up Fission, its key concepts, how to create and invoke a function, monitor its performance, and more.'
+heading = 'New! Download the Fission.io Refcard'
+[params.whatsnew.button]
+hero_class = 'mid'
+text = 'Download Now!'
+url = 'https://platform9.com/wp-content/uploads/2019/03/dzone-refcard-fissionio.pdf'
+
+[[params.whatsnew]]
+badge = 'BLOG'
+body = 'Learn how to use Fission functions with PostgreSQL database.'
+heading = 'Fission Functions with PostgreSQL Sample'
+[params.whatsnew.button]
+hero_class = 'mid-2'
+text = 'Read More'
+url = '/blog/how-to-use-postgresql-database-with-fission-functions/'
+
+[[params.whatsnew]]
+badge = 'BLOG'
+body = 'Tutorial to deploy Next.js applications with Fission.'
+heading = 'Next.js Application with Fission'
+[params.whatsnew.button]
+hero_class = 'mid-2'
+text = 'REad More'
+url = '/blog/serverless-next.js-example-blog-with-fission/'
+
+# User interface configuration
+[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 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.
+navbar_translucent_over_cover_disable = false
+# Enable to show the side bar menu in its compact state.
+sidebar_menu_compact = true
+# Set to true to hide the sidebar search box (the top nav search box will still be displayed if search is enabled)
+sidebar_search_disable = true
+
+# Adds a H2 section titled "Feedback" to the bottom of each doc. The responses are sent to Google Analytics as events.
+# This feature depends on [services.googleAnalytics] and will be disabled if "services.googleAnalytics.id" is not set.
+# If you want this feature, but occasionally need to remove the "Feedback" section from a single page,
+# add "hide_feedback: true" to the page's front matter.
+[params.ui.feedback]
+enable = true
+# The responses that the user sees after clicking "yes" (the page was helpful) or "no" (the page was not helpful).
+no = 'Sorry to hear that. Please tell us how we can improve.'
+yes = 'Glad to hear it! Please tell us how we can improve.'
+
+# Adds a reading time to the top of each doc.
+# If you want this feature, but occasionally need to remove the Reading time from a single page,
+# add "hide_readingtime: true" to the page's front matter
+[params.ui.readingtime]
+enable = false
+
+[[params.links.user]]
+desc = "Development takes place here!"
+icon = "fab fa-github"
+name = "GitHub"
+url = "https://github.com/microsoft/alguidelines"
+[params.links]
+[[params.links.user]]
+desc = "Follow us on Twitter to get the latest news!"
+icon = "fab fa-twitter"
+name = "Twitter"
+url = "https://twitter.com/search?q=%23bcalhelp"
+[[params.links.user]]
+desc = "Chat with other project users in #users"
+icon = "fab fa-discord"
+name = "Discord"
+url = "https://discord.gg/4wbfNv3"
+# End user relevant links. These will show up on left side of footer and in the community page if you have one.
+
+
+[sitemap]
+changefreq = "monthly"
+filename = "sitemap.xml"
+priority = 0.5
+
+[menu]
+[[menu.main]]
+name = "Docs"
+url = "/docs/"
+pre = ""
+weight = 10
+[[menu.main]]
+name = "Discussions"
+url = "https://github.com/microsoft/alguidelines/discussions/"
+pre = ""
+weight = 11
+[[menu.main]]
+name = "GitHub"
+pre = ""
+url = "https://github.com/microsoft/alguidelines"
+weight = 13
+[[menu.main]]
+name = "Discord"
+pre = ""
+url = "https://discord.gg/4wbfNv3"
+weight = 14
diff --git a/layouts/partials/logo.html b/layouts/partials/logo.html
new file mode 100644
index 00000000..6794868f
--- /dev/null
+++ b/layouts/partials/logo.html
@@ -0,0 +1,14 @@
+
+
+ ALGuidelines.Dev
+
+
+Code is Poetry ...
\ No newline at end of file
diff --git a/layouts/partials/navbar.html b/layouts/partials/navbar.html
new file mode 100644
index 00000000..8cff9995
--- /dev/null
+++ b/layouts/partials/navbar.html
@@ -0,0 +1,37 @@
+{{ $cover := and (.HasShortcode "blocks/cover") (not .Site.Params.ui.navbar_translucent_over_cover_disable) }}
+
diff --git a/layouts/partials/page-meta-lastmod.html b/layouts/partials/page-meta-lastmod.html
new file mode 100644
index 00000000..28b7aead
--- /dev/null
+++ b/layouts/partials/page-meta-lastmod.html
@@ -0,0 +1,11 @@
+{{ if and (.GitInfo) (.Site.Params.github_repo) -}}
+