Showing posts with label Ax2012. Show all posts
Showing posts with label Ax2012. Show all posts

Monday, 8 June 2015

Default Dimension Information


To find the setting of a known attribute, feed the following (In this case, the inventJournalTrans table).

// _journalId = unique journal Id
// _attrToFind = the name of the attribute you are looking for

    InventJournalTrans                            inventJournalTrans;
    DimensionAttributeValueSet          dimAttrValueSet;
    DimensionAttributeValueSetItem  dimAttrValueSetItem;
    DimensionAttributeValue                dimAttrValue;
    DimensionAttribute                           dimAttr;
    DimensionFinancialTag                   dimFinTag;
   
    str 60 attrToFind;
    str 10 journalid;
    str 30 findValue;
   
    ;

    select inventJournalTrans        
           where inventJournalTrans.JournalId == _journalId        
           join dimAttrValueSet where     InventJournalTrans.defaultDimension == dimAttrValueSet.recId        
           join dimAttrValueSetItem where dimAttrValueSetItem.DimensionAttributeValueSet == dimAttrValueSet.RecId        
           join dimAttrValue where        dimAttrValue.RecId == dimAttrValueSetItem.DimensionAttributeValue        
           join dimAttr where             dimAttr.RecId == dimAttrValue.DimensionAttribute 
                        &&                dimAttr.Name == _attrToFind;


And to get the description of the attribute;


    _findValue = dimAttrValue.getValue();
    select firstOnly dimFinTag where dimFinTag.Value == _findValue;


Monday, 1 June 2015

Temporary Tables And Forms


Getting a form to display data in a temporary table can be difficult, so I have made a step-by-step method which has worked for me.


1. Create the temporary table

2. Create a form

3. Attach the temporary table as a dataSource of the form

4. Create a form method to populate the temporary table from normal datasources. This will be created in the upper Methods tree of the form. You can call it anything, but I am making the effort to call it populate() for consistency.

5. Override the init method of the form and add the following line after the super() call.

If the temporary table is of the type TempDB then the line should read;

<tempTableName>.linkPhysicalTableInstance(element.populate());

If the temporary table is of the type InMemory then the line should read;
<tempTableName>.setTmpData(element.populate());

Where <tempTableName> is the name of the temporary table.

On purpose I have made this as simple as I can, if you need the technical stuff, then I have copied the following from HariKiran Varre ;


In Ax 2012 we have 3 different types of tables. one type of temporary table is a TempDB table. We call them TempDB tables because their TableType property value is TempDB. This value comes from the TableType::TempDB enum value. The TableType property value can be set at AOT > Data Dictionary > Tables >MyTempDBTable > Properties > TableType.
  • Regular - a standard
  •      physical table
  • InMemory - the type
  •      of temporary table which existed in the previous versions of Dynamics Ax.
  •      Such tables are held in memory and written to a local disk file once they
  •      grow beyond a certain point
  • TempDB - a new
  •      option in Ax 2012. They are "physical" temporary tables held in
  •      the SQL Server database.
The new TempDB tables operate in a similar manner to InMemory tables but support more features of standard physical tables:
  • More powerful
  •      joins with physical tables are possible, and are properly supported by the
  •      database
  • Can be
  •      per-company or global
  • Support for
  •      normal tts transactions
To create a new instance link (populating data/copying reference) from one table instance
variable to the other with Temporary type tables:

For more details on TempDB capabilities, Limitations, How to use, Its lifetime, please check here - http://msdn.microsoft.com/en-us/library/gg845661.aspx


Friday, 17 April 2015

Get Inventory Dimension


Given just the item Id, find or create the inventory dimension.


public InventDim GetInventoryDimension(ItemId _itemId)

{

    InventTable                 inventTable = inventTable::find(_itemId);    

    InventItemOrderSetupType    setupType   = InventItemOrderSetupType::Invent;    

    InventDim                   inventDim;   

 ;    

// Default Site    

inventDim.InventSiteId = inventTable.inventItemOrderSetupMap(setupType).inventSiteId(inventDim.InventSiteId, inventTable);    

// Default Location    

inventDim.InventLocationId  = inventTable.inventItemOrderSetupMap(setupType,                                                                   InventDim::findOrCreate(inventDim).InventDimId).inventLocationId(inventDim.InventLocationId,                                      inventTable, inventDim.InventSiteId);

    // Default ConfigId    

inventDim.ConfigId = inventTable.StandardConfigId;   

 // Find Default Item Dimension    

inventDim = InventDim::findOrCreate(inventDim);    

return inventDim;

}


Thursday, 12 March 2015

Attach a document



static void attachDoc(RefTableId _refTableId, RefRecId _refRecId, selectableDataArea _refCompanyId, 
                                       FileName    _name)
{
    DocuRef docuRef;
    DocuActionArchive archive;
    ;
    docuRef.clear();
    docuRef.RefRecId = _refRecId;
    docuRef.RefTableId = _refTableId;
    docuRef.RefCompanyId = _refCompanyId;
    docuRef.Name = _name;
    docuRef.TypeId = 'File';
    docuRef.insert();
    archive = new DocuActionArchive();
    archive.add(docuRef, _name);
}

Thursday, 19 February 2015

Query Build Range in Forms

Override the executeQuery() method the data source (on the Form/Data Sources/data source) to set the query. Note that the ranges are cleared before setting as they are persistent.

This example assumes that there is one datasource table, to make it easier;


public void executeQuery()
{  
    <variable definitions here>

    QueryBuildRange qbr;

    <dataNameOne> = element.design().controlName("<controlNameOne>").valueStr();
    <dataNameTwo> = str2enum(prodStatus,element.design().controlName("<controlNameTwo>").valueStr());
    <dataNameThree = str2enum(calcType,element.design().controlName("<controlNameThree>").valueStr());
    <dataNameFour> = str2enum(bomCalc,element.design().controlName("<controlNameFour>").valueStr());
    this.query().dataSourceTable(tableNum(<datasourceTable>)).clearRanges();
    qbr = this.query().dataSourceTable(tableNum(<datasourceTable>)).addRange(fieldNum(<datasourceTable>,<fieldNameOne>));
    qbr.value(<dataNameOne>);
    qbr = this.query().dataSourceTable(tableNum(<datasourceTable>)).addRange(fieldNum(<datasourceTable>,<fieldNameTwo>));
    qbr.value(strFmt("%1",<dataNameTwo>));
    qbr = this.query().dataSourceTable(tableNum(<datasourceTable>)).addRange(fieldNum(<datasourceTable>,<fieldNameThree>));
    qbr.value(strFmt("%1",<dataNameThree>));
    qbr = this.query().dataSourceTable(tableNum(<datasourceTable>)).addRange(fieldNum(<datasourceTable>,<fieldNameFour>));
    qbr.value(strFmt("%1",<dataNameFour>));
super();
}

Then change the trigger, in this case a click event, to run the execute the query above. Note the _ds extension, which denotes a datasource object.

void clicked()
{
    super();
    <datasourceTable>_ds.executeQuery();
}

This is not best practice, as you can build the range as a global object and add the values in the ranges in the executeQuery() method.


Tuesday, 17 February 2015

Product Attribute Value


Feed the method with an itemid and the attribute name and get back the value of the attribute (if it exists)


public str getProductAttribute(ItemId _itemId, Name _attributeName)
{
    inventTable                 InventTable;
    EcoResProductAttributeValue ecoResProductAttributeValue;
    EcoResAttribute             ecoResAttribute;
    EcoResValue                 ecoResValue;

     select InventTable where InventTable.itemid == _itemId
        join RecId from ecoResProductAttributeValue
        where ecoResProductAttributeValue.Product == InventTable.Product
            join Name from ecoResAttribute
            where ecoResProductAttributeValue.Attribute == ecoResAttribute.RecId &&
                  EcoResAttribute.Name == _attributeName
                join ecoResValue
                where ecoResValue.RecId == ecoResProductAttributeValue.Value;

    return ecoResValue.value();

}

Thursday, 11 December 2014

Get Inventory On Hand



static void InventoryOnHand(Args _args)
{

    InventDim inventDim;
    InventDimParm inventDimParm;
    InventOnHand inventOnHand;
    InventTable inventtable;
    ItemId itemId;
    ;

    itemId = '28-010020';

    inventDim.InventSiteId = "1";   // For site # 1
    inventDimParm.initFromInventDim(inventDim);

        inventOnHand = InventOnHand::newItemDim(itemId, inventDim, inventDimParm);
        if (inventOnHand.availPhysical())
        {
            info(strfmt("Item Id %1 Available Physical %2",
                inventtable.ItemId,inventOnHand.availPhysical()));
        }


}

Friday, 28 November 2014

Query Object Not Initialised On RDP


The error (in this case, printing the External Customer Account Statement)


Query object not initialised.
Stack trace
(C)\Classes\CustAccountStatementExtController\prePromptModifyContract - line 4
(C)\Classes\SrsReportRunController\startOperation - line 7
(C)\Classes\SrsPrintMgmtController\startOperation - line 14
(C)\Classes\SrsPrintMgmtFormLetterController\startOperation - line 14
(C)\Classes\CustAccountStatementExtController\main - line 6

The cause

[
    SRSReportQueryAttribute(queryStr(CustAccountStatementExtBaseData)),
    SRSReportParameterAttribute(classStr(CustAccountStatementExtContract))
]
public class CustAccountStatementExtDP extends SrsReportDataProviderPreProcess
{
    CustAccountStatementExtTmp custAccountStatementExtTmpNew;
    boolean giroCreated;
}

The Solution


In Visual Studio, select the dataset that the report uses (CustAccountStatementExtDS) and set the property 'Dynamic Filters' to false. This will cause the data map to be re-read.

Then rebuild and redeploy the report.









Thursday, 23 October 2014

Dynamic Selection in Form

Dynamic Selection in Form


1. On the Class Declaration of the form, add a query filter

    public class FormRun extends ObjectRun
   {
        QueryFilter queryFilterCustId;
    }

2. On the Data Sources ladder of the form, override the init method, note that it has the _ds extension

    public void init()
   {
        super();
        queryFilterCustId = WMPPickingLines_ds.query().addQueryFilter(WMPPickingLines_ds.queryBuildDataSource(),"CustAccount");

    }

3. Change the Control that will be used for the filter to have the property of AutoDeclaration set to Yes 

4. Override the modified method of the control to trigger the query

    public boolean modified()
   {
        boolean ret;
        WMPPickingLines_ds.executeQuery();
        return ret;
    }

5. On the Data Sources ladder of the form, override the executeQuery method, not that the controlName is required (in this case "CustomerId"). ValueStr is the method for getting a value from a drop-down.

public void executeQuery()
{

    QueryFilterCustId.value(element.design().controlName("CustomerId").valueStr());
    super();


Wednesday, 22 October 2014

Unretrieved

Unretrieved Data

If the data shown on a table based datasource is showing as 'unretrieved' then follow these steps;

1.- Recompile and restore original table. Check fields with Table explorer; if table explorer doesn't show your field(s) properly, anything based on that table won't either. So, if this fails, go to step 3

2.- Recompile and restore form. Generate incremental CIL. Check it. If fail, go to step 3

3.- Restart Ax client and retry steps 1+2. If fail, go to 4

4.- Restart AOS. Retry steps 1+2.

Monday, 20 October 2014

File Dialog


<URL> is the start folder


  FilePath  _selectedPath = @"<URL>";
  str _caption = "Test";
  boolean _showNewFolderButton;
  destinationFolder = WinAPI::browseForFolderDialog(_caption, _selectedPath, _showNewFolderButton);

Clearing Personalisation


Sometimes a personalisation of a screen can cause errors, the easiest way to clear any personalisation errors is to reset.


1.             In your Dynamics AX instance, you will want to navigate to ‘File’->Tools->’Options’
2.             Click on ‘Usage Data’ in the ‘Options’ form that opens up (top right menu)
3.             Click on the ‘Reset’ button in the ‘Usage Data’ form that opens up (bottom right)
4.             Click ‘Yes’. NOTE: This will only reset the usage data for your user, not anyone else’s

Monday, 6 October 2014

Multiline grid selection


When something is clicked, work through the grid to get just the selected lines (ticked or highlighted)

void clicked()
{
    CustPaymModeTable custPaymModeTablelocal;
    MultiSelectionHelper helper = MultiSelectionHelper::construct(); // construct the helper class
    helper.parmDatasource(custPaymModeTable_ds); // set the datasource of the helper
    custPaymModeTablelocal = helper.getFirst(); //get the first selected record of the helper

    while (custPaymModeTablelocal.RecId != 0)
    {
        info(custPaymModeTablelocal.PaymMode);
        custPaymModeTablelocal = helper.getNext(); //get the next selected record of the helper
    }
}


Friday, 3 October 2014

Current price of an item


public  PriceCur currentPrice(ItemId itemId, real qty, AccountNum accountNum )
{
    CustPriceGroup priceGroup;

    InventDim inventDim;
    InventTable inventTable;
    PriceDisc priceDisc;
    str unitId;
    Price price;
    ;
    itemId          = this.itemNumber;
    inventTable = InventTable::find(itemId);
    if(!inventTable)
    {
        return 0;
    }
    inventDim.initFromInventTable(inventTable);
    unitId = inventTable.salesUnitId();
    priceGroup   = CustTable::find(AccountNum).PriceGroup;
    priceDisc = new PriceDisc(ModuleInventPurchSales::Sales,ItemId,inventDim,unitId,today(),qty,accountNum);

    if (priceDisc.findPrice(priceGroup))
    {
        price = priceDisc.price();
    }
    else if (priceDisc.findItemPrice())
    {
        price = priceDisc.price();
    }

    return Price;
}

Or

priceDisc = new PriceDisc(ModuleInventPurchSales::Sales, itemId, InventDim::findOrCreateBlank(), prodBom.UnitId, systemdateget(), 1, custAccount, SalesTable.CurrencyCode);        

if (priceDisc.findPrice(custTable.PriceGroup))        
{        
    price = priceDisc.price();        
}        
else if (priceDisc.findItemPrice())        
{        
    price = priceDisc.price();        
}
        
salesPrice = priceDisc.price();



Thursday, 2 October 2014

XSLT - Take out Microsoft's in SEPA



<?xml version="1.0" encoding="UTF-8"?>
<!-- Version 0.1, 27-09-2014 -->
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:sepa="urn:iso:std:iso:20022:tech:xsd:pain.001.001.03">
  <xsl:template match="/">
    <xsl:copy-of select="//sepa:Document" />
  </xsl:template>
</xsl:stylesheet>

Ax2012 Parent/Child Linking In Grids


In the Data Sources of the Form, select the child table. In the data category of the child table, set the following;

  • JoinSource = <Parent table Name>
  • Link Type = delayed
  • Name = <Child table name>
  • Table = <Child table name>
Now as the data changes in the parent table grid, the data will change in the child grid.

NB : There has to be a relation set between the two data sources

Selective Data In Form Display



On the data sources section of the form ladder, under the data source, change the init method to select the data you require.

public void init()
{
    QueryBuildRange qryBuildRange;
    super();
    qryBuildRange = this.query().dataSourceName(this.name()).addRange(fieldNum(Addresses,AddressType));
    qryBuildRange.value(queryValue('D'));
    qryBuildRange = this.query().dataSourceName(this.name()).addRange(fieldNum(Addresses,dateCommited));
    qryBuildRange.value(queryValue(dateNull()));
    qryBuildRange.status(RangeStatus::Hidden);

}

The .Status being set to RangeStatus::Hidden stops the user over-ruling the selection manually.

Display Images In A Grid



  • Step 1 - Declare global variables in <yourform>.classdeclaration
public class FormRun extends ObjectRun
{
    Imagelist imageList;
    Image image;
}
  • Step 2 - Create Image List in <yourForm>.init
public void init()
{
    imageList = new ImageList(ImageList::smallIconWidth(), ImageList::smallIconHeight());
    image = new Image();
    image.loadResource(1030);  // tick icon
    imagelist.add(image);
    image.loadResource(927); // info icon
    imageList.add(image);
    image.loadResource(928); // warning icon
    imageList.add(image);
    image.loadResource(929); // red x icon
    imageList.add(image);

    super();
    
}

  • Step 3 - Create a window field on the grid
Set the AutoDeclaration of the window field to yes in the properties dialog
Load the image list to the window in <yourForm>.init after the super();

public void init()
{
    imageList = new ImageList(ImageList::smallIconWidth(), ImageList::smallIconHeight());
    image = new Image();
    image.loadResource(1030); 
    imagelist.add(image);
    image.loadResource(927);
    imageList.add(image);
    image.loadResource(928);
    imageList.add(image);
    image.loadResource(929);
    imageList.add(image);

    super();
    <windowField>.imageList(imageList);
}

  • Step 4 - Create method to set image on window field
On the table that is driving the grid, add a display method to set the picture in the window field.
The picture set is the data item in the array, not the icon number

public client server display int setIcon()
{
    #define.TickIcon(0)
    #define.InfoIcon(1)
    #define.WarningIcon(2)
    #define.StopIcon(3)

    
    if(!InventTable::find(this.itemNumber))
    {
        return #StopIcon;
    }
    else if(this.qtyOnHand() <= 0)
    {
        return #WarningIcon;
    }

    return #TickIcon;
}


  • Step 5 - Link window field to data
On the properties of the window field to point to the data method in step 4

DataSource  = <table in step 4>
DataMethod = <display method in step 4>