Tuesday, May 15, 2012

Tuesday, June 14, 2011

Adding a custom lookup to a form control

In Dynamics AX one way you can add a custom lookup or drop down list to a form control by overriding the lookup method of the control.  Below is some example code for doing this.

public void lookup() 
 { 
     //This lookup basis the list criteria on information in 2 tables
     //All display fields come from Table1, Table2 is used for limiting criteria
     
     //super(); 
     
     //This Next Line initializes the the sysTableLookupClass
     sysTableLookup          sysTableLookup  =
                     SysTableLookup::newParameters(tableNum(<TABLENAME1>), this); 
     
     Query                   query           = New Query(); 
     QueryBuildDataSource    qbdsTbl1, qbdsTbl2; 
     QueryBuildRange         qbr; 
     ; 
     
     //Add Tables to the data source
     qbdsTbl1      = query.addDataSource(tableNum(<TABLENAME1>)); 
     qbdsTbl2     = qbdsTbl1.addDataSource(tableNum(TABLENAME2)); 
  
  
     //Add Query Ranges to limit List records     
     qbr = qbdsTbl1.addRange(fieldNum(<TABLENAME1>,<RANGEFIELDNAME1>)); 
     qbr.value(<CRITERIAVALUE>); 
  
     qbr = qbdsTbl1.addRange(fieldNum(<TABLENAME1>, <RANGEFIELDNAME2>)); 
     qbr.value(<CRITERIAVALUE>); 
  
     qbr = qbdsTbl1.addRange(fieldNum(<TABLENAME1>, <RANGEFIELDNAME3>)); 
     qbr.value(<CRITERIAVALUE>); 
     
     //Since we have multiple tables in the query turn on relations
     qbdsTbl1.relations(true); 
  
     //Add Ranges to the Table2
     qbr = qbdsTbl2.addRange(fieldNum(<TABLENAME2>, <RANGEFIELDNAME4>)); 
     qbr.value(<CRITERIAVALUE>); 
     
     qbdsTbl2.relations(true); 
  
     //The next 2 lines actualy adds the fields that will be displayed in the lookup grid
     sysTableLookup.addLookupfield(fieldNum(<TABLENAME1>, <DISPLAYFIELDNAME>)); 
     sysTableLookup.addLookupfield(fieldNum(<TABLENAME1>, <DISPLAYFIELDNAME>)); 
     
     //Hand the created query to the sysTableLookupClass
     sysTableLookup.parmQuery(query); 
  
     //Display the drop down 
     sysTableLookup.performFormLookup(); 
  
 }




Another example single table with field and table names instead of variable place holders



//Single table example with actual table and field names instead of  place holders
 public void lookup() 
 { 
     //This lookup basis the list criteria on information in 2 tables
     //super(); 
     sysTableLookup          sysTableLookup  = 
                 SysTableLookup::newParameters(tableNum(salesBillOfLadingJournalTable), this); 
     Query                   query           = New Query(); 
     QueryBuildDataSource    qbdsJT, qbdsBLR; 
     QueryBuildRange         qbr; 
     ; 
  
     qbdsJT      = query.addDataSource(tableNum(salesBillOfLadingJournalTable)); 
     qbdsBLR     = qbdsJT.addDataSource(tableNum(salesBLReservation)); 
  
  
  
     qbr = qbdsJT.addRange(fieldNum(salesBillOfLadingJournalTable,isMaster)); 
     qbr.value(enum2str(noYes::No)); 
  
     qbr = qbdsJT.addRange(fieldNum(salesBillOfLadingJournalTable, custAccount)); 
     qbr.value((salesBillOfLadingJournalTableMaster.CustAccount)); 
  
     qbr = qbdsJT.addRange(fieldNum(salesBillOfLadingJournalTable, masterBLID)); 
     qbr.value(&quot;&quot;&quot;&quot;); 
  
     qbdsJT.relations(true); 
  
     qbr = qbdsBLR.addRange(fieldNum(salesBLReservation, blOpen)); 
     qbr.value(enum2str(noYes::Yes)); 
     qbdsBLR.relations(true); 
  
     sysTableLookup.addLookupfield(fieldNum(salesBillOfLadingJournalTable, BillOfLadingID)); 
     sysTableLookup.addLookupfield(fieldNum(salesBillOfLadingJournalTable, salesID)); 
     sysTableLookup.parmQuery(query); 
  
     sysTableLookup.performFormLookup(); 
  
 }
 

Thursday, June 9, 2011

Sales order creation at the most basic with ax Classes

 

Programmatic creation of a sales order can range from complex to easy, this is by far the Easy example making use of the axSalesTable and axSalesLine classes you dont get much easier than this.   The example should be a good starting point the ID’s used are part of the standard Demo data.

static void CreateSalesOrder_Job(Args _args)
{
    axSalesTable axSalesTable;
    salesID salesID;
    axSalesline axSalesLine;
 
    ;
    ttsBegin;
    axSalesTable = new axSalesTable();
 
    axSalesTable.parmCustAccount('1101');
    axSalesTable.save();
 
    salesID = axSalestable.salesTable().SalesId;
 
    info(salesID);
 
    axSalesline = new axSalesLine();
    axSalesLine.parmSalesId(salesID);
    axSalesline.parmItemId('7009');
    axSalesLine.parmSalesQty(10);
    axSalesLine.save();
    
    //Line with dimensions
    axSalesline = new axSalesLine();
    axSalesLine.parmSalesId(salesID);
    axSalesline.parmItemId('1001');
    axSalesLine.parmSalesQty(1);
    axSalesLine.axInventDim().parmConfigId('HD');
    axSalesLine.axInventDim().parmInventSizeId('42');
    axSalesLine.axInventDim().parmInventColorId('02');
    axSalesLine.save();
    ttsCommit;
 
}

Tuesday, November 16, 2010

Programmatic choice of select statements and the Next operator


Occasionally I’ve had situations where using a while select statement to loop through a set of records and needed the fields used in the selection criteria to be dynamic or the entire selection statement to be chosen dynamically based on arguments passed to the method.  A simple example would be for a set of logic to sometimes act on a group of records but other times act on a single record.  Dynamics AX X++ offers a slick way of dealing with this that is used in the standard application code but is still pretty obscure.  The following method demonstrates using a variable select statement based on passed in arguments and using the NEXT keyword to move the record set forward.  In this example we’re just outputting the Item ID to an info log but you could get much more creative….

void SelectNextSample(salesID _salesID, inventtransID _inventTransID = '')
{
    salesLine salesLine;
    ;
    
    if(_inventTransID)
    {
        select salesLine 
            where  salesLine.SalesId == _salesID
                && salesLine.InventTransId == _inventTransID;
    }
    else
    {
        select salesLine
            where salesLine.SalesId == _salesID;
    }
    
    while(salesLine)
    {
        info(salesLine.ItemId);
 
        next salesLine;
    }
}

I realize that the above is a simplistic example but it does demonstrate the ability in X++ to determine at run time the exact select statement that will be looped through against your Common object, although in reality the above example does not require variable select statements at all, a bad example in practical use but does show the general construct.  Below is another example, again very simple output of an info log but the variance in the select statements is greater, here it is a difference of acting logic on all sales lines with particular item ID vs. just sales line with the given item ID for a specific warehouse.   In the case below the content of the inventDim source is not needed in the logic portion of the code so when criteria restriction on inventLocationID is not needed we can have a simpler selection statement.   We could enjoy the same dynamic flexibility at run time using a query objects but the point here is to really demonstrate the use of the Next operator with variable selection statements.


void SelectNextSample2(itemID _itemID, inventLocationID _inventLocationID = '')
{
    salesLine salesLine;
    inventDim inventDim
    ;
    
    if(_inventLocationID)
    {
        select salesLine 
            where  salesLine.itemID == _ItemID
        Join tableID from inventDim
            where  inventDim.InventLocationId == _inventLocationID
                && salesLine.InventDimId == inventDim.inventDimId;
    }
    else
    {
        select salesLine
            where salesLine.ItemId == _itemID;
    }
    
    while(salesLine)
    {
        info(strFmt('%1, %2', salesLine.SalesId, salesLine.ItemId));
 
        next salesLine;
    }
}

Friday, October 22, 2010

Workflow setup error on Event Log name

Here is a new one for me…
I was setting up workflow at a client and it finished with error, (that parts not new). The new part was the following message in the log…
            An error occurred during setup of Workflow.
Reason: Only the first eight characters of a custom
log name are significant, and there is already another
log on the system using the first eight characters of the name
given. Name given: ‘Microsoft Dynamics AX Workflow',
name of existing log: 'Microsoft-Windows-EventCollector/Operational'.
Workflow was left in a partially installed state.

Odd because the first 8 characters of the log name are in the word Microsoft and there are already two other event logs starting with the name Microsoft.

Some searching with the old internet took me to the following post…

They offer a good description to the why and how to get around the issue by renaming the exisiting logs in the registry while you run the setup.  Below are screen shots of the Registry entries, as always ensure you know what your doing if you go into the registry and make appropriate backups …

 










Item import from CSV file made easy (Kinda)

The job below uses the axInventTable class to create items from a CSV file.  We gave the client an Excel file with columns identified for them to fill in  for new items they wanted then saved that without the header to a CSV file.  This job reads in a CSV file then uses the axInventTable class to create the new Item.  The axInventTable class manages all related tables for us.  If you look at the code below, you can see through this class we can easily set values in the related tables as well as the main inventTable record.  In the below job you can see we used a mix of values read from the file, and values hard coded, you can easily add fields to your import file to incorporate into the creation of your item as you need them.
One thing to be aware of is if Item templates exist AX will try to find the default Item template to use when it does the initialization of the tables.  If there is not a default template it will use the first template found. This is easy enough to work around, just create a new item with minimal fields set and make a template from that item and set it as default using the SysRecordTemplateTable form.
Note: As a side note Atlas 4.0 appears makes use of a similar process for item imports and has the same issue with the Item Template described above.
//-JobStart -->
static void GL_ItemImport(Args _args)
{
    axInventTable axInventTable;
    inventTable inventTable;
    itemID itemID;
    textBuffer tb = new textBuffer();
    int cnt;
    int numLines;
    int c;
    container inLine;
    Dimension finDim;
    itemType ItemType;
    ;
    tb.fromFile('C:\\items.txt');  //File name  with Path ...
    numLines = tb.numLines();
    if(numLines)
    {
        for(cnt = 0; cnt < numLines; ++cnt)
        {
            inLine = str2Con(tb.nextToken(true));
            if(conpeek(inLine, 1))  //first field item ID don’t do anything if blank ....
            {
                axInventTable =  new axInventTable();
                axInventTable.parmItemId(conPeek(inLine, 1));
                axInventTable.parmItemName(conPeek(inLine, 2));
                axInventTable.parmNameAlias(conPeek(inLine, 3));
                axInventTable.parmItemGroupId(conPeek(inLine, 4));
                axInventtable.parmItemType(str2enum(ItemType, conPeek(inLine, 5)));
                axInventTable.parmModelGroupId(conPeek(inLine, 6));
                axInventTable.parmDimGroupId(conPeek(inLine, 7));
                axInventTable.axInventTableModule_Sales().parmTaxItemGroupId(conPeek(inLine, 13));
                axInventTable.parmPrimaryVendorId(conPeek(inLine, 14));
                axInventTable.axInventTableModule_Purch().parmPrice(ConPeek(inLine, 15));
                //Set financial dimension
                finDim[2] = conPeek(inLine, 18);
                axInventTable.parmDimension(finDim);
                axInventTable.axInventTableModule_Purch().parmUnitId(conPeek(inLine, 20));
                axInventTable.axInventTableModule_Sales().parmUnitId(conPeek(inLine, 21));
                axInventTable.axInventTableModule_Invent().parmUnitId(conPeek(inLine, 22));
                axInventTable.parmPackagingGroupId('TMP');
                axInventTable.parmBOMUnitId(conPeek(inLine, 22));
                axInventTable.axInventTableModule_Invent().parmOverDeliveryPct(20);
                axInventTable.axInventTableModule_Invent().parmUnderDeliveryPct(20);
                axInventTable.axInventTableModule_Sales().parmOverDeliveryPct(20);
                axInventTable.axInventTableModule_Sales().parmUnderDeliveryPct(20);
                axInventTable.axInventTableModule_Purch().parmOverDeliveryPct(20);
                axInventTable.axInventTableModule_Purch().parmUnderDeliveryPct(20);
                axInventTable.save();
                ++c;
            }
            
        }
        info(strFmt('Imported %1 Items', c));
    }
}
//-Job End <--