change nav 1-patterns formatting
This commit is contained in:
parent
ba12637e35
commit
7e4e21d223
54 changed files with 211 additions and 250 deletions
|
|
@ -2,8 +2,6 @@
|
||||||
title = "Argument Table"
|
title = "Argument Table"
|
||||||
weight = 220
|
weight = 220
|
||||||
+++
|
+++
|
||||||
## Argument table pattern
|
|
||||||
|
|
||||||
_Originally By Nikola Kukrika and waldo_
|
_Originally By Nikola Kukrika and waldo_
|
||||||
|
|
||||||
### Abstract
|
### Abstract
|
||||||
|
|
|
||||||
|
|
@ -90,16 +90,13 @@ Entities where the Blocked Entity has been implemented include:
|
||||||
|
|
||||||
The [Released Entity][anchor4].
|
The [Released Entity][anchor4].
|
||||||
|
|
||||||
[watch?v=O2R fTSup1o&list=PLhZ3P LY7CqmVszuvtJLujFyHpsVN0Uw&index=16][anchor5]
|
{{< youtube O2R-fTSup1o >}}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
[anchor0]: 2260.BlockedEntityPattern.png
|
[anchor0]: 2260.BlockedEntityPattern.png
|
||||||
[anchor1]: /nav/w/designpatterns/247.data-driven-blocked-entity/edit
|
[anchor1]: /nav/w/designpatterns/247.data-driven-blocked-entity/edit
|
||||||
[anchor2]: 8637.BlockedEntityPattern_5F00_5F00_5F00_Boolean.png
|
[anchor2]: 8637.BlockedEntityPattern_5F00_5F00_5F00_Boolean.png
|
||||||
[anchor3]: 3056.BlockedEntityPattern_5F00_5F00_5F00_Option.png
|
[anchor3]: 3056.BlockedEntityPattern_5F00_5F00_5F00_Option.png
|
||||||
[anchor4]: /nav/w/designpatterns/115.released-entity.aspx
|
[anchor4]: /nav/w/designpatterns/115.released-entity.aspx
|
||||||
[anchor5]: https://www.youtube.com/watch?v=O2R-fTSup1o&list=PLhZ3P-LY7CqmVszuvtJLujFyHpsVN0U_w&index=16
|
|
||||||
|
|
||||||
|
|
||||||
[image0]: 2260.BlockedEntityPattern.png
|
[image0]: 2260.BlockedEntityPattern.png
|
||||||
|
|
|
||||||
|
|
@ -2,7 +2,7 @@
|
||||||
title = "Conditional Cascading Update"
|
title = "Conditional Cascading Update"
|
||||||
weight = 370
|
weight = 370
|
||||||
+++
|
+++
|
||||||
_Originally by Jan Hoek at IDYN _
|
_Originally by Jan Hoek at IDYN_
|
||||||
|
|
||||||
## Abstract
|
## Abstract
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -100,7 +100,7 @@ In the standard version of Microsoft Dynamics NAV, the Copy Document functionali
|
||||||
3. Copy Service Document (REP5979)
|
3. Copy Service Document (REP5979)
|
||||||
4. Copy Assembly Document (REP901)
|
4. Copy Assembly Document (REP901)
|
||||||
|
|
||||||
[watch?v=aTiwroXwW0&list=PLhZ3P LY7CqmVszuvtJLujFyHpsVN0Uw&index=17][anchor5]
|
{{< youtube aTiwroXwW0 >}}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -189,7 +189,7 @@ Implement the solution in the standard version of Microsoft Dynamics NAV and ext
|
||||||
|
|
||||||
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.
|
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.
|
||||||
|
|
||||||
[watch?v=F0CTvoyKSmI&list=PLhZ3P LY7CqmVszuvtJLujFyHpsVN0Uw&index=20][anchor6]
|
{{< youtube F0CTvoyKSmI >}}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -125,7 +125,7 @@ BusinessChart::DataPointClicked(point : DotNet "Microsoft.Dynamics.Nav.Client.Bu
|
||||||
|
|
||||||
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.
|
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.
|
The **DataPointDoubleClicked** trigger has the same implementation logic as the DataPointClicked trigger.
|
||||||
|
|
||||||
### NAV Specific Example 2
|
### NAV Specific Example 2
|
||||||
|
|
||||||
|
|
@ -133,11 +133,11 @@ Implementation of chart part 1390 on the **Small Business Role Center** page (90
|
||||||
|
|
||||||
[![ ][image5]][anchor5]
|
[![ ][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.
|
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]
|
[![ ][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.
|
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.
|
Users can also change the period length.
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -54,7 +54,7 @@ The Change Observer table identifies the Subscriber using Server ID and Session
|
||||||
|
|
||||||
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.
|
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.
|
_**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.
|
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
|
```al
|
||||||
|
|
@ -112,7 +112,7 @@ Our Message Broker Codeunit will also serve as a central place to create Subscri
|
||||||
END;
|
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:
|
**__** 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
|
```al
|
||||||
//OnQueryClosePage(CloseAction : Action None) : Boolean**
|
//OnQueryClosePage(CloseAction : Action None) : Boolean**
|
||||||
|
|
|
||||||
|
|
@ -2,7 +2,7 @@
|
||||||
title = "Currently Active Record"
|
title = "Currently Active Record"
|
||||||
weight = 450
|
weight = 450
|
||||||
+++
|
+++
|
||||||
Authors: Henrik Langbak and Kim Ginnerup, Bording Data
|
_Authors: Henrik Langbak and Kim Ginnerup, Bording Data_
|
||||||
|
|
||||||
## Abstract
|
## Abstract
|
||||||
|
|
||||||
|
|
@ -65,7 +65,9 @@ Remember to set the link table property before you save it.
|
||||||
|
|
||||||
### 3. Implement the code
|
### 3. Implement the code
|
||||||
|
|
||||||
IF PriceView.FINDSET THEN // You have them
|
```al
|
||||||
|
IF PriceView.FINDSET THEN // You have them
|
||||||
|
```
|
||||||
|
|
||||||
### 4. Create a deployment codeunit
|
### 4. Create a deployment codeunit
|
||||||
|
|
||||||
|
|
@ -86,8 +88,8 @@ You need to deploy in three steps:
|
||||||
|
|
||||||
If you later want to change the view, you need to follow these rules:
|
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 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.
|
* 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
|
### Code example that accomplish the same but without using the pattern
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,9 +1,7 @@
|
||||||
+++
|
+++
|
||||||
title = "Data Migration Facade"
|
title = "Data Migration Façade"
|
||||||
weight = 480
|
weight = 480
|
||||||
+++
|
+++
|
||||||
# **Data Migration Façade**
|
|
||||||
|
|
||||||
_By David Bastide and Soumya Dutta at Microsoft Development Center Copenhagen_
|
_By David Bastide and Soumya Dutta at Microsoft Development Center Copenhagen_
|
||||||
|
|
||||||
[![ ][image0]][anchor0]
|
[![ ][image0]][anchor0]
|
||||||
|
|
@ -124,7 +122,7 @@ The following example shows how to migrate items without staging tables:
|
||||||
end;
|
end;
|
||||||
```
|
```
|
||||||
|
|
||||||
_Figure 2: Example of Item and Item Unit of Measure migration without staging tables _
|
_Figure 2: Example of Item and Item Unit of Measure migration without staging tables_
|
||||||
|
|
||||||
### Usage with staging tables:
|
### Usage with staging tables:
|
||||||
|
|
||||||
|
|
@ -139,10 +137,8 @@ The overall workflow is:
|
||||||
* From the event subscribers, call the façade procedures to create entities and set their field values:
|
* 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".CreateItemIfNeeded('ITEM042','My Item Description';'My Item Description 2';ItemTypeToSet::Inventory)**
|
||||||
*
|
* **"Item Data Migration Façade".SetBaseUnitOfMeasure('BOX')**
|
||||||
**"Item Data Migration Façade".SetBaseUnitOfMeasure('BOX')**
|
* **"Item Data Migration Façade".ModifyItem(true)**
|
||||||
*
|
|
||||||
**"Item Data Migration Façade".ModifyItem(true)**
|
|
||||||
|
|
||||||
[![ ][image2]][anchor2]
|
[![ ][image2]][anchor2]
|
||||||
|
|
||||||
|
|
@ -267,7 +263,7 @@ The following Entity data migration façade codeunits are available:
|
||||||
|
|
||||||
## References:
|
## References:
|
||||||
|
|
||||||
Façade pattern on Wikipedia: https://en.wikipedia.org/wiki/Facade\_pattern
|
Façade pattern on Wikipedia: https://en.wikipedia.org/wiki/Facade_pattern
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -2,7 +2,7 @@
|
||||||
title = "Discovery Event"
|
title = "Discovery Event"
|
||||||
weight = 500
|
weight = 500
|
||||||
+++
|
+++
|
||||||
by waldo
|
_by waldo_
|
||||||
|
|
||||||
# Abstract
|
# Abstract
|
||||||
|
|
||||||
|
|
@ -51,7 +51,7 @@ When you publish an event, it should obviously be raised somewhere in the code a
|
||||||
|
|
||||||
## 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
|
## 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:
|
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]
|
[![ ][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.
|
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.
|
||||||
|
|
|
||||||
|
|
@ -25,7 +25,7 @@ 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).
|
* 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"
|
* 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 ...)
|
**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)
|
* 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)
|
||||||
|
|
||||||
|
|
@ -63,7 +63,7 @@ Then simply add your subpage as new line in the designer, and adjust the propert
|
||||||
|
|
||||||
**[![ ][image1]][anchor1]**
|
**[![ ][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.
|
**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:
|
Once created:
|
||||||
|
|
||||||
|
|
@ -74,7 +74,7 @@ Save our page and add it to the Role Page "Order Processor Role Center" for exam
|
||||||
|
|
||||||
Now, observe how "Line No." is calculated on the first line, and when inserting a new line between the first and second one.
|
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
|
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
|
## NAV Usages
|
||||||
|
|
||||||
|
|
@ -98,7 +98,7 @@ Use Series No. Pattern for your documents, and Copy Document to implement docume
|
||||||
## References
|
## References
|
||||||
|
|
||||||
Walkthrough: Creating a Document Page : [http://msdn.microsoft.com/en-us/library/dd338599(v=nav.71).aspx][anchor2]
|
Walkthrough: Creating a Document Page : [http://msdn.microsoft.com/en-us/library/dd338599(v=nav.71).aspx][anchor2]
|
||||||
[][anchor2][watch?v=S9cRD2D4c0&list=PLhZ3P LY7CqmVszuvtJLujFyHpsVN0Uw&index=27][anchor3]
|
{{< youtube S9cRD2D4c0>}}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -17,7 +17,7 @@ To guide users, we can prompt them with a dialog on which they can update the se
|
||||||
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.
|
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:
|
Call the code. For example:
|
||||||
|
```AL
|
||||||
Local IsXAvailable : Boolean
|
Local IsXAvailable : Boolean
|
||||||
If field X <\> '' then
|
If field X <\> '' then
|
||||||
Exit(True)
|
Exit(True)
|
||||||
|
|
@ -39,7 +39,7 @@ The calling code
|
||||||
SetupTable.VerifyAndSetX
|
SetupTable.VerifyAndSetX
|
||||||
|
|
||||||
..
|
..
|
||||||
|
```
|
||||||
## NAV Specific Example
|
## NAV Specific Example
|
||||||
|
|
||||||
****
|
****
|
||||||
|
|
@ -53,10 +53,11 @@ 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).
|
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:
|
Were the code is called:
|
||||||
|
```AL
|
||||||
IF ("EAN No." <\> '') THEN
|
IF ("EAN No." <\> '') THEN
|
||||||
|
|
||||||
SalesSetup.VerifyAndSetOIOUBLPathSetup(SalesHeader."Document Type");
|
SalesSetup.VerifyAndSetOIOUBLPathSetup(SalesHeader."Document Type");
|
||||||
|
```
|
||||||
|
|
||||||
If the setup is not updated properly, the user is prompted to update it as follows.
|
If the setup is not updated properly, the user is prompted to update it as follows.
|
||||||
|
|
||||||
|
|
@ -82,7 +83,7 @@ The anti-pattern is to do a testfield on a field that is not in the table that y
|
||||||
|
|
||||||
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.
|
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.
|
||||||
|
|
||||||
[watch?v=oeASJN zqTo&list=PLhZ3P LY7CqmVszuvtJLujFyHpsVN0Uw&index=18][anchor2]
|
{{< youtube oeASJN-zqTo>}}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -14,8 +14,8 @@ Missing, invalid or incomplete data is a common issue during data processing in
|
||||||
|
|
||||||
This article describes how to use the Error Message component in NAV, which in short gives you the possibility to:
|
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.
|
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.
|
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.
|
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.
|
||||||
|
|
||||||
|
|
@ -31,13 +31,13 @@ By clicking on the error message, the user will be presented with the entity, wh
|
||||||
|
|
||||||
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:
|
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******
|
* **LogIfEmpty**
|
||||||
* **LogIfLengthExceeded******
|
* **LogIfLengthExceeded**
|
||||||
* **LogIfInvalidCharacters******
|
* **LogIfInvalidCharacters**
|
||||||
* **LogIfOutsideRange******
|
* **LogIfOutsideRange**
|
||||||
* **LogIfGreaterThan******
|
* **LogIfGreaterThan**
|
||||||
* **LogIfEqualTo******
|
* **LogIfEqualTo**
|
||||||
* **LogMessage******
|
* **LogMessage**
|
||||||
|
|
||||||
The following parameters must be provided to these functions:
|
The following parameters must be provided to these functions:
|
||||||
|
|
||||||
|
|
@ -52,17 +52,18 @@ When the processing is complete, you can check if any error messages of type "Er
|
||||||
|
|
||||||
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.
|
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);
|
PROCEDURE ExportChartOfAccounts@1(Year@1000 : Integer;Month@1001 : Integer);
|
||||||
VAR
|
VAR
|
||||||
**TempErrorMessage@1003 : TEMPORARY Record 700;
|
TempErrorMessage@1003 : TEMPORARY Record 700;
|
||||||
**BEGIN
|
BEGIN
|
||||||
**TempErrorMessage.ClearLog;** // only necessary if variable is global
|
TempErrorMessage.ClearLog; // only necessary if variable is global
|
||||||
...
|
...
|
||||||
CreateXMLHeader(Document,RootNode,CatalogoNodeTxt,Namespace,Year,Month,'1.1');
|
CreateXMLHeader(Document,RootNode,CatalogoNodeTxt,Namespace,Year,Month,'1.1');
|
||||||
IF GLAccount.FINDSET THEN BEGIN
|
IF GLAccount.FINDSET THEN BEGIN
|
||||||
REPEAT
|
REPEAT
|
||||||
**TempErrorMessage.LogIfEmpty (GLAccount,GLAccount.FIELDNO(Name),TempErrorMessage."Message Type"::Error);
|
TempErrorMessage.LogIfEmpty (GLAccount,GLAccount.FIELDNO(Name),TempErrorMessage."Message Type"::Error);
|
||||||
**
|
|
||||||
XMLDOMManagement.AddElement(RootNode,'Ctas','',Namespace,Node);
|
XMLDOMManagement.AddElement(RootNode,'Ctas','',Namespace,Node);
|
||||||
XMLDOMManagement.AddAttribute(Node,'CodAgrup',GLAccount."SAT Account Code");
|
XMLDOMManagement.AddAttribute(Node,'CodAgrup',GLAccount."SAT Account Code");
|
||||||
...
|
...
|
||||||
|
|
@ -72,18 +73,19 @@ The code below is an example of how the error message component was used in one
|
||||||
GLAccount."Debit/Credit"::Credit:
|
GLAccount."Debit/Credit"::Credit:
|
||||||
XMLDOMManagement.AddAttribute(Node,'Natur','A');
|
XMLDOMManagement.AddAttribute(Node,'Natur','A');
|
||||||
ELSE
|
ELSE
|
||||||
**TempErrorMessage.LogMessage(
|
TempErrorMessage.LogMessage(
|
||||||
GLAccount,GLAccount.FIELDNO("Debit/Credit"),TempErrorMessage."Message Type"::Error,
|
GLAccount,GLAccount.FIELDNO("Debit/Credit"),TempErrorMessage."Message Type"::Error,
|
||||||
STRSUBSTNO(GLAccountTypeErr,GLAccount."Debit/Credit",GLAccount.RECORDID));
|
STRSUBSTNO(GLAccountTypeErr,GLAccount."Debit/Credit",GLAccount.RECORDID));
|
||||||
** END;
|
END;
|
||||||
UNTIL GLAccount.NEXT = 0;
|
UNTIL GLAccount.NEXT = 0;
|
||||||
END ELSE
|
END ELSE
|
||||||
**TempErrorMessage.LogSimpleMessage(TempErrorMessage."Message Type"::Error,NoSATAccountDefinedErr);
|
TempErrorMessage.LogSimpleMessage(TempErrorMessage."Message Type"::Error,NoSATAccountDefinedErr);
|
||||||
**
|
|
||||||
**IF NOT TempErrorMessage.HasErrors(TRUE) THEN
|
IF NOT TempErrorMessage.HasErrors(TRUE) THEN
|
||||||
** SaveXMLToClient(Document,Year,Month,'CT');
|
SaveXMLToClient(Document,Year,Month,'CT');
|
||||||
**TempErrorMessage.ShowErrorMessages(FALSE);
|
TempErrorMessage.ShowErrorMessages(FALSE);
|
||||||
**END;
|
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.
|
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.
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -27,8 +27,7 @@ _
|
||||||
< --\[if gte mso 9\]\>< \[endif\]--\>< --\[if gte mso 9\]\>< \[endif\]--\>< --\[if gte mso 9\]\>< \[endif\]--\>< --\[if gte mso 10\]\>
|
< --\[if gte mso 9\]\>< \[endif\]--\>< --\[if gte mso 9\]\>< \[endif\]--\>< --\[if gte mso 9\]\>< \[endif\]--\>< --\[if gte mso 10\]\>
|
||||||
< \[endif\]--\>
|
< \[endif\]--\>
|
||||||
|
|
||||||
_Figure 1: Qualifier, Payload and emphasized text.
|
_Figure 1: Qualifier, Payload and emphasized text._
|
||||||
_
|
|
||||||
|
|
||||||
## Usage
|
## Usage
|
||||||
|
|
||||||
|
|
@ -149,11 +148,9 @@ To format headlines, you should use Codeunit 1439 Headline Management functions:
|
||||||
|
|
||||||
_Figure 2: Sequence diagram of headline usage_
|
_Figure 2: Sequence diagram of headline usage_
|
||||||
|
|
||||||
##
|
## Usages in NAV:
|
||||||
Usages in NAV:
|
|
||||||
|
|
||||||
* Essential Business Headlines extension_
|
* Essential Business Headlines extension
|
||||||
_
|
|
||||||
|
|
||||||
< --\[if gte mso 9\]\>< \[endif\]--\>< --\[if gte mso 9\]\>< \[endif\]--\>< --\[if gte mso 9\]\>< \[endif\]--\>< --\[if supportAnnotations\]--\>< --\[endif\]--\>< --\[if gte mso 10\]\>
|
< --\[if gte mso 9\]\>< \[endif\]--\>< --\[if gte mso 9\]\>< \[endif\]--\>< --\[if gte mso 9\]\>< \[endif\]--\>< --\[if supportAnnotations\]--\>< --\[endif\]--\>< --\[if gte mso 10\]\>
|
||||||
< \[endif\]--\>
|
< \[endif\]--\>
|
||||||
|
|
|
||||||
|
|
@ -63,6 +63,7 @@ The key question is: Where to tap into this flow when using a local data structu
|
||||||
|
|
||||||
In W1, the codeunit simply outputs the same set of general journal lines that it receives as an input:
|
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")
|
OnRun(VAR Rec : Record "Gen. Journal Line")
|
||||||
|
|
||||||
GenJnlLine.COPYFILTERS(Rec);
|
GenJnlLine.COPYFILTERS(Rec);
|
||||||
|
|
@ -92,6 +93,7 @@ LOCAL CreateTempJnlLines(VAR FromGenJnlLine : Record "Gen. Journal Line";VAR Tem
|
||||||
// To fill TempGenJnlLine from the source identified by filters set on FromGenJnlLine
|
// To fill TempGenJnlLine from the source identified by filters set on FromGenJnlLine
|
||||||
|
|
||||||
TempGenJnlLine := FromGenJnlLine;
|
TempGenJnlLine := FromGenJnlLine;
|
||||||
|
```
|
||||||
|
|
||||||
In a country, such as Italy, the codeunit will have the following functions:
|
In a country, such as Italy, the codeunit will have the following functions:
|
||||||
|
|
||||||
|
|
@ -103,6 +105,7 @@ In a country, such as Italy, the codeunit will have the following functions:
|
||||||
|
|
||||||
1. Outputs the temporary general journal lines that will be further processed and exported, exactly as in W1\.
|
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")
|
OnRun(VAR Rec : Record "Gen. Journal Line")
|
||||||
|
|
||||||
GenJnlLine.COPYFILTERS(Rec);
|
GenJnlLine.COPYFILTERS(Rec);
|
||||||
|
|
@ -222,6 +225,7 @@ Description := VendorBillLine.Description;
|
||||||
INSERT;
|
INSERT;
|
||||||
|
|
||||||
END;
|
END;
|
||||||
|
```
|
||||||
|
|
||||||
The derived local feature flow is as follows:
|
The derived local feature flow is as follows:
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -8,7 +8,7 @@ _By Eric Wauters ([waldo][anchor0]), Partner-Ready-Software_
|
||||||
|
|
||||||
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.
|
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
|
## 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.. .
|
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.. .
|
||||||
|
|
||||||
|
|
@ -28,42 +28,42 @@ I recommend to use this concept on:
|
||||||
|
|
||||||
## Usage
|
## 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:
|
**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)
|
* 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.
|
* 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:
|
**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 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 .. :-))
|
* 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..
|
* 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:
|
**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 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
|
* 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.
|
* 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.
|
**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
|
## 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:
|
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"
|
**Step 1**: create codeunit "Sales-Post Hook"
|
||||||
|
|
||||||
[![ ][image0]][anchor1]
|
[![ ][image0]][anchor1]
|
||||||
|
|
||||||
Step 2: create the hook function "OnBeforePostDocument"
|
**Step 2**: create the hook function "OnBeforePostDocument"
|
||||||
|
|
||||||
[![ ][image1]][anchor2]
|
[![ ][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.
|
**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]
|
[![ ][image2]][anchor3]
|
||||||
|
|
||||||
Step 4: implement the business logic, by calling out to a new function. And implement the test-codeunit.
|
**Step 4**: implement the business logic, by calling out to a new function. And implement the test-codeunit.
|
||||||
|
|
||||||
[![ ][image3]][anchor4]
|
[![ ][image3]][anchor4]
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -2,7 +2,7 @@
|
||||||
title = "Surrogate keys using Autoincrement Pattern"
|
title = "Surrogate keys using Autoincrement Pattern"
|
||||||
weight = 630
|
weight = 630
|
||||||
+++
|
+++
|
||||||
_By [Soren Klemmensen][anchor0], [_Partner-Ready-Software_ ][anchor1]& [360 Visibility][anchor2]_
|
_By [Soren Klemmensen][anchor0], [_Partner-Ready-Software_ ][anchor1] & [360 Visibility][anchor2]_
|
||||||
|
|
||||||
## **Abstract**
|
## **Abstract**
|
||||||
|
|
||||||
|
|
@ -137,7 +137,7 @@ Other risks could be if Transfer Fields are used and data is being copied uninte
|
||||||
|
|
||||||
This is a new pattern not currently used in Microsoft Dynamics NAV.
|
This is a new pattern not currently used in Microsoft Dynamics NAV.
|
||||||
|
|
||||||
## **Ideas for improvement ******
|
## **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.
|
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.
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -42,7 +42,7 @@ Dismissible dialogs show the instructional message about the functionality, with
|
||||||
|
|
||||||
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.
|
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.
|
**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
|
## FastTabs with Instructional Text
|
||||||
|
|
||||||
|
|
@ -76,21 +76,21 @@ Example of the embed code:
|
||||||
|
|
||||||
<iframe width="560" height="315" src="//www.youtube.com/embed/7SGp9pA9cAY" frameborder="0" allowfullscreen\></iframe\>
|
<iframe width="560" height="315" src="//www.youtube.com/embed/7SGp9pA9cAY" frameborder="0" allowfullscreen\></iframe\>
|
||||||
|
|
||||||
You must assign**src attribute **to src of the embed code, for example:
|
You must assign **src attribute** to src of the embed code, for example:
|
||||||
|
|
||||||
VideoPlayer.SetFrameAttribute('src', 'https://www.youtube.com/embed/7SGp9pA9cAY');
|
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:
|
Without this, the video will not play. You can use the same function to assign other attributes, for example to remove frame border use:
|
||||||
|
|
||||||
VideoPlayer.SetFrameAttribute(' frameborder', '0');
|
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.
|
Height and width should be set by using the following functions, since they ensure that the video will be centered on the page.
|
||||||
|
|
||||||
VideoPlayerAddIn.SetHeight(Height) and VideoPlayerAddIn.SetWidth(Width);
|
VideoPlayerAddIn.SetHeight(Height) and VideoPlayerAddIn.SetWidth(Width);
|
||||||
|
|
||||||
If you would like to reuse the **Mini Video Player Page** page (1395), then use:
|
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.
|
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
|
4\. As a last step you need to implement the action on the group and assign a video icon
|
||||||
|
|
||||||
|
|
@ -136,7 +136,7 @@ Provide the support for the invoking any Help topics (URL on the Help Server fro
|
||||||
|
|
||||||
Implement tooltips across the application and in all country versions. (Requires a run-time infrastructure system.)
|
Implement tooltips across the application and in all country versions. (Requires a run-time infrastructure system.)
|
||||||
|
|
||||||
[watch?v=loobQ1TVO3o&list=PLhZ3P LY7CqmVszuvtJLujFyHpsVN0Uw&index=14][anchor6]
|
{{< youtube loobQ1TVO3o>}}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -2,7 +2,7 @@
|
||||||
title = "Integration of Addresses"
|
title = "Integration of Addresses"
|
||||||
weight = 680
|
weight = 680
|
||||||
+++
|
+++
|
||||||
[watch?v=60Wrx9N gfY&list=PLhZ3P LY7CqmVszuvtJLujFyHpsVN0Uw&index=19][anchor0]
|
{{< youtube 60Wrx9N-gfY>}}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -118,6 +118,7 @@ Following the flow above, the code (in the SEPA Credit Transfer example) is as f
|
||||||
|
|
||||||
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.
|
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)
|
CreateNew(GenJnlLine : Record "Gen. Journal Line";NewText : Text)
|
||||||
|
|
||||||
SetLineFilters(GenJnlLine);
|
SetLineFilters(GenJnlLine);
|
||||||
|
|
@ -161,6 +162,7 @@ DeleteJnlBatchErrors(GenJnlLine : Record "Gen. Journal Line")
|
||||||
IF JnlBatchHasErrors(GenJnlLine) THEN
|
IF JnlBatchHasErrors(GenJnlLine) THEN
|
||||||
|
|
||||||
DELETEALL;
|
DELETEALL;
|
||||||
|
```
|
||||||
|
|
||||||
## NAV Usages
|
## NAV Usages
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -119,29 +119,30 @@ The General Journals Batches page (251) is linked to the Gen. Journal Batch sour
|
||||||
|
|
||||||
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:
|
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:
|
||||||
|
|
||||||
Lookup("Gen. Journal Template".Recurring WHERE (Name=FIELD(Journal Template Name)))
|
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:
|
Similarly, the Template Type field is a FlowField that gets its value from the parent table:
|
||||||
|
|
||||||
Lookup("Gen. Journal Template".Type WHERE (Name=FIELD(Journal Template Name)))
|
Lookup("Gen. Journal Template".Type WHERE (Name=FIELD(Journal Template Name)))
|
||||||
|
|
||||||
### Setting up a New Batch
|
### 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:
|
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:
|
||||||
|
|
||||||
"Bal. Account Type" := GenJnlTemplate."Bal. Account Type";
|
"Bal. Account Type" := GenJnlTemplate."Bal. Account Type";
|
||||||
|
|
||||||
"Bal. Account No." := GenJnlTemplate."Bal. Account No.";
|
"Bal. Account No." := GenJnlTemplate."Bal. Account No.";
|
||||||
|
|
||||||
"No. Series" := GenJnlTemplate."No. Series";
|
"No. Series" := GenJnlTemplate."No. Series";
|
||||||
|
|
||||||
"Posting No. Series" := GenJnlTemplate."Posting No. Series";
|
"Posting No. Series" := GenJnlTemplate."Posting No. Series";
|
||||||
|
|
||||||
"Reason Code" := GenJnlTemplate."Reason Code";
|
"Reason Code" := GenJnlTemplate."Reason Code";
|
||||||
|
|
||||||
"Copy VAT Setup to Jnl. Lines" := GenJnlTemplate."Copy VAT Setup to Jnl. Lines";
|
"Copy VAT Setup to Jnl. Lines" := GenJnlTemplate."Copy VAT Setup to Jnl. Lines";
|
||||||
|
|
||||||
"Allow VAT Difference" := GenJnlTemplate."Allow VAT Difference";
|
"Allow VAT Difference" := GenJnlTemplate."Allow VAT Difference";
|
||||||
|
|
||||||
### Cascade record deletion
|
### Cascade record deletion
|
||||||
|
|
||||||
|
|
@ -174,7 +175,7 @@ References
|
||||||
|
|
||||||
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.
|
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.
|
||||||
|
|
||||||
[watch?v=xtsZ5beNdZg&list=PLhZ3P LY7CqmVszuvtJLujFyHpsVN0Uw&index=12][anchor7]
|
{{< youtube xtsZ5beNdZg>}}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -2,7 +2,7 @@
|
||||||
title = "Master Data"
|
title = "Master Data"
|
||||||
weight = 780
|
weight = 780
|
||||||
+++
|
+++
|
||||||
__By [Soren Klemmensen][anchor0], [_Partner-Ready-Software_ ][anchor1]& [360 Visibility][anchor2]__
|
_By [Soren Klemmensen][anchor0], [_Partner-Ready-Software_ ][anchor1] & [360 Visibility][anchor2]_
|
||||||
|
|
||||||
# Abstract
|
# Abstract
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -2,7 +2,7 @@
|
||||||
title = "Multi-file Download"
|
title = "Multi-file Download"
|
||||||
weight = 800
|
weight = 800
|
||||||
+++
|
+++
|
||||||
#### _By Martin Dam at Microsoft Development Center Copenhagen_
|
_By Martin Dam at Microsoft Development Center Copenhagen_
|
||||||
|
|
||||||
## Abstract
|
## Abstract
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -58,37 +58,36 @@ For example, the NAV page Sales List (page ID 45), which displays the Sales Head
|
||||||
|
|
||||||
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:
|
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:
|
||||||
|
|
||||||
CASE "Document Type" OF
|
CASE "Document Type" OF
|
||||||
|
|
||||||
"Document Type"::Quote:
|
"Document Type"::Quote:
|
||||||
|
|
||||||
PAGE.RUN(PAGE::"Sales Quote",Rec);
|
PAGE.RUN(PAGE::"Sales Quote",Rec);
|
||||||
|
|
||||||
"Document Type"::Order:
|
"Document Type"::Order:
|
||||||
|
|
||||||
PAGE.RUN(PAGE::"Sales Order",Rec);
|
PAGE.RUN(PAGE::"Sales Order",Rec);
|
||||||
|
|
||||||
"Document Type"::Invoice:
|
"Document Type"::Invoice:
|
||||||
|
|
||||||
PAGE.RUN(PAGE::"Sales Invoice",Rec);
|
PAGE.RUN(PAGE::"Sales Invoice",Rec);
|
||||||
|
|
||||||
"Document Type"::"Return Order":
|
"Document Type"::"Return Order":
|
||||||
|
|
||||||
PAGE.RUN(PAGE::"Sales Return Order",Rec);
|
PAGE.RUN(PAGE::"Sales Return Order",Rec);
|
||||||
|
|
||||||
"Document Type"::"Credit Memo":
|
"Document Type"::"Credit Memo":
|
||||||
|
|
||||||
PAGE.RUN(PAGE::"Sales Credit Memo",Rec);
|
PAGE.RUN(PAGE::"Sales Credit Memo",Rec);
|
||||||
|
|
||||||
"Document Type"::"Blanket Order":[
|
"Document Type"::"Blanket Order":[
|
||||||
][anchor2]
|
][anchor2]
|
||||||
|
|
||||||
PAGE.RUN(PAGE::"Blanket Sales Order",Rec);
|
PAGE.RUN(PAGE::"Blanket Sales Order",Rec);
|
||||||
|
|
||||||
END;
|
END;
|
||||||
|
|
||||||
### **NAV Usages**[
|
### **NAV Usages**[ ][anchor3]
|
||||||
][anchor3]
|
|
||||||
|
|
||||||
Some of the NAV implementations of this pattern can be found in the following pages:
|
Some of the NAV implementations of this pattern can be found in the following pages:
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -74,7 +74,7 @@ Some of the NAV implementations of this pattern are:
|
||||||
|
|
||||||
## Related Topics
|
## 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**
|
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**
|
**Multilanguage Application Data**
|
||||||
**Extended Text**
|
**Extended Text**
|
||||||
**Supports translation of application data**
|
**Supports translation of application data**
|
||||||
|
|
|
||||||
|
|
@ -154,7 +154,7 @@ Where:
|
||||||
* **DefaultNoSeriesCode** is typically found in the application domain setup table.
|
* **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"**
|
* **OldNoSeriesCode** is typically the previous value of the "No. Series" code, which is found in **xRec."No. Series"**
|
||||||
* **NewDate** is typically 0D (empty date)
|
* **NewDate** is typically 0D (empty date)
|
||||||
* NewNo and **NewNoSeriesCode** are the current values found in **"No." ** and **"No. Series"**
|
* NewNo and **NewNoSeriesCode** are the current values found in **"No."** and **"No. Series"**
|
||||||
|
|
||||||
## NAV Usages
|
## NAV Usages
|
||||||
|
|
||||||
|
|
@ -254,7 +254,7 @@ IF AssistEdit(xRec) THEN
|
||||||
|
|
||||||
CurrPage.UPDATE;
|
CurrPage.UPDATE;
|
||||||
|
|
||||||
[watch?v=1lG9rYdmM4&list=PLhZ3P LY7CqmVszuvtJLujFyHpsVN0Uw&index=8][anchor3]
|
{{< youtube 1lG9rY_dmM4>}}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -2,7 +2,7 @@
|
||||||
title = "In-context Notifications"
|
title = "In-context Notifications"
|
||||||
weight = 640
|
weight = 640
|
||||||
+++
|
+++
|
||||||
___By Soumya Dutta at Microsoft Development Center Copenhagen_
|
_By Soumya Dutta at Microsoft Development Center Copenhagen_
|
||||||
|
|
||||||
## Context
|
## Context
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -2,11 +2,9 @@
|
||||||
title = "Notification Lifecycle Management Pattern"
|
title = "Notification Lifecycle Management Pattern"
|
||||||
weight = 880
|
weight = 880
|
||||||
+++
|
+++
|
||||||
**Notification Lifecycle Management pattern**
|
_By David Bastide at Microsoft Development Center Copenhagen_
|
||||||
|
|
||||||
_ By David Bastide at Microsoft Development Center Copenhagen _
|
[![ ][image0]][anchor0]
|
||||||
|
|
||||||
[![ ][image0]][anchor0]
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -26,11 +24,11 @@ However, some cases can be more complicated. For example, when you are adding li
|
||||||
|
|
||||||
[![ ][image1]][anchor1]
|
[![ ][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. _
|
_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]
|
[![ ][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. _
|
_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:
|
Here is the code for this behavior:
|
||||||
|
|
||||||
|
|
@ -132,7 +130,7 @@ The simple case is:
|
||||||
|
|
||||||
[![ ][image4]][anchor4]
|
[![ ][image4]][anchor4]
|
||||||
|
|
||||||
_Figure 4: without additional context****_
|
_Figure 4: without additional context_
|
||||||
|
|
||||||
[![ ][image5]][anchor5]
|
[![ ][image5]][anchor5]
|
||||||
|
|
||||||
|
|
@ -156,7 +154,7 @@ The realistic case is:
|
||||||
|
|
||||||
[![ ][image6]][anchor6]
|
[![ ][image6]][anchor6]
|
||||||
|
|
||||||
_Figure 6: delayed insert, with additional context _
|
_Figure 6: delayed insert, with additional context_
|
||||||
|
|
||||||
**Usages in NAV:**
|
**Usages in NAV:**
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -2,7 +2,7 @@
|
||||||
title = "Observer"
|
title = "Observer"
|
||||||
weight = 900
|
weight = 900
|
||||||
+++
|
+++
|
||||||
#### _By Nikolai L'Estrange, from TVision Technology Ltd. in the UK__
|
_By Nikolai L'Estrange, from TVision Technology Ltd. in the UK_
|
||||||
_
|
_
|
||||||
|
|
||||||
### Abstract
|
### Abstract
|
||||||
|
|
|
||||||
|
|
@ -16,7 +16,7 @@ For a processing routine to behave correctly, it needs sometimes input of a user
|
||||||
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
|
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
|
3. The processing codeunit receives the processing table, and therefor also the parameters
|
||||||
|
|
||||||
**[![ ][image0]][anchor0]**
|
[![ ][image0]][anchor0]
|
||||||
|
|
||||||
## Usage
|
## Usage
|
||||||
|
|
||||||
|
|
@ -65,7 +65,7 @@ On the other hand, as we are handling tables as being "classes" in many cases, i
|
||||||
|
|
||||||
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.
|
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.
|
||||||
|
|
||||||
[watch?v=SxywT2XSpcI&list=PLhZ3P LY7CqmVszuvtJLujFyHpsVN0Uw&index=7][anchor4]
|
{{< youtube SxywT2XSpcI>}}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -16,7 +16,7 @@ When you want to refer to product name in informational messages or errors, it's
|
||||||
|
|
||||||
Instead of hardcoding the product name like "Dynamics NAV 2017", it's recommended to use **ProductName** System Object that platform provides.
|
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.
|
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.
|
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.
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -23,11 +23,11 @@ Let's consider the **VAT Entry** table as below: [
|
||||||
|
|
||||||
[![ ][image0]][anchor1]
|
[![ ][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.
|
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**
|
#### **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.
|
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]
|
[![ ][image1]][anchor2]
|
||||||
|
|
||||||
|
|
@ -45,7 +45,7 @@ Running the query yields a single record per document. You can notice in the sec
|
||||||
|
|
||||||
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.
|
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.
|
**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]
|
[![ ][image4]][anchor5]
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,9 +1,8 @@
|
||||||
+++
|
+++
|
||||||
title = "use Queries to Detect Duplicate Records"
|
title = "Use Queries to Detect Duplicate Records"
|
||||||
weight = 1340
|
weight = 1340
|
||||||
+++
|
+++
|
||||||
_Originally by Abshishek Ghosh and Bogdan Sturzoiu at Microsoft Development Center Copenhagen**
|
_Originally by Abshishek Ghosh and Bogdan Sturzoiu at Microsoft Development Center Copenhagen_
|
||||||
**_
|
|
||||||
|
|
||||||
## **Abstract**
|
## **Abstract**
|
||||||
|
|
||||||
|
|
@ -33,13 +32,13 @@ Before Dynamics NAV 2013, the only possibility was to iterate through the table
|
||||||
|
|
||||||
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.
|
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.****
|
Starting with Dynamics NAV 2013, we can use queries to create a more efficient implementation of the same logic.
|
||||||
|
|
||||||
## **Usage**
|
## **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.
|
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**
|
**Step 1** -- **Creating the Query**
|
||||||
|
|
||||||
* The query must be created with the table we want to search in as the dataitem.
|
* 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.
|
* The field we want to search for must be created as a grouped field.
|
||||||
|
|
@ -84,7 +83,7 @@ Here is an alternate implementation of the **HasDuplicateCustomers** method usin
|
||||||
|
|
||||||
**Examples**
|
**Examples**
|
||||||
|
|
||||||
* **Acc. Sched. Chart Management **codeunit (762),
|
* **Acc. Sched. Chart Management** codeunit (762),
|
||||||
methods CheckDuplicateAccScheduleLineDescription and CheckDuplicateColumnLayoutColumnHeader
|
methods CheckDuplicateAccScheduleLineDescription and CheckDuplicateColumnLayoutColumnHeader
|
||||||
* **Analysis Report Chart Mgt.** codeunit (770),
|
* **Analysis Report Chart Mgt.** codeunit (770),
|
||||||
methods CheckDuplicateAnalysisLineDescription and CheckDuplicateAnalysisColumnHeader
|
methods CheckDuplicateAnalysisLineDescription and CheckDuplicateAnalysisColumnHeader
|
||||||
|
|
|
||||||
|
|
@ -2,7 +2,7 @@
|
||||||
title = "Use Queries to Replace Nested Loops"
|
title = "Use Queries to Replace Nested Loops"
|
||||||
weight = 1350
|
weight = 1350
|
||||||
+++
|
+++
|
||||||
#### _Originally by Bogdan Sturzoiu, Microsoft Development Center Copenhagen_
|
_Originally by Bogdan Sturzoiu, Microsoft Development Center Copenhagen_
|
||||||
|
|
||||||
## **Abstract**
|
## **Abstract**
|
||||||
|
|
||||||
|
|
@ -28,7 +28,7 @@ The join operation has traditionally been done in C/AL by record looping. When N
|
||||||
|
|
||||||
[![ ][image0]][anchor0]
|
[![ ][image0]][anchor0]
|
||||||
|
|
||||||
Figure 1\. The pattern elements
|
_Figure 1\. The pattern elements_
|
||||||
|
|
||||||
### **Usage**
|
### **Usage**
|
||||||
|
|
||||||
|
|
@ -72,7 +72,7 @@ The classic C/AL approach is to:
|
||||||
END;
|
END;
|
||||||
|
|
||||||
|
|
||||||
******Solution Using Query**
|
**Solution Using Query**
|
||||||
|
|
||||||
The new query-based approach involves:
|
The new query-based approach involves:
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -2,8 +2,7 @@
|
||||||
title = "Read-once Initialization and Validation"
|
title = "Read-once Initialization and Validation"
|
||||||
weight = 970
|
weight = 970
|
||||||
+++
|
+++
|
||||||
_By Tim Grant**
|
_By Tim Grant_
|
||||||
**_
|
|
||||||
|
|
||||||
## **Abstract**
|
## **Abstract**
|
||||||
|
|
||||||
|
|
@ -39,7 +38,7 @@ E.g. If there is a bespoke change, then this existing **GetGLSetup** can be call
|
||||||
|
|
||||||
[][anchor3][![ ][image4]][anchor4]
|
[][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.****
|
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.
|
||||||
|
|
||||||
****
|
****
|
||||||
|
|
||||||
|
|
@ -47,8 +46,7 @@ This means that by the time the code reaches the original **GetGLSetup** code re
|
||||||
|
|
||||||
Merge **Singleton Table** with dynamic validation field meta configuration. This removes the coding of **TESTFIELD** to a central setup check.
|
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****
|
Example with the **Sales & Receivables Setup**
|
||||||
**
|
|
||||||
|
|
||||||
[![ ][image5]][anchor5]
|
[![ ][image5]][anchor5]
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -2,7 +2,7 @@
|
||||||
title = "Released Entity"
|
title = "Released Entity"
|
||||||
weight = 1000
|
weight = 1000
|
||||||
+++
|
+++
|
||||||
Authors: Henrik Langbak and Kim Ginnerup, Bording Data
|
_Authors: Henrik Langbak and Kim Ginnerup, Bording Data_
|
||||||
|
|
||||||
## **Abstract**
|
## **Abstract**
|
||||||
|
|
||||||
|
|
@ -53,7 +53,7 @@ The important and critical part of this pattern is that the whole application ne
|
||||||
|
|
||||||
## **Usage**
|
## **Usage**
|
||||||
|
|
||||||
## To use this pattern you need to create an Option Field named: "Release State" with at least the two states: Open, Released.
|
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
|
### Automatic pending or release
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -48,7 +48,7 @@ If the Report Selection is related to a Test Report relating to a Document:
|
||||||
|
|
||||||
4) Print after posting. If the Report Selection is related to an existing Document (posted):
|
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.
|
* The specific document related posted header table is used along with a typical function: **PrintRecords** within the respective table.
|
||||||
|
|
||||||
## **Example**
|
## **Example**
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,9 +1,7 @@
|
||||||
+++
|
+++
|
||||||
title = "1-sensitive-data-encapsulation.md"
|
title = "Sensitive Data Encapsulation"
|
||||||
weight = 120
|
weight = 120
|
||||||
+++
|
+++
|
||||||
## Sensitive Data Encapsulation
|
|
||||||
|
|
||||||
_By Bogdana Botez at Microsoft Development Center Copenhagen_
|
_By Bogdana Botez at Microsoft Development Center Copenhagen_
|
||||||
|
|
||||||
_
|
_
|
||||||
|
|
@ -40,7 +38,7 @@ To apply this pattern in Dynamics NAV, a table structure similar to Table 1261 S
|
||||||
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:
|
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.
|
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).
|
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 Data Encapsulation][image1]][anchor3]
|
||||||
|
|
||||||
|
|
@ -72,13 +70,13 @@ _Figure 2 - Data access before and after **Sensitive** **Data** **Encapsulation.
|
||||||
|
|
||||||
* **Clarity:** when all sensitive data is encapsulated in one place, it is clear which is the information that needs to be protected.
|
* **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.
|
* **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.****
|
* **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\]
|
* **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.
|
* **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:**
|
**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.
|
* **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.
|
* **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.
|
* **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.
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,9 +1,7 @@
|
||||||
+++
|
+++
|
||||||
title = "2-data-encryption.md"
|
title = "Data Encryption"
|
||||||
weight = 140
|
weight = 140
|
||||||
+++
|
+++
|
||||||
## Encryption
|
|
||||||
|
|
||||||
_By Bogdana Botez at Microsoft Development Center Copenhagen_
|
_By Bogdana Botez at Microsoft Development Center Copenhagen_
|
||||||
|
|
||||||
__
|
__
|
||||||
|
|
@ -38,9 +36,9 @@ _Figure 1- **Encryption** becomes easier after first applying [**Sensitive Data
|
||||||
|
|
||||||
Table 1- Encryption functionality in Dynamics NAV (found in codeunit 1266 Encryption Management)
|
Table 1- Encryption functionality in Dynamics NAV (found in codeunit 1266 Encryption Management)
|
||||||
|
|
||||||
**Procedure******
|
**Procedure**
|
||||||
|
|
||||||
**Description******
|
**Description**
|
||||||
|
|
||||||
**EnableEncryption**
|
**EnableEncryption**
|
||||||
|
|
||||||
|
|
@ -140,7 +138,7 @@ _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.).
|
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:
|
**NAV Usages.** Encryption examples can be found in NAV in the following places:
|
||||||
|
|
||||||
* Page 1260 Bank Data Conv. Service Setup
|
* Page 1260 Bank Data Conv. Service Setup
|
||||||
* Page 1270 OCR Service Setup
|
* Page 1270 OCR Service Setup
|
||||||
|
|
@ -154,7 +152,7 @@ In the end, Stan chooses a location for the encryption key on disk, in a standar
|
||||||
**Anti-patterns:**
|
**Anti-patterns:**
|
||||||
|
|
||||||
* **Performance:** Do not encrypt everything, because it will have a performance impact on the system. Only important information should be encrypted.
|
* **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 <action\>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 calls and "Do It Yourself" solutions**: Although system calls for encryptions are available in NAV (ENCRYPT, DECRYPT and <action\>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.
|
* **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**
|
**References**
|
||||||
|
|
|
||||||
|
|
@ -1,9 +1,7 @@
|
||||||
+++
|
+++
|
||||||
title = "3-single-point-of-access.md"
|
title = "Single Point of Access"
|
||||||
weight = 160
|
weight = 160
|
||||||
+++
|
+++
|
||||||
## Single Point of Access
|
|
||||||
|
|
||||||
_By Bogdana Botez at Microsoft Development Center Copenhagen_
|
_By Bogdana Botez at Microsoft Development Center Copenhagen_
|
||||||
|
|
||||||
_[![ ][image0]][anchor0]
|
_[![ ][image0]][anchor0]
|
||||||
|
|
@ -19,8 +17,8 @@ __
|
||||||
|
|
||||||
**Forces:**
|
**Forces:**
|
||||||
|
|
||||||
* **Code duplication: **if each entity attempts to write its own routines for data access, invariably this will bring duplication.
|
* **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
|
* **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.
|
* **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).
|
* **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.
|
* **"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.
|
||||||
|
|
@ -33,7 +31,7 @@ You can observe that, in panel 3, each usage needs to access separately the encr
|
||||||
|
|
||||||
[![ ][image1]][anchor4]
|
[![ ][image1]][anchor4]
|
||||||
|
|
||||||
_Figure 1- **Single Point of Access **pattern applied._
|
_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.
|
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.
|
||||||
|
|
||||||
|
|
@ -43,9 +41,9 @@ This shows how pattern application is an iterative process, where one step follo
|
||||||
|
|
||||||
* **Simplicity**: only one implementation exists.
|
* **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:
|
* **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.
|
* **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.
|
* **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.
|
* **Easy maintenance**: a change in specification needs only one code update.
|
||||||
|
|
||||||
**Consequences:**
|
**Consequences:**
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,9 +1,7 @@
|
||||||
+++
|
+++
|
||||||
title = "4-masked-text.md"
|
title = "Masked Text"
|
||||||
weight = 180
|
weight = 180
|
||||||
+++
|
+++
|
||||||
## Masked Text
|
|
||||||
|
|
||||||
_By Bogdana Botez at Microsoft Development Center Copenhagen_
|
_By Bogdana Botez at Microsoft Development Center Copenhagen_
|
||||||
|
|
||||||
_[![ ][image0]][anchor0]
|
_[![ ][image0]][anchor0]
|
||||||
|
|
@ -25,7 +23,7 @@ __
|
||||||
|
|
||||||
**Solution:** Use the "Masked" field property to display dots instead of characters on the sensitive text field 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.
|
**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]
|
[![ ][image1]][anchor1]
|
||||||
|
|
||||||
|
|
@ -33,11 +31,11 @@ _Figure 1 - The field "Password" is masked._
|
||||||
|
|
||||||
To apply this pattern in Dynamics NAV, the developer has two choices:
|
To apply this pattern in Dynamics NAV, the developer has two choices:
|
||||||
|
|
||||||
**1\. ****Mask everywhere**
|
**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.
|
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**
|
**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.
|
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.
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,9 +1,7 @@
|
||||||
+++
|
+++
|
||||||
title = "5-ssl-in-nav.md"
|
title = "SSL in NAV"
|
||||||
weight = 190
|
weight = 190
|
||||||
+++
|
+++
|
||||||
## SSL in NAV
|
|
||||||
|
|
||||||
_By Bogdana Botez at Microsoft Development Center Copenhagen_
|
_By Bogdana Botez at Microsoft Development Center Copenhagen_
|
||||||
|
|
||||||
_[![ ][image0]][anchor0]
|
_[![ ][image0]][anchor0]
|
||||||
|
|
@ -17,7 +15,7 @@ __
|
||||||
|
|
||||||
**Forces:**
|
**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.
|
* **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?
|
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?
|
||||||
|
|
||||||
|
|
@ -31,9 +29,6 @@ Figure 1 shows data communication between the client (where the user enters data
|
||||||
|
|
||||||
_Figure 1 - Data transmission before (http://...) and after SSL encryption (https://...)._
|
_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].
|
**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:**
|
**Benefits:**
|
||||||
|
|
|
||||||
|
|
@ -2,12 +2,9 @@
|
||||||
title = "Security"
|
title = "Security"
|
||||||
weight = 1030
|
weight = 1030
|
||||||
+++
|
+++
|
||||||
## **Security patterns for NAV
|
|
||||||
**
|
|
||||||
|
|
||||||
_By Bogdana Botez at Microsoft Development Center Copenhagen_
|
_By Bogdana Botez at Microsoft Development Center Copenhagen_
|
||||||
|
|
||||||
### ****
|
## **Security patterns for NAV**
|
||||||
|
|
||||||
### **Problem statement**
|
### **Problem statement**
|
||||||
|
|
||||||
|
|
@ -8,8 +8,7 @@ _By Jan Hoek at IDYN_
|
||||||
|
|
||||||
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.
|
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
|
## **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.
|
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.
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -2,20 +2,18 @@
|
||||||
title = "Singleton"
|
title = "Singleton"
|
||||||
weight = 1090
|
weight = 1090
|
||||||
+++
|
+++
|
||||||
## Singleton
|
|
||||||
|
|
||||||
_By Bogdana Botez at Microsoft Development Center Copenhagen_
|
_By Bogdana Botez at Microsoft Development Center Copenhagen_
|
||||||
|
|
||||||
[![ ][image0]][anchor0]
|
[![ ][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].**
|
**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.
|
**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:**
|
**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
|
* **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.
|
* **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].
|
**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].
|
||||||
|
|
||||||
|
|
@ -26,7 +24,7 @@ The implementation varies depending on the technology and language used. In obje
|
||||||
* **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.
|
* **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.
|
* **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\.
|
**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\.
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -2,13 +2,10 @@
|
||||||
title = "Singleton Codeunit"
|
title = "Singleton Codeunit"
|
||||||
weight = 1100
|
weight = 1100
|
||||||
+++
|
+++
|
||||||
## Singleton Codeunit
|
_By Bogdana Botez at Microsoft Development Center Copenhagen_
|
||||||
|
|
||||||
_By Bogdana Botez at Microsoft Development Center Copenhagen
|
[![ ][image0]][anchor0]
|
||||||
_
|
|
||||||
|
|
||||||
_[![ ][image0]][anchor0]
|
|
||||||
_
|
|
||||||
|
|
||||||
**Problem**: In some situations, global state needs to be preserved at runtime throughout a session.
|
**Problem**: In some situations, global state needs to be preserved at runtime throughout a session.
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -28,13 +28,13 @@ In a functionality that is large enough (such as sales, inventory, fixed) you ma
|
||||||
|
|
||||||
**Implementation**
|
**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.
|
**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\.
|
**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
|
**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
|
**4\. Use:** Access the rule in code and use it
|
||||||
|
|
||||||
****
|
****
|
||||||
|
|
||||||
|
|
@ -87,7 +87,7 @@ The **Singleton Table** has two established applications in Dynamics NAV:
|
||||||
|
|
||||||
YouTube Video of NAV Singleton:
|
YouTube Video of NAV Singleton:
|
||||||
|
|
||||||
[watch?v=aQPu s9FkYI&list=PLhZ3P LY7CqmVszuvtJLujFyHpsVN0Uw&index=13][anchor3]
|
{{< youtube aQPu-s9FkYI>}}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -2,13 +2,10 @@
|
||||||
title = "Cue Table"
|
title = "Cue Table"
|
||||||
weight = 440
|
weight = 440
|
||||||
+++
|
+++
|
||||||
## Cue Table
|
_By Bogdana Botez at Microsoft Development Center Copenhagen_
|
||||||
|
|
||||||
_By Bogdana Botez at Microsoft Development Center Copenhagen
|
[![ ][image0]][anchor0]
|
||||||
_
|
|
||||||
|
|
||||||
_[![ ][image0]][anchor0]
|
|
||||||
_
|
|
||||||
|
|
||||||
Cues are the second usual application of the [**Singleton Table**][anchor1] pattern in Dynamics NAV, after [**Setup Tables**][anchor2].
|
Cues are the second usual application of the [**Singleton Table**][anchor1] pattern in Dynamics NAV, after [**Setup Tables**][anchor2].
|
||||||
|
|
||||||
|
|
@ -22,8 +19,6 @@ The overview information consists of summed-up numbers, calculated from business
|
||||||
|
|
||||||
**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.
|
**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.
|
**Solution:** Store overview information in a singleton table.
|
||||||
|
|
||||||
|
|
@ -37,14 +32,13 @@ There are two ways of calculating overview information in NAV.
|
||||||
* * The way to calculate the overview is too complex for flow fields, or
|
* * 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 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].
|
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**
|
**NAV Usages**
|
||||||
|
|
||||||
Table 1 - Cue tables in Dynamics NAV****shows some examples of singleton tables used for creating Cues.
|
_Table 1 - Cue tables in Dynamics NAV_ shows some examples of singleton tables used for creating Cues.
|
||||||
|
|
||||||
Table ID
|
Table ID
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -2,11 +2,9 @@
|
||||||
title = "Setup Table"
|
title = "Setup Table"
|
||||||
weight = 1070
|
weight = 1070
|
||||||
+++
|
+++
|
||||||
## Setup Table
|
|
||||||
|
|
||||||
_By Abhishek Ghosh, at Microsoft Development Center Copenhagen_
|
_By Abhishek Ghosh, at Microsoft Development Center Copenhagen_
|
||||||
|
|
||||||
## [![ ][image0]][anchor0]
|
[![ ][image0]][anchor0]
|
||||||
|
|
||||||
This is the first and most well-known of the two usual applications of the **Singleton Table** pattern in Dynamics NAV.
|
This is the first and most well-known of the two usual applications of the **Singleton Table** pattern in Dynamics NAV.
|
||||||
|
|
||||||
|
|
@ -58,7 +56,7 @@ Several Setup tables in NAV implement this pattern. Some of those are:
|
||||||
* Table 313 Inventory Setup
|
* Table 313 Inventory Setup
|
||||||
* Table 242 Source Code 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.
|
**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].
|
**Related resources:** [Considerations on optimizing the Singleton Table, by Søren Klemmensen][anchor2].
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -52,7 +52,7 @@ When saving the journal lines, the Save as Standard Gen. Journal report (750) is
|
||||||
1. Standard General Journal
|
1. Standard General Journal
|
||||||
2. Standard Item Journal
|
2. Standard Item Journal
|
||||||
|
|
||||||
[watch?v=XeTKmO2Eqgw&list=PLhZ3P LY7CqmVszuvtJLujFyHpsVN0Uw&index=21][anchor9]
|
{{< youtube XeTKmO2Eqgw>}}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -4,19 +4,19 @@ weight = 1190
|
||||||
+++
|
+++
|
||||||
_Originally by Abhishek Ghosh, at Microsoft Development Center Copenhagen_
|
_Originally by Abhishek Ghosh, at Microsoft Development Center Copenhagen_
|
||||||
|
|
||||||
Abstract
|
## 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.
|
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.
|
||||||
|
|
||||||
[![ ][image0]][anchor0][][anchor1]
|
[![ ][image0]][anchor0][][anchor1]
|
||||||
|
|
||||||
Description
|
## 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.
|
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.
|
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
|
## Usage
|
||||||
|
|
||||||
This pattern takes a two-step approach to displaying the data:
|
This pattern takes a two-step approach to displaying the data:
|
||||||
|
|
||||||
|
|
@ -72,7 +72,7 @@ With the first approach, in the Integer-OnAfterGetRecord trigger, you must make
|
||||||
|
|
||||||
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.
|
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
|
## 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
|
Goal: To print a report that lists all contacts that have open documents (both sales and purchase) and displays those documents in chronological order
|
||||||
|
|
||||||
|
|
@ -139,9 +139,8 @@ This is a commonly used pattern in several reports, such as:
|
||||||
|
|
||||||
* Report 206 -- Sales -- Invoice
|
* Report 206 -- Sales -- Invoice
|
||||||
|
|
||||||
[watch?v=QHn5oEOJv0Q&list=PLhZ3P LY7CqmVszuvtJLujFyHpsVN0Uw&index=10][anchor8]
|
{{< youtube QHn5oEOJv0Q>}}
|
||||||
|
|
||||||
* *
|
|
||||||
|
|
||||||
[anchor0]: /cfs-file.ashx/__key/communityserver-wikis-components-files/00-00-00-00-42/0250.Temporary-Dataset-Report-1.gif
|
[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
|
[anchor1]: /cfs-file.ashx/__key/communityserver-wikis-components-files/00-00-00-00-42/8424.Temporary-Dataset-Report-1.gif
|
||||||
|
|
|
||||||
|
|
@ -58,7 +58,7 @@ In order to always update the totals, the system must have a way to mark that a
|
||||||
* On the lines:
|
* On the lines:
|
||||||
* A new field, Recalculate Invoice Discount, was added and set to FALSE by default.
|
* 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.
|
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]
|
[![ ][image2]][anchor2]
|
||||||
|
|
||||||
|
|
@ -153,7 +153,7 @@ The pattern can easily be extended to new sales and purchase documents. If the p
|
||||||
|
|
||||||
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.
|
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.
|
||||||
|
|
||||||
[watch?v=U3noU WT8Xk&list=PLhZ3P LY7CqmVszuvtJLujFyHpsVN0Uw&index=22][anchor12]
|
{{< youtube U3noU-WT8Xk>}}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -2,7 +2,7 @@
|
||||||
title = "Transfer Custom Fields"
|
title = "Transfer Custom Fields"
|
||||||
weight = 1230
|
weight = 1230
|
||||||
+++
|
+++
|
||||||
[watch?v=cGaBqwfGCws&list=PLhZ3P LY7CqmVszuvtJLujFyHpsVN0Uw&index=9][anchor0]
|
{{< youtube cGaBqwfGCws>}}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -2,9 +2,7 @@
|
||||||
title = "Variant Facade"
|
title = "Variant Facade"
|
||||||
weight = 1440
|
weight = 1440
|
||||||
+++
|
+++
|
||||||
# Variant Façade
|
_By Nikola Kukrika, waldo and Gary Winter_
|
||||||
|
|
||||||
By Nikola Kukrika, waldo and Gary Winter
|
|
||||||
|
|
||||||
# Abstract
|
# Abstract
|
||||||
|
|
||||||
|
|
@ -48,9 +46,9 @@ Problems arising from this way of implementing code are:
|
||||||
|
|
||||||
**Testing the code** is hard since the tests need to be replicated.
|
**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. ****
|
**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.**
|
**Conclusion** - **If the functionality needs to be used for many records the approach of duplicating the functions should be avoided.**
|
||||||
|
|
||||||
# Solution
|
# Solution
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue