Thursday, December 25, 2008

成本计算的问题



成本计算的问题

标准成本5块钱,你卖6块则赚,卖4快则亏。
可能很多成品(finished goods) 的物料成本(item's costs)是暂估出来的,只有等月结之后成本才比较准确。另外,似乎ax不能把期间的营运费用摊分到销售,所以,到底是赚还是亏,需要知道成品的真实成本和对于的期间费用之和,才比较明确。



Financial inventory以及physical inventory的区别?

  • Financial Inventory是有发票的出入库 (采购和销售订单中:Invoice属于financial inventory)
    为了进行帐面处理

  • Physical inventory是没有发票出入库的( physical inventory类似于暂估金额,但它包含了采购和销售两部分没有发票的。For eg, 采购和销售订单中:Packing slip属于physical inventory)
    为了仓库管理,比如安排仓库的收发等

Wednesday, December 24, 2008

Nice DAX blogs to read about:

  1. Cesar de la Torre - BLOG (A great Architect Evangelist, with deep technical and development knowledge in c#, AX, CRM)

    http://blogs.msdn.com/cesardelatorre

    Great article to read about .net business connector:
    Using Dynamics 'AX .NET Business Connector' to access Dynamics AX 4.0 from .NET world
    http://blogs.msdn.com/cesardelatorre/archive/2007/07/06/accessing-dynamics-ax-4-0-from-net-using-the-dynamics-ax-net-business-connector.aspx

  2. Developing adapters using WCF
    http://blogs.msdn.com/sonuarora/archive/2007/03/26/about-wcf-lob-adapter-sdk.aspx

AOS service fails to start

Often time, we might get the annoying error message saying 'AOS service fails to start with error code specific 100. '  (Unable to start the AOS services).

To make sure to have smooth installation of the AX, it is recommend that :
1. Create the AX database by using the AX setup program, or
2. Create this login manually and grant it access to the database. 

sp_grantlogin and sp_grantdbaccess will do the trick :

EXEC sp_grantlogin 'NT AUTHORITY\NETWORK SERVICE' 
EXEC sp_grantdbaccess 'NT AUTHORITY\NETWORK SERVICE', 'Your_Axapta_DB' 

In some of the weired cases, it was cases that it was nessesary to create a login for the computer hosting the AOS service, such as MACHINE_NAME$ on the SQL server. 

NOTE: Make sure that the MACHINE_NAME$ on the SQL server is the name where your AX application is installed.
NOTE2: Do remember to grant permission to createserversessions and createusersessions as well.

Tuesday, December 23, 2008

Change the combobox options in RunbaseBatch class

** This article is forwarded from Fred Shen's blog **

RunbaseBatch class supplies some features, including batch processing, pack/unpack and progress bar etc. On the other hand, we get this request frequently, usually to modify the properties of dialog control in runtime.

For example, we add a combobox control on the dialog, but the options of combobox can't be predefined. Well, the combobox can be very conveniently filled with field lists if you are using a form, while the combobox's selection lists in RunbaseBatch is determined by :

Enum values (dialog.addField(TypeId(someEnum) ).
How can we accomplish this?

First option, change the eunm's values dynamically? Oh, terrible, this means that Axapta needs to add, edit and save AOT node, then synchronize in run time.

Second option, use Form instead of RunbaseBatch class, if you want this Form to be savable and batchable, you need to manually to add some codes on it.

Well, it is no point to do that if there is any other work around.
There is a third option, and I believe this should do the trick.

Let's start with a simple RunBaseBatch class and a simple scenario, dynamically adding printers' name to the combobox:
  1. Define a BaseEnum called TestRunBaseDialog (ensure that the style property is set to combo box), and create an element for it which is called TestComboboxElement.
  2. Then we can set the TestComboboxElement's label as "Select a printer".Now we can create the RunbaseBatch class from scratch.


Declare the classclass
TestAddComboOptionsInRunbase extends runbasebatch
{
int printerName;
DialogField dialogPrinter;
FormComboboxControl controlCombobox;
#define.CurrentVersion(1)
#localmacro.CurrentList printerName #endmacro
}

Pack the value
public container pack()
{
return [#CurrentVersion, #CurrentList];
}


Unpack the value
public boolean unpack(container packedClass)
{
boolean _ret;
Integer _version = runbase::getVersion(packedClass);

switch (_version)
{
case #CurrentVersion:
[_version, #CurrentList] = packedClass;
break;
default:
_ret = false;
}
return _ret;
}


Design the dialog, we need to activate the dialogSelectCtrl method. And please notice that, there should be some other dialog control to be added before dialogPrinter.

The reason is that after the Dialog form has been built, the selected control can not be the dialogPrinter, otherwise, we can't implement the dialogSelectCtrl method when we look up the options in dialogPrintercombobox. So we add a Text before adding combobox.

protected Object dialog(DialogRunbase _dialog, boolean _forceOnClient)
{
DialogRunBase dialog; ;
controlCombobox = new FormComboboxControl();
dialog = super(_dialog, _forceOnClient);
dialog.caption("Dialog for printer option");
dialog.addText("Choose Printer");
dialogPrinter = dialog.addField(typeId(TestRunBaseDialog));
dialog.allowUpdateOnSelectCtrl(true);
return dialog;
}


Okey, we can define how to change the selection list in the combobox now.

public void dialogSelectCtrl()
{
controlCombobox = dialogPrinter.fieldControl();
controlCombobox.clear();
controlCombobox.add("Printer One");
controlCombobox.add("Printer Two");
controlCombobox.add("Printer three");
//… more printers here
}


Process your business logic in run()

void run()
{
;

switch (printerName)
{
case 0:
info ("You are choosing printer one.");
break;

case 1: info ("You are choosing printer two.");
break;

case 2: info ("You are choosing printer three.");
break;
}
//Put your logic here
}


Finally, put the codes to Main()

static void main(Args _args)
{
TestAddComboOptionsInRunbase testRunBase = new TestAddComboOptionsInRunbase ();
;

if (testRunBase.prompt())
{
testRunBase.run();
}
}


The reason why we use dialogSelectCtrl() is that the dialog control in RunbaseBatch class is not built before the prompt method, the dialogPrinter is still the object of FormBuildControl.

Only FormComboboxControl object can add and change the selection list. After Axapta implement the runbaseBatch.prompt(), dialogPrinter has become the object of FormComboboxControl. So in the dialogSelectCtrl(), we can modify the selection list of dialogPrinter combobox.



Date data type in Query Range Value Expression

** This article is forwarded frm Fred Shen's blog **

Query range value expressions can be used in any query, where you need to express a range that is more complex than that made possible with the usual dot-dot notation.
Especially to use Or clause in query, you definitely need to use Query Range Value Expression.

Today I just want to investigate more about the date data type in Query Range Value Expression.
To retrieve records where the transaction date is before 21st June, 2006. You can use Date2StrXpp() to format the date:

queryBuildRange.value(strFmt('(TransDate < %1)', Date2StrXpp(21062006)));

or to format the date using date literals directly:

queryBuildRange.value(strFmt('(TransDate < %1)', @'21\06\2006')));


Besides the expression mentioned above, there is a third way to specify date data type in Query Range Value Expression, even it does not quite make sense.


In Dynamics AX, date data type is represented as 32-bit (4 byte) integer. The second byte, the third byte and the last byte store the information of the day, the month and the year respectively.
So in Query Range Value expression, you can use the following instead to allow AX to transform the date directly,

Date transDate = 21\06\2006;
int dayInt, monthInt, yearInt;
;

dayInt = dayofMth(transDate);
monthInt = mthofyr(transDate);
yearInt = year(transDate);

//dateInt should be integer type value rather than real type value
//otherwise AX can’t figure out!!


dateInt = (dayInt-1)*Power(2,16) + (monthInt-1)*Power(2,8) + (yearInt-1900);
queryBuildRange.value(strFmt('(TransDate < %1)', dateInt));


Honestly, this is a bit complicated, but just want to show you how AX holds the date value. Actually, I seldom use the third expression in my codes.

And that is the reason why the scope of date data type is from 1\1\1901 to 31\12\2154.

(In AX 3.0, max date is 31\12\2153, and 31\12\2154 in AX 4.0)

Because AX can only hold dates for up to Power(2, 8), which is 256 years (1900 to 2155).

'Not Like' in Dynamics AX

** This article is forwarded from Fred Shen's blog **

In X++, we can use Like '*someIdentifier' to implement the Like keyword.e.g.
select firstonly purchTable

where purchTable.purchId like '00007*';

However if you want to use 'Not Like' in X++ SQL statement, you have three options:
The first option, using '!' as 'not', e.g.

select firstonly purchTable
where !(purchTable.purchId like '00007*');


The second option, using notExists join
e.g.

PurchTable purchTable, refPurchTable; ;

select firstonly purchTable

notExists join refPurchTable
where purchTable.purchId == '00007*';


Please make sure that you do put purchTable.purchId in condition statement, otherwise the SQL statement will retrieve an empty result set. The last option, using Query

e.g.


Query query = new Query(); QueryRun queryRun; ;
query.addDataSource(tableNum(PurchTable)).addRange(fieldNum(PurchTable, PurchId)).value('!00007*');

queryRun = new QueryRun(query);
if(queryRun.next())
{
purchTable = queryRun.get(tableNum(PurchTable));
print purchTable.PurchId;
pause;
}

Using NotExists join seems more complicated than the first option, but actually there is no performance difference between them.