Monday 18 November 2019

Try/catch is not working in AX 2012 / D365

Sometimes we see catch statement does not catch the errors even though the code looks good.

The below code looks good for me. But, still not showing my custom exceptions.

while (resultSet.next())
{
    //some logic (...)

    ttsbegin;

    //This is a custom table that stores to where I want to integrate the given customer
    while select integrationCompany
            where integrationCompany.CRMCompany == customerInfo.parmCRMDataAreaId()
    {
        changeCompany(integrationCompany.ERPCompany)
        {
            try
            {
                customerInfo.createCustomer();

                //.. some more logic


            }
            catch
            {
                // My catch Block, that should update the source database to set
                // the processing status to "error"


                ttsAbort;
            }
        }
    }

    ttsCommit;

}



Reason: 
Because the try and catch statement is inside the transaction(ttsbegin and ttscommit) and this is what causing the issue.

Solution:

ttsbegin and ttscommit should be always inside the try and catch statements as shown below.



while (resultSet.next())
{
    //some logic (...)

    //This is a custom table that stores to where I want to integrate the given customer
    while select integrationCompany
            where integrationCompany.CRMCompany == customerInfo.parmCRMDataAreaId()
    {
        changeCompany(integrationCompany.ERPCompany)
        {
            try
            {
               ttsbegin;
              customerInfo.createCustomer();

                //.. some more logic
              ttscommit


            }
            catch
            {
                // My catch Block, that should update the source database to set
                // the processing status to "error";
               ttsbegin;
               //you update statement here
               ttscommit;
            }
        }
    }

}

For more details please refer : https://stackoverflow.com/questions/27529308/strange-behavior-with-try-catch-on-dynamics-ax-2012  

Wednesday 13 November 2019

How to decode/unescape/Special characters HTML in Dynamics AX 2012 / Dynamics 365 finance and operations

Below is the  simple job I have written in D365 for removing encoded values from a string. Usually we see this kind of encoded special characters in the API's.

class SW_HTMLDecode
    /// <summary>
    /// Runs the class with the specified arguments.
    /// </summary>
    /// <param name = "_args">The specified arguments.</param>
    public static void main(Args _args)
    {
        str input   = ' Test &amp; notes';
        str output  = System.Web.HttpUtility::HtmlDecode(input);
        ;

        info(output);
    }

}


Result  = Test & notes

@Rahul

Monday 4 November 2019

Function DimensionValidationRequest::newForLedgerDimensionType has been incorrectly called inD365 F&O

Usually we see the below error when we create payment/general journals using X++ code.

Function DimensionValidationRequest::newForLedgerDimensionType has been incorrectly called.

The reason for this error is ledger dimension/offset ledger dimension(Main account) format is incorrect and assigned this format to ledgerjournalTrans.LedgerDimension or ledgerjournalTrans.OffsetLedgerDimension.

Below is the way to create and assign ledger dimensions.

//Variables
        DimensionDynamicAccount     offsetLedgerDim;
Array  acctDimAttrArray                     = new Array(Types::String);
            acctDimAttrArray.value(1,"MainAccount");

            Array acctDimArray                          = new Array(Types::String);
            acctDimArray.value(1,'123456');

            DefaultDimensionIntegrationValues DefaultDimensionIntegrationValues   = DimensionResolver::getEntityDisplayValue
                    (acctDimAttrArray, acctDimArray, extendedTypeStr(DimensionDynamicAccount), LedgerJournalACType::Ledger);
            DimensionDynamicAccountResolver DimensionDynamicAccountResolver     = DimensionDynamicAccountResolver::newResolver
                (DefaultDimensionIntegrationValues, LedgerJournalACType::Ledger, curExt());
            ledgerjournalTrans.AccountType = LedgerJournalACType::Ledger;
            ledgerjournalTrans.LedgerDimension = DimensionDynamicAccountResolver.resolve();
            ledgerjournalTrans.OffsetAccountType = LedgerJournalACType::Cust;

            offsetLedgerDim = LedgerDynamicAccountHelper::getDynamicAccountFromAccountNumber
                ('1000005', LedgerJournalACType::Cust);
            ledgerjournalTrans.OffsetLedgerDimension = offsetLedgerDim;


In the same way if have main account with multiple dimensions you can follow the format.

Array  acctDimAttrArray                     = new Array(Types::String);
        acctDimAttrArray.value(1,"MainAccount");
        acctDimAttrArray.value(2,"Brand");
        acctDimAttrArray.value(3,"Channel");

        Array acctDimArray                          = new Array(Types::String);
        acctDimArray.value(1,_dataContract.parmMainAccount());//Main account
        acctDimArray.value(2,_dataContract.parmBrand());//Brand dimension
        acctDimArray.value(3,_dataContract.parmChannel());//Channel dimension

  DefaultDimensionIntegrationValues DefaultDimensionIntegrationValues   = DimensionResolver::getEntityDisplayValue
                    (acctDimAttrArray, acctDimArray, extendedTypeStr(DimensionDynamicAccount), LedgerJournalACType::Ledger);
            DimensionDynamicAccountResolver DimensionDynamicAccountResolver     = DimensionDynamicAccountResolver::newResolver
                (DefaultDimensionIntegrationValues, LedgerJournalACType::Ledger, curExt());
           ledgerjournalTrans.AccountType = LedgerJournalACType::Ledger;
            ledgerjournalTrans.LedgerDimension = DimensionDynamicAccountResolver.resolve();
            ledgerjournalTrans.OffsetAccountType = LedgerJournalACType::Cust;

            offsetLedgerDim = LedgerDynamicAccountHelper::getDynamicAccountFromAccountNumber
                ('1000005', LedgerJournalACType::Cust);
            ledgerjournalTrans.OffsetLedgerDimension = offsetLedgerDim;

That's it.

@Rahul

System does not support setup 'continuous' of number sequence in Dynamics AX 2012 / D365 F&O

If you are facing the issue System does not support setup 'continuous' of number sequence, enclose your code of number sequence assignment within a transaction (TTSBegin & TTSCommit)


NumberSeq  numberSeq;
ttsbegin;
numberSeq = NumberSeq::newGetNum(HrmParameters::numRefApplicantId()).num()

ttscommit;

Error will be resolved!!!

Thursday 24 October 2019

SQL queries to get customers and vendor addresses and contact details in D365 F&O/ AX X++

Below are the sample queries to get the vendors and customers addresses and contact details. Change the code according to your requirement.
All Vendors
SELECT * FROM VENDTABLE WHERE VENDTABLE.DATAAREAID='CEU'

All Addresses - Vendor 
SELECT * FROM  DirPartyPostalAddressView  JOIN VENDTABLE ON  DirPartyPostalAddressView.PARTY =VENDTABLE.PARTY
WHERE VENDTABLE.DATAAREAID=''CEU'

All Addresses with Purpose
SELECT LOGISTICSLOCATIONROLE.*,DirPartyPostalAddressView.*,VENDTABLE.* FROM  DirPartyPostalAddressView  JOIN VENDTABLE ON  DirPartyPostalAddressView.PARTY =VENDTABLE.PARTY
JOIN DIRPARTYLOCATIONROLE ON  DIRPARTYLOCATIONROLE.PARTYLOCATION =DirPartyPostalAddressView.RECID
JOIN LOGISTICSLOCATIONROLE ON DIRPARTYLOCATIONROLE.LOCATIONROLE =LOGISTICSLOCATIONROLE.RECID
WHERE VENDTABLE.DATAAREAID='CEU'
(Click Organization administration > Setup > Global address book > Address and contact information purpose.)

All Contact Details - Vendor 
select * from dirPartyContactInfoView JOIN VENDTABLE ON  dirPartyContactInfoView.PARTY =VENDTABLE.PARTY
WHERE VENDTABLE.DATAAREAID='CEU'

--All Customers
--SELECT DIRPARTYTABLE.NAMEALIAS ,CUSTTABLE.* FROM CUSTTABLE JOIN DIRPARTYTABLE ON  CUSTTABLE.PARTY =DIRPARTYTABLE.RECID
--WHERE CUSTTABLE.DATAAREAID='CEU'

--All Addresses - Customer 
--SELECT DirPartyPostalAddressView.*,CUSTTABLE.PARTY  FROM  DirPartyPostalAddressView  JOIN CUSTTABLE
--ON  DirPartyPostalAddressView.PARTY =CUSTTABLE.PARTY
--WHERE CUSTTABLE.DATAAREAID='CEU'

--All Addresses with Purpose - Customer
--SELECT LOGISTICSLOCATIONROLE.NAME,DirPartyPostalAddressView.PARTY,CUSTTABLE.PARTY FROM  DirPartyPostalAddressView  JOIN CUSTTABLE
--ON  DirPartyPostalAddressView.PARTY =CUSTTABLE.PARTY
--JOIN DIRPARTYLOCATIONROLE ON  DIRPARTYLOCATIONROLE.PARTYLOCATION =DirPartyPostalAddressView.RECID
--JOIN LOGISTICSLOCATIONROLE ON DIRPARTYLOCATIONROLE.LOCATIONROLE =LOGISTICSLOCATIONROLE.RECID
--WHERE CUSTTABLE.DATAAREAID='CEU'

--All Contact Details - Customer 
--select dirPartyContactInfoView.* from dirPartyContactInfoView JOIN CUSTTABLE ON  dirPartyContactInfoView.PARTY =CUSTTABLE.PARTY
--WHERE CUSTTABLE.DATAAREAID='CEU'

--Bank Details Customer
 --SELECT distinct CUSTTABLE.PARTY ,CUSTTABLE.DATAAREAID ENTITY, CUSTTABLE.ACCOUNTNUM, DIRPARTYTABLE.NAME,Address.ADDRESS ,
 --CUSTTABLE.CURRENCY ,CUSTTABLE.CUSTGROUP  ,
 --CUSTTABLE.PAYMTERMID,CUSTTABLE.TAXGROUP VATGROUP ,CUSTTABLE.CASHDISC ,
 --VendBankAccount.ACCOUNTID BankAccount ,VendBankAccount.NAME 'Bank Name' ,VendBankAccount.ACCOUNTNUM 'Bank account number',
 --VendBankAccount.RegistrationNum 'Routing Number',VendBankAccount.SWIFTNo,VendBankAccount.BankIBAN
 --from CUSTTABLE left outer JOIN VendBankAccount ON VendBankAccount.VENDACCOUNT = CUSTTABLE.ACCOUNTNUM --AND  VendBankAccount.ACCOUNTID = CUSTTABLE.BANKACCOUNT
 --left outer join DIRPARTYTABLE ON DIRPARTYTABLE.RECID = CUSTTABLE.PARTY
 --left outer join LOGISTICSPOSTALADDRESS AS Address ON Address.LOCATION = DIRPARTYTABLE.PRIMARYADDRESSLOCATION
 -- WHERE CUSTTABLE.DATAAREAID IN ('CEU') --and CUSTTABLE.ACCOUNTNUM ='test033'
 --Order by  CUSTTABLE.DATAAREAID,CUSTTABLE.ACCOUNTNUM

Below SQL  Query to extract a quick customer contact list from Dynamics AX 2012.

REF
REF

SELECT 
  VENDTABLE.ACCOUNTNUM AS CUSTID,
  DIRPARTYTABLE.NAME AS CUSTNAME,
  CASE LOGISTICSELECTRONICADDRESS.TYPE WHEN 1 THEN 'Phone' WHEN 2 THEN 'Email' END AS CONTACTTYPE,
  LOGISTICSELECTRONICADDRESS.DESCRIPTION AS CONTACTNAME,
  LOGISTICSELECTRONICADDRESS.LOCATOR AS CONTACTDETAILS
FROM DIRPARTYTABLE AS DIRPARTYTABLE
INNER JOIN VENDTABLE ON DIRPARTYTABLE.RECID = VENDTABLE.PARTY
INNER JOIN DIRPARTYLOCATION ON DIRPARTYTABLE.RECID = DIRPARTYLOCATION.PARTY
INNER JOIN LOGISTICSELECTRONICADDRESS ON DIRPARTYLOCATION.LOCATION = LOGISTICSELECTRONICADDRESS.LOCATION
WHERE VENDTABLE.DATAAREAID='CEU'
ORDER BY DIRPARTYTABLE.NAME


Vendor Bank Details SQL Query in AX


Vendor Bank Address in X++ Code and save it in CSV in AX


Fetch Customers Primary Address or Vendor Primary  Address having Transactions (SQL SERVER Query) in AX D365


Fetch Product Master 

SELECT B.DISPLAYPRODUCTNUMBER,DESCRIPTION,NAME,B.SEARCHNAME FROM ECORESPRODUCTTRANSLATION A JOIN ECORESPRODUCT B ON A.PRODUCT =B.RECID WHERE B.DISPLAYPRODUCTNUMBER in ('A0001','A0002')


--Storage Dimensions
select ECORESSTORAGEDIMENSIONGROUPITEM.ITEMID ,ECORESSTORAGEDIMENSIONGROUP.NAME from INVENTTABLE JOIN ECORESSTORAGEDIMENSIONGROUPITEM ON
 INVENTTABLE.ITEMID =ECORESSTORAGEDIMENSIONGROUPITEM.ITEMID AND
 INVENTTABLE.DATAAREAID  =ECORESSTORAGEDIMENSIONGROUPITEM.ITEMDATAAREAID
 JOIN  ECORESSTORAGEDIMENSIONGROUP ON
 ECORESSTORAGEDIMENSIONGROUPITEM.STORAGEDIMENSIONGROUP =ECORESSTORAGEDIMENSIONGROUP.RECID
 WHERE INVENTTABLE.DATAAREAID='CEU'


--Tracking Dimensions
select ECORESTRACKINGDIMENSIONGROUPITEM.ITEMID ,ECORESTRACKINGDIMENSIONGROUP.NAME from INVENTTABLE JOIN ECORESTRACKINGDIMENSIONGROUPITEM ON
 INVENTTABLE.ITEMID =ECORESTRACKINGDIMENSIONGROUPITEM.ITEMID AND
 INVENTTABLE.DATAAREAID  =ECORESTRACKINGDIMENSIONGROUPITEM.ITEMDATAAREAID
 JOIN  ECORESTRACKINGDIMENSIONGROUP ON
 ECORESTRACKINGDIMENSIONGROUPITEM.TRACKINGDIMENSIONGROUP =ECORESTRACKINGDIMENSIONGROUP.RECID
 WHERE INVENTTABLE.DATAAREAID='CEU'


 --Item Model Group Units
 select * from INVENTTABLEMODULE where DATAAREAID='CEU'

 --Released Products
 Select * from INVENTTABLE where DATAAREAID='CEU'

 --Item Group
 select * from INVENTITEMGROUPITEM  where ItemDATAAREAID='CEU'

Vend Open Trans

SELECT VENDTRANS.ACCOUNTNUM,VendTrans.VOUCHER,VendTrans.Invoice,VendTrans.TRANSDATE AS DueDate,VendTrans.AmountCur, VendTrans.AmountCur-VendTrans.SETTLEAMOUNTCUR as BalanceAmount 
FROM VENDTRANS
where VENDTRANS.closed = 0
AND ((VendTrans.TransType = 36)
OR (VendTrans.TransType = 3) --Purch
OR (VendTrans.TransType = 14)) --Vend
AND ((VendTrans.AmountCur<=0)) AND ((VendTrans.Approved = 1))
--AND VENDTRANS.ACCOUNTNUM = '1001' AND VENDTRANS.DATAAREAID = 'USMF'


SELECT VENDTRANS.ACCOUNTNUM,VendTrans.VOUCHER,VendTrans.Invoice,VendTrans.TRANSDATE AS DueDate,VendTrans.AmountCur, VendTrans.AmountCur-VendTrans.SETTLEAMOUNTCUR as BalanceAmount  ,*
FROM VENDTRANS where
--where VENDTRANS.closed = 0
--AND ((VendTrans.TransType = 36) OR (VendTrans.TransType = 3)
-- OR (VendTrans.TransType = 14)) AND ((VendTrans.AmountCur<=0)) AND ((VendTrans.Approved = 1))
 VENDTRANS.DATAAREAID = 'pui'

SELECT VENDTRANS.ACCOUNTNUM,VendTrans.VOUCHER,VendTrans.Invoice,VendTrans.TRANSDATE AS DueDate,VendTrans.AmountCur, VendTrans.AmountCur-VendTrans.SETTLEAMOUNTCUR as BalanceAmount 
FROM VENDTRANS
where VENDTRANS.closed = 0 and
VENDTRANS.DATAAREAID = 'pui'
AND ((VendTrans.TransType = 36) --RTax25_BadDebtDebitAmortisation
OR (VendTrans.TransType = 3) --Purch
OR (VendTrans.TransType = 14)) --Vend
AND ((VendTrans.AmountCur<=0)) AND ((VendTrans.Approved = 1))

--AND VENDTRANS.ACCOUNTNUM = '1001' AND VENDTRANS.DATAAREAID = 'USMF'

Wednesday 16 October 2019

Get Customer Address By Type In D365/AX X++

Below is the piece of code to get customer address by type in AX/D365 X++


public static LogisticsPostalAddress getPostalAddressByType(DirPartyRecId _party, LogisticsLocationRoleType _type)
{
    DirPartyLocation        partyLocation;
    DirPartyLocationRole    partyLocationRole;
    LogisticsLocation       location;
    LogisticsLocationRole   locationRole;
    LogisticsPostalAddress  postalAddress;

    select firstonly postalAddress
        exists join location
            where location.RecId == postalAddress.Location
        exists join locationRole
            where locationRole.Type  == _type
        exists join partyLocation
            where 
                partyLocation.Location == location.RecId &&
                partyLocation.Party == _party
        exists join partyLocationRole
            where partyLocationRole.PartyLocation == partyLocation.RecId &&
                partyLocationRole.LocationRole == locationRole.RecId;

    return postalAddress;
}

Wednesday 2 October 2019

How to hide New and Delete buttons in a form in Dynamics AX/ D365 F&O X++

Below is the simple code I have written in form init() method to hide new and delete button from the user interface.

    public void init()
    {
        this.form().design().showNewButton(0);
        this.form().design().showDeleteButton(0);
        super();

    }




or

[ExtensionOf(FormStr(InventBatch))]
public final class P_InventBatchForm_Extension
{
    void run()
    {
        next run();
        inventBatch_ds.allowCreate(false); // Disable System Defined NEW Button
      }

}

Let me know if you have any questions

@Rahul

Friday 27 September 2019

Extend Clicked method using Extensions/Evnthandler in D365 Finance and Operations

1. Create extension of 'CustInvoiceJour' form


2. Open extension of that form from the solution explorer and add a button



3. We have added a button, now we have to perform some operations on click of that button. And for that we need to write the 'OnClicked' event. In order to do that, expand the 'Event's under that control and right click on 'onClicked' event and select 'Copy Event Handler Method'

4. 'OnClicked' event handler is copied and now you can paste as a method in any of your custom class or you can create a new class. I am using one of my custom classes.

5. Now the question is, how we will get the selected record on that form or how would we get the form data source, if you take a look of this above method, you will seen we have a formContol as a parameter and by using that we can get the form data source and selected records.


After getting the table buffer and form data source, we can perform other operations.

Thursday 19 September 2019

Post sales PickingList AX 2012/D365 X++

Step 1

Create WMSShipment record.
        WMSShipment                     wmsShipment;
    SalesTable                      salesTableLocal;
    salesTableLocal = SalesTable::find(‘SO-101328’);
    wmsShipment.clear();
    wmsShipment.initTypeOrderPick();
    wmsShipment.insert();

Step 2

Create WMSPickingRoute record.
    WMSPickingRoute                 wmsPickingRoute;
    wmsPickingRoute.clear();
    wmsPickingRoute.initTypeOrderPick(wmsShipment, WMSExpeditionStatus::Activated,                  WMSPickRequestTable::construct(salesTableLocal), ”, true);
    wmsPickingRoute.ActivationDateTime = DateTimeUtil::utcNow();
    wmsPickingRoute.insert();

Step 3

Create WMSPickingRouteLink record.
    WMSPickingRouteLink          wmsPickingRouteLink;
    wmsPickingRouteLink.initFromSalesTable(salesTableLocal);
    wmsPickingRouteLink.initFromWMSPickingRoute(wmsPickingRoute);
    wmsPickingRouteLink.insert();

Step 4

Create SalesPickingList line using WMSOrderCreate class.
    SalesLine                       salesLineLocal;
    InventMovement                  inventMovement;
    WMSOrder                        wmsOrder;
    WMSOrderCreate                  orderCreate;
    WMSOrderTrans                   wmsOrderTrans;

// Creating records for each salesline
while select salesLineLocal
        where salesLineLocal.SalesId == salesTableLocal.SalesId
    {
        // Inventory Movement object is required to create new           SalesPickingList lines
        inventMovement = InventMovement::construct(salesLineLocal);
        orderCreate = WMSOrderCreate::newMovement(inventMovement, -cancelQty);
        orderCreate.parmMustBeWMSOrderControlled(true);
        orderCreate.parmQty(cancelQty);
        orderCreate.parmRecalculateMaxQtyForValidation(false);
        orderCreate.parmMaxCWQty(cancelQty);
        orderCreate.parmMaxQty(cancelQty);
        orderCreate.run();
        wmsOrder = orderCreate.parmWMSOrder();
        wmsOrder.updateShipment(wmsShipment, cancelQty, wmsPickingRoute.PickingRouteID, false);

        // Updating status to activated 
        while select forupdate wmsOrderTrans
            where wmsOrderTrans.inventTransId ==  salesLineLocal.inventTransId
               && wmsOrderTrans.expeditionStatus == WMSExpeditionStatus::Registered
        {
            ttsBegin;
            wmsOrderTrans.expeditionStatus = WMSExpeditionStatus::Activated;
            wmsOrderTrans.update();
            ttsCommit;
        }
    }

Monday 16 September 2019

How to get Exchange rate in D365 F&O x++ ?



public display Amount TIDF_ExchangeRate()
{
    ExchangeRate     exchangeRate;
    ExchangeRateType ExchangeRateType;
    ExchangeRateCurrencyPair exchangeRateCurrencyPair;
    real             exchRate;

    CurrencyCode fromCurrency  = this.CurrencyCode;
    CurrencyCode toCurrency    = Ledger::find(Ledger::current()).AccountingCurrency ; //"AED";
    TransDate    transDate     = this.TransDate;


    select firstonly exchangeRateCurrencyPair
    where
        exchangeRateCurrencyPair.ExchangeRateType == Ledger::find(Ledger::current()).DefaultExchangeRateType
    &&  exchangeRateCurrencyPair.FromCurrencyCode == fromCurrency
    &&  exchangeRateCurrencyPair.ToCurrencyCode   == toCurrency;

   // if ( date2str(this.TransDate ,1,1,1) == 5/9/2018);
    exchRate = exchangeRate::findByDate(exchangeRateCurrencyPair.RecId,transDate).ExchangeRate;
    //info(strFmt("%1",exchRate/100));

    return exchRate/100;

}








Thursday 12 September 2019

How to get a unique record from a table without index in dynamics AX/D365 X++

Some times we need to find an unique record from a table. In this example I'm find an unique record based on created date time and grouping by salesId. Change the code according to your requirement.

Usually we use this format code when the primary index is not available in the table.


 while select maxof(LogCreatedDateTime) from SalesOrderLogView
                    join salesTable
                    group by SalesOrderLogView.SalesId
                     where  SalesOrderLogView.LogCreatedDateTime >= _fromdatetime
                                            && SalesOrderLogView.LogCreatedDateTime <= _todatetime

{
    info(SalesOrderLogView.salesID);
}

@Rahul

How to override the AX 2012 sales tax from 3rd party system using X++?

Most of the time we got the requirement to create Sales Orders in AX  (X++) using the input from the 3rd party system and in few case, we need to use the sales tax that is already calculated by the 3rd party financial system rather calculating it in AX. Here is the sample code which override the 3rd party sales tax rather use the AX sales tax calculation process.

2. After SO creation override the Sales tax provided by the 3rd party financial system.
Create and post SO Code2
After this if you want to check the sales tax , go to the invoice voucher and check the sales tax amount that has got invoice is equal to the 3rd party sales tax amount.

Adding a newline into a string in C# and X++

Below is the sample code we can use for  adding a newline after every occurrence of "@" symbol in the string in C#   using System...