Monday, April 3, 2023

How to override form data source field lookup method.

 /// <summary>

/// Handles events raised by <c>SalesTable</c> form.
/// </summary>
public class SalesTableEventHandler
{
    /// <summary>
    /// Post event handler for <c>SalesTable</c> <c>SalesLine</c> Initialized event.
    /// </summary>
    /// <param name=“_sender”></param>
    /// <param name=“_e”></param>
    [FormDataSourceEventHandler(formDataSourceStr(SalesTable, SalesLine), FormDataSourceEventType::Initialized)]
    public static void SalesLine_OnInitialized(FormDataSource _sender, FormDataSourceEventArgs _e)
    {
        var overrides = SalesTableFormExtensionOverrides::construct();
 
        _sender.object(fieldNum(SalesLine, ItemId)).registerOverrideMethod(methodStr(FormDataObject, lookup),
            methodStr(SalesTableFormExtensionOverrides, ItemId_OnLookup), overrides);
    }
}
 
/// <summary>
/// Contains methods which are used to override <c>SalesLine</c> data source field methods.
/// </summary>
public class SalesTableFormExtensionOverrides
{
    protected void new()
    {
    }
 
    /// <summary>
    /// Constructs a new instance of <c>SalesTableFormExtensionOverrides</c> class.
    /// </summary>
    /// <returns>
    /// A <c>SalesTableFormExtensionOverrides</c> class.
    /// </returns>
    public static SalesTableFormExtensionOverrides construct()
    {
        return new SalesTableFormExtensionOverrides();
    }
 
    /// <summary>
    /// Provides a lookup for the <c>InventTable</c> table
    /// </summary>
    /// <param name = "_callingControl">
    /// The form string control object with which to perform the lookup.
    /// </param>
    public void ItemId_OnLookup(FormStringControl _callingControl)
    {
        SysTableLookup sysTableLookup = SysTableLookup::newParameters(tableNum(InventTable), _callingControl);
 
        sysTableLookup.addLookupfield(fieldNum(InventTable, ItemId));
        sysTableLookup.addLookupfield(fieldNum(InventTable, NameAlias));
 
        sysTableLookup.performFormLookup();
    }
}

Sunday, July 3, 2022

D365FO – AX – CACHE LOOKUP PROPERTY OF TABLE

 Caches are used on both the client and the server. It increases the performance, the ax will get data from the cache instead of doing round trips and DB calls. So for each table, it’s good to use cache lookup property. Microsoft Dynamics Ax runtime manages the cache by removing old records when new records are added to the cache.

Client Cache
A Client-side cache can be used only by the client. The client cache is used when a select statement is executed from the client tier. If no records are found in the client cache, the client then searches in the server cache for the records. If the record isn’t located in the server cache, it will retrieve from the database. The maximum number of records can be maintained in a client cache is 100 records per table for the selected company.

Server Cache
A server-side cache can be used by any connection to the server. The server cache is used when a select is executed on the server tier. If no record found in the server cache, it will retrieve from the database. The maximum number of records maintained in a server cache is 2000 records for the selected company.

Types of Cache Lookup

  • None
  • EntireTable
  • Found
  • NotInTTS
  • FoundAndEmpty

None
No data is cached or retrieved from the cache for this table. This property value should be used for tables that are heavily updated or where it’s unacceptable to read outdated data.

EntireTable
Creates a set-based cache on the server. The entire table is cached as soon as at least one record is selected from the table. An EntireTable cahce is flushed whenever an insert, update or delete is made to the table. So first select read all records from DB for the selected company and all the further selects will take data from the cache instead of DB calls.

Below is a list which shows to use the different type of cache lookup property as per table group.

Table GroupCache Lookup
Miscellaneous* See notes below
 Parameter EntireTable
 Group Found
 Main Found
 Transaction NotInTTS
 WorksheetHeader NotInTTS
 WorksheetLine NotInTTS
 Framework N/A
 Reference Found
 Worksheet NotInTTS
 TransactionHeader NotInTTS
 TransactionLine NotInTTS

Found
All successful caching key selects are cached. All caching key selects are returned from the cache if the record exists there. A select forUpdate in a transaction forces reading from the database and replaces the record in the cache.
This is typically used for static (lookup) tables, such as Unit, where the record usually exists.

NotInTTS
All successful caching key selects are cached.
When in a transaction (after ttsBegin), no caches made outside the transaction are used. When inside a transaction, the record is read once from the database and subsequently from the cache. The record is select-locked when reading in a transaction, which ensures that the record cached is not updated while the transaction is active.
A typical example of the NotInTTS property is on the CustTable in the Microsoft Dynamics AX application. It is acceptable to read outdated data from the cache outside a transaction, but when data is used for validation or creating references, it is ensured that the data is real-time.

FoundAndEmpty 
All selects on caching keys are cached, including selects that are not returning data.
All caching key selects are returned from caching if the record exists there, or the record is marked as nonexistent in the cache. A select forUpdate in a transaction forces reading from the database and replaces the record in the cache.
An example of FoundAndEmpty record caching is in the Discount table in the Microsoft Dynamics AX standard application. By default, the Discount table has no records. By using a FoundAndEmpty cache on this table, the keys that are queried for but not found are stored in the cache. Subsequent queries for these same non-existent records can be answered from the cache without a round trip to the database. 



Original post: D365FO – AX – Cache Lookup Property of table – AX / Dynamics 365 For Finance and Operations blog (d365ffo.com)

Friday, May 6, 2022

Change recovery mode

 Change Database recovery mode to normal mode

DBCC CHECKDB ('YourDBname') WITH NO_INFOMSGS, ALL_ERRORMSGS

Shrink database size
DBCC SHRINKDATABASE ('YourDBname') ,10);  

Monday, February 7, 2022

Data entity methods execution sequence in Dynamics 365 F&O X++

 Here is a sequence of method’s calls during export:


1. initValue
2. validateField
3. validateWrite
4. update
4.1. doUpdate
4.1.1. persistEntity
4.1.1.1. doPersistEntity
4.1.1.1.1. initializeDataSources
4.1.1.1.1.1. initializeEntityDataSource
Note: initializeDataSource is called once for each DataSource in Entity.
4.1.1.1.2. mapEntityToDataSources
Note: initializeDataSource is called once for each DataSource in Entity.
4.1.1.1.3. saveDataSources
4.1.1.1.3.1. updateEntityDataSource
4.1.1.1.4. mapEntityToDataSource (maybe for another record)
4.1.1.1.5. saveDataSources
4.1.1.1.5.1. updateEntityDataSource for update operation and (insertEntityDataSource for insert)
4.1.1.1.5.1.1. mapDataSourceToEntity
4.1.1.1.5.1.2. doSaveDataSource
4.1.1.1.5.1.2.1. updateDataSource
4.1.1.1.5.1.2.1.1. preupInsertDataSource
4.1.1.1.5.1.2.1.1.1. validateWrite of table
5.0 postLoad


Here are some method’s calls during Import:


defaultCTQuery
copyCustomStagingToTarget
postGetStagingData
preTargetProcessSetBased
postTargetProcess

Friday, November 5, 2021

Dynamic SSRS report name in D365 F&O

 When there is a requirement to generate dynamic report name. For example when saving report, the report name should be appended with Customer ID, Invoice ID etc. To do this in the controller class we have to write code for parmDialogCaption() method shown below:-

 

class DemoController extends SrsReportRunController
{
 
    public void prePromptModifyContract()
    {
        ProjInvoiceJour              projInvoiceJour;
        DemoContract             contract;
        FormDataSource               fds;
        
        contract    = this.parmReportContract().parmRdpContract() as DemoController;
        projInvoiceJour = args.record();
        fds = args.record().dataSource();
        contract.parmProjInvoiceId(projInvoiceJour.ProjInvoiceId);  
    }

    public static client void main(Args args)
    {
        DemoController             demoController;
        ProjInvoiceJour              projInvoiceJour;
        demoController= new DemoController();
        demoController.parmArgs(args);
        demoController.parmReportName(ssrsReportStr(RAR_AmazonBillingReport, Report));
        demoController.parmShowDialog(false);
       
        demoController.parmReportContract().parmReportCaption("Project Invoice Report");
        
        projInvoiceJour = args.record();
        demoController.parmDialogCaption("Project Invoice Report" + "_"+ projInvoiceJour.ProjInvoiceId); // This will generate dynamic report name        
        demoController.startOperation();
    }


when you export the report in excel, pdf or csv, the report name will be appended with ProjInvoiceId.


Source: D365FO Technical Blog: Dynamic SSRS report name in D365 FnO (axd365focode.blogspot.com)

Wednesday, October 20, 2021

Set up MT940 format for bank reconciliation in D365FO

 In D365FO advance bank reconciliation is a feature to import bank statement file and automatically reconcile with related bank accounts. There are many formats which a bank commonly used i.e. ISO20022, MT940, BAI2.


In this post, we will see how to set up the MT940 format which is commonly used in most banks nowadays.

Let's get started.

Step 1: Get sample entity template and transformation files
To transform the source file into F&O format, you need few files and these are available under 'Resources' node of AOT. Files names are as below



Step 2: Create an import project
Under Data management workspace, create an import project with name MT940. Add a new file with below details
I. Entity Name: Bank statements
II. Upload file name: SampleBankCompositeEntity (which you got from Resource node)



Once file successfully uploaded, click on view map. On next screen select BankStatementDocumentEntity from the list and click on 'View Map' and go to 'Transformation' tab. Click new and click upload file, select different XLST file one by one, in the sequence shown in below image.



Step 3: Setup Bank statement format
Navigate to Cash and Bank management > Setup > Advance bank reconciliation setup > Bank statement format.
Here create a new record as below



Step 4: Configure the bank account to use Advance reconciliation option
Navigate to Cash and Bank management  > Bank accounts. Select a Bank account to view details. Under Reconciliation tab,
I. Set the 'Advance bank reconciliation' option to 'yes'. This is a one-time setup, the system doesn't allow to undo/change once set to yes.


II. Set Statement format field to format which we have created in step 3 i.e. MT940




Step 5: Testing
Navigate to Cash and Bank management  > Bank accounts. On Reconcile tab click on Bank statements.
On the next screen click on Import statement. A dialog will pop up. Fill/select details as below

I. Import statement for multiple bank account in all entities. : Set this as Yes if your file contains more than one bank accounts.
II. Bank Account: If the source file contains a single bank, select that bank here.
III. Statement Format: Select your statement format here it must be MT940.
IV: Bank statement file import: Select source file and click upload.
V: Click ok and it must import transaction in the current form.

Note: After every DB refresh you need to redo import project. DB will break the links and you need to remove the entity from your import project and add upload the transformation files accordingly.