Tuesday, 20 May 2014

Data import export Framework - Copy entity data between companies

The Data import export Framework has the possibility to import or export data from external sources, but you can also copy data from one company to another within AX itself. 

Below you'll find the how to (like importing data, again this is really easy), but first some downsides in my opinion:


  • You can't see the selected companies after having run the wizard. I'd like to now this information to double check. You'd want to see the in the [Copy processing group] form. 
  • Business validations cannot be run when copying the data. I'd want the possibility to check if mandatory metadata is present in my new company and if not, warn me about it, like when importing data. Now you should be careful to validate your data in the staging table and correct errors if there are any. If you forget to do this, you'll enter invalid data in AX and loose your data integrity. 
How to copy the data


To use it, you must select option: [Copy entity data between companies]. It lets you select an entity (in this case Asset):


Next up is the selection of the Source and the Target company, simply select one Source company and one or more Target companies: 


Click next and finish the wizard to set up the copy template. Now all that needs to be done is to Get the Staging data (hit run to fetch the records).


And finally run the job [Copy data to target] to copy the data in the actual AX table. 


The assets are now present in the selected company.




Wednesday, 14 May 2014

Create and update production order through code

Here's how to create a production order and update it completely up until ending the order through code for AX2009: 


static void JLH_ProductionOrderViaCode(Args _args)
{
    NumberSeq                       numberSeq;
    ProdTable                       prodTable;
    InventDim                       inventDim;
    ProdMultiCostEstimation         prodMultiCostEstimation;
    ProdMultiScheduling             prodMultiScheduling;
    ProdMultiRelease                prodMultiRelease;
    ProdMultiStartUp                prodMultiStartUp;
    ProdMultiReportFinished         prodMultiReportFinished;
    ProdMultiHistoricalCost         prodMultiHistoricalCost;
    Args                            args   = new Args();
    ;

    ttsbegin;
    NumberSeq                   = NumberSeq::newGetNumFromCode(ProdParameters::numRefProdId().numberSequence);
    ProdTable.ProdId            = NumberSeq.num();
    ProdTable.ItemId            = '5001'; // Item

    prodTable.initFromInventTable(InventTable::find(prodTable.ItemId));
    prodTable.initBOMVersion();
    prodTable.initRouteVersion();
    inventDim.InventSiteId      = 'S1'; // Site
    inventDim.InventLocationId  = 'MW'; // Warehouse
    inventDim = InventDim::findOrCreate(inventDim);
    prodtable.InventDimId       = inventDim.inventDimId;
    prodtable.QtySched          = 2; // Quantity
    prodtable.insert();
    ttscommit;

    // Estimation
    prodMultiCostEstimation = ProdMultiCostEstimation::construct(args);
    runBaseMultiParm::initParm(ProdMultiCostEstimation);
    prodMultiCostEstimation.insert(prodTable, ProdMultiCostEstimation.defaultParmBuffer());
    prodMultiCostEstimation.run();

    // Scheduling
    prodMultiScheduling = ProdMultiScheduling::construct(ProdSchedMethod::OperationScheduling);
    runBaseMultiParm::initParm(prodMultiScheduling);
    prodMultiScheduling.insert(prodTable, prodMultiScheduling.defaultParmBuffer());
    prodMultiScheduling.run();

    // Release
    prodMultiRelease = ProdMultiRelease::construct(args);
    runBaseMultiParm::initParm(ProdMultiRelease);
    prodMultiRelease.insert(prodTable, prodMultiRelease.defaultParmBuffer());
    prodMultiRelease.run();

    // Start up
    prodMultiStartup = ProdMultiStartUp::construct(args);
    runBaseMultiParm::initParm(prodMultiStartup);
    prodMultiStartup.insert(prodTable, prodMultiStartup.defaultParmBuffer());
    prodMultiStartup.run();

    // Report as Finished
    prodMultiReportFinished = ProdMultiReportFinished::construct(args);
    runBaseMultiParm::initParm(ProdMultiReportFinished);
    prodMultiReportFinished.insert(prodTable, ProdMultiReportFinished.defaultParmBuffer());
    prodMultiReportFinished.run();

    // End the Order
    prodMultiHistoricalCost = ProdMultiHistoricalCost::construct(args);
    runBaseMultiParm::initParm(ProdMultiHistoricalCost);
    prodMultiHistoricalCost.insert(prodTable, ProdMultiHistoricalCost.defaultParmBuffer());
    prodMultiHistoricalCost.run();

}

Wednesday, 7 May 2014

Data Import Export Framework in AX2012 R3

AX2012 R3 comes with the Data Import Export Framework which is a separate module in AX. It is also part of R2 CU7 and available as a download for 2012 Featurepack (via Information Source). 

I did install it before in the Featurepack version, but it had some minor bugs which held me from futher investigation this possibly cool tool. But after re-discovering this tool, my first impression for the out of the box options are:
Pros: It looks nice, it's comprehensive and let's you preview data which you'd want to import.
Cons: It stills seems a bit shaky, the slightest mistake can result an error which doesn't explain the reason of the failure. Also the drag and drop isn't very smooth (yet?), but it gets the job done.

So how does it work?

In this blog I'll briefly show how you can import data via CSV, but you can also upload via ODBC or via other AX instances. I'll import 4 new main accounts in this example.

First you need to create a Source Data format. In this case I've created CSV of the type File and have set the column delimiter to Semicolon. 



Next, create a new Processing Group (MA with description Main Account in my case) and click Entities, here you can select the type of import (I choose Main Account, but it could also be customers, sales orders, vendors etc.). Select source data format to be CSV and then click Generate Source file to create the input template. Select the fields you want and generate a sample file. 


In the Sample File Path, select your enriched file which you'll import. Then the other options in the top menu become available. 

Select Generate Source Mapping and based on the header line in your import file, the links are created. You can view (and modify) the link from the import file to the table via the button "Modify Source mapping" as can be seen below. When you modify a link (drag and drop), remember to hit the Save button. 


You can preview the file which is to be imported by clicking Preview Source File, it will show the upload to the staging table with the values and fields. 

Close the entities form and return to the Processing Group, hit "Get staging data" to upload the data and click Run. In my case, it results in an infolog which says that 4 records have been inserted in the staging table. Via "Execution History" you can preview the data in the staging table which can now be imported to the table(s) in AX. A last check if all data is correct and then click "Copy data to target" and click Run to import. 

This resulted in 4 new records and my main accounts are created: 


It's really that simple, so go for it and check it out!

Saturday, 3 May 2014

Importing Demo Data in DAX 2012 R3

Today I installed AX2012 R3 on my laptop and of course I want the demo data to be imported as well (download via Partnersource). You need to use the Test Data Transfer tool which can be downloaded via InformationSource.

The next step is to extract the demo data and to install the test data transfer tool. After that has been done, open the command prompt in admin mode. Navigate to the folder where the test tool is installed and enter the following command: 

dp [direction] [directory] [database] [server]

direction = Import or Export (import in this case)
directory = the directory which holds the extracted demo data
database = the name of the AX2012 R3 database
server = name of the server (can be left blank when importing on the local machine)



Hit Y to confirm that this is a test environment and you want to import the data. The import will start to process the files.

When the import is complete, you'll have the new companies! The whole import took approximately one hour, great work.


Wednesday, 5 March 2014

Clean up your databaselog

A cool thing in AX is to keep track of vital changes using the DatabaseLog. A problem however is that your databaselog can grow exponentially so there are two things you need when using it:
1. Choose carefully which data you want to keep track of. Mostly it's enough to track some updates on some fields and some delete actions. You should periodically review if it is still necessary to log these changes. 
2. You need to clean up your log for it can grow enormously within time. Disadvantage of the clean up functionality in AX is that it simply deletes your log. 

Therefor I created a job which exports your log (with the old and the new value) to a csv file which you can save and afterwards delete the log in AX. The job is just to show what could be possible to save your databaselog. You could improve it with date ranges, delete records after saving them, create a class which can run in batch, only a number of records to keep the file size normal and then on the next file, etcetera. 

static void JLH_DatabaseLogToCSV(Args _args)
{
    #AviFiles
    SysOperationProgress    progress = new SysOperationProgress();
    int                     i;
    AsciiIo                 asciiIo;
    Filename                filename;
    Str                     line;
    Container               con;
    SysDatabaseLog          databaseLog;
    str                     dataBaseLogOld;
    str                     dataBaseLogNew;
    str                     dataBaseLogDate;
    str                     dataBaseLogUser;
    str                     tableName;
    str                     fieldName;
    container               databaseLogContainer;
    int                     containerLength;
    int                     counter;
    int                     countRecords;
    int                     tableId;
    int                     fieldId;
    ;

    progress.setCaption("Export To CSV in progress...");
    progress.setAnimation(#AviTransfer);

    filename = "C:\\FileShare\\DAX2009\\ImportExport\\ExportToCSV.txt";
    asciiIo = new AsciiIo(filename,'W');
    asciiIo.outRecordDelimiter('\r\n');
    asciiIo.outFieldDelimiter(";");
    asciiIo.write('Table;Field;NewValue;OldValue;Date;User');

    while select dataBaseLog
    {
        progress.setText(strfmt("DataBaseLog Recid %1", databaselog.RecId));

        containerLength = conLen(dataBaseLog.Data);

        for (counter = 1; counter <= containerLength; counter++)
        {
            dataBaseLogContainer    = conPeek(databaseLog.Data, counter);
            dataBaseLogOld          = conpeek(databaselogContainer,3);
            dataBaseLogOld          = strKeep(dataBaseLogOld,' 1234567890,.abcdefghijklmnopqrstuvwxyz');
            dataBaseLogNew          = conpeek(databaselogContainer,2);
            dataBaseLogNew          = strKeep(dataBaseLogNew,' 1234567890,.abcdefghijklmnopqrstuvwxyz');
            dataBaseLogDate         = dateTime2str(databaseLog.createdDateTime);
            dataBaseLogUser         = databaseLog.createdBy;
            tableName               = tableid2name(databaseLog.table);

            tableID                 = databaseLog.table;
            fieldId                 = conpeek(databaseLogContainer,1);
            fieldName               = fieldid2name(tableID,fieldId);

            if (fieldName != 'RecVersion')
            {
                asciiIo.write(strfmt('%1;%2;%3;%4;%5;%6',
                tableName,
                fieldName,
                dataBaseLogNew,
                dataBaseLogOld,
                dataBaseLogDate,
                dataBaseLogUser));
            }
        }
    }
    asciiIo = null;
}

Tuesday, 25 February 2014

SysQueryRangeUtil, dynamic dates in Reports

There are multiple reports in Dynamics AX which you run on a daily or weekly basis and where you need to change the date range every time you run the report. For instance, you want to see your turnover of the past week on every monday. That means that every monday, you need to change your date range.

There a clever query utility which let's you create dynamic ranges. 

The different options are (today's date is 25-02-2014): 

  • (dayRange(-30,0)) - Results in a date range for the last 30 days: "26-01-2014".."25-02-2014"
  • (day(-1)) - Results in yesterday's date: 24-02-2014
  • (day(0)) - Results in today's date: 25-02-2014 
  • (day(1)) Results in tomorrow's date: 26-02-2014
  • (greaterThanDate(2)) - Results in every date after today plus 2: > 27-02-2014
  • (lessThanDate(-1)) - Results in every date of today minus 1: < 24-02-2014
  • (monthRange(0,2)) - Results in first day till the last day of the month's choosen (0 = current month): "01-02-2014".."30-04-2014"
  • (yearRange(-1,-1)) - Results in first day till the last day of the chosen year: "01-01-2013".."31-12-2013"

Now, how to apply this in a report. In a date field, simply key in the rangetype you need. Below you see the range (dayRange(-30,0)). The result will be that every day you run this report, it will display the transactions of today minus 30 days up until today. This way, you can set reports like this to run in Batch and to sent them periodically to a fileshare or e-mail them without having the need to always having to change the date range. 



To see the results in a job or to play around with the different date options, you can copy and use the job below:
static void JLH_sysQueryRangeUtil(Args _args)
{
    str         dateRange;
    ;

    // In the following date examples, date of today (25-02-2014) is taken

    dateRange = sysQueryRangeUtil::dayRange(-30,0); // Results in a date range for the last 30 days: "26-01-2014".."25-02-2014"
    info(strfmt(dateRange));

    dateRange = sysQueryRangeUtil::day(-1); // Results in yesterday's date: 24-02-2014
    info(strfmt(dateRange));

    dateRange = sysQueryRangeUtil::day(1); // Results in tomorrow's date: 26-02-2014
    info(strfmt(dateRange));

    dateRange = sysQueryRangeUtil::greaterThanDate(2); // Results in every date after today plus 2: > 27-02-2014
    info(strfmt(dateRange));

    dateRange = sysQueryRangeUtil::lessThanDate(-1); // Results in every date of today minus 1: < 24-02-2014
    info(strfmt(dateRange));

    dateRange = sysQueryRangeUtil::monthRange(0,2); // Results in first day till the last day of the month's chosen (0 = current month): "01-02-2014".."30-04-2014"
    info(strfmt(dateRange));

    dateRange = sysQueryRangeUtil::yearRange(-1,-1); // Results in first day till the last day of the chosen year: "01-01-2013".."31-12-2013"
    info(strfmt(dateRange));

}

Wednesday, 8 January 2014

ICP / OB declaration via Digipoort in The Netherlands

In AX, the ICP and OB declarations could be sent electronically to the tax authority using the PIN method. However, as of 01-02-2014, it is no longer possible to submit your declaration via BAPI with PIN and as of 01-06-2014 it's also no longer possible to sent it via BAPI PKI (but this was never possible in AX). It is to be sent via Digipoort using a "PKIoverheid" service certificate and besides that the declaration should an XBRL format instead of the old XML. 

The new certificate can be optained via:
• Digidentity (https://www.digidentity.eu/)
• ESG (http://www.de-electronische-signatuur.nl/)
• KPN (http://certificaat.kpn.com/)

• QuoVadis (http://www.quovadisglobal.nl/)

The new XBRL layout is mandatory (SBR - Standard Business Reporting) and the taxonomy can be found here: http://www.nltaxonomie.nl/ (version 8.0).

For Dynamics AX 2009, AX2012 and AX2012 R2, a hotfix has been created by Microsoft, you can download it here via Partnersource.

Currently, for AX2009 it is only possible to create the XBRL message, you cannot sent it yet, but Microsoft is working on it and has said to release a.s.a.p.. 


At this moment I've created the new messages in both AX2009 and AX2012, but have not yet send them. As soon as the AX2009 fix is there and all connection strings are complete, I'll try to post an update with the sending process as well.

Creating the declaration

After installing the update, nothing has changed. You create the ICP / OB declaration as usual in AX and then create the message via [General Ledger - Periodic - Sales tax payment - Netherlands - Electronic ICP declaration]. First create the declaration for a given period and then click [Send XML]. Your declaration cannot be sent yet, but the message is created and you can view it in the Outgoing BAPI folder. Open the file and you'll see the new layout according to the required XBRL format. This works the same for both AX2009 and AX2012.

Below you see the new output, looks good :-)!