2013年4月10日星期三

How to transfer CLR Array to AX List

If you use any amount of CLRInterop programming in Dynamics AX you have probably at some point or another needed to use X++ code to interact with an array object passed back from a bit of CLR code.
I created the following static class to ease the process of converting a CLRArray object into a X++ style list object.
It only requires two parameters to be passed for the conversion:
  1. CLRObject _input – this is the object array you need converted.
  2. Types _type – this is the X++ type that you want the individual CLR objects converted to.
static List CLRArrayToList(CLRObject _input, Types _type)
{
    System.Collections.ArrayList list;

    List l = new List(_type);
    AnyType x;

    int cnt, i;
;
    new InteropPermission(InteropKind::ClrInterop).assert();
    try
    {
        list = new CLRObject('System.Collections.ArrayList',_input);

        cnt = list.get_Count();

        for(i = 0; i < cnt;i++)
        {
            x = CLRInterop::getAnyTypeForObject(list.get_Item(i));
            l.addEnd(x);
        }
    }
    catch
    {
        l = new List(_type);
    }
    CodeAccessPermission::revertAssert();

    return l;
}
To utilize the method add it to a class (we will name it QuickTools for this example) and call it like this:
CLRObject   bytes;
List        intList = new List(Types::Integer);

// Some code here to populate the bytes object with numeric values
intList = QuickTools::CLRArrayToList(bytes,Types::Integer);
// intList now contains the values stored in bytes
Using this example and a bit of creativity you could create a static method to perform the opposite action and convert a X++ list into a CLR Array object

2013年3月21日星期四

How to updated the caller Form/DataSource

If we need to notify events to the caller form we can try this pattern:

In the child form, make a method named updateCaller and invoke it when you want to notify the parent:

void updateCaller()
{
    Common common;
    Object dataSource;
    Object caller;
    ;

    //-----------------------------------
    //We are notifying using the dataSource
    common = element.args().record();
    if (common
        && common.isFormDataSource()
        && formDataSourceHasMethod(common.dataSource(), identifierstr(SomethingWasHappend)))
    {
        dataSource = common.dataSource();
        dataSource.SomethingWasHappend();
    }
    //-----------------------------------

    //-----------------------------------
    //We are notifying using the form
    caller = element.args().caller();
    if (caller
        && classidget(caller) == classnum(SysSetupFormRun)
        && formHasMethod(caller, identifierstr(SomethingWasHappend)))
    {
        caller.SomethingWasHappend();
    }
    //-----------------------------------
}

Implement the handling method in the parent form:

void SomethingWasHappend()
{
    ;
    info("Something was happend (Form)");
}

Or if you prefer, in the DataSource:

void SomethingWasHappend()
{
    ;
    info("Something was happend (DataSource)");
}

Invoking it from a simple button inside the child form:

void clicked()
{
    super();

    element.updateCaller();
}


See a pratical sample in the form MarkupTrans (DataSource -> method write).

2013年3月17日星期日

How to get Financial Dimensions value 2

static void THK_7519_getFinDimension(Args _args)
{   
    void getDimensionValue(RecId _recIdFinDimension)
    {
        CustTable                         custTable = CustTable::find("1101");
        DimensionAttributeValueSetStorage dimStorage;
        Counter i;

        dimStorage = DimensionAttributeValueSetStorage::find(_recIdFinDimension);//custTable.DefaultDimension);
        setPrefix(strFmt("RecId :%1",_recIdFinDimension));
        for (i=1 ; i<= dimStorage.elements() ; i++)
        {
            info(strFmt("%1,%2 = %3",i,DimensionAttribute::find(dimStorage.getAttributeByIndex(i)).Name,
                                   dimStorage.getDisplayValueByIndex(i)));
        }
       
    }
    ;
    getDimensionValue(22565430233);
}

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

static void THK_7519_getFinDimensionByName(Args _args)
{

    DimensionValue getDimensionValue(RefRecID _dimensionSetRecID,Name _attributeName = "Shop_HK")
{
    DimensionAttributeValueSet      dimAttrValueSet;
    DimensionAttributeValueSetItem  dimAttrValueSetItem;
    DimensionAttributeValue         dimAttrValue;
    DimensionAttribute              dimAttribute;
;
    dimAttrValueSet = DimensionAttributeValueSet::find(_dimensionSetRecID);

    select firstOnly dimAttrValueSetItem
        where   dimAttrValueSetItem.DimensionAttributeValueSet      == dimAttrValueSet.RecId
    join dimAttrValue
        where   dimAttrValue.RecId                                  == dimAttrValueSetItem.DimensionAttributeValue
    join dimAttribute
        where   dimAttribute.RecId                                  == dimAttrValue.DimensionAttribute
        &&      dimAttribute.Name                                   == _attributeName;

    return dimAttrValue.getValue();
}
    ;
    info(getDimensionValue(22565430233));
}

How to force using code printing ranges on report


Business Requirement: printing ranges on a report

This code will be used for print the report ranges in the page header, like if you mark the "Print ranges" checkbox of report print options:

Printing the ranges with which a Report has been executed is only available for a AutoDesignSpecs Design of a Report.


 

Unfortunately this option is not available for Reports having a Generated Design.

Solution: override executeSection method in PageHeader


You can print the ranges with some X++ code modifications in your report.

  1. Add a PageHeader control to the Report, if there isn’t yet such a control in your Report.
  2. Override the executeSection method of the PageHeader control as follows:
    
    public void executeSection()
    {
      // BGN Printing ranges
      SysReportRun locSysReportRun;
      ;
      //END
    
      super();
    
      // BGN Printing ranges
      locSysReportRun = element;
      locSysReportRun.printRanges(true);
      SysReportRun::executePrintRangeSection(element);
      // END
    }

2013年3月14日星期四

How to using code to control the report printer paper Orientation

public void run()
{
super();
element.printJobSettings().fitToPage(false);
element.printJobSettings().paperOrientation(PrinterOrientation::Landscape);
}

How to pass temp table from Form to report

method 1
dierct pass element from the form's tmptable datasource to the report's datasource on queryrun.setCuror.

public boolean fetch()
{
queryRun qr;
FormDataSource formDataSource;
;

qr = new QueryRun(this.query());

if (qr.prompt())
{
formDataSource = element.args().record().dataSource();
qr.setCursor(formDataSource.cursor(),1);

while (qr.next())
{
tmpAssetConsumptionProposal = qr.get(tableNum(TmpAssetConsumptionProposal));
this.send(tmpAssetConsumptionProposal);
}
return true;
}

return false;
}
-------------------------------------------------------------
using pass paramters element to report
RecordSortedList sortedList1;
Args args;
;

args = new Args();
sortedList1= new recordSortedList(tableNum(table1));
while select table1
{
sortedList1.ins(table1)
}
args.caller(element);
args.object(sortedList1);
new MenuFunction(…).run(args); //pass to report


on report
Table1 table1;
recordSortedList sortedList1;
;
sortedList1 = element.args().object();
if (sortedList1.first(table1))
print table1.field1;
while(sortedList1.next(table1))
{
print table1.field1;
}

2013年3月4日星期一

How to using code to retrieve dimension value

The following code snippet will return a specific financial dimension value attached to a record. It can be used for example to retrieve the cost center attached to a customer.

DimensionValue getDimensionValue(RefRecID dimensionSetRecID,Name attributeName) 
{
    
    DimensionAttributeValueSet      dimAttrValueSet;
    DimensionAttributeValueSetItem  dimAttrValueSetItem;
    DimensionAttributeValue         dimAttrValue;
    DimensionAttribute              dimAttribute;
    
    dimAttrValueSet = DimensionAttributeValueSet::find(dimensionSetRecID);
    
    select dimAttrValueSetItem
        where   dimAttrValueSetItem.DimensionAttributeValueSet      == dimAttrValueSet.RecId
    join dimAttrValue    
        where   dimAttrValue.RecId                                  == dimAttrValueSetItem.DimensionAttributeValue
    join dimAttribute        
        where   dimAttribute.RecId                                  == dimAttrValue.DimensionAttribute
        &&      dimAttribute.Name                                   == attributeName;
    
    return dimAttrValue.getValue();        
}  
static void THK_7519_defaultDimension(Args _args)
{
    VendTable                       vendTable;
    DimensionAttributeValueSet      dimAttrValueSet;
    DimensionAttributeValueSetItem  dimAttrValueSetItem;
    DimensionAttributeValue         dimAttrValue;
    DimensionAttribute              dimAttr;
    Common                          dimensionValueEntity;
    ;
    vendTable = VendTable::find('22222');
    dimAttrValueSet = DimensionAttributeValueSet::find(vendTable.DefaultDimension);
    while select dimAttrValueSetItem
        where   dimAttrValueSetItem.DimensionAttributeValueSet   == dimAttrValueSet.RecId
    {
        dimAttrValue        = DimensionAttributeValue::find(dimAttrValueSetItem.DimensionAttributeValue);
        dimAttr             = DimensionAttribute::find(dimAttrValue.DimensionAttribute);
        info(strFmt("%1,%2,%3",dimAttr.Name, dimAttrValue.getValue(), dimAttrValue.getName()));
    }
}

And to retrieve the default cost center attached to a customer:

info(getDimensionValue(CustTable::find('1101').DefaultDimension,'CostCenter'));

This code shows the general table relations, but you're better off using the DimensionStorage class/API to read and update individual dimension values.

The following code shows how to check all customers with a specific cost center value:

static void ScanRecordsByDimensionValue(Args _args)
{

    // Check all customers that have a specific value for cost centre.    
    
    Name                            attrName = 'CostCenter';
    DimensionValue                  dimValue = 'OU_3566';
    
    CustTable                       custTable;

    DimensionAttributeValueSet      dimAttrValueSet;
    DimensionAttributeValueSetItem  dimAttrValueSetItem;
    DimensionAttributeValue         dimAttrValue;
    DimensionAttribute              dimAttribute;
    ;

    dimAttribute    = DimensionAttribute::findByName('CostCenter');
    dimAttrValueSet = DimensionAttributeValueSet::find(custTable.DefaultDimension);

    while select custTable
    join dimAttrValueSetItem
        where   dimAttrValueSetItem.DimensionAttributeValueSet      == custTable.DefaultDimension
        &&      dimAttrValueSetItem.DisplayValue                    == dimValue
    join dimAttrValue
        where   dimAttrValue.RecId                                  == dimAttrValueSetItem.DimensionAttributeValue
        &&      dimAttrValue.DimensionAttribute                     == dimAttribute.RecId
    {        
        info(custTable.AccountNum);                       
    }   

}