Saturday, 21 February 2015

Importing an Excel spread sheet with multiple columns

You may have seen it before, but here’s our take:

Case: The end-user has a spreadsheet they want to import, and creating a csv-file is not an option.

Challenge: In Excel the end user needs to set a “ ’ ” in front of the numbers if they are to behave as strings e.g. an account no “123435”. When read in AX the value will be read as 123.435,0000 since is interpreted as a real. To overcome this, the end user must set the ‘ in front of the string e.g. ‘123435. Since there may be several hundred or thousands of records, it is not an option for the end user.

How to import from Excel and format the cells’ content when assigning to variables in AX.

Suggested solution below:

So, you create a dialog etc. for the file import and in the actual method you read the sheet, the code looks like this:


private void readExcelFile()
{
    SysExcelApplication     application;
    SysExcelWorkbooks       workbooks;
    SysExcelWorkbook        workbook;
    SysExcelWorksheets      worksheets;
    SysExcelWorksheet       worksheet;
    SysExcelCells           cells;
    COMVariantType          type;
    COMVariant              variant;
    int                     row=1,errors = 0;

    smmBusRelTable                      smmBusRelTable;
    smmBusRelAccount                    smmBusRelAccount;
    XXX_SortingId                       sortingId;
    container                           errorCon;

    ;
    application = SysExcelApplication::construct();
    workbooks   = application.workbooks();


    ttsBegin;

    try
    {
        workbooks.open(filenameopen);
    }
    catch (Exception::Error)
    {
        throw error("@SYS19358.");
    }
    workbook    = workbooks.item(1);
    worksheets  = workbook.worksheets();
    worksheet   = worksheets.itemFromNum(1);
    cells       = worksheet.cells();

    type = cells.item(row+1,1).value().variantType();

    while (type != COMVariantType::VT_EMPTY)
    {
        row++;
        // find variant of cell
        variant         = cells.item(row, 1).value();

        // set variant type to smmBusRelAccount
        smmBusRelAccount = this.variant2str(variant);

        if(smmBusRelAccount)
        {
            smmBusRelTable = smmBusRelTable::find(smmBusRelAccount,true);
            // if there is a prospect proceed
            if(smmBusRelTable.RecId)
            {
                variant = cells.item(row, 3).value();
                sortingId = this.variant2str(variant);
                if(!sortingId)
                {
                    sortingId = cells.item(row, 3).value().toString();
                }
                if(XXX_Table::exist(sortingId,8))
                {
                    smmBusRelTable.XXX_Field = sortingId;
                    smmBusRelTable.update();
                }
                else
                {
                    errorCon = conIns(errorCon,maxInt(),strFmt("Error with sorting %1",row,sortingId));
                }
            }
            // write to error log
            else
            {
                errorcon = conIns(errorCon,maxInt(),strFmt("Error with prospect %1",row));
            }
        }
        type = cells.item(row+1, 1).value().variantType();
    }

    application.quit();

    ttsCommit;

    info('Import done');
    // Header was counted as successful import. 1 is substracted from row to reflect that headers should not count
    info(strFmt('Number of items imported %1',(row-1)-conLen(errorCon)));

    setprefix(strfmt('@SYS344649',conLen(errorCon)));

    while (errors < conLen(errorCon))
    {
        errors++;
        info(conPeek(errorCon,errors));
    }
}


In the
while (type != COMVariantType::VT_EMPTY)
we check if there is something the cell read.

Then you assign the variant of the cell

variant         = cells.item(row, 1).value();


which you then cast to string:

private str variant2str(COMVariant _variant)
{
    str valueStr;
    ;

    switch(_variant.variantType())
    {
        case COMVariantType::VT_EMPTY   :
            valueStr = '';
            break;

        case COMVariantType::VT_BSTR    :

            valueStr = _variant.bStr();
            break;

        case COMVariantType::VT_R4      :
        case COMVariantType::VT_R8      :

            if(_variant.double())
            {
                valueStr = num2Str0(_variant.double(),0);
                
            }
            break;

        default                         :
            throw error(strfmt("@SYS26908",
                                _variant.variantType()));
    }

    return valueStr;

}

Friday, 20 February 2015

Easy steps to create a number sequence in AX2012


Following are steps to create new NumberSequence in Ax 2012

1. Open the NumberSeqModuleXXX (XXX is for the module name e.g. NumberSeqModuleCustomer, NumberSeqModuleHRM etc) class in the Application Object Tree (AOT) and add the following code to the bottom of the loadModule() method:

datatype.parmDatatypeId(extendedTypeNum(YYYY)); //EDT used for number sequence

datatype.parmReferenceHelp("zzzzzzzzzzz");

datatype.parmWizardIsContinuous(false);

datatype.parmWizardIsManual(NoYes::No);

datatype.parmWizardIsChangeDownAllowed(NoYes::Yes);

datatype.parmWizardIsChangeUpAllowed(NoYes::Yes);

datatype.parmWizardHighest(999);

datatype.parmSortField(20);

datatype.addParameterType(

NumberSeqParameterType::DataArea, true, false);

this.create(datatype);

2.Create a new job with following code and run it:

static void NumberSeqLoadAll(Args _args)

{

NumberSeqApplicationModule::loadAll();

}

3.Run the number sequence wizard on the Organization

administration >Common >Number sequences > Number sequences > Generate and click on the Next button. Click on Details for more information. Delete the lines except the desired lines ( lines with your module and reference to your EDT). Click next and finish the wizard.

4.You will find the newly created numberSequence in the respective module's parameters form under numbersequence tab.
In the parameters table(zzzzParameters) in the AOT create the following method:

public server static NumberSequenceReference numRefYYYY()

{

return NumberSeqReference::findReference(extendedTypeNum(YYYY));

}

5.To use the number sequence refer to the following code :

public void initValue()

{

NumberSeq NumSeq;

;

super();

NumSeq = NumberSeq::newGetNum(zzzzParameters::numRefYYYY(),true);

//NumSeq.num(); this will create new numbers.

}

Monday, 16 February 2015

How to redirect/drain user clients in a cluster AOS-setup


It may become necessary to redirect users to a specific AOS instance even though the client configuration is setup to use a AOS cluster.

If the redirect is a temporary fix such as setting an AOS-server offline for maintenance instead of editing all axc-client configurations, it is possible in the user interface to tell an AOS-server to redirect user connection to other AOS-instances in the cluster quite easily:

Open AX and navigate to the System Administration module:


In the group Users, open Online users:



On the tab Server Instances confirm that are are 2 or more AOS-instances connected to the application:



Mark the AOS-instance that you want to redirect from and press the Reject new clients button, e.g. 


A prompt will ask for confirmation, press OK:


Status for the AOS instance will shift to Draining, which means that connections to the AOS-instance is in the process of drained from the AOS-instance(s).
 

To allow connections to the AOS-instance again, navigate to the same form, highlight the redirecting AOS instance and press the “Accept new clients”.

In this way, we have redirected new connections to an AOS-cluster away from a specific AOS-instance, without altering the client configuration.


Monday, 6 May 2013

Checking up on Backup history?

Ever needed to show the history of your backups? Here is the SQL script to do it


SELECT top 10
    database_name,
    recovery_model,
    CASE bs.type
        WHEN 'D' THEN 'FULL'
        WHEN 'I' THEN 'DIFFERENTIAL'
        WHEN 'L' THEN 'TRANSACTION LOG'
        ELSE 'UNKNOWN'
    END AS backup_type,
    backup_start_date,
    backup_finish_date,
    backup_size,
    compressed_backup_size
FROM msdb.dbo.backupset bs
where database_name = 'AX2012'
order by backup_finish_date desc



Thursday, 11 April 2013

Database Maintenance Strategies for Dynamics AX - reblog

I just happened to come across these blog posts while perusing my news feed:

http://blogs.msdn.com/b/axinthefield/archive/2012/08/01/database-maintenance-strategies-for-dynamics-ax.aspx

http://www.brentozar.com/archive/2009/02/index-fragmentation-findings-part-1-the-basics/

That reminded me of an article which I found a couple of years back that described index fragmentation in this manner:

"Index fragmentation is a lot like cholesterol. The bad kind, not the good kind. It builds up slowly. Some deletes occur, leaving empty space in a data page here. Inserts occur, but the target page is packed, so a page split occurs so the record can be inserted in the correct order, yet the other page is now mostly empty. Updates are a double whammy. Over time, your index pages continue to be less and less full, meaning you have to perform that many more reads per query.

Just like cholesterol, it’s not perceptible. Sure, if you compared yourself now to 10 years ago, you’d be able to instantly recognize that you feel tired all the time, or occasionally dizzy spells or have blurred vision. Then all of a sudden — HEART ATTACK!"


Wednesday, 23 November 2011

Agile development and ERP

Here is small collection of blog-posts, articles and case-studies on Agile and Enterprise Resource Planning implementations.
The post Can Agile Practices Prevent ERP Disaster sums up different sources on the topic and Agile practices in large system integration projects introduces a series of posts all dealing with agile and ERP.

MSDynamicsworld.com has a post titled How to Substitute Agile ERP Implementation for the Waterfall Approach which is spot on, when it comes strong link between the waterfall model and ERP-implementations.

InfoQ has a whole subsite dedicated to agile processes and Jesper Boeg's introduction to Kanban "Priming Kanban" can be downloaded for free.

Wednesday, 3 November 2010

Dynamics AX Easter Egg

Jacob posted this easter egg on his blog - funny little thing
I know it's a "bit" off-season, but funny nonetheless

static void easterEgg(Args _args)
{;
    info(conPeek(new HeapCheck().createAContainer(), 4));
}