Monthly Archives: December 2013

Region as an Internal Organisation

System should allow users to create Region within the internal organization hierarchy. For this we have to do some customization

First create new operating unit type element in OMOperatingUnitType base enum

f1

OMOperatingUnitType is used in OMOperatingUnit tables so we need to Restore OMOperatingUnit tables.

Create new View which named as DimAttributeOMRegion as shown in below screen

f2

 Create Internal Region Organization type

Organization Administration > Common > Organization > Internal Organization

Create new region from Internal Organization action pane button as shown in below screen

f3

Click Ok

f4

General Ledger > Setup > Financial Dimensions > Financials Dimensions

Newly created organization should appear within Financial Dimension Form and could be used throughout AX.

f5


Customize Existing Number Sequence to Fiscal Year Number Sequence

Here we are customizing existing Number sequence and convert it in to Fiscal year number sequence Many customer demands for Purchase order creation with automatic year means how to add year to a number sequence in AX2012, e.g. to generate IDs such as 2013-xxxxx and 2014-xxxxx, that would automatically use the current year. Some people understand that number sequence scopes should allow that, but they don’t know how, from very little bit of customization we can automate purchase order number sequence with year

Customize Purchase order Number Sequence Class:

Add fiscal calendar parameter in NumberSeqModulePurchaseIOrder (LoadModule) method as shown in below screen

 datatype.addParameterType(NumberSeqParameterType::FiscalCalendarPeriod, true, true);
fk1

Load NumberSeqModulePurchaseIOrder (Load Module) method Through Job

 NumberSeqModulePurchaseOrder NumberSeqModulePurchaseOrder = new NumberSeqModulePurchaseOrder();
NumberSeqModulePurchaseOrder.load();
fk2

Before going to run the Job, Remove purchase order existing scope parameter record in NumberSequenceDatatype table, if we don’t remove previous Purchase order scope parameter record it will not effect in Segment configuration as shown below

CEU/Organization administration/Number Sequence/ Segment configuration

fk3

Add Fiscal Year Relation to PurchParamater Table:

CEU/Procurement and sourcing/procurement and Sourcing parameters

fk4

We have customized procurement and Sourcing parameters form for Purchase order Fiscal Number sequence, because we need to select which Fiscal year the purchase order is created every year.

Add fiscal year table relation to PurchParamter table

fk5

Add a fiscal year Field on Procurement and Sourcing parameters form

fk6

Customize Purchase Order Number Sequence Table (PurchParameter):

Add following code in Purchase order Number sequence Parameter table method (numRefPurchId), in this existing method we have change code according to fiscal year.

static client server NumberSequenceReference  numRefPurchId(TransDate _date = systemdateget())
{
   //
   PurchParameters PurchParameters;
   NumberSeqScope  scope;

select firstOnly PurchParameters; // get Selected fiscal year in 
                                  // procurement and    Sourcing parameters Form 

   scope = NumberSeqScopeFactory::CreateDataAreaFiscalCalendarPeriodScope
                            (curext(),FiscalCalendars::findPeriodByPeriodCodeDate(FiscalCalendar::findByCalendarId(PurchParameters.FiscalCalendar).RecId, _date).RecId);
   //

     return NumberSeqReference::findReference(extendedTypeNum(PurchId),scope);
}
fk7

Fiscal Year Number Sequence Configuration

 

General ledger -> Setup -> Fiscal calendars.

Create a new Fiscal year or use existing Fiscal year, but In our case we have created our own Fiscal year for Ten years because we need to create Purchase order automated for Ten years

fk8

If you want to add more years follow below screen.

fk9

Click the Calendar -> Calendar year -> New fiscal year button.

Change Copy from last fiscal year from ‘true’ to ‘false’.

Change Unit from ‘Months’ to ‘Years’.

Click the Create button.

Close the Fiscal calendars form.

Number sequences creation

 

fk10

 

Click Area Page node: Organization administration -> Common -> Number sequences -> Number sequences. Click the Number sequence -> New -> Number sequence button.

fk11

Change Number sequence code from ” to ‘POFiscal’.

Change Name from ” to ‘2013’.

Change Scope from ‘Shared’ to ‘Company and Fiscal calendar period’.

Change Company from ” to ‘ceu’.

Change Calendar from ” to ‘FiscalYear’.

Change Fiscal year from ” to ‘2013’.

Change Period name from ” to ‘Period 1′.

fk12

 

Make it very clear that we are creating PO number sequence for the year 2013 and we have to create PO number sequence again for Year 2014, 2015, 2016.xxxxxx 2023 with same Number sequence Code i.e. ‘POFiscal’  as shown in below screen for Year 2014

fk13

 

Creating Purchase orders:

Before going to create Purchase order make sure that Fiscal year must be selected in Procurement and sourcing Parameter form which we have customized earlier above

fk14

 

This selected Fiscal year must be same which we have selected in creating Purchase order Fiscal year Number Sequence.


Consuming External Web Service through a .Net Class Library Wrapper in Microsoft Dynamics AX 2009

Direct referencing a web service in AOT may lead to different issues some times when it comes to use service reference in the x++ code.

I will show you a simple recipe of how we can consume an external web service in AX2009 by wrapping this web service into a .net assembly wrapper.

Open a visual studio, create a new project.
Select the .net class library as template.
Give it a meaning full name so it can be accessed from x++ code.

th1

th2

Add a web service reference, I’m using a public WSDL of the currency convert.
Click Ok and build the project.
Now right click on the project and go to the project directory.
Copy the dll from bin and rename app.config file to AX32Srv.exe and AX32.exe.
Copy these files to the directory of AOS server and the bin of client if needed.
Restart the AOS.
Now you can consume this webservice from x++ code.
ServiceWrapper.ServiceReference .
If you don’t copy the config file to AOS bin you face this error because It does not read the end points from configuration file when instantiating the service client.

 System.Reflection.TargetInvocationException: Exception has been thrown by the target of an invocation. —> System.InvalidOperationException: Could not find default endpoint element that references contract ‘ServiceReference1.ICyclelutionService’ in the ServiceModel client configuration section. This might be because no configuration file was found for your application, or because no endpoint element matching this contract could be found in the client element.

 at System.ServiceModel.Description.ConfigLoader.LoadChannelBehaviors(ServiceEndpoint serviceEndpoint, String configurationName)

 at System.ServiceModel.ChannelFactory.ApplyConfiguration(String configurationName, Configuration configuration)

 at System.ServiceModel.ChannelFactory.ApplyConfiguration(String configurationName)

 at System.ServiceModel.ChannelFactory.InitializeEndpoint(String configurationName, EndpointAddress address)

 at System.ServiceModel.ChannelFactory`1..ctor(String endpointConfigurationName, EndpointAddress remoteAddress)

at System.ServiceModel.ConfigurationEndpointTrait`1.CreateSimplexFactory()

at System.ServiceModel.ConfigurationEndpointTrait`1.CreateChannelFactory()

 at System.ServiceModel.ClientBase`1.CreateChannelFactoryRef(EndpointTrait`1 endpointTrait)

 at System.ServiceModel.ClientBase`1.InitializeChannelFactoryRef()

 at System.ServiceModel.ClientBase`1..ctor()

 at ServiceRef.ServiceReference1.CyclelutionServiceClient..ctor()

— End of inner exception stack trace —

 at System.RuntimeTypeHandle.CreateInstance(RuntimeType type, Boolean publicOnly, Boolean noCheck, Boolean& canBeCached, RuntimeMethodHandleInternal& ctor, Boolean& bNeedSecurityCheck)

 at System.RuntimeType.CreateInstanceSlow(Boolean publicOnly, Boolean skipCheckThis, Boolean fillCache, StackCrawlMark& stackMark)

at System.RuntimeType.CreateInstanceDefaultCtor(Boolean publicOnly, Boolean skipCheckThis, Boolean fillCache, StackCrawlMark& stackMark)

 at System.Activator.CreateInstance(Type type, Boolean nonPublic)

at System.RuntimeType.CreateInstanceImpl(BindingFlags bindingAttr, Binder binder, Object[] args, CultureInfo culture, Object[] activationAttributes, StackCrawlMark& stackMark)

 at System.Activator.CreateInstance(Type type, BindingFlags bindingAttr, Binder binder, Object[] args, CultureInfo culture, Object[] activationAttributes)

at System.Activator.CreateInstance(Type type, Object[] args)

at Microsoft.Dynamics.AX.ManagedInterop.ClrBridgeImpl.NewClrObject(ClrBridgeImpl* , Char* pszClassName, Char* assemblyName, Int32 argsLength, ObjectWrapper** arguments, Boolean* isException

Another way to wrap the web service is to wrap it in a web service project in VS. But for that, the web service must be hosted to IIS.

 


Import ledger, AP and AR invoice journal lines

Recently, I came across a requirement in which users want to import the ledger, AP and AR invoice journal lines from a pre-defined excel format in general journal AX2012R2. So I developed a functionality hope that helps you

Below snap shot shows the flat list of all objects in this project.

t1

 Below snap shot shows the UI.

t2

Below is the list of all functions created in a class.

t3

class SLSalesPurchLedgerJourLinesImport extends RunBase
{
    int                                 row;
    int                                 col;
    LedgerJournalId                     journalNum;
    DialogRunbase                       dialog;
    DialogGroup                         dialogGrp;
    FilePath                            filePath;
    DialogField                         dialogFieldsalesPurchLedger,
                                        dialogFieldFilePath;
    SLSalesPurLedger                    salesPurLedger;

    SysExcelApplication                 application;
    SysExcelWorkbooks                   workbooks;
    SysExcelWorkbook                    workbook;
    SysExcelWorksheets                  worksheets;
    SysExcelWorksheet                   worksheet;
    SysExcelCells                       cells;
    COMVariantType                      type;
    str                                 mainAccount;
    LedgerJournalTrans                  ledgerJournalTrans;
    LedgerJournalACType                 accountType;
    LedgerDimensionAccount              ledgerDim;
    DimensionStorage                    dimensionStorage;
    DimensionServiceProvider            dimensionServiceProvider;
    LedgerAccountContract               LedgerAccountContract;
    DimensionAttributeValueContract     ValueContract;
    List                                ListValueContract;

    #DEFINE.CurrentVersion(1)
    #LOCALMACRO.CurrentList
        filePath,
        salesPurLedger
    #ENDMACRO
}

private void clearList(List _list)
{
    ListIterator iterator;
    str          value;

    iterator = new ListIterator(_list);

    while (iterator.more())
    {
        value = iterator.value();

        if (true) // check value
        {
            iterator.delete();
        }
        else
        {
            iterator.next();
        }
    }
}

str COMVariant2Str(COMVariant _cv, int _decimals = 0, int _characters = 0, int _separator1 = 0, int _separator2 = 0)
{
    switch (_cv.variantType())
    {
        case (COMVariantType::VT_BSTR):
        return _cv.bStr();

        case (COMVariantType::VT_R4):
        return num2str(_cv.float(),_characters,_decimals,_separator1,_separator2);

        case (COMVariantType::VT_R8):
        return num2str(_cv.double(),_characters,_decimals,_separator1,_separator2);

        case (COMVariantType::VT_DECIMAL):
        return num2str(_cv.decimal(),_characters,_decimals,_separator1,_separator2);

        case (COMVariantType::VT_DATE):
        return date2str(_cv.date(),123,2,1,2,1,4);

        case (COMVariantType::VT_EMPTY):
        return "";

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

private void createCustInvoiceJourLines()
{
    this.initilizeExcel();

    try
    {
        ttsbegin;
        do
        {
            row++;

            ledgerJournalTrans.clear();
            ledgerJournalTrans.initValue();
            ledgerJournalTrans.JournalNum = this.parmJournalNum();
            ledgerJournalTrans.Voucher = this.generateVoucherNum();
            if(!ledgerJournalTrans.Voucher)
                throw error('Voucher number is missing.');
            ledgerJournalTrans.TransDate = cells.item(row, 1).value().date();
            ledgerJournalTrans.Invoice = this.COMVariant2Str(cells.item(row, 2).value());
            ledgerJournalTrans.Txt = this.COMVariant2Str(cells.item(row, 3).value());
            ledgerJournalTrans.AmountCurDebit = cells.item(row, 4).value().double();
            ledgerJournalTrans.AmountCurCredit = cells.item(row, 5).value().double();
            ledgerJournalTrans.AccountType = str2enum(accountType, this.COMVariant2Str(cells.item(row,6).value()));
            if(ledgerJournalTrans.AccountType != LedgerJournalACType::Cust)
            {
                throw error('The sheet does not only contain the customer invoice journal lines.');
            }
            ledgerJournalTrans.OffsetAccountType = str2enum(accountType, this.COMVariant2Str(cells.item(row,18).value()));

            ledgerDim = 0;
            ledgerDim = DimensionStorage::getDynamicAccount(this.COMVariant2Str(cells.item(row,7).value()), LedgerJournalACType::Cust);
            ledgerJournalTrans.LedgerDimension = ledgerDim;

            ledgerDim = 0;
            if(ledgerJournalTrans.OffsetAccountType == LedgerJournalACType::Cust)
            {
                ledgerDim = DimensionStorage::getDynamicAccount(this.COMVariant2Str(cells.item(row,19).value()), LedgerJournalACType::Cust);
                ledgerJournalTrans.OffsetLedgerDimension = ledgerDim;
            }

            ledgerDim = 0;
            if(ledgerJournalTrans.OffsetAccountType == LedgerJournalACType::Vend)
            {
                ledgerDim = DimensionStorage::getDynamicAccount(this.COMVariant2Str(cells.item(row,19).value()), LedgerJournalACType::Vend);
                ledgerJournalTrans.OffsetLedgerDimension = ledgerDim;
            }

            if(ledgerJournalTrans.OffsetAccountType == LedgerJournalACType::Ledger)
            {
                col = 18;
                if(!ListValueContract.empty())
                    this.clearList(ListValueContract);
                do
                {
                    col ++;

                    if(cells.item(row, col).value().bStr() == '')
                    {
                        continue;
                    }

                    ValueContract = new DimensionAttributeValueContract();
                    ValueContract.parmName(this.COMVariant2Str(cells.item(1 , col).value()));
                    ValueContract.parmValue(this.COMVariant2Str(cells.item(row , col).value()));
                    ListValueContract.addEnd(ValueContract);

                    mainAccount = this.COMVariant2Str(cells.item(1, col + 1).value());
                }
                while (mainAccount != 'MainAccount');

                ledgerAccountContract.parmMainAccount(this.COMVariant2Str(cells.item(row, col + 1).value()));
                LedgerAccountContract.parmValues(ListValueContract);

                dimensionStorage =  DimensionServiceProvider::buildDimensionStorageForLedgerAccount(LedgerAccountContract);
                ledgerJournalTrans.OffsetLedgerDimension = DimensionAttributeValueCombination::find(dimensionStorage.save()).RecId;
            }

            if(ledgerJournalTrans.validateWrite())
            {
                ledgerJournalTrans.insert();
            }

            type = cells.item(row+1, 1).value().variantType();
        }
        while (type != COMVariantType::VT_EMPTY);

        application.quit();
        ttscommit;
    }
    catch
    {
        Error('Import Failed.');
    }
}

private void createLedgerInvoiceJourLines()
{
    int     dynamicCol;
    this.initilizeExcel();

    try
    {
        ttsbegin;
        do
        {
            row++;

            ledgerJournalTrans.clear();
            ledgerJournalTrans.initValue();
            ledgerJournalTrans.JournalNum = this.parmJournalNum();
            ledgerJournalTrans.Voucher = this.generateVoucherNum();
            if(!ledgerJournalTrans.Voucher)
                throw error('Voucher number is missing.');
            ledgerJournalTrans.TransDate = cells.item(row, 1).value().date();
            ledgerJournalTrans.Invoice = this.COMVariant2Str(cells.item(row, 2).value());
            ledgerJournalTrans.Txt = this.COMVariant2Str(cells.item(row, 3).value());
            ledgerJournalTrans.AmountCurDebit = cells.item(row, 4).value().double();
            ledgerJournalTrans.AmountCurCredit = cells.item(row, 5).value().double();
            ledgerJournalTrans.AccountType = str2enum(accountType, this.COMVariant2Str(cells.item(row, 6).value()));
            if(ledgerJournalTrans.AccountType != LedgerJournalACType::Ledger)
            {
                throw error('The sheet does not contain only the ledger invoice journal lines.');
            }
            ledgerJournalTrans.OffsetAccountType = str2enum(accountType, this.COMVariant2Str(cells.item(row, 18).value()));

            ledgerDim = 0;

            col = 6;
            mainAccount = '';
            if(!ListValueContract.empty())
                this.clearList(ListValueContract);
            do
            {
                col ++;

                if(cells.item(row, col).value().bStr() == '')
                {
                    continue;
                }

                ValueContract = new DimensionAttributeValueContract();
                ValueContract.parmName(this.COMVariant2Str(cells.item(1 , col).value()));
                ValueContract.parmValue(this.COMVariant2Str(cells.item(row , col).value()));
                ListValueContract.addEnd(ValueContract);

                mainAccount = this.COMVariant2Str(cells.item(1 , col + 1).value());
            }
            while (mainAccount != 'MainAccount');

            dynamicCol = col + 9;
            ledgerAccountContract.parmMainAccount(this.COMVariant2Str(cells.item(row, col + 1).value()));
            LedgerAccountContract.parmValues(ListValueContract);

            dimensionStorage =  DimensionServiceProvider::buildDimensionStorageForLedgerAccount(LedgerAccountContract);
            ledgerJournalTrans.LedgerDimension = DimensionAttributeValueCombination::find(dimensionStorage.save()).RecId;

            ledgerDim = 0;
            if(ledgerJournalTrans.OffsetAccountType == LedgerJournalACType::Cust)
            {
                ledgerDim = DimensionStorage::getDynamicAccount(this.COMVariant2Str(cells.item(row,18).value()), LedgerJournalACType::Cust);
                ledgerJournalTrans.OffsetLedgerDimension = ledgerDim;
            }

            ledgerDim = 0;
            if(ledgerJournalTrans.OffsetAccountType == LedgerJournalACType::Vend)
            {
                ledgerDim = DimensionStorage::getDynamicAccount(this.COMVariant2Str(cells.item(row,18).value()), LedgerJournalACType::Vend);
                ledgerJournalTrans.OffsetLedgerDimension = ledgerDim;
            }

            if(ledgerJournalTrans.OffsetAccountType == LedgerJournalACType::Ledger)
            {
                col = dynamicCol;
                mainAccount = '';
                if(!ListValueContract.empty())
                    this.clearList(ListValueContract);
                do
                {
                    col ++;
                    if(cells.item(row, col).value().bStr() == '')
                    {
                        continue;
                    }
                    ValueContract = new DimensionAttributeValueContract();
                    ValueContract.parmName(this.COMVariant2Str(cells.item(1 , col).value()));
                    ValueContract.parmValue(this.COMVariant2Str(cells.item(row , col).value()));
                    ListValueContract.addEnd(ValueContract);

                    mainAccount = this.COMVariant2Str(cells.item(1, col + 1).value());
                }
                while (mainAccount != 'MainAccount');

                ledgerAccountContract.parmMainAccount(this.COMVariant2Str(cells.item(row, col + 1).value()));
                LedgerAccountContract.parmValues(ListValueContract);

                dimensionStorage =  DimensionServiceProvider::buildDimensionStorageForLedgerAccount(LedgerAccountContract);
                ledgerJournalTrans.OffsetLedgerDimension = DimensionAttributeValueCombination::find(dimensionStorage.save()).RecId;
            }

            if(ledgerJournalTrans.validateWrite())
            {
                ledgerJournalTrans.insert();
            }

            type = cells.item(row + 1, 1).value().variantType();
        }
        while (type != COMVariantType::VT_EMPTY);

        application.quit();
        ttscommit;
    }
    catch
    {
        Error('Import Failed.');
    }
}

private void createVendInvoiceJourLines()
{
    this.initilizeExcel();

    try
    {
        ttsbegin;
        do
        {
            row++;

            ledgerJournalTrans.clear();
            ledgerJournalTrans.initValue();
            ledgerJournalTrans.JournalNum = this.parmJournalNum();
            ledgerJournalTrans.Voucher = this.generateVoucherNum();
            if(!ledgerJournalTrans.Voucher)
                throw error('Voucher number is missing.');
            ledgerJournalTrans.TransDate = cells.item(row, 1).value().date();
            ledgerJournalTrans.Invoice = this.COMVariant2Str(cells.item(row, 2).value());
            ledgerJournalTrans.Txt = this.COMVariant2Str(cells.item(row, 3).value());
            ledgerJournalTrans.AmountCurDebit = cells.item(row, 4).value().double();
            ledgerJournalTrans.AmountCurCredit = cells.item(row, 5).value().double();
            ledgerJournalTrans.AccountType = str2enum(accountType, this.COMVariant2Str(cells.item(row,6).value()));
            if(ledgerJournalTrans.AccountType != LedgerJournalACType::Vend)
            {
                throw error('The sheet does not contain only the vendor invoice journal lines.');
            }
            ledgerJournalTrans.OffsetAccountType = str2enum(accountType, this.COMVariant2Str(cells.item(row,18).value()));

            ledgerDim = 0;

            ledgerDim = DimensionStorage::getDynamicAccount(this.COMVariant2Str(cells.item(row,7).value()), LedgerJournalACType::Vend);
            ledgerJournalTrans.LedgerDimension = ledgerDim;

            ledgerDim = 0;
            if(ledgerJournalTrans.OffsetAccountType == LedgerJournalACType::Cust)
            {
                ledgerDim = DimensionStorage::getDynamicAccount(this.COMVariant2Str(cells.item(row,19).value()), LedgerJournalACType::Cust);
                ledgerJournalTrans.OffsetLedgerDimension = ledgerDim;
            }

            if(ledgerJournalTrans.OffsetAccountType == LedgerJournalACType::Vend)
            {
                ledgerDim = DimensionStorage::getDynamicAccount(this.COMVariant2Str(cells.item(row,19).value()), LedgerJournalACType::Vend);
                ledgerJournalTrans.OffsetLedgerDimension = ledgerDim;
            }

            if(ledgerJournalTrans.OffsetAccountType == LedgerJournalACType::Ledger)
            {
                col = 18;
                if(!ListValueContract.empty())
                    this.clearList(ListValueContract);
                do
                {
                    col ++;

                    if(cells.item(row, col).value().bStr() == '')
                    {
                        continue;
                    }

                    ValueContract = new DimensionAttributeValueContract();
                    ValueContract.parmName(this.COMVariant2Str(cells.item(1 , col).value()));
                    ValueContract.parmValue(this.COMVariant2Str(cells.item(row , col).value()));
                    ListValueContract.addEnd(ValueContract);

                    mainAccount = this.COMVariant2Str(cells.item(1, col + 1).value());
                }
                while (mainAccount != 'MainAccount');

                ledgerAccountContract.parmMainAccount(this.COMVariant2Str(cells.item(row, col + 1).value()));
                LedgerAccountContract.parmValues(ListValueContract);

                dimensionStorage =  DimensionServiceProvider::buildDimensionStorageForLedgerAccount(LedgerAccountContract);
                ledgerJournalTrans.OffsetLedgerDimension = DimensionAttributeValueCombination::find(dimensionStorage.save()).RecId;
            }

            if(ledgerJournalTrans.validateWrite())
            {
                ledgerJournalTrans.insert();
            }

            type = cells.item(row+1, 1).value().variantType();
        }
        while (type != COMVariantType::VT_EMPTY);

        application.quit();
        ttscommit;
    }
    catch
    {
        Error('Upload Failed.');
    }
}

public Object dialog()
{
    FormBuildControl    setupGroupControl;
    FormRun             formRun;
    #Excel
    dialog = super();

    dialog.alwaysOnTop(true);
    dialog.windowType(FormWindowType::Standard);
    dialogGrp = dialog.addGroup('Import journal lines');
    dialogFieldFilePath = dialog.addFieldValue(extendedTypeStr(FilenameOpen), filePath,  "File path",    "");
    dialog.filenameLookupFilter(["@SYS28576",#XLSX]);
    dialog.filenameLookupTitle("Upload from EXCEL");

    dialogFieldsalesPurchLedger = dialog.addFieldValue(extendedTypeStr(SLSalesPurLedger), salesPurLedger,  "Account Type",    "");

    return dialog;

}

private Num generateVoucherNum()
{
    LedgerJournalName   ledgerJournalName;
    NumberSeq           numberseq;
    select firstOnly ledgerJournalName
        where ledgerJournalName.JournalType == LedgerJournalType::Daily;

    return new JournalVoucherNum(JournalTableData::newTable(LedgerJournalTable::find(journalNum))).getNew(true);

}

public boolean getFromDialog()
{
    boolean ret;

    ret = super();

    salesPurLedger = dialogFieldsalesPurchLedger.value();
    filePath       = dialogFieldFilePath.value();

    return ret;
}

public boolean init()
{
    boolean ret;

    ret = super();

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

    return ret;
}
private void initilizeExcel()
{
    try
    {
        workbooks.open(filePath);
    }
    catch (Exception::Error)
    {
        throw error('File cannot be opened.');
    }

    workbook            =   workbooks.item(1);
    worksheets          =   workbook.worksheets();
    worksheet           =   worksheets.itemFromNum(1);
    cells               =   worksheet.cells();
}

public void new()
{
    super();

    dimensionServiceProvider = new DimensionServiceProvider();
    LedgerAccountContract    = new LedgerAccountContract();
    ListValueContract        = new List(Types::Class);
}

container pack()
{
    return [#CurrentVersion,#CurrentList];
}

public LedgerJournalId parmJournalNum(LedgerJournalId _journalNum = journalNum)
{
    journalNum = _journalNum;

    return journalNum;
}

public void run()
{
    super();

    switch (salesPurLedger)
    {
        case SLSalesPurchLedger::Customer :
            this.createCustInvoiceJourLines();
            break;

        case SLSalesPurchLedger::Vendor :
            this.createVendInvoiceJourLines();
            break;

        case SLSalesPurchLedger::Ledger :
            this.createLedgerInvoiceJourLines();
            break;
    }

}

boolean unpack(container _packedClass)
{
    Integer     version = conpeek(_packedClass,1);

    switch (version)
    {
        case #CurrentVersion:
            [version,#CurrentList] = _packedClass;
            break;
        default :
            return false;
    }

    return true;
}

public static void main(Args args)
{
    SLSalesPurchLedgerJourLinesImport   salesPurchLedgerJourLinesImport;
    LedgerJournalTable                  ledgerJournalTable;

    if (args && args.record())
    {
        ledgerJournalTable = args.record();
        salesPurchLedgerJourLinesImport = new SLSalesPurchLedgerJourLinesImport();
        salesPurchLedgerJourLinesImport.init();
        salesPurchLedgerJourLinesImport.parmJournalNum(ledgerJournalTable.JournalNum);
        if(salesPurchLedgerJourLinesImport.prompt())
        {
            salesPurchLedgerJourLinesImport.run();
        }
    }
    else
    {
        throw error('arguments are missing');
    }
}

t4The above snap shot method must be written to the import button click event.

Now I’m going to show you the predefined excel templates these templates can accommodate up to 10 ledger dimensions for the account type ledger. Make sure that when you create the excel template the data is defined on the correct alphabetical columns and the dimension column names must be the same as the dimensions you created in the system and they should be in reverse order than you enter it in segmented entry field in the system as below.

 Customer Template

t5

Ledger Template

t6

Vendor Template

t7

 


Human Capital Management in Dynamics AX 2012

Human resource management in Dynamics AX 2012 is still a little explored field and you will not find many blog posts that will cater specifically to the HCM and Payroll modules in Dynamics AX 2012.

If you are looking for an option on how to help your client to manage talent within their organization, this blog post is for you!

Today, many clients are looking for a single solution for their HR problems including talent management. Talent management however, becomes extremely confusing because many HCM applications and resource planning solutions do not provide Talent Management as an option.

If your client is looking for an end to end HCM solution including talent management, Microsoft Dynamics AX 2012 offers a whole pack of features that you can pitch to your client:

Recruitment Module: The HR manager can create an internal project for recruitment, for any sort of job opening. Both internal and external job postings can be managed through the recruitment module, with various types of advertisements to obtain application data. The entire recruitment process can be followed with evaluation of applications, interviews and confirmation of applications.

Competency Management: This area allows definition of specific competency elements, skills, education levels and areas of interest that should typically be part of a job. Based on the hiring process, a certain employee when hired can be compared against these competencies and the hiring or rejection decision can be based on these competencies.

Compensation Plans: Dynamics AX 2012 is extremely flexible in terms of compensation plans. From benefits to earning codes or allowances, and from fixed to variable pay plans, a hiring can be done and specific criteria and pay grades can be assigned in compensation plans.

Performance Evaluations: Goals, KPIs and appraisals all can be managed through the performance evaluation and management module. This module is extremely powerful as it allows the alignment of all employees towards certain goals and KPIs that the organization wants to focus on. Moreover, personal development of employees can be managed as well through personal goals and performance appraisals.

Course Management: Course management is a little explored area even though it can be extremely useful. In course management, the organization can keep track of and manage courses and trainings that employees can take. Arrangements, attendance and conclusion, all elements related to courses can be managed through this module.

Next up, we shall explore questionnaire development and management in Microsoft Dynamics AX 2012, which will help you to design an employee performance appraisal form and follow the process for collection of information on the form.

Which elements in HCM are you interested in exploring that we can present you with blog posts on?

 


Find All AOT Object Layers

Sometimes you might need to know the total objects on different layers in AOT.

The below job that I wrote will show all the tables exist on different layers you can copy and paste it to excel from the data in the infolog after the job has been executed.

In order to know about the other objects than the tables just change “#TablesPath” macro which are available in #AOT.

static void displayAOTObjLayers(Args _args)

{

#AOT

#Properties

#define.LayerCount(16)

str                     layers;

str                     aotObjName;

int                     i, j, objectLayers;

str                     total;

TreeNode        treeNode, treeNodeInLayer;

Array               layerArray;

DictEnum        dictEnum;

UtilElements   utilElements;

TreeNode        baseTreeNode;

;

layerArray = new Array(Types::String);

dictEnum = new DictEnum(enumNum(UtilEntryLevel));

//#TablesPath is the path of tables in AOT. Look other object paths in #AOT macro.

baseTreeNode = TreeNode::findNode(#TablesPath);

treeNode = BaseTreeNode.AOTfirstChild();

while(treeNode)

{

for (i = 0; i < dictEnum.values(); i++)

{

layerArray.value(i+1, dictEnum.value2Name(i));

}

objectLayers = treeNode.applObjectLayerMask();

for (i = 0; i < #LayerCount; i++)

{

j = objectLayers;

j = j & power(2, i);

 

if (j)

{

aotObjName = TreeNode.AOTname();

layers += layerArray.value(i + 1)+’ ‘;

}

}

info(strfmt(‘%1 || %2′, aotObjName, layers));

treeNode = treeNode.AOTnextSibling();

aotObjName = ”;

layers = ”;

}

}


Using button grids to customize POS behavior

Screen Layouts in Microsoft Dynamics AX for Retail POS help to define the visual appearance of the till. One element of screen layouts is the ability to setup button grids. Button grids help to define both the functionality and visual appearance of buttons used to perform different actions at POS, as well as control the flow of actions. For example, you can setup multiple button grids to appear at POS, and/or define sub-menus that appear on clicking a POS button. Multiple layers of sub-menus can also be setup.

Without divulging in the details of how to setup button grids and button specific actions, which can be further read here; let’s see how button grids can be used to complete a business process.

For example, in case of a restaurant business, “Order Taking” is one of most basic and frequent business processes. As this involves a number of steps, a guided order taking menu helps to facilitate POS user or waiter to ensure completeness of the order and avoid missing any step, especially in case of new users which is a common occurrence in service industry due to frequent employee turnover.

Below screenshots demonstrate how this process typically works

User logs into POS, where user is presented a number of menu options. The options shown are part of a button grid, where each button covers a specific function. Amongst these, a “Place Order” button has been custom created.

1 hz

Clicking the “Place Order” button takes user to a sub-menu of further buttons.

2 hz

In order to place the order, user will select items from “Main Course”, “Toppings” and “Drink”.

User selects “Main Course” and is presented a list of items or meals.

3 hz

Selecting “Pizza” (which has been setup as a Master Product having multiple SKUs) gives user a number of options between ‘Flavor’ and ‘Size’.

4 hz5 hz

User is also prompted to confirm the type of crust, setup as a reason code against the particular meal (pizza).

6 hz

This adds the meal to the electronic journal and displays the current transaction totals.

7 hz

Similarly, user can select “Toppings” from a list.

8 hz

9 hz

In this way, user can complete the order and add more items as required.

The printed receipt with entire information captured can be passed on to Kitchen Staff for meal preparation, ensuring there is no miscommunication in the order placed.

Now let’s see how the menu options and step-by-step order-taking guidance was setup, using Button Grids.

First let’s setup the sub-menu options that will be displayed when user selects “Place Order” button. To do this, create a new button grid titled “Order Taking” as shown below.

10 hz

Once setup, click on ‘Designer’ button in the toolbar. This opens up the design form for the Button Grid. Click on ‘New design’ and define the number of rows and columns (in our case we need a 2 x 2 grid).

11 hz

Click ‘OK’ and this generates the grid of buttons.

12 hz

Right click on any button to define the properties of that button

13 hz

This opens the ‘Configure Button’ form. The ‘Action’ combo contains a list of actions that will invoke when button is clicked on POS.

14 hz

Select the appropriate action required and also fill in remaining fields for defining the visual attributes of the button.

In this particular illustration, for the “Main Course” button, we want user to be presented with a list of meals along with feature to search the list. For this purpose, set ‘Action’ field to “Search”, ‘Search type’ field to “Category”, and select the specific retail hierarchy from the list. Once done, drill down to the category level for which a list of products items/meals (products) should be displayed. In this case, the category “Main Course” has been selected.

15 hz

Click “OK” to complete the changes. Similarly, define button properties for other buttons.

16 hz

Now we need to link the button grid so that it appears as a sub-menu when “Place Order” button is clicked.

Go back to Button Grid form and select the existing button grid “Manager Tasks” and click on ‘Designer’.

17 hz

 

In Designer form, right click on “Place Order” button to modify the button properties.

18 hz

In the ‘Action’ field, select “Sub menu”. This will populate a list of options in ‘Menu’ combo. Since we want our custom button grid to appear as a sub-menu on clicking “Place Order” button at POS, select “Order Taking” option in the combo. (This is also why we setup the “Order Taking” button grid before modifying the “Manager Tasks” grid)

19 hz

Similarly repeat these steps by setting up button grids as sub-menus for “Toppings” and “Drink”. Run “A-1090” (Registers) job to view changes to POS. Make sure to restart POS for the changes to take effect.

In this way, we can see how button grids can be used to achieve a business process for ordering taking for a restaurant retailer. Buttons grids, along with overall Profile maintenance, can be used to customize the layout and functionality of POS specific to the retailer’s need, which shows just why Microsoft Dynamics AX 2012 is a perfect choice for implementing your retail solution.


Understanding Role Based Security in Microsoft Dynamics AX 2012

Security has a very important role to play in any ERP implementation. The security aspect of an ERP helps the administrator to control and restrict access of different users to data in the ERP. The administrator can control the rights of access to different modules and forms of different users tailoring the access, based on user groups or each individual user. Microsoft Dynamics AX controls security through a role based security system.

Security Architecture of Dynamics AX

1

Role-based security:-

  • In earlier versions of Microsoft Dynamics AX, the IT administrator wasted a lot of time and effort in managing the application security by creating user groups.
  • Microsoft Dynamics AX 2012 now offers managing security within the application by using some predefined roles and providing role based access based on these roles.
  • It also provides predefined business-related duties that are assigned with roles and matched with the users assigned to a specific role

Roles:-

  • All of the users must be assigned at least one security role to have access to Microsoft Dynamics AX. By managing the user’s access through security roles, it saves up a lot of time as the administrators only have to manage the security roles rather than each individual user.

Duties:-

  • The administrator in Dynamics AX assigns duties to the role and the administrator can assign many different duties to any role.

Privilege:-

  • A privilege in Microsoft Dynamics AX specifies the access level that is required to complete an assignment, solve a problem or perform a job.

Permissions:-

  • Permissions group all the securable objects and the different access levels that a user requires to run a function. This includes any forms, server side methods, fields or tables that can be accessed through security points.

Benefit of Having Role-based security:-

  • The new concept of role based security in AX 2012 has made it easier to manage security. Roles can be applied across all the companies and so the administrator does not have to maintain separate user groups for each and every company in the organization.

Assigning users to different roles (adding roles to users):-

  • Go to System administration | Common | Users | Users.
  • Select the required user from the user’s list.

2

  • From the upper left-hand corner of the user list page, click on Edit.
  • In the center of the user form, click on Assign roles.

3

  • Select a role from the list of predefined roles.
  • Select an option in the Role name menu to assign to the selected user.

4

  • Click on OK.
  • Click on Close to close the user form.