Friday, 4 October 2019

Get exchange rate

Somehow it may be not obvious how to get exchange rate for currency code and given date

I've just put a code below to Global class, to have an access from any place in system to this easy way of getting exchange rate.
But of course you can put it wherever you want.

public static CurrencyExchangeRate getExchangeRate(CurrencyCode _transactionCurrency, TransDate _transactionDate)
{
    ExchangeRateHelper      exchangeRateHelper;
    CurrencyExchangeRate    exchangeRate;


    exchangeRateHelper  = ExchangeRateHelper::newExchangeDate(Ledger::current(), _transactionCurrency, _transactionDate);

    exchangeRate       = exchangeRateHelper.getExchangeRate1();

    exchangeRate = exchangeRate / 100; //please be aware of that 100! Check how it is in your system.

    return exchangeRate;
}

There are similar methods in the system in class ExchangeRateHelper, but you have to give one more argument, which in my opinion should be defaulted, but it isn't
Anyway, you can use standard method getExchangeRate1_Static()

Tuesday, 21 May 2019

Get file from table field which is a container type

Last time I had a task to modify an image which is displayed on collection letter.
Just simple change.
The issue was, that nobody at company had original image.

Image was stored in one of Dynamics AX tables, so I had to take it out from there

Below you can find a simple code how to get this image and save it to some disk location

static void GetFile_PKI(Args _args)
{

    CustCollectionLetterLine    collectionLetterLine;
    container                   image;    
    Bindata                     bin = new bindata();    
    str                         tmpImageHolder;    


    //I knew what RecId of that record is, so it's just simple select statement
    select collectionLetterLine 
        where collectionLetterLine.recId == 5637144577;

    //This is a table field where I have my image. Field is container type
    image           = collectionLetterLine.NotesLogo_PKI;
    
    bin.setData(image);
    
    // This will give me a string encoded as base64
    tmpImageHolder  = bin.base64Encode();

    // I've chosen .jpg extension but in fact it does not matter
    //You have to have a look on file header (for example in notepad) to get
    //information what type of file it is.
    AifUtil::saveBase64ToFile(@"C:\temp\test.jpg", tmpImageHolder);

}

Tuesday, 6 February 2018

LedgerDimension for newly created Customer or Vendor

There are plenty of blogs which shows how to create/merge ledgerDimension for ledgerAccount. 

But the issue is with Vendor or Customer account.

And...
There are plenty of blogs which describes how to import transactions to ledger journal for customer's or vendor's transactions.

But, the problem is, when customer account or vendor account is just created, there where no transactions posted yet for that account in ledger journals.
In such case there is no record in  DimensionAttributeValueCombination.
Such record is created automatically when user creates a record in LedgerJournalTrans with such Customer or Vendor account. Even without posting.

So, how to create an entry for that table for Customer or for Vendor from X++?
Because we need that to import transactions, and without asking users to put 

We need a recId to use it in multitype account in ledger journal

DimensionStorage::accountNum2LedgerDimension(vendTable.AccountNum, LedgerJournalACType::Vend); 

or

DimensionStorage::accountNum2LedgerDimension(custTable.AccountNum, LedgerJournalACType::Cust); 

Those examples are returning recId for such ledgerDimension , ready to use

For example

ledgerJournalTrans.ledgerDimension = DimensionStorage::accountNum2LedgerDimension(custTable.AccountNum, LedgerJournalACType::Cust); 

or if you want to have it  done better

AxLedgerJournalTrans                trans;

trans.parmLedgerDimension(DimensionStorage::accountNum2LedgerDimension(custTable.AccountNum, LedgerJournalACType::Cust));

Enjoy

Friday, 30 October 2015

Multi select lookup on form field

We have ready-to-use lookups in system.
Unfortunately we can choose only one item from such lookup.
What if we need to select more than one element?

We have to use class SysLookupMultiSelectCtrl

I've prepared an example how to use this on AOT form, but there is no issue to use it on dialog field from SysOperation Framework.
You must remember that it can be used when dialog is already build, so on SysOperation framework it can be used on method postBuild or postRun.

It's not so easy to use it on RunBase classes or when you're trying to build, because you have to overload a method lookup for that dialog field. Overloading method can be done only once, and this SysLookupMultiSelectCtrl is overloading it once again, it cause runtime errors.
If you really need to have such multilookup on Runbase dialog you have to copy some pieces of code from SysLookupMultiSelectCtrl and overload lookup method.

So....this example is for form fields, and last line from init method can be transfered to postBuild() method on SysOperation framework to UIBuilder class.

I've created very simple form with one field ItemId



I've changed two properties on that field
Autodeclaration: Yes
ExtendedDataType: ItemId

You don't have to put EDT on this form control. You'll receive multilookup anyway.
I've used EDT to have proper label, help text and size of that control.

Remember that EDT ItemId has limitation of lenght, so it may be not long enough to store more than 1-2 item Ids! It's just an example

You have to modify three method on form

classDeclaration




init



close



Instead of modifying close method, you can add some button on our form.
I'm using close method to catch value from our ItemId control and display this value.

This is how our lookup looks like


When we press OK on lookup, we will get chosen values to our field



After form closing, we will receive a message with chosen values.




Tuesday, 25 August 2015

Unretrieved fields

Sometimes there might be a problem with new field just added to a table.

There is no possibility to put a value to such field on form, neither in Table Browser.
Unretrieved value is visible, and such field is grayed out.

There are few steps which may help, but the bad news is that, that all those steps may not help and at the end you'll have to restart AOS (it worked always for me).
So, if you have this issue with "Unretrieved" fields on your DEV or TEST machine and you can restart it.....just do it.

So, there are few steps which may help.
1. Sychronize this table
2. Restore form where is this field. (right click on form from AOT and then -> Restore)
2. Clean all cache (Dev enviromnent, Tools\Caches\...)
3. Close AX client and delete all .auc files (C:\Users\YOURUSER\AppData\Local\Microsoft\Dynamics Ax\VSAssemblies{xxxxxxxxxxx\


There is one known additional issue regarding this subject. 
This new field may be visible on form and usable, but all select statement will return blank/null or (even worse) default value of Enum from that field, if that field is a type of Enum.
This last thing is the worst scenario in my opinion, because it's very hard to find why select statement is fetching wrong data.

Friday, 14 August 2015

FilePath folder lookup does not work. Why?

There is a new field on our table. Field bases on Extended Data Type FilePath.

This fields has been added to a form. We can see an automatic lookup button. It looks like a folder button.



The issue is, that this folder button does not work. We can click on it, but there is no new window when we can choose a folder to store files.

The "magic trick" is, that we have to add to method node of our form new method
Metod name is filePathLookupTitle

This method returns some string.

Example

str filePathLookupTitle()
{
    // Archive directory
    return "@SYS26605";
}

The good example is on system form DocuType


Tuesday, 11 August 2015

New SSRS instance. How to deploy all reports

We may have more then one SSRS instance on our server.

Every time when we will create such instance we have to create new configuration in our AX and create new report folder (also from AX)

System administration\Setup\Business intelligence\Reporting Services\Report servers
There is a button for creating a new report folder.

This folder will be empty.

We can deploy every report from AOT one by one: right click on report and position Deploy

It's much faster to use AX PowerShell for that purpose.
It has to be run from server where Reporting Services are installed.
There should be also installed a part of Dynamics AX, to have AX PowerShell and Dynamics AX Configuration. PowerShell will use default config set in AX Configuration tool.

We have to export AX configuration to have .axc file with stored information about that config. This file .axc has to be renamed to Microsoft.Dynamics.AX.ReportConfiguration.axc
When name of that file will be changes, we have to copy that file to folder with our new SSRS instance.
Default folder is usually:
C:\Program Files\Microsoft SQL Server\OurInstanceName\Reporting Services\ReportServer\bin

After that we have to run Microsoft Dynamics AX 2012 Management Shell (our Powershell with AX extensions)

command to use Publish-AXReport -ReportName *

Instead of '*' we can put a name of report which we want to deploy, or we can put for example S* - it will deploy all reports with names starting from S (SalesInvoice for example).

Such deploy process started from Shell is much faster then deploying from AOT.