prasad's profilePRASADZPhotosBlogListsMore Tools Help

PRASADZ

Every block of stone has a statue inside it and it is the task of the sculptor to discover it.
May 26

Local DAX installation method also works for Dynamics Ax 5.0 Build 366

The 'near-domainless' installation method I outlined in About local Dynamics Ax 4.0x installations on Windows XP works for Dynamics Ax 5.0 Build 366 too! +5 to Marc for testing it out.

Editing the registry as outlined at the end of the post referenced above and originally here (http://axforum.info/forums/showthread.php?t=17711) also works, both on Ax 4.0 and Ax 5.0. Thanks to an AxForum.info member for providing me with this alternative and Don for testing it out on Ax 5.0!



From :http:// http://gatesasbait.spaces.live.comrom


Data dictionary synchronization errors

Every object related to the SQL Database (tables, table fields, table indexes) is identified by 'name' in the Database, but identified by a 'unique id' in Dynamics Ax. This unique id is kept in code, in its respective layer file (*.aod). This unique id is a constant; SalesTable for example has had the table id '366' since forever. The object id found in the layer is matched with the Database object name through a table called 'SQLDictionary'. This table maps DAX table ids to SQL table names, and DAX field ids to SQL field names. This means that in theory and practice, a field could have a certain name under DAX, and a different name under the SQL Databse; this happens for example when the object name under the DAX AOT is longer than 30 characters.
 
During synchronization of the dictionnary under the DAX AOT, DAX looks through the SQLDictionary table and verifies that all tables and fields can be found, that they have the proper id and that the corresponding SQL object exists with the proper name and proper type.
 
Possible scenarios at synchronization 
  1. An object (table or field) exists on the SQL Database, exists on a DAX layer and is properly mapped in the SQLDictionary table: DAX is properly aware of this object and nothing will occur at synchronization.
  2. An object (table or field) exists on the SQL Database, but not on any DAX layer nor in the SQLDictionary table: DAX is not aware of this object and nothing will occur at synchronization.
  3. An object (table or field) exists in the AOT and cannot be found in the SQLDictionary table: DAX will attempt to create the object in the SQL Database and insert a corresponding cross-reference record in SQLDictionary. Since DAX is not aware of the Database contents beyond the information contained in SQLDictionary a synchronization error will occur if an object of the same name already exists in the Database.
  4. An object (table or field) exists in the AOT, can be found in the SQLDictionary table, but doesn't exist in the SQL Database: DAX will attempt to create the object in the SQL Database and update the corresponding cross-reference record in SQLDictionary.
  5. A field exists in the AOT (suppose an Int64) and can be found in the SQLDictionary table (as an Int) as well as in the Database: A synchronization error will occur.
  6. An object (table or field) exists in the AOT, can be found in the SQLDictionary table but with a different id, as well as in the Database (happens when a object was moved between layers and restoring a prior database): DAX wil attempt to recreate the object in the Database and will fail with a synchronization error. The original may be dropped.
  7. An index exists in the AOT, but not in the SQL Database: DAX will attempt to introduce this new index to the table in the database. If the index is unique and records are seen as duplicates against it, a synchronization error will occur.

Diagnosing synchronization errors
 
Sometimes, DAX will fail to synchronize a table and give you an error message that does not identify the problematic table. This x++ job will help you determine where the cause of the error is:
 
public static void manualSync(Args _args)
{
    #define.unknown("UNKNOWN")
    #define.tableIdMax(65536)
    int i = 1;
    ;
    while (i < #tableIdMax)
    {
        if (tableid2name(i) != #unknown)
        {
            print(tableid2name(i));
            try
            {
                if (!appl.dbSynchronize(i, false, true))
                    throw Exception::Error;
            }
            catch
            {
                error(tableid2name(i));
            }
        }
        i++;
    }
}
 
When ids don't match...
 
Generally you will have table id and field id mismatches upon re-attaching a database when objects where moved across different layers. The reason for this is that of the 65 536 available tables ids, ids [0, 10 000] are reserved for SYS, ids [20 000, 30 000] are reserved for BUS, ids [40 000, 50 000] are reserved for CUS, etc; with no overlap between the grops of ids (I may be slightly off, this is from memory).
 
While migrating a customer from Ax 2.5 to Ax 4.0, they revised they're customization strategy to bring mods onto the CUS layer, when originally they were on the USR layer. We didn't want to spend hours importing data using the data import-export tool, so I ran jobs to fix the SQLDictionary table (you would also need to fix the data in any RefTableId field). This was the basic idea:
 
(NEVER RUN THIS CODE IN A PRODUCTION ENVIRONMENT)
 
/*
 *  Warning, this code does not work 'as-is', it is only given as example
 *  This will completely destroy an environment if run 'as-is'!
 */
public static void fixSQLDictionary(Args _args)
{
    SQLDictionary sqlDictionary;
    #Define.CUSLAYERSTART(40000)
    #Define.USRLAYEREND  (60000)
    boolean validate()
    {
        if(sqlDictionary.FieldId == 0 && sqlDictionary.Name != tableid2name(sqlDictionary.TabId))
        {
            return false;
        }
        else if(sqlDictionary.FieldId != 0
            && sqlDictionary.Name != fieldid2name(sqlDictionary.TabId, sqlDictionary.FieldId))
        {
            return false;
        }
        return true;
    }
    ;
    try
    {
        ttsbegin;
        //FIX CUSTOM TABLES AND THEIR FIELDS
        while select forupdate sqlDictionary
            where sqlDictionary.TabId >= #CUSLAYERSTART
                && sqlDictionary.TabId < #USRLAYEREND
        {
            sqlDictionary.TabId = tablename2id(sqlDictionary.Name);
            if (sqlDictionary.orig().FieldId != 0)
                sqlDictionary.FieldId = fieldname2id(sqlDictionary.TabId, sqlDictionary.Name);
            if (!validate())
            {
                //TODO: Need to deal with DEL_* objects and objects with names > 30 chars
                continue;
            }
            //TODO: Uncomment
            //sqlDictionary.doUpdate();
        }
        //FIX CUSTOM FIELDS ON STD TABLES
        while select forupdate sqlDictionary
            where (sqlDictionary.FieldId >= #CUSLAYERSTART
                    && sqlDictionary.FieldId < #USRLAYEREND)
                && (sqlDictionary.Tabid < #CUSLAYERSTART)
        {
            sqlDictionary.FieldId = fieldname2id(sqlDictionary.TabId, sqlDictionary.Name);
            if(!validate())
            {
                //TODO: Need to deal with DEL_* objects and objects with names > 30 chars
                continue;
            }
           
            //TODO: Uncomment
            //sqlDictionary.doUpdate();
        }
        ttscommit;
    }
    catch
    {
        throw error("Aborted");
    }
}


From : http://gatesasbait.spaces.live.com
July 23

Trace Parser

http://blogs.msdn.com/axperf/

July 16

Index and Index hint

In the Axapta community, there is still a big confusion about the "index" and "index hint" statements used in connection with selects.

So, what is the Axapta kernel *really* doing:

Using "index": when you add the statement "index MyIndex", the Axapta kernel will add an "ORDER BY" with all the fields of the index.

Example: select * from InventTable index GroupItemIdx will generate the following SQL statement to the database:

SELECT A.ITEMGROUPID, A.ITEMID, A.ITEMNAME,.... FROM INVENTTABLE A ORDER BY A.ITEMGROUPID, A.ITEMID

The Index ItemGroupIdx of the InventTable exactly contains the two fields ItemGroupID and ItemId (in that order). Using "index", you still give the control of which index to use to the database optimizer. So, if the optimizer finds a better index to use, it will use it.

Using "index hint": when you add the statement "index hint MyIndex", the Axapta kernel will add a statement to instruct the database to use that index and no other one.

Example: select * from InventTable index hint GroupItemIdx will generate the following SQL statement to the database:

SELECT /*+ INDEX(A I_175GROUPITEMIDX) */ A.ITEMGROUPID, A.ITEMID, A.ITEMNAME,.... FROM INVENTTABLE A

Using "index hint", you take away the control of which index to use from the database optimizer. So, if there may be a better index, the database will not use it.

Conclusion:

Adding the "index" statement to an Axapta select, it does NOT mean that this index will be used by the database. What it DOES mean is that Axapta will send an "order by" to the database.

Adding the "index hint" statement to an Axapta select, it DOES mean that this index will be used by the database (and no other one).

This rule applies to both the MSSQL and Oracle databases.

Tip: On the Axapta 3.0 installation CD, you will find a document called "Performance enhancements using the Cost-Based optimizer" (file name: AX-300-TIP-024-v01.00-ENUS.doc) that will tell you some more things about Axapta and database optimizers.



From : http://axaptafreak.blogspot.com

Enterprise portal blog

http://blogs.msdn.com/epblog/
June 24

Performance check list for Axapta installations

Performance check list for Axapta installations

This checklist is intended to be a guide on how to determine the causes of perceived data access performance problems in Axapta. It is not a guide on how to tune an Oracle or SQL server installation.

  1. You will need to populate your application tables with a realistic amount of data to make any evaluation of the applications performance. This is due to the fact that with sufficiently small amounts of data,it will not matter whether the access is via indexes or via a table scan.

  1. If you are running Oracle, be sure to run the “calculate statistics” found in the administrator databasemenu’s before evaluating performance.

  1. Run your Axapta application with querytimeLimit: 2000, this will list all SQL that runs for more than 2seconds in the sql.log file. This will give a first indication of which statements to investigate further. It will not however reveal faulty logic, like performing a well performing SQL statement so many times that it becomes critical for performance. The latter type of problem can be found incorporating homegrown timing logic.

  1. For each of the long-running select SQL statements think about :

    ----> Does the accessed table have an index that matches the where clause used. See Axapta and Indexes

    ----> Have you given the optimiser all available information, that is, does the where clause include all knowninformation. Specifying FirstFast is also information to the optimiser telling it to favour retrieval of the first row over the total retrieval time.

    ---> Does the statement include an order by that is not essential? An order by forces either access via an index that matches the order by, or a sort. A sort of a large result-set will degrade the performance because all records have to be retrieved and sorted before the first record can be presented.

  2. For each long-running insert SQL statement, think about:

    1. Are there many “long” indexes on the table? See Axapta and indexes
    2. Is the table index organised on a key that is continually ascending? See Axapta and Indexes
    3. Does it have unnecessary containers?

  3. For each long-running update SQL statements, think about:

    • Are there many “long” indexes on the table, which include the updated colum
    • Does it have a clustering index, which includes the updated column?


June 12

Creating a active directory in windows 2003 server

How to create an active directory on a windows server 2003 OS

http://www.visualwin.com/AD-Controller

June 11

Whatz new in Dynamics AX_2009

You need a partner source login

     https://mbs.microsoft.com/downloads/customer/Ax2009/AX50_ENUS_WN_Tech.pdf

June 06

Number Sequence details

Number sequences handle the automatic allocation of ID numbers, vouchers, andjournal numbers.
A number sequence is created under

MAIN MENU->BASIC->SETUP->NUMBERSEQUENCES->NUMBER SEQUENCE.

From a developers point of view, these are the tables which are important.

NumberSequenceTable contains the definitions of each number sequence.

NumberSequenceList holds numbers for continuous number sequences that have not been completed or are currently reserved.

NumberSequenceReference holds which number sequence is used for which function.

NumberSequenceGroup is a list of number sequence groups.

NumberSequenceGroupRef contains the number sequence references specific to a group.

NumberSequenceTTS holds the transaction id of a number before it has been completely assigned. It is used during the clean up process, in case of a system crash.

NumberSequenceHistory holds a log of all changes made to the number sequence.
From a developers point of view, these are the classes which are important.

NumberSeq assigns numbers and vouchers, handles continuous number sequences, and calls clean up when appropriate.

NumberSeq_Fast is used for number sequences that are not continuous. It does not keep a record of the status or store transaction ids for later clean up, and is better performance wise.

NumberSeqCleanUp looks for numbers in the list that have not been completed, looks for the session that created them, and, if the session is no longer active, frees up the number for later use.

NumberSeqDataArea is used in the clean up process.

NumberSeqFormHandler is used whenever a number sequence assigns a number in a form. It handles records being deleted and ensures that two users cannot use the same number.

NumberSeqReference creates the link between the function and the number sequence. NumberSeqReference is the super class used, and there is a sub class for each module.

NumberSeqNumCache contains the method to manipulate the cache of reserved numbers.




Using Macros


Using Macros
http://www.axaptapedia.com/Abstract_macro

Posting entry in GL


How to create a posting entry in General Ledger (GL) module of Dynamics AX?

Answer:
There are many ways to create posting entries in to General Ledger module. In my experience the simplest way is to use the Journal classes. There are two parts of posting create a heade entry and then create the add lines to it. To create a header entry use the LedgerJournalTable table and use the LedgerJournalTrans table. Finally use the ledgerJournalCheckPost class object to post these entries into the system.
Below is a job explaing the Posting process in a simple way.

static void postVoucherThroughJournal(Args _args)
{
Dialog dialog = new Dialog("Ledger Posting Example");
DialogField ledgerAccountField;
DialogField offsetAccountField;
DialogField ledgerJournalDescription;
DialogField ledgerAmount;
DialogField ledgerTransDescription;

LedgerJournalTable ledgerJournalTable;
LedgerJournalTrans ledgerJournalTrans;
LedgerJournalCheckPost ledgerJournalCheckPost;
NumberSeq numberSeq;
;
dialog.addGroup("Post Ledger Voucher");
//Add Fields
ledgerAccountField = dialog.addField(typeid(LedgerAccount), "Ledger Account");
offsetAccountField = dialog.addField(typeid(LedgerAccount), "Offset Account");
ledgerJournalDescription = dialog.addField(typeid(Name), "Journal Name");
ledgerAmount = dialog.addField(typeid(AmountCurDebit), "Debit Amount");
ledgerTransDescription = dialog.addField(typeid(Name), "Transaction Text");

if(dialog.run())
{
ttsbegin;
//STEP1: INSERT Journal Header
ledgerJournalTable.JournalName = 'Day1'; /This is the journal name.
ledgerJournalTable.initFromLedgerJournalName();
ledgerJournalTable.Name = ledgerJournalDescription.value();
ledgerJournalTable.insert();

//STEP2: INSERT Journal Details
numberSeq = NumberSeq::newGetVoucherFromCode(LedgerJournalName::find(ledgerJournalTable.JournalName).VoucherSeries);
ledgerJournalTrans.Voucher = numberSeq.voucher();
ledgerJournalTrans.JournalNum = ledgerJournalTable.JournalNum;
ledgerJournalTrans.CurrencyCode = CompanyInfo::standardCurrency();
ledgerJournalTrans.ExchRate = Currency::exchRate(ledgerJournalTrans.CurrencyCode);
ledgerJournalTrans.AccountNum = ledgerAccountField.value();
ledgerJournalTrans.AmountCurDebit = ledgerAmount.value();
ledgerJournalTrans.TransDate = Today();
ledgerJournalTrans.OffsetAccount = offsetAccountField.value();
ledgerJournalTrans.Txt = ledgerTransDescription.value();
ledgerJournalTrans.insert();
//Post Journal into Ledger Accounts
ledgerJournalCheckPost = LedgerJournalCheckPost::newLedgerJournalTable(ledgerJournalTable, NoYes::Yes);
ledgerJournalCheckPost.run();
ttscommit;
Info(StrFmt("Journal %1 is posted", ledgerJournalTable.JournalNum));
}
}



From the blog    http://msdax.blogspot.com/

June 05

Functional Production help

How would I go about setting up the following.

A specific product can be manufactured on machine 1. (this being the preferred
machine). However it could also be manufactured on machine 2 or machine 3.

What I would like to be able to do if the Machine 1 has reached capacity,
then the demand must be moved to machine 2, then to machine 3 whilst I am
planning. (MPS should do this for me)

Is there a way of doing this?

------------------------------------------------------------------

Answer:

Yes, this can be achieved using task groups by assigning one of them to the production order's operation.

A task group has a list of work centers which should be used when the best possible work center is being searched for performing the operation. The best work center means - the one that can do job faster(earlier).

Each line in the task group has a sorting criteria (it is called Requirement) where you can specify a number which will be used if there are equaly fast work centers and you need to define which one should be used first. The lowest number is used first.

Here is how you can set this up in AX:

1) Create an item of type BOM

2) Add one operation to the route (Items > Route button) and set the Work center field to e.g. "Machine 1".

3) Go to tab page Setup, do right click on the Task group field and go to the Main table form.

4) Create a new task group and on the tab page Work centers add two lines for the two alternative work centers - "Machine 2" and "Machine 3". Set the Requirement field to 10 for "Machine 2" and to 20 for "Machine 3". This will assure the sequence of how the machines will be used.

5) Assign the newly created task group to the operation.

6) Make sure all machines (1, 2, 3) have parameter "Finite capacty" set. (Work centers - tab page General).

7) Create a production order for the item created at the step 1 and do Update > Job scheduling. Check results in the Production > Jobs form. You can see that the selected work center is "Machine 1".

8) Do the same as on the step 7 (create another production order and run job scheduling for it) and you will see that now system has selected the work center - "Machine 2" for the same time interval (because "Machine 1" is booked).

9) If you repeat the step 7 again (create the third production order and job schedule it), then the time interval will be the same, but the selected work center will be - "Machine 3"(because "Machine 2" was booked on the step 8).

10) If you do it one more time, then there is no more alternative work centers and system will book a later time and the selected work center will be "Machine 1" again and if you continue creating production orders you will get the same results - all alternative work centers will be used.

Graphically it looks like below:
                                                          Blog Image

Figure 1. Production job scheduling on alternative work centers.

For simplicity production orders on the figure have only one operation and only one job of type Process, so they look like one bar.


Can find the same on : http://daxfaq.studioerudit.com/

June 03

Links could be helpful


Solving the ELEMENT ID problem
http://blogs.msdn.com/mfp/archive/2008/05/21/solving-the-element-id-problem.aspx


New Layers in AX2009
http://blogs.msdn.com/mfp/archive/2008/04/22/new-layers-in-dynamics-ax-2009.aspx


Writing highly secure code
http://download.microsoft.com/documents/uk/msdn/security/The%20Developer%20Highway%20Code.pdf


Writing less code :"The else statement"
http://blogs.msdn.com/mfp/archive/2008/02/23/writing-less-code-the-else-statement.aspx


Anyone can try to write developer documentation of X++ code
http://blogs.msdn.com/mfp/archive/2008/01/31/anyone-interested-in-developer-documentation-for-x-code.aspx

Dynamics AX 4.0 Meta Model
http://blogs.msdn.com/mfp/archive/2007/12/12/dynamics-ax-4-0-meta-model.aspx


Compiler Warnings
http://blogs.msdn.com/x/archive/2007/08/21/correction-guide-compiler-warnings.aspx


AOT MEtrics
http://blogs.msdn.com/mfp/archive/2006/10/06/AOT-Metrices.aspx


Compiler Warnings
http://blogs.msdn.com/mfp/archive/2007/09/12/compiler-warnings-and-so-what.aspx


How to use a form tree control
http://blogs.msdn.com/mfp/pages/how-to-use-a-formtreecontrol.aspx



Articles on X++ development
http://blogs.msdn.com/mfp/archive/2007/08/23/articles-on-x-development.aspx


Right to left support in Dynamics AX5.0
http://blogs.msdn.com/mfp/archive/2007/05/08/right-to-left-support-in-dynamics-ax-5-0.aspx
May 28

Look up

Lookup Form  From Axaptapedia

Axapta usually creates required lookup forms on the fly, but it also allows the developer to create a custom lookup form and assign it as "FormHelp" to an extended datatype, so that it is automatically used. Why would you do that? There might be many reasons. Typically you need to display the data in a non standard order or you want to show only a subset of the data depending on some criteria. Here are the steps needed to create a proper lookup form.In the following example I assume you create a lookup for table "xyz" and the ID field of that table is called "id".

Create the form

First create a basic form. Usually this form contains not much more than a grid. But you might add additional controls. Then set the following properties on the datasource, so that the form can not be used for editing:

     AllowCheck:    No
AllowCreate: No
AllowDelete: No
AllowEdit: No
AutoNotify: No
InsertAtEnd: No
InsertIfEmpty: No

Additionally set for the design the following properties to make it look like a proper lookup:

      AlwaysOnTop:   Yes
Frame: Border
HideToolbar: Yes
WindowType: Popup


Make it selectable

Now we need to tell the lookup form, which control will return the selected value. Lets assume you have a form with a grid and in that grid you have a control called "xyz_id". In this case you overwrite the init method of the form as shown below. Note that if you are using Axapta v3.0 or higher, then you can set the control's AutoDeclaration property to Yes and use the control name directly in the selectMode() call.

public void init()
{
FormControl xyz_id;
 ;
 
super();
 
xyz_id= element.design().control(control::xyz_id);
element.selectMode(xyz_id);
}

Make sure the form opens with the previously selected value

Strange enough the following is missing in most/all custom lookup forms, which you find in Axapta Standard. But it is required, if you want your lookup to mark the previously selected value as active record. Therefore overwrite the ExecuteQuery method of your datasource:

public void executeQuery()
{
FormStringControl callerControl = SysTableLookup::getCallerStringControl(element.args());
 ;
 
super();
 
xyz_ds.findValue(fieldnum(xyz,id),callerControl.text());
}

With a lot of pain I had top learn that the above will not work, if you use the standard query, which is automatically created by Axapta. This might be due to existing Dynalinks or other reasons, which I am not aware of. But it will work fine, if you create your own query for example in the Init method of the datasource. Here you can also define your custom sort or range criteria:

public void init()
{
Query q = new Query();
QueryBuildDataSource qbds;
 ;
 
super();
 
qbds = q.addDataSource(tablenum(xyz));
qbds.orderMode(OrderMode::OrderBy);
qbds.addSortField(fieldNum(xyz,some_other_field));
this.query(q);
}

Make lookup react to search by wild card

The following you will find in the official documentation and also in a lot of Axapta examples. If you type in a standard Axapta StringEdit Control something like "abc*", then automatically the lookup will open and only the matching subset of records will be shown. You achieve this by overwriting the run method of the form:

public void run()
{
FormStringControl callerControl = SysTableLookup::getCallerStringControl(element.args());
Boolean filterLookup = false;
 ;
 
// if lookup was called with filter, then supress autoSearch
if (callerControl.text() && callerControl.hasChanged())
{
filterLookup = true;
xyz_ds.autoSearch(false);
}
 
super();
 
// after call of super filter search manually by applying past filter
if (filterLookup)
{
xyz_ds.research();
xyz_ds.filter(fieldnum(xyz,id),callerControl.text());
}
}

Make Form Default lookup for an Extended Data Type

To have Axapta use your form by default every time a lookup is performed, you can set your form's name in the Extended Data Type's property called 'FormHelp'. You do not ncessarily need a display menu item, the property is your actual form's name.

So that's it. Now you have a wonderfully working custom lookup.

 

Retrieved from Axaptapedia

 

May 21

How to be a Programmer: A Short, Comprehensive, and Personal Summary

How to be a Programmer: A Short, Comprehensive, and Personal Summary
http://samizdat.mines.edu/howto/HowToBeAProgrammer.html#id2790277

Dynamics AX related sites

Dynamics AX Temporary tables
http://msdn.microsoft.com/en-us/library/bb314749.aspx\


Dynamics AX system requirements
http://www.microsoft.com/dynamics/ax/product/systemrequirements.mspx


Microsoft Dynamics AX 4.0 Service Pack 1 (SP1) Release Notes
http://www.trucosax.com/htmldocs/ax40_sp1/ReleaseNotes.htm?NSNST_Flood=63a05617d14e7665cc23fcfdddc3f1dc

Technical Best Practices for Dynamics AX - Shared and AOT Object Standards
http://www.packtpub.com/article/technical-best-practices-for-dynamics-ax-shared-and-aot-object-standards

Mulitdivisional companies: Integrate systems across your enterprise
http://www.microsoft.com/dynamics/ax/product/hubandspoke.mspx



March 31

Containers

http://msdn2.microsoft.com/en-us/library/aa882886.aspx

http://www.eggheadcafe.com/software/aspnet/31749113/what-is-the-difference-be.aspx




Debugging Ax Kernel

http://www.microsoft.com/whdc/devtools/debugging/debugstart.mspx

March 25

Useful links

http://axgeek.spaces.live.com/

http://axapta.blogcu.com/1947360/

http://ax.nom.es/uncategorized/axapta-comman-line-parameters-2



How to enable single user mode

USE master
EXEC sp_dboption 'database name, 'single user', 'TRUE'
use database name
dbcc checktable ('LedgerTrans', REPAIR_ALLOW_DATA_LOSS)





Axapta Performance Test links

http://blogs.msdn.com/axperf/

 

prasad pendyala

Occupation
Location
No list items have been added yet.
No list items have been added yet.