Friday 31 January 2014

what is difference between unique index and primary index

Primary Key uniquely identifies the record and the 
UNIQUE index on the primary key ensures that no duplicates values will be stored in the table
1)Primary key creates an implicit clustered index on a table, 
  unique key does not create any implicit index.

2)Columns defined as primary key does not allow null values,
where as columns defined as unique can allow null values.

3)A table can have just one primary key (can be a composite key) 
where as there can be multiple unique columns (keys).

4)Primary key is a unique key with the above mentioned features
http://msdn.microsoft.com/en-us/library/aa656402.aspx

Tuesday 28 January 2014

Classes, Tables, Forms and Methods used to post the sales orders in ax 2009

Classes, Tables, Forms and Methods used to post the sales orders

SalesTableType and SaleslineType classes will get called while creating the orders.
SalesFormLetter* classes will be used to post the sales order at various document status(packing, invoice etc).
SalesParm* tables are used to prepare the data for posting
CustConfirmJour, CustConfirmTrans - when a sales order gets confirmed
CustPackingSlipJour, CustPackingSlipTrans - when a packing slip is posted.
CustInvoiceTable,CustInvoiceTrans - when an invoice is posted.
These are some of the maily used tables.

public class FormRun extends ObjectRun
{
    SalesFormLetter_Confirm salesFormLetter_Confirm;
    SalesFormLetter_Invoice salesFormLetter_Invoice;
    SalesFormLetter_PackingSlip salesFormLetter_PackingSlip;
    SalesFormLetter_PickingList salesFormLetter_PickingList;
}

// when you click

void clicked()
{
    SalesTable salesTable;

    if(SalesId.valueStr()!="")
    {

        salesTable = SalesTable::find(SalesId.valueStr(), true);// retrives the sales id based on the slected sales id on text box;

    if(ActionType.valueStr() == enum2str(SalesOrderType::Confirm)) // if Sales order type is confirm
    {
        if(salesTable.DocumentStatus != DocumentStatus::None)// if sales order is confirmed, other wise else part
        {
            info(strFmt("@MAD39",salesTable.SalesId));

        }
        else
        {
            salesFormLetter_Confirm =SalesFormLetter::construct(DocumentStatus::Confirmation);
            salesFormLetter_Confirm.update(salesTable, SystemDateGet(), SalesUpdate::All,AccountOrder::None,false,false );
            info(strFmt("@MAD41",salesTable.SalesId));
        }
    }
    else
        if(ActionType.valueStr() == enum2str(SalesOrderType::Invoice)) // if saler order type is invoice
        {
            if(salesTable.SalesStatus == SalesStatus::Invoiced) // if sales order is invoiced ,other wise elese part.
            {
            info(strFmt("@MAD37",salesTable.SalesId));
            }
            else
            {
            salesFormLetter_Invoice =SalesFormLetter::construct(DocumentStatus::Invoice);
            salesFormLetter_Invoice.update(salesTable, SystemDateGet(), SalesUpdate::All,AccountOrder::None,false,false );

            info(strFmt("@MAD43",salesTable.SalesId));
            }
    }
    else
        if(ActionType.valueStr() == enum2str(SalesOrderType::PackingSlip)) // if sales order type is packing slip
        {
            if(salesTable.SalesStatus == SalesStatus::Delivered) // if packing slip is done for the saler order, other wise else part
            {
            info(strFmt("@MAD38",salesTable.SalesId));
            }
            else
            {
            salesFormLetter_PackingSlip =SalesFormLetter::construct(DocumentStatus::PackingSlip);
            salesFormLetter_PackingSlip.update(salesTable, SystemDateGet(), SalesUpdate::All,AccountOrder::None,false,false );
            info(strFmt("@MAD44",salesTable.SalesId));
            }
        }
    else
        if(actiontype.valuestr() == enum2str(salesordertype::pickinglist)) // if sales order type is picking list
        {
            if(salestable.salesstatus == salesstatus::delivered) // if picking list is done for the sales order , other wise elese part.
            {
            info(strfmt("@mad40",salestable.salesid));
            }
            else
            {
                salesformletter_pickinglist =salesformletter::construct(documentstatus::pickinglist);
                salesformletter_pickinglist.update(salestable, systemdateget(), salesupdate::PickingList,accountorder::none,false,false );
                info(strfmt("@mad45",salestable.salesid));
            }
        }
    }
    else
    {
        Box::stop("@MAD47","@SYS8500");
    }

    super();
}

Monday 27 January 2014

Microsoft Dynamics AX2012 Import-AXModelStore


The suggested method for deploying Dynamics AX2012 code into production environment (Eg. Go live) is to import the model store.
Read more about the difference between model and model store at this link: http://technet.microsoft.com/en-us/library/hh335184.aspx
Compare AX4, AX2009 and AX2012 deployment:
- AX4, AX2009: Copy layer and label files, replace the destination files (in application folder)
- AX2012: Export model store and import model store
There’re two ways to import model store:
1. Using PowerShell
2. Using AXUtil.exe

The rest of the text below will use PowerShell as the AXUtil will be obsolete in future version of Dynamics AX.
Start Dynamics AX PowerShell from here: All Programs > Administrative Tools > Microsoft Dynamics AX Management Shell
The steps:
1.             Export model store from your source (Eg. UAT environment)
(Similar to AX2009 copy layer & label files)
- Export model store
  
2.             Import model store to destination (Eg. Production environment)
(Similar to AX2009 replace layer & label files)
- Create a temporary schema
- Import model store to temporary schema
- Apply the model store

Export model store
Export model store is quite straight forward.
Syntax: Export-AXModelStore -File <filename> -Details
Eg. Export-AXModelStore -File “UAT.axmodelstore” –Details

What happen behind the scene?
The Export-AXModelStore connect to SQL server and access the AX database, then select and save the data into file (with the file name you specified in the command).

It gets the database name from AX server (AOS) configuration.
 
 

Export-AXModelStore with file name "STD_ModelStore"


Import model store

Create a temporary schema
Create a temporary schema in AX database to be used on next step during import model store.

Steps:
1. Open SQL Server Management Studio
2. Connect to the SQL Server instance where AX database resides
3. Expand to Databases > [AxDatabaseName] > Security > Schemas
4. Right click > New schema
5. Type in the temporary schema name (Eg. TempSchema) and select the schema owner and click "OK"
6. Initialize the temporary schema (Eg. Initialize-AXModelStore -AOSAccount "Domain\AccountName" -SchemaName <NewSchema> -Server <ServerName> -Database <DatabaseName>)

Import model store to temporary schema
Syntax: Import-AXModelStore -File <filename> -SchemaName 
Eg. Import-AXModelStore -File UAT.axmodelstore -SchemaName TempSchema

What happen behind the scene?
The Import-AXModelStore read the *.axmodelstore file and do bulk insert.
It uses .NET SqlBulkCopy class and through TDS (Tabular Data Stream) pushing the data into AX model store tables.

You’ll see the command in SQL server like this: insert bulk [TempSchema].[ModelElementData] ([LayerId] Int, [ParentHandle] Int, [ElementHandle] Int, [LegacyId] Int, [Properties] VarBinary(max), [FileHeader] VarBinary(max), [BASEVERSION] Int, [VERSION] Int, [SAVECOUNT] Int, [CONSISTENCYDATA] Int, [MODIFIEDDATETIME] DateTime, [MODIFIEDBY] NVarChar(5) COLLATE SQL_Latin1_General_CP1_CI_AS, [CREATEDDATETIME] DateTime, [CREATEDBY] NVarChar(5) COLLATE SQL_Latin1_General_CP1_CI_AS, [ElementVersion] Int, [ModelId] Int) with (TABLOCK)

This "INSERT BULK" is different than the "BULK INSERT", it can’t be used in SQL server directly, it has to go through API.

Import-AXModelStore with file name "UAT.axmodelstore" and schema "TempSchema"
Apply the model store
Syntax: Import-AXModelStore -Apply:TemporarySchema
Eg: Import-AXModelStore -Apply:TempSchema

What happen behind the scene?
It execute several stored procedures to transfer the temporary schema to default.
Eg.
- XU_IsSchemaEmpty
- XU_CreateSchema
- XU_TransferObjectsToSchema
and etc

Apply model store (Import-AXModelStore -Apply) with the temporary schema imported earlier

You doesn't need to manually remove the temporary schema after apply it as they won't remain in the AX database after the APPLY process.

But if you decided not to apply the model store, you can remove it with the following syntax.
Syntax: Initialize-AXModelStore –Drop 
Eg: Initialize-AXModelStore –Drop TempSchema

Alternative
If you prefer to directly import the model store into default schema and replace it, do remember when it hit ID conflict, the import will stop.

For more details about working on model store, visit this link:
http://technet.microsoft.com/en-us/library/hh433530.aspx
http://technet.microsoft.com/en-us/library/hh433540.aspx

Misc. screenshot during my test

Create AX model store (Initialize-AXModelStore)

Import-AXModelstore error due to database referred in AOS settings does not exist


Initialize-AXModelStore error on pre-release AX2012 database due to stored procedure - "XU_DropSchema" does not exist


Import-AXModelStore error when schema does not exist


Import-AXModelStore error when Id conflict detected
  

Apply AX model store (Import-AXModelStore -Apply) error when applying the RTM model store to pre-release AX database due to XU_CreateSchema does not exist in pre-release database


Apply AX model store (Import-AXModelStore -Apply) error when applying the RTM model store to pre-release AX database due to pre-release database stored proc is outdated, has different number of parameters
 

Some of the tables created using Initialize-AXModelStore (to get the full list, you can query the list of objects attached to the schema you created through the Initialize-AXModelStore)

Saturday 25 January 2014

Form templates in ax 2012

ListPage: Use the template to create a list page you can use to find, analyze, and performs actions on master data.
Main entry in to a module

DetailsFormMaster : Use the template to create a Details form to view, edit, and act on master data.

DetailsFormTransaction: Use the template to create details form with lines to view, edit, and act on master data that has line items.
view and edit transactional and worksheet data .

SimpleListDetails: Use the template to create a simple list and details form to view, edit, and act on dependent and reference data.
reference and setupdata

SimpleList :Use the template to create a simple list form to view, edit, and act on dependent or reference data.
Basic inquiry

TableOfContents:Use the template to create a table of contents form to view and edit configuration or setup data
Module configuration parameter

Dialog:Use the template to create a dialog window that provides a response to a question.
Quick user interaction.

DropDialog:Use the template to create a drop dialog form to perform an action with data.

Few of the Nodes in ax 2012

Delegate Note of the class in AOT is true:-
        You must declare the Same type of parameters in a delegate as in the method that are subscribed to the delegate.

Full text index:
         Full text index can include multiple string fields, a full text index stores information about the location of each significant word in a string field of a table , you can reference a full text index in a query by creating a query range of type full text index

Model:
       logical grouping of elements with in a layer .

Model store:
      Metadata and application Code.

Wednesday 22 January 2014

Connot execute a data definition language command on in ax 2009 / Force Synchronisation of AOT tables through X++ in AX:

Synchronize failed on 1 table(S) in ax 2009
static void forceDbSynchronize(Args _args)
{
    Dictionary              dict;
    int                     idx, lastIdx, totalTables;
    TableId                 tableId;
    Application             application;
    SysOperationProgress    progress;
    StackBase               errorStack;
    ErrorTxt                errorTxt;
    ;

    application = new Application();
    dict = new Dictionary();
    totalTables = dict.tableCnt();
    progress = new SysOperationProgress();
    progress.setTotal(totalTables);
    progress.setCaption("@SYS90206");
    errorStack = new StackBase(Types::String);

    lastIdx = 0;
    try
    {
        for (idx = lastIdx+1; idx <= totalTables; idx++)
        {
            tableId = dict.tableCnt2Id(idx);
            progress.setText(dict.tableName(tableId));

            lastIdx = idx;
            application.dbSynchronize(tableId, false, true, false);
            progress.incCount();
        }
    }
    catch (Exception::Error)
    {
        errorTxt = strFmt("Error in table '%1' (%2)", tableId, dict.tableName(tableId));
        errorStack.push(errorTxt);
        retry;
    }

    setPrefix("@SYS86407");
    errorTxt = errorStack.pop();
    while (errorTxt)
    {
        error(errorTxt);
        errorTxt = errorStack.pop();
    }
}

--------
AX to SQL data dictionary synchronization issues
One of my clients restored their AX database from PROD to DEV environment for AX 2009 and on database sync following error message was thrown

Cannot execute a data definition language command on ().
The SQL database has issued an error.
Problems during SQL data dictionary synchronization.
The operation failed.
Synchronize failed on 1 table(s)

This error message does not tell you which table is failing during sync, when I checked from event log I found following error logs which tells you table name but error message was totally misleading.
“Object Server 01: The database reported (session 5 (#####)): [Microsoft][SQL Native Client][SQL Server]There is already an object named '<TABLENAME>' in the database”
"Object Server 01:  The database reported (session 23 (#####)): [Microsoft][SQL Server Native Client 10.0][SQL Server]Cannot drop the table
'<TABLENAME>', because it does not exist or you do not have permission.. The SQL statement was: "DROP TABLE <TABLENAME>"

NOTE: In my case it was MarkupTrans table

Now, let’s play around and try to find the exact issue and resolve it.
 Take table properties in AOT and check ID – For me it was 30088
Open SQLDictionary table from SQL Management Studio and filter it with your table name, put ‘Markuptrans’ in name column. TableId column there will tell you the ID for MarkupTrans table, for my case it was 30086 which is different as shown from AOT
You need to change TableID value in SQLDictionary table same as we have in AOT – I changed it to 30088. You cannot change directly this TableId in SQLDictionary Table, either you can update it from SQL server management studio or make TableId field in SQLDictionary Table editable by changing its property AllowEdit = YES. You MUST have to change it back to previous state after updating tableId.
You can also check IDs for all fields in AOT for your table, these must be same as we have in SQLDictionary Table.


Synchronize your application again – it will be all good and will not throw such errors

Monday 20 January 2014

LedgerTable and LedgerTrans table name changes in ax 2012

LedgerTable To MainAccounts in ax 2012

LedgerTrans to General Journal Tables in ax 2012

GeneralJOurnalAccountEntry
GeneralJournalEntry
SubledgerVoucherGeneralJournalEntry
LedgerEntry(Optional)
LedgerEntryJournal(Optional)
LedgerEntryJournalizing(Optional)

Complete select statement
This pattern and example demonstrate how to select the general journal records that replace a single
LedgerTrans record.

Example
select RecId from generalJournalAccountEntry
join RecId from generalJournalEntry
where generalJournalEntry.RecId == generalJournalAccountEntry.GeneralJournalEntry
join RecId from subledgerVoucherGeneralJournalEntry
where subledgerVoucherGeneralJournalEntry.GeneralJournalEntry ==
generalJournalEntry.RecId
outer join RecId from ledgerEntry
where ledgerEntry.GeneralJournalAccountEntry == generalJournalAccountEntry.RecId
outer join RecId from ledgerEntryJournal
where ledgerEntryJournal.RecId == generalJournalEntry.LedgerEntryJournal
outer join RecId from ledgerEntryJournalizing
where ledgerEntryJournalizing.GeneralJournalAccountEntry ==
generalJournalAccountEntry.RecId

Select for a specific transaction date
This pattern and example demonstrate how to select the general journal records for a specific
transaction date.

Example
select RecId from generalJournalAccountEntry
join RecId from generalJournalEntry
where generalJournalEntry.RecId == generalJournalAccountEntry.GeneralJournalEntry
&& generalJournalEntry.AccountingDate == <transaction date input>

Select for a specific voucher and transaction date 
This pattern and example demonstrate how to select the general journal records for a specific voucher
and transaction date

Example
select RecId from generalJournalAccountEntry
join RecId from generalJournalEntry
where generalJournalEntry.RecId == generalJournalAccountEntry.GeneralJournalEntry
join RecId from subledgerVoucherGeneralJournalEntry
where subledgerVoucherGeneralJournalEntry.GeneralJournalEntry ==
generalJournalEntry.RecId
&& subledgerVoucherGeneralJournalEntry.Voucher == <voucher input>
&& subledgerVoucherGeneralJournalEntry.VoucherDataAreaId == <voucher data area ID
input>
&& subledgerVoucherGeneralJournalEntry.AccountingDate == <transaction date input>

Thursday 16 January 2014

Full CIL or incremental CIL in ax 2012

In AX development, it is a bit confusing about the real meaning of “Compile”, “generate
 Full CIL or incremental CIL”,
Compile: -The compile is to convert x++ source code into p-code, it is in 2009
Sync:-Checking each and every table defining the value of your EDT
full CIL :-generate CIL is about to convert the p-code into CIL, referred to as the byte code which, at run time, is used by the CLR to convert into native code for execution on the computer. It is easier to understand that a full CIL generation involves converting all p-code into CIL, finally it converts to the binary Lang
Incremental CIL :-incremental CIL generation involves only the “changed” p-code artifacts that need  to be converted into target CIL. Incremental CIL would compile only the objects that were modified since the last incremental compilation.
Some X++ batch jobs can now complete in much less time. All X++ code that runs on the AX32.exe client still runs as interpreted p-code. However, all batch jobs now run as Microsoft .NET Framework CIL, which is often much faster than p-code.

The X++ developer must now complete the extra step of compiling X++ p-code to CIL. To compile p-code to CIL, right-click AOT, and then click Add-ins > Incremental CIL generation from X++.

Next, if the installation uses multiple instances of Application Object Server (AOS), all AOS instances must be stopped and then restarted. This causes the AOS instances to load the updated assembly.

Finally, a service or a batch job must run the X++ code that was compiled to CIL. Services and batch jobs now run only as CIL

Sunday 12 January 2014

RunBaseBatch in ax 2009

RunBaseBatch:-

New: Create a new instance of the class, called when executed in batch

Pack: Class variables are persisted by using a local macro, called when the dialog box is closed by selecting “OK”

UnPack: The Class variables in the local macro are re- instantiated. The unpack method is called before the run. Called when executed in batch.

Dialog: Controls are added to the dialog box and the values of the controls are set by the values of their corresponding class variables.

getFormdailog: The values of the class variables are assigned by the values of the controls that have been added to the dialog box .

Validate: the input parameters can be validated, if the input are invalid, an info log can be displayed so the user can take action.

Run:The central method of the class . Called when executed in batch

InitParmDefault: called when the unpack method return false no usage data found ,class variables that are in the macro list should be initialized in the init parm default method.

QueryRun : if the class uses a query , this mehod should return a queryrun object

CanGOBatchJournal: Indicates if the class (job) can be included in a batch journal Defult is false.

CanGoBatch: Indicates if the class can be executed in batch. The framework adds the “Batch” tab page to the dialog if this method return true. Default is true.

RunImpersonated: Indicates if the job should be run using the submitting user’s account or the batch user’s account. The default is to use batch user’s account. If true is returned , the batch server will execute the job using the X++ “RunAs” Statement version 4 and later . Default is false.

Description(Static)
   Return the description of the (base)class and will be displayed as the description in the batch list and determines the title of the dialog.


Caption: The description of the class can be overridden by returning the descrition of the child class. It will be displayed as the description in the batch list and determines the title of the dialog

Difference between MapIterator and MapEnumerator in ax 2012

MAP-ITERATOR:
    The Mapiterator loops through the complete Map.

MAP-Enumerator:-

    Map Enumerator class is like map iterator Class ,But allows the deletion of elements during enumeration and mapiterator  does not.


1. X++ Maps: it can be used as a temp data store for the given scope of a process. This takes us less over head, and is much quicker than a TempTable. For Further reading

2. AOT Maps: A map can unify the access to similar columns and methods that are present in multiple tables. You associate a map field with a field in one or more tables. This enables you to use the same field name to access fields with different names in different tables. Methods on maps enable you to create or modify methods that act on the table fields that the map references

Macro information in ax 2012

DEFINE: -   defines macro variables, which can be used as a constant or to make macros perform differently according to the macro variable value.
Ex:-
#define.Maxlength(100)
;
Int intarray[#Maxlength]

UNDEF: -    Cancels a macro variable

#MACROLIB: - loads a macro library, libraries must be loaded before the macros within them can be used.

#GLOBALMACRO: - Declares a global macro (header). A global macro can be used everywhere in Morphx after declaration

#LOCALMACRO: - Declares a local macro (header)

#ENDMACRO: - Signals the end of a macro declaration

#IF: - Tests if the macro variable following the #IF has a particular value includes the statements between #IF and #ENDIF

#IF.EMPTY():- Tests the arguments within the brackets. Includes the statements between #IF and #ENDIF if the argument is empty

#IFNOT.EMPTY():- Tests the argument within the brackets, and includes the statements between #IF and #ENDIF if the argument is not empty.

#ENDIF: - signals the end of a conditional macro declaration (#IF).


#LINENUMBER: - return the current line number, Mainly useful for debugging .

Transaction Statements in ax 2012

Delete_from : delete multiple records at a time
EX:-
Custtable _custtable;
;
delete_from custtable where custtable.Accountnum == “4018”;
Update_recordset : Update multiple records at a time.
Ex:-
Salestable   _salestable;
;
Update_recordset _salestable setting salesName =”New enterprises” where _salestable.CustAccount == “2001”
       
Insert_recordset: insert multiple records at a time.
Ex:- MYtable1  mytable1;
     MYtable2 mytable2;
     ;
     Ttsbegin;
     Insert_recordset mytable2 (myField1, myField2) select myField1, myField2 from mytable1;
     Ttscommit;


Difference between arrays and containers

Arrays: An array is a list of items with same data type and the same name only the index differs,

Containers: A Container is a dynamic list of items containing primitive data types and /or some composite data types

Saturday 11 January 2014

Transaction integrity checking in ax 2012

TTS – Transaction tracking system: - To help you ensure database integrity
The following keywords help in integrity checking
Ttsbegin- Indicates the beginning of the transaction
Ttscommit – indicates the successful end of a transaction, the ensures the transaction performed as intended upon completion
Ttsabort- Used as an exception to abort and roll back a transaction to the state before the ttsbegin

Nested ttsbegins and ttscommits are ignored in that a lock is held until the last ttscommit is reached however the system does keep track of the tts level, Each time a tts begin is called, the tts level increases by one. Every ttscommit decreases the level by one.

Default index for a table in AX / X++ Or System index in ax

System index:
         Microsoft dynamics ax requires index on each table. If there are no indexes on a table or all the indexes are disabled, a system index is automatically created. The system index is created on the recid and DataAreaid fields if the DataAreaid field exists. Otherwise the system index is created on the recid field. You can see system index in the database, but aren’t visible in the AOT.

        If there are indexes on a table but none of them are unique, the runtime estimates the average key length of the existing indexes, selects the index with the smallest key length, and then appends the recid columns to create a unique index