donderdag 16 december 2010

Adding dimension selection to report

When you create a report that shows dimensions, which code do you need to add to the report to allow the user to select which dimensions are displayed?
This is how the dialog looks like:

image

Code:

classDeclaration
    DialogGroup         dialogDimGroup;
    InventDimParm       inventDimParm;

initParmDefault
    inventDimParm.initItemDimAllGrp();
    if (InventParameters::find().MultiSiteActivated)
        inventDimParm.InventSiteIdFlag = true;

dialog
    dialog = super(_dialog);
    dialogDimGroup  = inventDimParm.addFieldsToDialog(dialog,"@SYS53654",true, false, "@SYS102592");

getFromDialog
    inventDimParm.getFromDialog(dialog,dialogDimGroup);

run
    InventDimCtrl::updateReportVisible(element, inventDimParm);
    super();

maandag 25 oktober 2010

Report design - Column Headings

When ie several customers are repeated on the same page, only the top of the page shows the column headers of that customer.
To have the column headers repeat for every customer:

Fetch method:
<...snip...>
while (qrCust.next())

{
    custTable = qrCust.get(TableNum(CustTable));
    element.executeBodyColumnHeadings(TableNum(CustTable));
    element.columnHeadings(TableNum(CustTable));
    element.send(custTable);
<...snip...>

 Setting property NoOfHeadingLines to 0 effectively removes the header, because no room is reserved for the headers anymore.

dinsdag 28 september 2010

Form - Adding Dimensions to Grid

See example in http://bmdax.blogspot.com/2010/09/adding-dimension-display-button-to-form.html

Form - Temporary Table datasource - loop over data

How to loop over the data in a datasource that is a Temporary Table?

See method \Forms\CustVendTransReorg\Methods\reorganize
This form has a DataSource "TmpCustVendTransReorg" that points to temporary table "TmpCustVendTransReorg"

void reorganize()
{
    TmpCustVendTransReorg   tmpCustVendTransReorgLocal;
    ;
    ttsbegin;
    for (tmpCustVendTransReorgLocal = tmpCustVendTransReorg_ds.getFirst();
         tmpCustVendTransReorgLocal;
         tmpCustVendTransReorgLocal = tmpCustVendTransReorg_ds.getNext())
    {
        custVendTransReorg.reorganize(tmpCustVendTransReorgLocal);
    }
    custVendTransReorg.end();
    ttscommit;
}

Adding Dimension Display Button to Form

You have to add the InventDim datasource to your form,
and add a display menuItemButton: \Menu Items\Display\InventDimParmFixed

To get the menuItemButton working, see the following methods in \Forms\InventItemBarcode
classDeclaration
updateDesign
init
inventDimSetupObject

Pay attention to the methods on both datasources
to guarantee a correct functioning of InventDim !!


For general Dimension editing, use InventDimCtrl_Frm_EditDimensions in the updateDesign method.
If you use i.e. InventDimCtrl_Frm_ItemBarcode, then the mustEnableField method on that class will only allow the ItemDimensions to be enabled.

vrijdag 17 september 2010

Bill of Material : BOMTable - BOMVersion - BOM

BOMTable
The BOMTable table contains all the bills of materials.
A bill of material is connected to a site and an item group.
For each bill of material the information whether it has been approved and by whom is stored.

BOMVersion
The BOMVersion table contains all bill of materials versions.
It connects to the BOMTable table in order to specify to which bill of materials the version refers
and it connects to the InventTable table in order to specify to which item the BOM version is assigned.
The BOMVersion table also connects to the InventDim table in order to specify a site for the BOM version.
Additionally the BOMVersion table stores information regarding approval and activation for each BOM version.

BOM
The BOM table contains bill of materials lines.
A BOM line connects to an item which is to be consumed and a BOM version to which the line applies.


TablePrimary Key
BOMTableBOMId
BOMVersionBOMId, ItemId, RecId
BOMBOMId, LineNum, RecId




BOMTable
|
|
^
BOMVersion
|
|
^
BOM


Sources:
BOMTable http://msdn.microsoft.com/en-us/library/aa672428.aspx
BOMVersion: http://msdn.microsoft.com/en-us/library/aa630970.aspx
BOM http://msdn.microsoft.com/en-US/library/aa844882.aspx

Axapta Filename Extension Naming Conventions

See http://daxcoder.blogspot.com/2008/06/axapta-filename-extension-naming.html

Ax*.123

The first letter represents the owner of the file.
A: Application
K: Kernel

The second letter represents the contents of the file.
L: Label
O: Object
T: Text
D: Developer Documentation
H: Online Help

The third letter represents the type of file.
D: Data
I: Index
C: Cache
T: Temporary

Get current DateTime

Getting current system datetime:

DateTimeUtil::getSystemDateTime();

Getting current date:
SystemDateGet();

DateTimeUtil::getSystemDateTime

Use DateTimeUtil::getSystemDateTime instead of systemDateGet or today.
The today function uses the date of the machine.
The systemDateGet method uses the system date in Microsoft Dynamics AX.
Only DateTimeUtil::getSystemDateTime compensates for the time zone of the user.
Source: http://msdn.microsoft.com/en-us/library/aa605545.aspx

dinsdag 14 september 2010

Post packing slip for a single sales line

public void aduAutoPostPackingSlip(SalesLine            _salesLine,
                                   ADUPostPackingSlip   _aduPostPackingSlip = NoYes::No)
{
    SalesFormLetter salesFormLetter;
    SalesLine       salesLineLocal;
    SalesLine       salesLineUpdate;
    ;

    if (_aduPostPackingSlip == NoYes::Yes && _salesLine)
    {
        ttsbegin;
        salesLineLocal = SalesLine::findRecId(_salesLine.RecId, true);

        if (salesLineLocal)
        {
            //On beforehand (before salesFormLetter.update) set all salesLines of the order, except your line, to sales+inventdelivernow = 0
            //so that you don't accidentally post the delivernow data of another user on you packingslip.
            //this overwrites the existing data of the other users, but this is no problem, since this is data that is usually processed immediately.
            update_recordset salesLineUpdate
                setting      SalesDeliverNow = 0,
                             InventDeliverNow = 0
                where        salesLineUpdate.SalesId == salesLineLocal.SalesId
                          && salesLineUpdate.RecId != salesLineLocal.RecId;

            salesLineLocal.SalesDeliverNow = salesLineLocal.SalesQty;
            salesLineLocal.setInventDeliverNow(); //inventDeliverNow must be filled, otherwise the posting will fail
            salesLineLocal.update();
        }
        ttscommit;

        salesFormLetter = SalesFormLetter::construct(DocumentStatus::PackingSlip);
        salesFormLetter.update(_salesLine.salesTable(),
                               systemdateget(),
                               SalesUpdate::DeliverNow,
                               AccountOrder::None,
                               NoYes::No,
                               NoYes::No);
    }
}


See also http://www.mibuso.com/forum/viewtopic.php?p=98005

vrijdag 10 september 2010

Dimension active?

Method on InventTable

public boolean isConfigIdActive()
{
    InventDimSearch dimSearch = new InventDimSearch();
    ;
    if (dimSearch.find(this.DimGroupId,fieldnum(InventDim,ConfigId)))
        return dimSearch.dimActive();
    return false;
}

dinsdag 31 augustus 2010

Retrieve label of EDT

How to get the label of an EDT?

See \Classes\DialogField\init
setting the label:
o.label(dictType.label(f));
with the dictType being:
dictType = new DictType(xtype);
and xtype being:
xtype = type >> 16;


Why do we have to right shift 16?
see \Classes\SysDictField\new
there you find a left shift 16
and global::fieldExt2Id does:
return (fieldExtId & 0xffff);

Also see http://www.eggheadcafe.com/software/aspnet/29515376/addfield-parameters.aspx
exerpt:
typeid() function gives you the id of a type. For eg:
you can try the following code in a job:
static void job1()
{;
print typeid(custAccount);
pause;
}
This will print out 6488075. In the AOT if you check up the ID of CustAcount
it is 99. But the above number that get from typeid function is a
combination of the ID in the AOT and the flag indicating that it is a user
type. The upper 16 bits contain the ID from the AOT (99)and the lower 16
bits contain the flag that it is a user type (11)
6488705 == (99 << 16 | 11)


Example:
public boolean validate(Object calledFrom)
{
    boolean ret;
    ToDate todate;
    dictType dictTypeLocal;
    ;


    ret = super(calledFrom);


    if (!toDate)
    {
        dictTypeLocal = new dictType(typeId(todate) >> 16);
        ret = checkfailed (strfmt("@SYS110217", dictTypeLocal.label()));


    }
    return ret;
}


Example - short version:
static void Job8(Args _args)
{
;
info(new dictType(typeId(ToDate) >> 16).label());
}



In Global Class:
public static LabelString aduTypeLabel(extendedTypeId _typeId)

{
;
return new dictType(_typeId >> 16).label();
}


Also (this example is a static method on ConfigTable):
static FieldLabel aduLabel()

{
;
return new SysDictType(extendedtypenum(ConfigId)).label();
}

modifiedField - previous value

Source: http://efreedom.com/Question/1-209764/Get-Previous-Field-Value-ModifiedField-Method-Dynamic-Ax-Table

Q: I would like to be able to perform some logic in the table.modifiedField method which compares the previous value of a field to the new value. How do I get to the previous value?

A: The record buffer as it was before any modifications is available through the this.orig() method.

Example:
public void modifiedField(fieldId _fieldId)

{
    super(_fieldId);
    info(strfmt("Field number %1 changed from %2 to %3",_fieldId,this.orig().(_fieldId),this.(_fieldId)));
}

donderdag 26 augustus 2010

How to create a production order with x++

Source: http://www.eggheadcafe.com/software/aspnet/31983659/how-to-create-a-production-order-with-x.aspx


static void CreateProductionOrder(Args _args)
{
ProdQtySched productionQty = 1;
ItemId productionItem = "AKU-1000";


ProdTable prodTable;
InventTable inventTable;
;
inventTable = InventTable::find(productionItem);


//Initialise the base values
prodTable.initValue();
prodTable.ItemId = inventTable.ItemId;
prodTable.initFromInventTable(inventTable);


prodTable.DlvDate = today();


prodTable.QtySched = productionQty;
prodTable.RemainInventPhysical = prodTable.QtySched;


//Set the active bom and route
prodTable.BOMId = BOMVersion::findActive(prodTable.ItemId,
prodTable.BOMDate,
prodTable.QtySched).BOMId;
prodTable.RouteId = RouteVersion::findActive(prodTable.ItemId,
prodTable.BOMDate,
prodTable.QtySched).RouteId;


prodTable.initRouteVersion();
prodTable.initBOMVersion();


//Use ProdTableType class to create the production order
prodTable.type().insert();
}

The code above set the active BOM / Route and also creates the InventTrans
data.

woensdag 25 augustus 2010

Form layout - Dropdown with descriptive field

How to properly layout a dropdown and an accompanying descriptive field.
Like this:




Example while creating an item:



This is how the AOT looks like:















































And this is how to set the properties on the group that contains the dropdown and its descriptive field:

vrijdag 20 augustus 2010

InventOnHand vs InventDimOnHand

See: http://www.dynamicsaxtraining.com/tips-tricks/inventonhand-vs-inventdimonhand

Axapta InventOnHand class is wrapper for InventSum table. Unique index for InventSum table is ItemId + InventDimId. In other word, this class is used to get on hand for item with specific dimension. For example, if you require getting on-hand qty for “Bottle” items that have “green” color, “standard” size and are stored in “22” warehouse, “1” Aisle, “4” Shelf then you use InventOnHand class.

But, if you require getting on-hand qty for warehouse location then InventOnHand class couldn’t help us. Because one location could contains different items. Or if you require get on-hand qty for pallet. In these cases InventDimOnHand class must be used. This class is used when you require on-hand qty for specific inventDim. InventDimOnHand сlass consists of InventDimOnHandMember classes. Each InventDimOnHandMember class contains information about Item, Dimensions and Qty

For example, see link.

donderdag 12 augustus 2010

Brief description of ways to close a form in AX

See http://kashperuk.blogspot.com/2010/07/tutorial-brief-description-of-ways-to.html

There are “only” 5 ways to close a form:
  • Close - close the form. Similar to the 'X' button.
  • CloseOK – close the form, and set the OK flag – called by the Commandbutton::Ok
  • CloseCancel – close the form, and set the Cancel flag – called by the Commandbutton::Cancel
  • CloseSelectRecord – close the lookup form, and set return record
  • CloseSelect – close the lookup form, and set return value
The below methods (note their names are in past-tense) are used to determine if or how a form was closed:
  • Closed – Returns true, if the form is no longer open
  • ClosedOK – Return true, if the form was closed by the user clicking ‘OK’
  • ClosedCancel – Returns true, if the form was closed by the user clicking ‘Cancel’
Finally, CanClose() is called before any of the close methods get called. If CanClose() returns false, the form is not allowed to close.

dinsdag 10 augustus 2010

Adding Find\Filter functionality on Display method

See http://matrishmangal.blogspot.com/2010/05/adding-findfilter-functionality-on.html

Inside SalesFormLetter class : ReArrange

See http://www.ksaelen.be/wordpress/2010/08/inside-salesformletter-class-rearrange/

num2str - decimals based on EDT

num2str function with the number of decimals based on the property of an Extended DataType

Code based on Mirko Bonello's work: http://dynamicsax-dev.blogspot.com/2009/02/getting-number-of-decimal-places-for.html


str num2strEdt(
    real _number,           //The real number to convert to a string
    int _character = 0,     //The minimum number of characters required in the text.
    ExtCodeValue _edt,      //The EDT to be used as basis for required number of decimal places.
    int _separator1 = 2,    //DecimalSeparator
    int _separator2 = 0)    //ThousandSeparator
{
    #DEFINE.AUTO('Auto')
    // http://www.rgagnon.com/pbdetails/pb-0181.html
    #DEFINE.LOCALE_USER_DEFAULT(1024)
    #DEFINE.LOCALE_ICURRDIGITS(25)

    #AOT
    #PROPERTIES
    #WinAPI // Used for regional settings

    TreeNode treeNode;
    int decimalPlaces;
    ;
    treeNode = infolog.findNode(#ExtendedDataTypesPath + '\\' + _edt);
    if (!treeNode)
        return strfmt("%1", _number);

    if (findproperty(treeNode.AOTgetProperties(),#PropertyNoOfDecimals) == #AUTO)
    {
      // get the number of decimals from the regional settings
      decimalPlaces = str2int((WinAPI::getLocaleInfo(#LOCALE_USER_DEFAULT,   #LOCALE_ICURRDIGITS)));
    }
    else
    {
      // get the number of decimals set by the developer in the property inspector
      decimalPlaces = str2int(findproperty(treeNode.AOTgetProperties(),#PropertyNoOfDecimals));
    }

    return num2str(_number, _character, decimalPlaces, _separator1, _separator2);
}

usage example:

info(strfmt("%1", num2strEdt(20.34, 0, (identifierstr(myEDT)))));

vrijdag 6 augustus 2010

Remember network passwords after reboot (Vista home premium)

Source: http://www.osnn.net/windows-desktop-systems/88876-vista-home-premium-remember-network-passwords-after-reboot.html


Hi, I was having the same issue as you, and I was using Vista Ultimate 64bits,

I got it to work by doing this:

1 - Reboot the computer
2 - After you log it will say your mapped drive couldn't be connected and stuff.... ok
3 - Go into Control Pannel, User Accounts, Network Passwords, and add the \\server\share, with the username & password you want
4 - Open My Computer, and access your mapped drive
5 - Reboot the computer

After this, all the time windows boots up it shall reconnect without that f*cking message again

Notice: Its important to do the steps as described because if you only add the server password into the Network Passwords but you don't use it, windows will not store it and will not remember the password the next time you boot.


Enjoy!!

donderdag 5 augustus 2010

Interactive Infolog messages: SysInfoAction

Ever wanted to doubleclick an infolog message and go straight to a form/field... ?

See:
http://www.axaptapedia.com/SysInfoAction_class
http://www.eggheadcafe.com/software/aspnet/35484927/sysinfoaction--open-form-with-parameters-from-infolog.aspx
http://alexvoy.blogspot.com/2008/04/sysinfoaction-and-infolog.html

Outputting the Name of an Enum element, instead of its Label

Example:

Enum WorkTimeControl has 3 elements:

Name - Label - EnumValue: (labels are in Dutch)
Open - Openen - 0
Closed - Afgesloten - 1
UseBasic - Basiskalender - 2


We define a variable:
WorkTimeControl workTimeControl = WorkTimeControl::Closed;

This code will render the Label of the element:
info(strfmt("%1", workTimeControl));
output = Afgesloten

This code will render the Name of the element:
info(strfmt("%1", enum2symbol(enumnum(WorkTimeControl), workTimeControl)));
output = Closed

WorkCalendarDate::findDate

A new useful method on Table WorkCalendarDate:

\Data Dictionary\Tables\WorkCalendarDate\Methods\findDate


/// <summary>
/// Searches the nth day of type _workTimeControl, forward or backwards from _startDate
/// </summary>
/// <param name="_calendarId">
/// The calendar to use to look for open days
/// </param>
/// <param name="_lookAheadDays">
/// Search for the nth day (default 0)
/// positive value = search forward
/// negative value = search backwards
/// </param>
/// <param name="_startDate">
/// Start looking from this date (default: system date)
/// </param>
/// <param name="_forceReturnDate">
/// What should be returned if no date was found?
/// true  = _startdate is returned
/// false = no date is returned (default)
/// </param>
/// <param name="_workTimeControl">
/// Look for which type of days?
/// Open (default)
/// Closed
/// UseBasic
/// </param>
/// <returns>
/// The nth day (_lookAheadDays) of type _workTimeControl, starting from _startDate
/// </returns>
/// <remarks>
/// location = \Data Dictionary\Tables\WorkCalendarDate\Methods\findDate
/// </remarks>
static TransDate findDate(
    CalendarId          _calendarId,
    Counter             _lookAheadDays   = 0,
    TransDate           _startDate          = systemDateGet(),
    boolean             _forceReturnDate    = false,
    WorkTimeControl     _workTimeControl    = WorkTimeControl::Open)
{
    WorkCalendarDate workCalendarDate;
    Counter          counter = 0;
    ;
    
    if (_lookAheadDays >= 0) //search forward from _startDate
    {
        while
        select workCalendarDate
            order by TransDate
        where workCalendarDate.CalendarId      == _calendarId
           && workCalendarDate.TransDate       >= _startDate
           && workCalendarDate.WorkTimeControl == _workTimeControl
        {
            if (counter >= _lookAheadDays)
                return workCalendarDate.TransDate;
            counter++;
        }
    }
    else                        //search backward from _startDate
    {
        while
        select workCalendarDate
            order by TransDate DESC
        where workCalendarDate.CalendarId      == _calendarId
           && workCalendarDate.TransDate       <= _startDate
           && workCalendarDate.WorkTimeControl == _workTimeControl
        {
            if (counter <= _lookAheadDays)
                return workCalendarDate.TransDate;
            counter--;
        }
    }

    //no date found
    if (_forceReturnDate)
        return _startDate;
    else
        return dateNull();
}


Example Job:

static void WorkCalendarDate_findDate(Args _args)
{
    CalendarId          calendarId;
    TransDate           startDate;
    TransDate           nextDate;
    Counter             offsetDays;
    Name                startDateName;
    Name                nextDateName;
    boolean             forceReturnDate;
    WorkTimeControl     workTimeControl;
    ;
    //Example 1:
    //Search 3 days forward in calendar STD, starting from 05 AUG 2010 (thursday), looking for Open days
    calendarId      = "STD";
    startDate       = str2date("05/08/2010",123);
    offsetDays      = 3;
    forceReturnDate = false;
    workTimeControl = WorkTimeControl::Open;
    
    nextDate        = WorkCalendarDate::findDate(
                        calendarId, 
                        offsetDays,
                        startDate, 
                        forceReturnDate, 
                        workTimeControl);
                        
    startDateName   = dayname(dayofwk(startDate));
    nextDateName    = dayname(dayofwk(nextDate));
    global::enum2int(
    info(strfmt("Search %1 days, starting from %2(%3), with an offset of %4 days = %5(%6)",
            workTimeControl,
            startDate,
            startDateName,
            offsetDays,
            nextDate,
            nextDateName)));
    //output:
    //Search Openen days, starting from 5/08/2010(thursday), with an offset of 3 days = 10/08/2010(tuesday)

    //Example 2:
    //Search 3 days backwards in calendar STD, starting from 11 AUG 2010 (wednesday), looking for Open days
    calendarId      = "STD";
    startDate       = str2date("11/08/2010",123);
    offsetDays      = -3;
    forceReturnDate = false;
    workTimeControl = WorkTimeControl::Open;
    
    nextDate        = WorkCalendarDate::findDate(
                        calendarId, 
                        offsetDays,
                        startDate, 
                        forceReturnDate, 
                        workTimeControl);
                        
    startDateName   = dayname(dayofwk(startDate));
    nextDateName    = dayname(dayofwk(nextDate));
    global::enum2int(
    info(strfmt("Search %1 days, starting from %2(%3), with an offset of %4 days = %5(%6)",
            workTimeControl,
            startDate,
            startDateName,
            offsetDays,
            nextDate,
            nextDateName)));
    //output:
    //Search Openen days, starting from 11/08/2010(wednesday), with an offset of -3 days = 6/08/2010(friday)
}

donderdag 15 juli 2010

Disable dialog button


public void disableDialogOkButton(DialogRunbase dialog)
{
    ;
    dialog.dialogForm().formRun().design().controlName('OkButton').enabled(false);
}

maandag 12 juli 2010

Forms Tutorial_Resources and SysImageResources

Source: http://www.artofcreation.be/2010/06/28/forms-tutorial_resources-and-sysimageresources/

Depending on what AX version you are using, the name of the form is different:

Dynamics AX 4: form Tutorial_Resources
Dynamics AX 2009: form SysImageResources

donderdag 8 juli 2010

Form caching of frequently called non-display method

To cache a method on a form, it must be a display method.
How do we cache a method "myMethod" that is NOT a display method?
We cannot use cacheAddMethod.

In general:
Classdeclaration:
Create a map that with
key = the recid of the current record of the form datasource
and value = the return value of "myMethod"

ExecuteQuery:
Empty your map.
suppose "myMethod" returns a string:

myMap = new Map(Types::Integer, Types::String)


[location where method is called]:
Look in the map if the recid exists.
If it exists, return that value.
If not exists, execute "myMethod", store in myMap with current recId, and return that value.

ReRead:
Remove the current recid from the map, so that the method will be re-executed next time.
 

woensdag 30 juni 2010

Get objects modified in certain layer

Source: http://dynamicsuser.net/forums/p/17111/79705.aspx


Step 1: Create a new Project

Step 2: Double Click on the new project to open it

Step 3: Click on filter icon

Step 4: Under Grouping choose AOT

Step 5: Click on Select

Step 6: For utilLevel choose the layer you want to see what was modified

Step 7: Click OK to close search window

Step 8: Click OK on project Filter


This will add all objects to your new project that were modified in the specified layer.

woensdag 19 mei 2010

Access Form Control without Autodeclaration

Source: http://www.artofcreation.be/2010/04/13/odd-code-in-ax-2-control-enum-on-forms/

Example: disable control named "ButtonFunctions".
element.control(Control::ButtonFunctions).enabled(false);

Also: http://dynamicsuser.net/forums/p/43984/222374.aspx
Enable/disable form field in datasource field modified method:

public void modified()
{
    super();

     if (CEMPAbsence.Accrual == Accrual::Yes )
    {
        CEMPAbsence_ds.object(fieldNum(CEMPAbsence,AccrualType)).enabled(True);
    }

dinsdag 4 mei 2010

Detecting default values

Use the int prmIsDefault(anytype argument) function.

See: http://sysdictcoder.com/blog/detecting-default-values/

Example:
static void ADU_BMS_prmIsDefault(Args _args)
{
    container   myCon;
                
    str prmIsDefaultTest(container _con = myCon)
    {
        ;
        if (prmIsDefault(_con))
        {
            return "parameter was not provided, using default";
        }
        else
        {
            return "parameter was provided, using provided parameter";
        }
    }
    ;

    info(strfmt("test without parameter: %1", prmIsDefaultTest()));
    info(strfmt("test with parameter: %1", prmIsDefaultTest(myCon)));

    // Infolog output:
    // test without parameter: parameter was not provided, using default
    // test with parameter: parameter was provided, using provided parameter
}

Creating a user manual or step-by-step reproduction of a problem - Windows 7

Ever struggled with screencaptures and MS Word to create a manual, or step-by-step reproductions of a problem?
Try this: start – run – PSR (Windows 7 only)
It’s like the Task Recorder in AX.

InventOnHand vs InventDimOnHand

source: http://axforum.info/forums/showthread.php?t=31363
and http://www.dynamicsaxtraining.com/tips-tricks/inventonhand-vs-inventdimonhand

What's the difference between InventOnHand and InventDimOnHand classes and in what cases they must be used?

Axapta InventOnHand class is wrapper for InventSum table. Unique index for InventSum table is ItemId + InventDimId. In other word, this class is used to get on hand for item with specific dimension. For example, if you require getting on-hand qty for “Bottle” items that have “green” color, “standard” size and are stored in “22” warehouse, “1” Aisle, “4” Shelf then you use InventOnHand class.

But, if you require getting on-hand qty for warehouse location then InventOnHand class couldn’t help us. Because one location could contains different items. Or if you require get on-hand qty for pallet. In these cases InventDimOnHand class must be used. This class is used when you require on-hand qty for specific inventDim. InventDimOnHand сlass consists of InventDimOnHandMember classes. Each InventDimOnHandMember class contains information about Item, Dimensions and Qty.

X++ Code to find OnHand Stock for Item

See http://learnax.blogspot.com/2010/01/x-code-to-find-onhand-stock-for-item.html

vrijdag 9 april 2010

Form Radiobutton values

Ever wondered where the values in a Form RadioButton come from?
The properties of the Radiobutton AOT-object only show one value.
Where's the rest?

As an example, I'm using the DEV_SysTableBrowser, which can be found on Axaptapedia.

The AOT of that project - folded out to the radiobutton - looks like this:


When we run the form, it looks like this:



The radiobutton is grouped in the "Show fields" group, and has 3 values:
  • All fields
  • Field group
  • User setup
The properties of that radiobutton are:


So, we know that there are 3 items, and that item 1 has the value (Text) "All fields".
But where are the other 2 values?
Actually, they are also there, and here's how to reveal them:

Step 1: Select the Item property



Step 2: Enter the number of the item for which you want to see or set the value


Step 3: Tab out of the Item property


Notice that the Text property still says "All fields" (Item 1), while we have indicated that we want to see Item 2.
This is due to a refresh issue.
The Text doesn't get refreshed when the Item property changes.
We'll have to do the refresh ourselves.

Step 4: Select the Text property


Step 5: Tab out of the Text property


And there you have the value of the second item :)

These values are actually stored in an array.
This is the part of the .XPO file that defines this RadioButton.
See the highlighted part.




donderdag 8 april 2010

Lookups / Lookup Form

Lookups can be defined on the EDT, property "FormHelp".
see http://www.axaptapedia.com/index.php/Lookup_Form

Else, there can be an automatic or a manual lookup.
see http://www.axaptapedia.com/Lookups



Write your own lookup (override lookup method)
This is an example of an ItemId lookup:
public void lookup()
{
    SysTableLookup        sysTableLookup = SysTableLookup::newParameters(tablenum(InventTable), this);
    Query                 query = new Query();
    QueryBuildDataSource  dataSource = query.addDataSource(tablenum(InventTable));
    QueryBuildRange       range = dataSource.addRange(fieldnum(InventTable, ADUSawingItemId));
    ;

    range.value(SysQuery::valueNotEmptyString());
    sysTableLookup.addLookupfield(fieldnum(InventTable, ItemId));
    sysTableLookup.addLookupfield(fieldnum(InventTable, ItemName));
    sysTableLookup.parmQuery(query);
    sysTableLookup.performFormLookup();
}



Adding custom labels to the dropdown fields
After you add the field, you add the custom label:
public void lookup()
{
    SysTableLookup        sysTableLookup = SysTableLookup::newParameters(tablenum(ConfigTable), this);
    Query                 query = new Query();
    QueryBuildDataSource  dataSource = query.addDataSource(tablenum(ConfigTable));
    QueryBuildRange       range = dataSource.addRange(fieldnum(ConfigTable, ItemId));
    ;

    range.value(queryValue(aduSawingCodeItemId));
    sysTableLookup.addLookupfield(fieldnum(ConfigTable, ConfigId));
    sysTableLookup.setLabel("Field ConfigId");
    sysTableLookup.addLookupfield(fieldnum(ConfigTable, Name));
    sysTableLookup.setLabel("Field Name");

    sysTableLookup.parmQuery(query);
    sysTableLookup.performFormLookup();
}



Filtering CongfigId lookup
I encountered a situation where I had a form with
- an unbound StringEdit control of ExtendedDataType ItemId
- an unbound StringEdit control of ExtnededDataType ConfigId
The dropdown on the ConfigId seemed to always be filtered in such a way
that it always showed only the ConfigId's for the ItemId in the ItemId field.
After some debugging, I found out that it was because I had a method called itemId on my form.
Apparently, in the ConfigIdLookup form, the executeQuery of the ConfigTable datasource says:
void executeQuery()
{
    QueryBuildDataSource    qbds = this.query().dataSourceTable(tablenum(ConfigTable));
    fieldId                 fieldId;
;
    if(!ctrlTabPageConfig.isActivePage())
    {
        return;
    }

    fieldId = inventDimFormSetup.callerItemFieldId();
    if (fieldId)
    {
        qbds.addDynalink(
            fieldnum(ConfigTable,ItemId),
            inventDimFormSetup.callerItemIdFormDatasource().cursor(),
            fieldId);
    }
    else
    {
        qbds.addRange(fieldnum(ConfigTable,ItemId)).value(inventDimFormSetup.callerItemId());
    }

    super();
}


and inventDimFormSetup.callerItemId() is
ItemId callerItemId()
{
    if (! callerHasItemId)
        return '';

    if (callerItemIdMethod)
        return callingElement.args().caller().itemId();
    else
        return this.callerItemIdFormDatasource().cursor().(this.callerItemFieldId());
}


So, the ConfigIdLookup form effectively called my itemId method to filter it's ConfigTable datasource!

maandag 29 maart 2010

PickingJournal: create and show form

void createPickingJournal_bms()

{
ProdJournalTable jourTable; //the Journal Header
ProdJournalTableData jourTableData; //the data to put in the Journal Header

Args args = new Args();
MenuFunction menuFunction;
FormRun formRun;
;

//initialize the Journal Header
jourTableData = JournalTableData::newTable(jourTable);
jourTable.JournalId = jourTableData.nextJournalId();
jourTable.JournalType = ProdJournalType::Picklist;
jourTable.JournalNameId = jourTableData.journalStatic().standardJournalNameId(jourTable.JournalType);
jourTableData.initFromJournalName(ProdJournalName::find(jourTable.JournalNameId));
jourTable.ProdId = this.aduPosPieceProdId(projId, #ADUPosPiece);
jourTable.DetailSummary = DetailSummary::Summary ;
jourTable.VoucherDraw = JournalVoucherDraw::Post;
jourTable.insert();


//show the Journal Header form
args.record(jourTable);
formRun = new MenuFunction(menuitemdisplaystr(ProdJournalTablePickList), MenuItemType::Display).create(args);
formRun.run();

//show the Journal Lines form
args.caller(formRun); //make sure only the Journal Lines from our Journal Header are shown
new MenuFunction(menuItemDisplayStr(ProdJournalBOM), MenuItemType::Display).run(args);
}

Args and the Axapta Construction Pattern

http://blogs.msdn.com/davidferguson/archive/2006/03/28/563150.aspx

woensdag 6 januari 2010

Infolog error - see callstack

When an infolog appears with an error message,
you would like to see the callstack to know where the error originated from.
This can be done by setting a breakpoint in \Classes\Info\add.