Friday, 23 November 2018

Positive pay file in Dynamics 365 F&O



This post demonstrate how to create positive pay file and difference/changes between Dynamics 365 application 7.3 and 8.0. 


      Most of the banks prefer format of the positive pay file in “.txt”. In Dynamics 365 for Finance and Operations, the default format of the Positive Pay file output is XML. Microsoft has provided steps for the setup of Positive pay file as well as a sample XSLT file used to transform the output (7.3 version please see below for 8.0 version XSLT file)

      In 7.3 output file generates as “.xml” and we cannot the change the file extension in the code.
o   If we need the file in “.txt” or other format then we need to save manually “.xml” as “.txt”.
o   Method BankPositivePayExport > getFileExtensionFromURL()  is a private method so we cannot create extensions for private methods.
      Where as in 8.0 and later we can change the file extension to other extensions like “.txt”.
o   Microsoft changed Method BankPositivePayExport > getFileExtensionFromURL()  to protected so we can create extensions and change the file extension.

      In 7.3 the XSLT file accepts the data entity name in whatever way we mention like small letter or capital letter (BankPositivePayExportEntity) and capital letter field names.



      In 8.0 and later version accepts only capital letter data entity name and field names. (BANKPOSITIVEPAYEXPORTENTITY)










Sample XSLT File



<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"

  xmlns:msxsl="urn:schemas-microsoft-com:xslt" exclude-result-prefixes="msxsl xslthelper"

  xmlns="urn:iso:std:iso:20022:tech:xsd:pain.001.001.02"
  xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  xmlns:xslthelper="http://schemas.microsoft.com/BizTalk/2003/xslthelper"
  xmlns:Message="schemas.microsoft.com/.../Message"
  xmlns:BankPositivePay="schemas.microsoft.com/.../BankPositivePay">
  <xsl:output method="text" omit-xml-declaration="no" version="1.0" encoding="utf-8"/>
  <xsl:template name="last-day-of-month">
    <xsl:param name="date"/>
    <xsl:param name="y" select="substring($date, 1, 4)"/>
    <xsl:param name="m" select="substring($date, 6, 2)"/>
    <xsl:param name="cal" select="'312831303130313130313031'"/>
    <xsl:param name="leap" select="not($y mod 4) and $y mod 100 or not($y mod 400)"/>
    <xsl:param name="month-length" select="substring($cal, 2*($m - 1) + 1, 2) + ($m=2 and $leap)" />
    <xsl:value-of select="concat($y, $m, $month-length)" />
  </xsl:template>
  
  <xsl:template match="/">
    <Document>
      <xsl:for-each select="Document/BANKPOSITIVEPAYEXPORTENTITY">      <!--Cheque Detail begin-->
        <div>
          <xsl:value-of select='"#"'/>          <!--Placeholder-->
          <xsl:value-of select='"SDR"'/>          <!--Payment Method-->
          <xsl:value-of select='"#"'/>
          <xsl:value-of select='"IS"'/>          <!--Service Request Type-->
          <xsl:value-of select='"#"'/>
          <xsl:value-of select="substring(ACCOUNTNUM/text(), 1, 10)"/>          <!--Account Number-->
          <xsl:value-of select='"#"'/>
          <xsl:value-of select='"S"'/>          <!--Single / Range-->
          <xsl:value-of select='"#"'/>
          <xsl:value-of select='"#"'/>          <!--Account Currency-->
          <xsl:value-of select='"#"'/>          <!--Account Name-->
          <xsl:variable name="year" select="substring(TRANSDATE/text(),1,4)" />
          <xsl:variable name="month" select="substring(TRANSDATE/text(),6,2)" />
          <xsl:variable name="day" select="substring(TRANSDATE/text(),9,2)" />
          <xsl:value-of select="concat($year,$month,$day)" />          <!--Issue Date-->
          <xsl:value-of select='"#"'/>
          <xsl:variable name="amount" select="AMOUNTCUR" />
          <xsl:value-of select="format-number($amount, '#.00')" />          <!--Cheque Amount-->
          <xsl:value-of select='"#"'/>
          <xsl:value-of select='normalize-space(CHEQUENUM/text())'/>          <!--Cheque Number-->
          <xsl:value-of select='"#"'/>
          <xsl:value-of select='"#"'/>          <!--Plan Number-->
          <xsl:value-of select='"#"'/>          <!--Additional Data-->
          <xsl:value-of select='BANKNEGINSTRECIPIENTNAME/text()'/>          <!--Issued Payee Name 1-->
          <xsl:value-of select='"#"'/>
            <div>
              <xsl:value-of select='normalize-space(HSRECIPIENTADDRESS/text())'/>
            </div>          <!--Issued Payee Name 2-->
          <xsl:value-of select='"#"'/>
          <xsl:value-of select='"#"'/>          <!--FSI-->
          <xsl:call-template name="last-day-of-month">
            <xsl:with-param name="date" select= 'TRANSDATE'/>            <!--Rule-off Date-->
          </xsl:call-template>
          <xsl:value-of select='"#"'/>
          <xsl:value-of select='"#"'/>          <!--Placeholder-->
          <xsl:value-of select='"#"'/>          <!--Placeholder-->
          <xsl:value-of select='"#"'/>          <!--Duplicate Sequence Number-->
          <xsl:value-of select='"#"'/>          <!--Stop-->
          <xsl:value-of select='"#"'/>          <!--Voided Date-->
          <xsl:value-of select='"#"'/>          <!--Placeholder-->
          <xsl:value-of select='"#"'/>          <!--Placeholder-->
          <xsl:value-of select='"#"'/>          <!--Cents Compare-->
          <xsl:value-of select='"#"'/>          <!--Void-->
          <xsl:value-of select='"#"'/>          <!--Placeholder-->
          <xsl:value-of select='"#"'/>          <!--Comments-->
          <xsl:value-of select='"#"'/>          <!--Payee-->
          <xsl:text>&#xa;</xsl:text>
        </div>
      </xsl:for-each>
    </Document>
  </xsl:template>
</xsl:stylesheet>



Saturday, 27 May 2017

Accessing page numbers in SSRS report body

This post demonstrate how to access page numbers in SSRS report body AX 2012/AX 7/ Dynamics 365. 


1. Goto Report à Report Properties à Code in Visual studio

2. Write the below logic in code

Public Function PageNumber() as String
     Dim str as String
     str = Me.Report.Globals!PageNumber.ToString()
     Return str
End Function

Public Function TotalPages() as String
     Dim str as String
     str = Me.Report.Globals!TotalPages.ToString()
     Return str
End Function

3.  Create a text box and the below expression

=Cstr(Code.PageNumber()) & Space(2) & Labels!@SYS26401 & space(2) & Cstr(Code.TotalPages())


4. Example output 1 of 4

Saturday, 22 April 2017

Display or edit methods in Dynamics 365 or AX7

New Display or Edit method need to write in extension class.

In the below code is the example for display method in CustTable extension.

[ExtensionOf(tablestr(CustTable))]
final class CustTableEventHandlers_Extension
{
    [SysClientCacheDataMethodAttribute(true)]
    display RetailLoyaltyCardNumber primaryLoyaltyNumber()
    {
        RetailLoyaltyCard   retailLoyaltyCard;

        select firstonly CardNumber from retailLoyaltyCard
            where retailLoyaltyCard.Party == this.party
                && retailLoyaltyCard.Primary == NoYes::Yes;

        return retailLoyaltyCard.CardNumber;
    }
}

After writing logic we need to assign this method to the control in Form.

Set control properties "data method" in the below format

ExtensionClassName::MethodName



Saturday, 15 April 2017

Lookups in Dynamics 365 or AX7

Lookups are almost same as AX 2012 but need to write in extension class. Need to copy the event handler and paste in Class and write the same logic as AX 2012.

https://ranjithdax.blogspot.in/2015/01/creation-of-sys-look-in-microsoft.html (AX 2012)

Will get different lookup scenarios one of the scenario is
Scenario: How to filter lookup based on one field value in Dynamics 365.

So 1st  need to get the value of the field value to pass as a range, for that need to get the control value from FORM.

The below code is used to get the filtered lookup based on the other field value.



  [FormControlEventHandler(formControlStr(RetailLoyaltyCards, RetailLoyaltyCard_AuthorizedUser), FormControlEventType::Lookup)]
    public static void RetailLoyaltyCard_AuthorizedUser_OnLookup(FormControl sender, FormControlEventArgs e)
    {
        SysTableLookup sysTableLookup = SysTableLookup::newParameters(tableNum(DirPartyPostalAddressView), sender);
        QueryBuildDataSource            queryBuildDataSource;
        Query                           query;
        FormRun                         formRun;
        FormControl                     formCtrl;
       
        formRun = sender.formRun();

        formCtrl = formRun.design().controlName(formControlStr(RetailLoyaltyCards, RetailLoyaltyCard_Party2));

        sysTableLookup.addLookupfield(fieldNum(DirPartyPostalAddressView, LocationName), true);
        query = new Query();
        queryBuildDataSource = query.addDataSource(tableNum(DirPartyPostalAddressView));
        queryBuildDataSource.addRange(fieldNum(DirPartyPostalAddressView, Party)).value(SysQuery::value(str2Int64(formCtrl.valueStr())));
          
        sysTableLookup.parmQuery(query);
        sysTableLookup.performFormLookup();
    }




Friday, 10 June 2016

Request for the permission of type 'FileIOPermission' failed.


Problem: Generally while working with files we might get the below errors

Request for the permission of type 'FileIOPermission' failed.
(S)\Classes\FileIOPermission\demand
(S)\Classes\WinAPIServer\fileExists- line 11 

or 

Request for the permission of type 'FileIOPermission' failed.
(S)\Classes\FileIOPermission\demand
(S)\Classes\WinAPIServer\deleteFile- line 11 


Solution: 

Set permissionSet;

FileName fileName = ''Test.Xml"; 

permissionSet = new Set(Types::Class);

permissionSet.add(new InteropPermission(InteropKind::ClrInterop));
permissionSet.add(new FileIOPermission(fileName , 'rw'));
CodeAccessPermission::assertMultiple(permissionSet);

 if (WINAPIServer::fileExists(fileName))
    {
        if (Box::yesNo(strfmt("Do you want to re-write the file", filename), DialogButton::No))
        {
            WINAPIServer::deleteFile(filename);
         }
        else
        {
            return;
        }
    }
CodeAccessPermission::revertAssert();

A copy will also require FileIOPermission of 'r' on the source file.  A Move will require a permission of 'w' on the source.

Thursday, 9 June 2016

Microsoft.Dynamics.Ax.Xpp.InvalidRemoteCallException’ was thrown


Problem: While working on Files using WinAPI got the below error.

Microsoft.Dynamics.Ax.Xpp.InvalidRemoteCallException’ was thrown


Solution:

Change WinAPI to WinAPIServer

Example:

WINAPI::fileExists(fileName) to WINAPIServer::fileExists(fileName)

WINAPI::deleteFile(filename) to WINAPIServer::deleteFile(filename)



Saturday, 30 April 2016

Phone number validation in Microsoft dynamics AX 2012

The below code is used to validate the Phone number.

public boolean validateField(FieldId _fieldIdToCheck)
{
    boolean ret;
    Str         phonePattern = @"[0-9]";
 
    System.Text.RegularExpressions.Match  myMatch;

    ret = super(_fieldIdToCheck);

    if (_fieldIdToCheck == fieldNum(PracticeDemo, PhoneMobile))
    {
        myMatch = System.Text.RegularExpressions.Regex::Match(this.PhoneMobile, phonePattern);
         
        if (strLen(this.PhoneMobile)<10 || strLen(this.PhoneMobile)>10)
        {
            ret = checkFailed(strFmt("%1 please enter 10 digit mobile number", this.PhoneMobile));
        }
         
        if (!myMatch.get_Success())
        {
            ret = checkFailed(strFmt("%1 is not an valid phone number", this.PhoneMobile));
}
    }

    return ret;
}

Thursday, 14 April 2016

Email validation in Microsoft Dynamics AX 2012


The below code is used to validate the Email Id.

public boolean validateField(FieldId _fieldIdToCheck)
{
    boolean   ret;
    Str           emailPattern = @"\b[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,4}\b";
   
    System.Text.RegularExpressions.Match  myMatch;

    ret = super(_fieldIdToCheck);

    if (_fieldIdToCheck == fieldNum(PracticeDemo, EmailID))
    {
        myMatch = System.Text.RegularExpressions.Regex::Match(this.EmailID, emailPattern);

    if (!myMatch.get_Success())
{
            ret = checkFailed(strFmt("%1 is not an valid email id ", this.EmailID));
}
    }

    return ret;
}

Saturday, 21 November 2015

Department name using default dimension value in AX 2012

We can get the department name in different ways using default dimension value.


1st Way: Using Views.


static void DepartmentName(Args _args)
{
    DefaultDimensionView        defaultDimensionView;
    DimAttributeOMDepartment    dimAttributeOMDepartment;

   //DefaultDimension  =  5637151660
    select DisplayValue from defaultDimensionView
        where defaultDimensionView.DefaultDimension == 5637151660
            && defaultDimensionView.name == 'Department'
        join dimAttributeOMDepartment
            where dimAttributeOMDepartment.Value == defaultDimensionView.DisplayValue;

    info(dimAttributeOMDepartment.Name);
}


2nd Way: Using Tables.


static void getDepartmentName(Args _args)
{
str                                                       name;
DimensionAttributeValueSetItem      valueSetItem; 
DimensionAttribute                           dimAttribute; 
DimensionAttributeValue                 dimAttributeValue;

//Get the Department name for '5637151660' default dimension.
select DimensionAttributeValueSet from valueSetItem 
where valueSetItem.DimensionAttributeValueSet == 5637151660
            join RecId from dimAttributeValue 
                where valueSetItem.DimensionAttributeValue == dimAttributeValue.RecId 
            join RecId from dimAttribute 
                where dimAttributeValue.DimensionAttribute == dimAttribute.RecId 
                   && dimAttribute.Name == enum2str(sysdimension::Department);
       
         name = DimensionAttributeValue::find(dimAttributeValue.RecId).getName();
        
        info(name);
 }




Thursday, 12 March 2015

Adding Customer Address through x++ Coding



The Below code used to update or create address for an existing customer


static void BRR_Address(Args _args)
{
        CustTable                                  custTable;
        DirParty                                     DirParty;
        LogisticsPostalAddress             address;
        DirPartyPostalAddressView      addressView;
        LogisticsLocationRole               roles;


        custtable = Custtable::find("1304");

        address.Street     =   "DLF,";
        address.ZipCode    =   "500006";
        address.City       =   "CyberCity,";
        address.State      =   "TG";
        address.CountryRegionId = "IND";

        addressView.Party = custtable.Party;
        addressview.initFromPostalAddress(address);

        DirParty = DirParty::constructFromPartyRecId(CustTable.Party);
        roles = LogisticsLocationRole::findBytype(LogisticsLocationRoleType::Delivery);
        DirParty.createOrUpdatePostalAddress(addressView);
}

Wednesday, 11 March 2015

Inserting Customer's Financial Dimensions through X++ code


The Below code is used for Inserting Financial Dimensions for a particular Customer through code.

static void BRR_Finance(Args _args)
{
  CustTable custTable;
  Struct struct = new Struct();
  container ledgerDimension;
  DimensionDefault DimensionDefault;
  ;
  //set the values
  struct.add("CostCenter", "OU_4740");
  struct.add("BusinessUnit","00000005");
  struct.add("Department","OU_4771");  
  struct.add("ExpensePurpose","Training");
  ledgerDimension += struct.fields();
  ledgerDimension += struct.fieldName(1);
  ledgerDimension += struct.valueIndex(1);
  ledgerDimension += struct.fieldName(2);
  ledgerDimension += struct.valueIndex(2);
  ledgerDimension += struct.fieldName(3);
  ledgerDimension += struct.valueIndex(3);
  ledgerDimension += struct.fieldName(4);
  ledgerDimension += struct.valueIndex(4);
   ttsBegin;
  DimensionDefault = AxdDimensionUtil::getDimensionAttributeValueSetId(ledgerDimension);
    //Select the Customer
  custTable = CustTable::find("2003",true);
  custTable.DefaultDimension = DimensionDefault;
  custTable.update();
  ttsCommit;
}

Saturday, 7 March 2015

Finding Ledger Dimension by using MainAccount Number in X++



static void getLedgerDimension(Args _args)
{
DimensionAttributeValueCombination     dimensionAttributeValueCombination;
MainAccount                                             mainAccount;
;
select RecId,MainAccount from dimensionAttributeValueCombination
                join RecId from mainAccount
                  where dimensionAttributeValueCombination.DisplayValue == "1010"
                     && mainAccount.RecId == dimensionAttributeValueCombination.MainAccount;

info(strFmt("Ledger Dimension - %1",dimensionAttributeValueCombination.RecId));
}


                                                or 

we can find by using the method "getDefaultAccountForMainAccountNum()" which is available in DimensionStorage


static void getLedgerDimension(Args _args)
{
   DimensionStorage::getDefaultAccountForMainAccountNum("1010");
}

Tuesday, 24 February 2015

How to open web site pages on AX Forms

Today I will demonstrate you that how you can show any webpage on your AX Forms.

1. Create New Form.
2. Right click the Design node of form and add ActiveX control.
3. Select Microsoft Web Browser from AciveX control window list.
4. Go to method node of form and override Init method of form.
5. Add below line of code
           
    ActiveX.Navigate("www.google.com");

Note: - ActiveX is the name of control which we have added in the form previously.


6. Now run your form and check the output

Saturday, 21 February 2015

Finding the mandatory fields on table by using X++ code.


The below code is used to get the mandatory fields on table using X++.


static void CheckMandatoryFieldsOnTable(Args _args)
{
    DictTable   dictTable;
    DictField    dictField;
    int               i;
   //Pass the table name
    TableId       tableId = tablenum(SalesTable);
    ;

    dictTable = new DictTable(tableId);
    for (i=1 ; i<=dictTable.fieldCnt() ; i++)
    {
        dictField = new DictField(tableId, dictTable.fieldCnt2Id(i));

        if (dictField.mandatory())
        {
            info(dictField.name());
        }

    }
}


output :


Friday, 9 January 2015

Query based SSRS Reports.

Overview

There are a multiple of methods to develop SSRS reports in Dynamics AX. This tutorial will guide you through the process of developing SSRS reports using an AOT query.

Pre-requisites

  1. Microsoft Dynamics AX 2012
  2. Visual studio 2012
  3. SQL Server report server must be configured
  4. Reporting services extensions must be installed in Dynamics AX

Steps

  1. First of all we need an AOT query which will fetch data from AX and display it in a report. For this tutorial, I am using an existing query in AX which displays a list of customers.
  2. CustTableListPage query will be used in this tutorial. To find this query, open AOT àQueriesàCustTableListPage.
  3. The development of the SSRS report is done in Visual studio. So a new project needs to be created in Visual studio.
  4. Open Visual studio. Go to File à New à Project.
  5. In the section Installed templates select Microsoft Dynamics AX and then select Report Model in the right pane. Name the project “QueryBasedDemo“. Press OK.


  6. A new project will be created as shown below:


  7. Now add a new report in the project by right clicking on the project QueryBaseDemo à Add à Report.



    1. A report will be added to the project with the name “Report1″. Rename the report toQueryBasedDemo.
    2. Now double click the report to open it.

  8. The description of the individual node is given below:
    1. Datasets: Datasets retrieve data from the AOT query. It acts as a bridge between AX and the SSRS report. Only the fields added in the datasets can be used in a report.
    2. Designs: It defines the layout of the report.
    3. Images: It contains the images that you want to display in the SSRS report.
    4. Data Methods: It contains the business logic which can then be used in the report.
    5. Parameters: It is used to apply filtering to the data in a report.
  9. First of all, we will create a dataset. Right click Datasets àAdd Dataset to create a new Dataset. Name it “CustTable”.



  10. Select the data source and open the properties window. Make sure the Data Source Type is set toQuery. Then select the Query field. An ellipse button appears. Click it to open a dialog.


  11. This dialog lists all the queries present in the AOT. Select CustTableListPage and press Next.

  12. Select the fields you want to display in the report and press OK. Only the selected fields in this dialog can be shown in the report.

  13. There are two types of designs that can be created in a SSRS report:
    1. Auto Design: Visual studio automatically creates a design based on the dataset provided. Auto design is the preferred method because it is easy and usually fulfills the requirements for the majority of scenarios.
    2. Precision Design: It is used when you need custom placement of fields or the layout of the report is too complex.
  14. In this demo we will use Auto Design. Now right click the Designs nodeàAdd àAuto Design. A new design is added. Rename it to Design. It is recommended that you set the name of the Design to either ‘Design’ or ‘Report’

  15. Now drag CustTable form the Datasets node and drop it on the Design node. A table will be created which contain all the fields present in the data set. These fields will appear in the same order in the report. So if you want to arrange the fields, right click the field and select either move up or move down.
  16. The final design will look as shown below



  17. Now we have to define the layout of the report. Visual studio provides built in templates. Select the Design and open the properties window. Select ReportLayoutStyleTemplate in the LayoutTemplatefield. Give a suitable title to the report.

  18. Select the CustTableTable under the Design node and open the properties window. SelectTableStyleAlternatingRowsTemplate in the Style Template field.


  19. Report is now completed can be viewed. To preview the report, select the Design node, right click it and select preview. A preview window opens.


  20. Select Report tab. The report appears as shown below:



  21. To view this report from AX, it needs to be added in the AOT and deployed at the report server.
  22. Open the solution explorer and right click the project. Select Add QueryBasedDemo to AOT. This will add the report to the AOT. It will also add the project in the AOT with same name so if you want to modify the report in future, you can use that project.


  23. Now open AOT in AX. Go to SSRS reports à Reports à QueryBasedDemo. Right click the QueryBasedDemo Report and select Deploy Element

  24. A success message will appear if the report is successfully deployed.
  25. To open the report in AX, a menu item is required. Create a menu item that will open the report from AX.
  26. Go to Menu items à Output. Right click Output and select New Menu Item

  27. Set the following properties on the menu item as shown below.

  28. Right click the newly created menu item and select Open to view the report

  29. A parameter form will open. If you want to add parameters to report, you can add it by clicking Select. Press Ok to continue.

  30. The report is displayed as shown below.

Saturday, 3 January 2015

Creation of Sys Lookup in Microsoft Dynamics AX


Steps to create a Sys Lookup :-

1. Create a Table with fields ( AccountName,Account,City).

2. Create a Form and use  DataSource as created Table.

3. Expand the Form DataSource and Expand Fields.

4. Override the Lookup() method  on the field where the lookup has to appear (Example Field -  Account).

5. Write the following code.

public void lookup(FormControl _formControl, str _filterStr)
{

    SysTableLookup sysTableLookup = SysTableLookup::newParameters(tableNum(CustTable),_formControl);
    Query query = new  Query();
    QueryBuildDataSource qbds = query.addDataSource(tableNum(CustTable));
     super(_formControl, _filterStr);
     sysTableLookup.addLookupfield(fieldNum(CustTable,AccountNum));
    sysTableLookup.addLookupfield(fieldNum(CustTable,Currency));
    sysTableLookup.parmQuery(query);
    sysTableLookup.performFormLookup();

}

T

Wednesday, 3 December 2014

Table inheritance in Ax 2012


Step-1 Create a table called 'BaseTable'
Step-2 Set table property 'supportInheritance' to 'Yes'
Step-3 Create a filed name called 'InstanceRelationType' extends 'RelationType'
Step-4 Set table property 'InstanceRelationType' to 'InstanceRelationType'
Step-5 Create two filed called Id and Name as shown below screen
Step-6 Create another table called 'ChildTable'
Step-7 Set table property 'supportInheritance' to 'Yes'
Step-8 Set table property 'Extends' to 'BaseTable'
Step-9 Write a job to access the base table fields given below

static void dataInsert_BaseTable(Args _args)
{
BaseTable bt;
ChildTable ct;

ct.Id = '1000';
ct.Name = 'Test';
ct.insert();
}