Quantcast
Channel: Microsoft Dynamics Ax
Viewing all 181 articles
Browse latest View live

Dynamics AX: On Hand Inventory

$
0
0
MS Dynamics AX 2009 has a very detailed information about inventory items; it gives a comprehensive information regarding stock which gives a clear vision about what in stock, what is reserved for customer or to be issued out, what is ordered from vendor or received in, and what have been posted or still in open transaction.
The below table descript the information in Items On hand form under Inventory Management->Common Forms->Item details->On hand button
Or
 it could be accessed through 
Sales Order/ Purchase Order/Inventory Movement/Production Order under Inventory button->On Hand




#
Label
Descriptio
Comment
1
Physical inventory
Total available quantities
2
Physical reserved
Quantities reserved in Sales Order
3
Available Physical
Available physical quantities for transactions
3 = 1 – 2
4
Ordered in total
Total quantities in open
Purchase Order
 (did not received yet)
5
Ordered reserved
Quantities reserved from Purchase Order (did not received yet)
6
Available for reservation
Quantities in open Purchase Orders
(did not received yet) available for reservation
6 = 3 + (4 – 5)
7
On order in total
Quantity in open Sales Order
(did not delivered yet)
8
Total Available
Net available physical quantities for transaction
8 = 6 – 7
9
Posted quantity
Quantities posted financially
10
Deducted
Quantities have physical packing list
updates in Sales Order
11
Picked
Quantities have picking list
update in Sales Order
12
Received
Quantities have physical updates
 in Purchase Order
13
Registered
Quantities have registered
 in Purchase Order
14
Physical cost amount
Amount posted in ledger physically
15
Financial cost amount
Amount posted in leader financially
16
Cost price
Item average cost

Finding all data sources in a form through Job

$
0
0
Hi ,
Here is small trick to find the datasource in form. Open job node , and a new job and copy paste following code over there,.....

static void AllDataSourcesInForm(Args _args)
{
    Args args = new Args();
    FormRun fr;
    FormBuildDataSource formBuildDataSource;
    counter i;
    ;
    args.name("CustTable");   // its your FORM name
    fr = ClassFactory.formRunClass(args);
    for(i=1 ; i<=fr.form().dataSourceCount();i++)
    {
        formBuildDataSource = fr.form().dataSource(i);
        info(new DictTable(formBuildDataSource.table()).name());
    }
}



When you run this job you get all data source which is used in CustTable Form




















- Harry

How to refresh the data source of caller form using X++ in AX 2009?

$
0
0
In many cases you need to refresh the data source of the caller form when you finishing work with the current form like when you using posting form you need to refresh the data source of the caller form to see the effect write the following code in close method of the current form :


public void close()
{
 FormRun         callerForm;
;
    callerForm          = element.args().caller();
    callerForm.dataSource().refresh();
    callerForm.dataSource().reread();
    callerForm.dataSource().research();
    super();
}

Invoking a method on a field in a form's datasource

$
0
0
I needed to call the modified method on a form's datasource's field to invoke the logic, which updated a couple of fields on the form. I had defaulted a value in the field on the table's initValue()-method but this - of course - means that the modified method is not in play.
Therefore I needed to call the method in code, for which I had two options, either calling the form controls method by setting AutoDeclaration to Yes and then calling modified or calling the datasource table's field's method which I chose.

e.g. ProdTable_ds.object(fieldnum(ProdTable, ItemId)).modified();

but where to place the code? After some time I found out that if I placed it on active() of the datasource the form showed the correct information.

How to updated the caller Form/DataSource

$
0
0
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();
}

Calling methods on a caller form

$
0
0
Due to the poor design on some form I have to make changes to, I needed this thing here. I replaced a button on the form with code behind it (yes, business logic behind a button is a BAD practice !) by a MenuItemButton. The only problem was that the form had some objects (f.e. a ledgerJournalEngine object, …) that needed to be available in the class behind it…
Now, this is what I did to get the ledgerJournalEngine object from the caller form and pass it to the class I was calling :
// Get the caller form CIMVendTransInvoicePool to get the ledgerjournalengine object
if(ClassIdGet(_args.caller())==ClassNum(SysSetupFormrun)&&(setupFormRun.name()==formStr(CIMVendTransInvoicePool)))
   {
      if(formHasMethod(_args.caller(),identifierStr(parmLedgerJournalEngine)))
      {
         ledgerJournalEngine = _args.caller().parmLedgerJournalEngine();
         DAXCancelRegisteredInvoice.parmLedgerJournalEngine(ledgerJournalEngine);
      }
   }

Event Handling in X++

$
0
0
When you are writing code in C# you can create events and define delegate methods that will be called when the event is triggered. Unfortunately, this is not implemented in the X++ language.
I have read some nice posts about how eventing is implemented in .NET and about the design patterns used (observer, …) and decided to build this myself.
The method I used is using the observer pattern and lets you subscribe to an eventhandler object that can be defined on a class you want to monitor. It is actually quite simple, but can come in handy when available.
Note : Don’t mind the HEL prefix, this is just a prefix used for some tools here. And also, some objects can be extended with additional functionality to contain more information, …
First I created an HELEventHandler class which contains a map with registered listener objects. The methods attachListener and removeListener can be used to register / deregister an object method to listen for the event. The validateListener performs some checks to see if the callback method has the right structure. The fireEvent method is responsible to notify all the listeners of the event triggered.
The HELEventArguments class is created just to pass it when the event is fired. For now, this is an empty shell, but can be extended / adjusted to contain extra information on the fired event.
Now let’s see how we can put this to work. First we start by create a class for testing:HELTestClassEvent. We have a name member here and an eventhandler member.
class HELTestClassEvent
{
Name mName;
HELIEventHandler mNameChangedEventHandler;
}
The eventhandler is made public by adding a parameter method.
public HELIEventHandler parmNameChangedEventHandler(HELIEventHandler _nameChangedEventHandler = mNameChangedEventHandler)
{
;
mNameChangedEventHandler = _nameChangedEventHandler;
return mNameChangedEventHandler;
}
Now let’s say we want an event that is triggered when the name changes. Then we would like to add a method that can be called to notify that the name has changed.
privatevoid onNameChanged(Object _sender, HELEventArguments _arguments)
{
;
if(this.parmNameChangedEventHandler())
{
this.parmNameChangedEventHandler().fireEvent(_sender, _arguments);
}
}
And this will be called like this :
void run()
{
;
mName ="Kenny";
 
this.onNameChanged(this,new HELEventArguments());
 
info("I did my work");
}
For now, we have everything we need to let an object notify others that an event has happened. Now we will take a look at how we can register other objects to listen to this event. Let’s create an extra class that will be notified. There, we will first need to create a method that will do the actual work when the event is fired.
publicvoid HELTestClassEvent_OnNameChanged(Object _sender, HELEventArguments _eventArgs)
{
;
info("Who changed the name ?!");
}
And last but not least we actually attach this method to the event by doing this:
void run()
{
HELTestClassEvent test = HELTestClassEvent::construct();
;
 
// We want to attach a method of this class to the event that the name is changed in the HELTestClassEvent object
test.parmNameChangedEventHandler(HELEventHandler::construct());
test.parmNameChangedEventHandler().attachListener(this,methodStr(HELTestClassEventCalledBack, HELTestClassEvent_OnNameChanged));
 
// Run the HELTestClassEvent object
test.run();
}

PublicTocken=31bf3856ad364e35' package did not load correctly

$
0
0
Hi Everyone,
I installed Ax2012 R2.When i open the application explorer in Visual Studio,I am getting below error.I couldnot access the Application explorer.Before R2 installation it was working fine.Could any give me the suggession on this error.May I know the root cause of this error.



Verified Answer
  • Copy Assemblies files from ..SetupFolder\Msi\Components64\PrivateAssemblies to
    C:\Program Files (x86)\Microsoft Visual Studio 10.0\Common7\IDE\PrivateAssemblies
All Replies
  • Hi,
    I upgraded a perfectly working Ax2012FP environment to R2, and now I get the exact same error. Have tried all I can think of (reinstalling client and developer tools, repairing Visual Studio and resetting Visual Studio settings).
    Any help greatly appreciated.
    /caals
  • VS 2010 sp1 is required . Please check you have it or not. IF you have sp1 installed then post the eventvwr info.
  • Copy Assemblies files from ..SetupFolder\Msi\Components64\PrivateAssemblies to
    C:\Program Files (x86)\Microsoft Visual Studio 10.0\Common7\IDE\PrivateAssemblies 

Dynamics AX Tutorials - Tutorial 2 - Classes\Box

$
0
0
Here is a small project demonstrating capabilities of the class BOX in Dynamics AX.
Specifically, the following is covered in this tutorial:


A short description of the system class BOX
Creating a form from a class (using classFactory), launching different form methods, waiting for the form to be closed by the user and returning the selection
Differences in implementation between DAX 3.0 and DAX 4.0
You can download the project from the following link:

download (for Dynamics AX 3.0 - only user layer)
download (for Dynamics AX 4.0 - only user layer)

Classes\Box is a simple class used throughout the DAX application. It provides an interface for showing a standard dialog with a number of selection options and a short message. This is very useful, especially during development, because it provides a fast way to show some kind of text information to the user.

The class contains a number of methods that can be extended with new ones (as shown below). All the methods are static and have the keyword client in the declaration, which forces the code in them to be executed on the client side.

The most commonly used methods of this class are:

box::info("message"); (for debugging purposes),
box::warning("message"); (for showing a warning message that the user won't miss)
box::yesNo("Message", dialogButton::Yes"). (for allowing the user to select the course of action for the underlying business logic)
All the methods mentioned above are simply creating an object of class DialogBox, which is system and doesn't allow to see its implementation. This class is therefore not covered in this tutorial.

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

The rest of the methods is rarely found in the application code, and is suited for specific situations where you need to provide the user with the option of choosing from more options. (for example, when imporing a project, you see a dialog created by the method box::yesAllNoAllCancel();

The implementation of these methods is very much alike: (example mentioned above)

client static DialogButton yesAllNoAllCancel(str _text, //message
DialogButton _defaultButton, //default dialog button
str _title = "@SYS11132") //dialogTitle
{
Form form;
Args args;
Object formRun;
;
args = new Args();
args.name(formStr(SysBoxForm)); //form SysBoxForm is used to show the message
formRun = classFactory.formRunClass(args); //creating a FormRun object
formRun.init(); //calling init of the form
formRun.setTitle(_title); //setting title of the form (caption)
formRun.setText(_text); //setting message
formRun.setType(DialogBoxType::YESTOALLNOTOALLBOX); //setting the type of the form
formRun.setDefaultButton(_defaultButton); //setting the default button
formRun.run(); //calling run of the form
formRun.wait(); //waiting for user action
return formRun.dialogButton(); //returing the user selection
}

The example creates 2 objects that are needed to run a form from code:
Args args - not discusses here (used to specify the name of the form that is being created)
Object formRun - that is the actual object of the form being created.

You might have noticed that it is of type Object and not FormRun. (using the base type)
This is done on purpose, as there is no compile-time validationg of methods actually existing on the form. If the method doesn't exist, you will receive a runtime error, which is not really appropriate in a live environment. So in your code you should chech that the methods being called do actullay exist. (Global method formHasMethod can be used for this purpose)

After the form is initialized and all the setMethods() are called, the Run() method is called. This method executes the form and shows it on the screen.
After the Run Method we see a call to formRun.wait(); This method notifies the RunTime that the code execution should stop here and wait until the form is closed. (if this method is used on a form to call a child form, the parent form cannot be closed before the child form)
[Note: The alternative method here is the detach method (behavior of MenuItem calls)]

After the user closes the form, the dialogButton method on the form is called to return the selected value. [Note: Notice, that the methods on the form are still accessible, even though the form is already closed by the user. This happens because the formRun object is not yet destroyed]

-----------------
I added another method to the box class - yesNoWaitForm. It opens a form that looks kinda cooler than the standard forms, because it has the option of autoclosing after a set period of time. Well, everyone can find his own uses for this form. The form name is Dev_SysBoxForm and it is an almost complete duplicate of the system SysBoxForm form. The only big difference is that this form is set modal, and that the text of the default button is appended with the remaining amount of time before the form is autoclosed. [Note: SetTimeOut method is used, which is not covered in this tutorial]

There were differences in implementation (compared to the sytem form SysBoxForm). The main are listed below:
DAX 4.0 already has a modal window option (wait(TRUE)); So custom methos are removed from the form
DAX 4.0 has the minimize and maximize system buttons visible on the dialog form, and they had to be turned of on the form design.
In DAX 4.0 The return value of method formRunObj.dialogButton() was not automatically converted to the correct type. So an extra variable way added to the method to convert the result to a specific type of DialogButton. [Note: I think that this is a bug]
The font size of the html (message) was increased by 1, because the default font size seems too small to read.
The project also includes a job that contains a couple of examples of using the new method. Enjoy!

How to delete or truncate all transaction from table

$
0
0
Sometimes it's take long time when you delete record with the instruction MyTable.delete() or Delete_From My Table.

Then it is better to delete from SQL instruction.

For that you can create one class and put this method in.

static server void ClearTable(str _tablename)
{
    SqlDataDictionary   sqlDict;
    
    if(!tableName2Id(_tablename))
        return;
    sqlDict = new SqlDataDictionary() ;
    new SqlDataDictionaryPermission(methodstr(SqlDataDictionary, tableTruncate)).assert();
    sqlDict.tableTruncate(tableName2Id(_tablename),false);
    CodeAccessPermission::revertAssert();
}

then enjoy!

A Now() or GetDate() Method for AX

$
0
0
It should be noted, that after I made this post, it was revealed to me that the DateTimeUtil::utcNow() method accomplishes this goal...

Today I simply have a quick tip. I needed to be able to obtain the current date & time; something similar to the DateTime.Now() function in .NET or the getdate() function in SQL Server; However as far as I can tell, AX provides no quick function for this, so I wrote my own:


public utcdatetime Now()
{


str tDate;
str tTime;
utcdatetime utc3;


;


tDate = date2str(systemDateGet(), 321, DateDay::Digits2, DateSeparator::Hyphen, DateMonth::Digits2, DateSeparator::Hyphen, DateYear::Digits4);
tTime = time2Str(TimeNow(), TimeSeparator::Colon, TimeFormat::AMPM);
return str2datetime(tDate + "" + tTime, 321);


}

Timing an axapta x++ operation in milliseconds

$
0
0
TimeInMS        startTime, endTime;
;


startTime = WinAPI::getTickCount();


//[PERFORM OPERATION]


endTime = WinAPI::getTickCount();


info(strfmt("Execution time: %1 seconds.",(endTime-startTime) div 1000));
//info(strfmt("%1 Milliseconds",endTime-startTime));

Debugging Error: 'The X++ debugger work only for users who are in the 'Microsoft Dynamics AX Debugging Users' local group of the Windows.'

$
0
0
In the following sections, you determine which AOS service account to debug the RDP class in and add the account to the Microsoft Dynamics AX Debugging Users group. You create a server configuration with debug enabled and then you can add breakpoints in your code.

To determine the AOS service account
From the Start menu, point to All Programs, click Administrative Tools, and then click Services.
In Services, right-click the Microsoft Dynamics AX Object Server service and then click Properties.
In the Properties window, on the Log On tab, the AOS service account is specified in the This account field.
To add the AOS service account to the debug group
From the Start menu, point to All Programs, click Administrative Tools, click Computer Management, and then click Local Users and Groups.
In Local Users and Groups, double-click Groups, right-click Microsoft Dynamics AX Debugging Users and click Add to Group.
In the Properties window, click Add and add the AOS service account to the group.
Restart the machine.

How to Enable Remote Errors for SQL Server Reporting Services

$
0
0
Enabling remote errors for SQL Server Reporting Services means setting EnableRemoteErrors Reporting Services system property to True
There are two ways to set the EnableRemoteErrors property to true for a Reporting Services instance.  
Update Report Server database Configuration table for EnableRemoteErrors  
First way is updating the ConfigurationInfo table which is in the ReportServer database for configuration record named “EnableRemoteErrors”.
The default value for EnableRemoteErrors property in the ConfigurationInfo database table is “False”. You can simply run an update sql statement to enable remote errors.
But you may not be successful to see the changes made on the ConfigurationInfo table on the Reporting Services application, if the application is being used intensely.  
image009
   
   
   
   
   
   
   
   
   
  Enable EnableRemoteErrors property though running a script  
Second way to enable remote errors is using and running a script which updates the running configuration values of the Reporting Services.
The codes of the script that will update configuration values for EnableRemoteErrors is given in the SQL Server 2005 Books Online.
Here is the script codes :
Public Sub Main()
Dim P As New [Property]()
P.Name = “EnableRemoteErrors”
P.Value = True
Dim Properties(0) As [Property]
Properties(0) = P
Try
rs.SetSystemProperties(Properties)
Console.WriteLine(“Remote errors enabled.”)
Catch SE As SoapException
Console.WriteLine(SE.Detail.OuterXml)
End Try
End Sub  
Copy the above script code in an empty text file and as the script file as EnableRemoteErrors.rss on the root folder of your C drive. Of course you can select another name for the script file name and another folder to save your script. I chose C drive to keep it easy to run the below command prompt statement. Open the command prompt window by running the “cmd” command in the “Run” box. Then run the below command after you have replaced the ReportServerName with the actual name of the Reporting Services server you want to configure and the ReportServer instance name. You can keep ReportServer unchanged if you use the default configurations.
rs -i C:\EnableRemoteErrors.rss -s http://ReportServerName/ReportServer  
Enabling remote errors for a Reporting Services may help you to get more detailed information that may help for troubleshooting problems with Reporting Services applications and reports you are developing  
Errors on reports rendered by SQL Server Reporting Services 2005 and 2008 will not give you the full details of the issue if you are browsing on a machine other than that hosting SSRS. Instead, you just get this:  
  
With SSRS 2005 you had to manually edit a web.config or run code (SQL or other) in order to get more details beyond the SSRS box. Jim (Saunders) showed me something cool today in SSRS 2008 however:  
  
Setting EnableRemoteErrors here to TRUE will get you full report error details on remote clients. You access this screen via SQL Management Studio. Connect to SSRS, right-click on the server instance in Object Explorer and select “Properties”.  

Dynamics AX Call Non-Static Method Dynamically

$
0
0
I want to make a class that has the ability to dynamically call methods in other classes by name. Ideally, it would accept the class and method names as well as a collection of parameters. dictClass works well for this on static methods, but doesn't seem to work on instance methods.
Is there any way to make the following code work for non-static methods?
[SysEntryPointAttribute]
public str methodExecute(str className, str methodName, str params)
{
DictClass dictClass;
anytype retVal;
str connMessage;
ExecutePermission perm;

perm = new ExecutePermission();

// Grants permission to execute the DictClass.callStatic method.
// DictClass.callStatic runs under code access security.
perm.assert();

dictClass = new DictClass(className2Id(className));

if (dictClass != null)
{
retVal = dictClass.callStatic(methodName);
connMessage = strfmt("Return value is %1", retVal);
}

// Closes the code access permission scope.
CodeAccessPermission::revertAssert();

return connMessage;
}

The following code should do the trick. The code I have below can either use a class name or take in an object of the desired type that is already instantiated.
The largest problem was dealing with passing the parameters along. Since there are a variable number I am using a switch statement to pass the correct number of parameters to the method. This doesn't look very elegant, but I am not sure there is another way to accomplish this.
public static anytype callMethod(str _className, str _methodName, container _parameters, Object _object = null)
{
DictClass dictClass;
anytype returnValue;
Object object;
ExecutePermission permission;

// Grants permission to execute the DictClass.callObject method.
// DictClass.callObject runs under code access security.
permission = new ExecutePermission();
permission.assert();

if (_object != null)
{
dictClass = new DictClass(classidget(_object));
object = _object;
}
else
{
dictClass = new DictClass(className2Id(_className));
object = dictClass.makeObject();
}

if (dictClass != null)
{
switch (conLen(_parameters))
{
case 0:
returnValue = dictClass.callObject(_methodName, object);
break;
case 1:
returnValue = dictClass.callObject(_methodName, object, conPeek(_parameters, 1));
break;
case 2:
returnValue = dictClass.callObject(_methodName, object, conPeek(_parameters, 1), conPeek(_parameters, 2));
break;
//... Continue this pattern for the number of parameters you need to support.
}
}

// Closes the code access permission scope.
CodeAccessPermission::revertAssert();

return returnValue;
}

static void Job_Example_DictClass_CallObject(Args _args) 
{
DictClass dictClass;
anytype retVal;
str resultOutput;
ExecutePermission perm;

perm = new ExecutePermission();

// Grants permission to execute the DictClass.callObject method.
// DictClass.callObject runs under code access security.
perm.assert();

dictClass = new DictClass(classidget(infolog));
if (dictClass != null)
{
retVal = dictClass.callObject("toString", infolog);
resultOutput = strfmt("Return value is %1", retVal);
print resultOutput;
pause;
}

// Closes the code access permission scope.
CodeAccessPermission::revertAssert();
}
Also try looking into DictClass.MakeObject:
Maybe I'm not seeing the obvious, but I want to be able to pass in a Method name and a collection of parameters in order to dynamically call a non-static method. It doesn't seem this will do that. 
I've clarified the question a bit to better explain what I am trying to do. Thanks for the response. 

Dynamics AX batch job relative day parameter

$
0
0
When we add batch job into batch list, it is quite often we need to enter a date into the query. Sometimes we wanted to add them once and works forever, we doesn't want to manually edit the query value every now and then.

Eg. You want to add a report into batch job based on current month, instead of putting a fixed value range of 01/09/2011..30/09/2011, you can put (monthRange(0,0)) or (monthRange()).

All example below will assume today's date is 30/06/2011.
Query valueDescription
(day(relativeDays))client server public static str day([int relativeDays])

Relative days to today's date. Default to 0 when the 'relativeDays' optional parameter is ignored. Can be offset + or -.

Eg.
(day(-1)) will gives you 29/06/2011
(day(0)) will gives you 30/06/2011
(day(1)) will gives you 01/07/2011
(dayRange(relativeDaysFrom, relativeDaysTo))client server public static str dayRange([int relativeDaysFrom, int relativeDaysTo])

Relative day range from and to given today's date.
To get past 1 week + next 1 week date, you can use (-7, 7).

Eg.
(dayRange(0,0)) gives you 30/06/2011 to 30/06/2011
(dayRange(-2,2)) gives you 28/06/2011 to 02/07/2011
(dayRange(0,2)) gives you 30/06/2011 to 02/07/2011
(dayRange(-2,0)) gives you 28/06/2011 to 30/06/2011
(greaterThanDate())client server public static str greaterThanDate([int relativeDays])

Similar to (day()), but give the date of after the offset.

Eg.
(greaterThanDate(0)) gives you "> 30/06/2011"
(greaterThanDate(2)) gives you "> 02/07/2011"
(greaterThanDate(-2)) gives you "> 28/06/2011"
(lessThanDate())client server public static str lessThanDate([int relativeDays])

Similar to (greaterThanDate()), but the other way round.

Eg.
(lessThanDate(0)) gives you "< 30/06/2011"
(lessThanDate(2)) gives you "< 02/07/2011"
(lessThanDate(-2)) gives you "< 28/06/2011"
(monthRange(relativeMonthsFrom, relativeMonthsTo))client server public static str monthRange([int relativeMonthsFrom, int relativeMonthsTo])

Similar to (dayRange()), instead of days, it gives months.
(yearRange(relativeYearsFrom, relativeYearsTo))client server public static str yearRange([int relativeYearsFrom, int relativeYearsTo])

Similar to (dayRange()), instead of days, it gives years.

These methods is coming from the class "SysQueryRangeUtil".

*NOTE: It needs the bracket around the method name.
Eg.
- Correct: (day(0))
- Incorrect: day(0)

More details at: http://msdn.microsoft.com/en-us/library/cc618616(v=AX.50).aspx


Sample screenshot
*All screenshot taken at date 30/06/2011.

(day(0))

(day(1))

 (day(-1))

*This is the tips from my colleague - Matt.

X++ code to write data to XML File

$
0
0
static void CreateXml(Args _args)
{
XmlDocument xmlDoc; //to create blank XMLDocument
XmlElement xmlRoot; // XML root node
XmlElement xmlField;
XmlElement xmlRecord;
XMLWriter xmlWriter;
InventTable inventTable;
DictTable dTable = new DictTable(tablenum(InventTable));
DictField dField;
int i, fieldId;
str value;
#InventTags
;

xmlDoc = XmlDocument::newBlank();
xmlRoot = xmlDoc.createElement(#ItemRootNode);

// Loop through all the records in the inventTable
while select inventTable
where inventTable.ItemGroupId == "Parts"
{
// Create a XmlElement (record) to hold the
// contents of the current record.
xmlRecord = xmlDoc.createElement(#ItemRecords);
// Loop through all the fields in the record
for (i=1; i<=dTable.fieldCnt(); i++)
{
fieldId = dTable.fieldCnt2Id(i);
// Find the DictField object that matches
// the fieldId
dField = dTable.fieldObject(fieldId);

// Skip system fields
if (dField.isSystem())
continue;
// Create a new XmlElement (field) and
// have the name equal to the name of the
// dictField
xmlField = xmlDoc.createElement(dField.name());
// Convert values to string. I have just added
// a couple of conversion as an example.
// Use tableName.(fieldId) instead of fieldname
// to get the content of the field.
switch (dField.baseType())
{
case Types::Int64 :
value = int642str(inventTable.(fieldId));
break;
case Types::Integer :
value = int2str(inventTable.(fieldId));
break;
default :
value = inventTable.(fieldId);
break;
}
// Set the innerText of the XmlElement (field)
// to the value from the table
xmlField.innerText(value);
// Append the field as a child node to the record
xmlRecord.appendChild(xmlField);
}
// Add the record as a child node to the root
xmlRoot.appendChild(xmlRecord);
}
// Add the root to the XmlDocument
xmlDoc.appendChild(xmlRoot);
// Create a new object of the XmlWriter class
// in order to be able to write the xml to a file
xmlWriter = XMLWriter::newFile(@"c:\Items.xml");
// Write the content of the XmlDocument to the
// file as specified by the XmlWriter
xmlDoc.writeTo(xmlWriter);

//Open the file in Internet Explorer
WINAPI::shellExecute("c:\Items.xml");
}

Iterate XML file in X++/Axapta

$
0
0
XML file is a very common method of interaction when we are dealing with any kind of third party software. We can create the XML file in X++ and send it as parameters to the third party software. In case, when we receive the XML file as output from the external software, there can be various challenges in parsing the XML file but the very first step in the process is to traverse the XML source file and extract the values. Below is the code to iterate the XML file in X++:
static void XMLIteratorTest(Args _args)
{
    str                 sourceXMLFile, sNumber, sName, sClass;
    XmlDocument         xmlDocument;
    XmlNodeList         nodeList;
    XmlNode             node;
    XMLNodeListIterator xmlNodeListIterator;
    Counter             counter;
    ;
    sourceXMLFile = "<SCHOOL>" +
                        "<STUDENT>" +
                            "<NUMBER>001</NUMBER>" +
                            "<NAME>Stud_A</NAME>" +
                            "<CLASS>8</CLASS>" +
                        "</STUDENT>" +
                        "<STUDENT>" +
                            "<NUMBER>002</NUMBER>" +
                            "<NAME>Stud_B</NAME>" +
                            "<CLASS>9</CLASS>" +
                        "</STUDENT>" +
                        "<STUDENT>" +
                            "<NUMBER>003</NUMBER>" +
                            "<NAME>Stud_C</NAME>" +
                            "<CLASS>7</CLASS>" +
                        "</STUDENT>" +
                    "</SCHOOL>";

    xmlDocument = XmlDocument::newXml(sourceXMLFile);
    nodeList = xmlDocument.selectNodes('//STUDENT');

    xmlNodeListIterator = new xmlNodeListIterator(nodeList);
    while(xmlNodeListIterator.moreValues())
    {
        counter++;
        node = xmlNodeListIterator.value();

        if(node.selectSingleNode('NUMBER'))
            sNumber = node.selectSingleNode('NUMBER').text();
        if(node.selectSingleNode('NAME'))
            sName = node.selectSingleNode('NAME').text();
        if(node.selectSingleNode('CLASS'))
            sClass = node.selectSingleNode('CLASS').text();
       
        info(strFmt("Record %1: Number - %2, Name - %3, Class - %4",
                        counter,
                        sNumber,
                        sName,
                        sClass));
        xmlNodeListIterator.nextValue();
    }
}

Output should look like this:
Record 1: Number - 001, Name - Stud_A, Class – 8
Record 2: Number - 002, Name - Stud_B, Class – 9
Record 3: Number - 003, Name - Stud_C, Class - 7

Microsoft Dynamics AX 2012 Xpp – Vendors Import

$
0
0
Purpose: The purpose of this document is to illustrate how to write X++ code in Microsoft Dynamics AX 2012 in order to import Vendors with addresses and contact details.

Challenge: Data model changes in Microsoft Dynamics AX 2012 related to high normalization and introduction of surrogate keys made some imports more complex. Global Address Book data model was changed in Microsoft Dynamics AX 2012 which has an impact on Vendors import. Comparing to the previous release of Microsoft Dynamics AX 2009 more tables are involved into the process of Vendors import.

Solution: Appropriate tables buffers (DirPartyTable, VendTable, etc) will be used when writing X++ code in Microsoft Dynamics AX 2012 in order to import Vendors. Alternatively AxBC classes may be used instead of table buffers.

Assumption: The assumption is that appropriate reference data such as vendor groups, etc. was created in advance.

Data Model:

Table Name
Table Description
VendTable
The VendTable table contains vendors for accounts payable.
DirAddressBook
The DirAddressBook table contains address book records.
DirAddressBookParty
The DirAddressBookParty table is a relationship table that contains a link between an address book and party records.
DirPartyTable
The DirPartyTable table contains all the parties. The global address book stores all of the people and organizations that are used in the system.
DirPerson
The DirPerson table contains the party records of person.
DirOrganizationBase
The DirOrganizationBase table contains definitions for internal and external organizations.
DirOrganization
The DirOrganization table contains details for the external organizations in the system.
DirPersonName
The DirPersonName table contains the date effective names for each person in the system.
DirOrganizationName
The DirOrganizationName table contains the date effective names for each of the organizations in the system.
DirDunsNumber
The DirDunsNumber table contains definitions of the DUNS numbers.
DirNameAffix
The DirNameAffix table contains the name titles and suffixes that are defined in the system.
LogisticsLocation
The LogisticsLocation table contains the attributes for a location for postal address and contact information.
LogisticsLocationExt
The LogisticsLocationExt table contains additional information about the locations.
LogisticsLocationRole
The LogisticsLocationRole table contains the roles that are played by the locations in the system.
LogisticsLocationRoleTranslation
The LogisticsLocationRoleTranslation table contains the translation values for the location role descriptions in the languages that are supported in the system.
DirPartyLocation
The DirPartyLocation table contains the relationship between the party and location.
DirPartyLocationRole
The DirPartyLocationRole table is the relationship table between the location and function of the party.
LogisticsElectronicAddress
The LogisticsElectronicAddress table contains communication values that are defined for various parties.
LogisticsPostalAddress
The LogisticsPostalAddress table contains addresses in the system that could be associated with various entities.
LogisticsAddressCountryRegion
The LogisticsAddressCountryRegion table contains definitions of countries or regions. Addresses are associated with countries and regions through CountryRegionId field.
LogisticsAddressCountryRegionTranslation
The LogisticsAddressCountryRegionTranslation table contains the translations of the country region long and short names.
LogisticsAddressState
The LogisticsAddressState table contains states that are used by addresses.
LogisticsAddressCounty
The LogisticsAddressCounty table contains definitions of counties.
LogisticsAddresssCity
The LogisticsAddresssCity table contains the definitions of the cities.
LogisticsAddressDistrict
The LogisticsAddressDistrict table contains the definitions of the districts.
LogisticsAddressZipCode
The LogisticsAddressZipCode table contains zip codes that can be used by addresses.

Data Model Diagram:
<![if !vml]><![endif]>

Vendors


Development:

ttsBegin: Use ttsBegin to start a transaction.

clear: The clear method clears the contents of the record.

initValue: The initValue method initializes the fields of the record.
initFrom*: The initFrom* methods usually populate the fields of the child record based on the fields on the parent record. Example is initFromVendGroup method on VendTable table.

validateWrite: The validateWrite method checks whether the record can be written.
write: The write method writes the record to the database.

insert: The insert method inserts the record into the database.
doInsert: The doInsert method inserts the record into the database. Calling doInsert ensures that any X++ code written in the insert method of the record is not executed. Calling insert always executes the X++ code written in the insert method of the record.

ttsCommit: Use ttsCommit to commit a transaction.

Source code:

Person
static void VendorPersonsXppImport(Args _args)
{
    #define.Name("Alex Anikiev")
    #define.FirstName("Alex")
    #define.LastName("Anikiev")
    #define.PostalAddressName("Postal address")
    #define.City("Alpharetta")
    #define.County("FULTON")
    #define.Country("USA")
    #define.Street("1125 Sanctuary Pkwy #300")
    #define.State("GA")
    #define.ZipCode("30009")
    #define.ElectronicAddressName("Electronic address")
    #define.Locator("alexani@microsoft.com")
    #define.LocatorExt("")
    #define.VendAccount("Alex")
    #define.VendGroup("10")
    #define.Currency("USD")
    #define.CostCenter("OU_4803")
    #define.Department("OU_2311")
    #define.ExpensePurpose("Training")

    DirPerson                   dirPerson;
    DirPersonName               dirPersonName;
    LogisticsLocation           logisticsLocation;
    LogisticsPostalAddress      logisticsPostalAddress;
    LogisticsElectronicAddress  logisticsElectronicAddress;
    VendTable                   vendTable;

    try
    {
        ttsbegin;

        //Person
        dirPerson.clear();
        dirPerson.initValue();
        dirPerson.Name = #Name;

        if (dirPerson.validateWrite())
        {
            dirPerson.insert();

            if (dirPerson)
            {
                //Person name
                dirPersonName.clear();
                dirPersonName.initValue();
                dirPersonName.FirstName = #FirstName;
                dirPersonName.LastName = #LastName;
                dirPersonName.Person = dirPerson.RecId;
                dirPersonName.ValidFrom = DateTimeUtil::utcNow();
                dirPersonName.ValidTo = DateTimeUtil::maxValue();

                if (dirPersonName.validateWrite())
                {
                    dirPersonName.insert();
                }
                else
                    throw error("Person name");

                //Location
                logisticsLocation = LogisticsLocation::create(#PostalAddressName, NoYes::Yes);

                //Party location
                DirParty::addLocation(dirPerson.RecId, logisticsLocation.RecId, true, true, false, [LogisticsLocationRole::findBytype(LogisticsLocationRoleType::Home).RecId]);

                //Postal address
                logisticsPostalAddress.clear();
                logisticsPostalAddress.initValue();
                logisticsPostalAddress.City = #City;
                logisticsPostalAddress.County = #County;
                logisticsPostalAddress.CountryRegionId = #Country;
                logisticsPostalAddress.Street = #Street;
                logisticsPostalAddress.State = #State;
                logisticsPostalAddress.ZipCode = #ZipCode;
                logisticsPostalAddress.Address = LogisticsPostalAddress::formatAddress(
                #Street, #ZipCode, #City, #Country, #State, #County);
                logisticsPostalAddress.Location = logisticsLocation.RecId;
                logisticsPostalAddress.ValidFrom = DateTimeUtil::utcNow();
                logisticsPostalAddress.ValidTo = DateTimeUtil::maxValue();

                if (logisticsPostalAddress.validateWrite())
                {
                    logisticsPostalAddress.insert();
                }
                else
                    throw error("Postal address");

                //Location
                logisticsLocation = LogisticsLocation::create(#ElectronicAddressName, NoYes::No);

                //Party location
                DirParty::addLocation(dirPerson.RecId, logisticsLocation.RecId, false, true, false);

                //Electronic address
                logisticsElectronicAddress.clear();
                logisticsElectronicAddress.initValue();
                logisticsElectronicAddress.Location = logisticsLocation.RecId;
                logisticsElectronicAddress.Type = LogisticsElectronicAddressMethodType::Email;
                logisticsElectronicAddress.Locator = #Locator;
                logisticsElectronicAddress.LocatorExtension = #LocatorExt;
                logisticsElectronicAddress.ValidFrom = DateTimeUtil::utcNow();
                logisticsElectronicAddress.ValidTo = DateTimeUtil::maxValue();

                if (logisticsElectronicAddress.validateWrite())
                {
                    logisticsElectronicAddress.insert();
                }
                else
                    throw error("Electronic address");

                //Vendor
                vendTable.clear();
                vendTable.initValue();
                //vendTable.Currency = "";

                vendTable.AccountNum = #VendAccount;
                //vendTable.AccountNum = NumberSeq::newGetNum(VendParameters::numRefVendAccount()).num();
                vendTable.Party = dirPerson.RecId;

                vendTable.VendGroup = #VendGroup;
                vendTable.initFromVendGroup(VendGroup::find(#VendGroup));

                vendTable.Currency = #Currency;
                vendTable.DefaultDimension = AxdDimensionUtil::getDimensionAttributeValueSetId(
                [3, "CostCenter", #CostCenter, "Department", #Department, "ExpensePurpose", #ExpensePurpose]);

                if (vendTable.validateWrite())
                {
                    vendTable.insert();
                }
                else
                    throw error("Vendor");
            }
        }
        else
            throw error("Person");

        ttscommit;
    }
    catch
    {
        error("Error!");
        return;
    }

    info("Done!");
}

Organization
static void VendorOrganizationsXppImport(Args _args)
{
    #define.Name("Alex Anikiev")
   #define.PostalAddressName("Postal address")
    #define.City("Alpharetta")
    #define.County("FULTON")
    #define.Country("USA")
    #define.Street("1125 Sanctuary Pkwy #300")
    #define.State("GA")
    #define.ZipCode("30009")
    #define.ElectronicAddressName("Electronic address")
    #define.Locator("alexani@microsoft.com")
    #define.LocatorExt("")
    #define.VendAccount("Alex")
    #define.VendGroup("10")
    #define.Currency("USD")
    #define.CostCenter("OU_4803")
    #define.Department("OU_2311")
    #define.ExpensePurpose("Training")

    DirOrganization             dirOrganization;
    LogisticsLocation           logisticsLocation;
    LogisticsPostalAddress      logisticsPostalAddress;
    LogisticsElectronicAddress  logisticsElectronicAddress;
    VendTable                   vendTable;

    try
    {
        ttsbegin;

        //Organization
        dirOrganization.clear();
        dirOrganization.initValue();
        dirOrganization.Name = #Name;

        if (dirOrganization.validateWrite())
        {
            dirOrganization.insert();

            if (dirOrganization)
            {
                //Location
                logisticsLocation = LogisticsLocation::create('Postal address', NoYes::Yes);

                //Party location
                DirParty::addLocation(dirOrganization.RecId, logisticsLocation.RecId, true, true, false, [LogisticsLocationRole::findBytype(LogisticsLocationRoleType::Business).RecId]);

                //Postal address
                logisticsPostalAddress.clear();
                logisticsPostalAddress.initValue();
                logisticsPostalAddress.City = #City;
                logisticsPostalAddress.County = #County;
                logisticsPostalAddress.CountryRegionId = #Country;
                logisticsPostalAddress.Street = #Street;
                logisticsPostalAddress.State = #State;
                logisticsPostalAddress.ZipCode = #ZipCode;
                logisticsPostalAddress.Address = LogisticsPostalAddress::formatAddress(
                #Street, #ZipCode, #City, #Country, #State, #County);
                logisticsPostalAddress.Location = logisticsLocation.RecId;
                logisticsPostalAddress.ValidFrom = DateTimeUtil::utcNow();
                logisticsPostalAddress.ValidTo = DateTimeUtil::maxValue();

                if (logisticsPostalAddress.validateWrite())
                {
                    logisticsPostalAddress.insert();
                }
                else
                    throw error("Postal address");

                //Location
                logisticsLocation = LogisticsLocation::create('Electronic address', NoYes::No);

                //Party location
                DirParty::addLocation(dirOrganization.RecId, logisticsLocation.RecId, false, true, false);

                //Electronic address
                logisticsElectronicAddress.clear();
                logisticsElectronicAddress.initValue();
                logisticsElectronicAddress.Location = logisticsLocation.RecId;
                logisticsElectronicAddress.Type = LogisticsElectronicAddressMethodType::Email;
                logisticsElectronicAddress.Locator = #Locator;
                logisticsElectronicAddress.LocatorExtension = #LocatorExt;
                logisticsElectronicAddress.ValidFrom = DateTimeUtil::utcNow();
                logisticsElectronicAddress.ValidTo = DateTimeUtil::maxValue();

                if (logisticsElectronicAddress.validateWrite())
                {
                    logisticsElectronicAddress.insert();
                }
                else
                    throw error("Electronic address");

                //Vendor
                vendTable.clear();
                vendTable.initValue();
                //vendTable.Currency = "";

                vendTable.AccountNum = #VendAccount;
                //vendTable.AccountNum = NumberSeq::newGetNum(VendParameters::numRefVendAccount()).num();
                vendTable.Party = dirOrganization.RecId;

                vendTable.VendGroup = #VendGroup;
                vendTable.initFromVendGroup(VendGroup::find(#VendGroup));

                vendTable.Currency = #Currency;

                vendTable.DefaultDimension = AxdDimensionUtil::getDimensionAttributeValueSetId(
                [3, "CostCenter", "OU_4803", "Department", "OU_2311", "ExpensePurpose", "Training"]);

                if (vendTable.validateWrite())
                {
                    vendTable.insert();
                }
                else
                    throw error("Vendor");
            }
        }
        else
            throw error("Organization");

        ttscommit;
    }
    catch
    {
        error("Error!");
        return;
    }

    info("Done!");
}

Result:

Microsoft Dynamics AX 2012 – Global Address Book (Person)


Microsoft Dynamics AX 2012 – Global Address Book (Organization)


Microsoft Dynamics AX 2012 – Vendor (Person)


Microsoft Dynamics AX 2012 – Vendor (Organization)


Microsoft Dynamics AX 2012 – Address (Person)


Microsoft Dynamics AX 2012 – Address (Organization)


Microsoft Dynamics AX 2012 – Contact details (Person)


Microsoft Dynamics AX 2012 – Contact details (Organization)


Note: Microsoft Dynamics AX 2012 Demo Data (Company CEU) was used for this example

Version: Microsoft Dynamics AX 2012 RTM

Summary: In this document I explained how to write X++ code in Microsoft Dynamics AX 2012 in order to import Vendors with addresses and contact details. Appropriate table buffers were used when writing X++ code in Microsoft Dynamics AX 2012. This approach allows getting better performance during the import comparing to usage of Microsoft Dynamics AX 2012 Excel Add-in. Also this approach is more flexible for extending in the case of not yet fully defined or changing business requirements. Please consider using DMF (Data Migration Framework) for import of significant amounts of data when performance is an important consideration. DMF (Data Migration Framework) provides a standard template for import of Vendors

Author: Alex Anikiev, PhD, MCP

Tags: Dynamics ERP, Dynamics AX 2012, X++, Xpp, Data Import, Data Conversion, Data Migration, Vendors

Note: This document is intended for information purposes only, presented as it is with no warranties from the author. This document may be updated with more content to better outline the concepts and describe the examples.

How to Get Vendor(Supplier) Information

$
0
0
This is like get information from custTable, get vendor information(vendTable) is soo easy too. It will be illustrated on a job below :

static void GetVendorInfo(Args _args)
{
    VendTable       Vendor;
    ;
    Vendor = VendTable::find("7006");
    info(strFmt("Vendor Name : %1", Vendor.name()));
    info(strFmt("Telepon : %1", Vendor.phone()));
    info(strFmt("Email : %1", Vendor.email()));
    info(strFmt("Website : %1", Vendor.url()));
    info(strFmt("Primary Address : %1", Vendor.postalAddress().Address));
}

the result of above job is like this :

Viewing all 181 articles
Browse latest View live


<script src="https://jsc.adskeeper.com/r/s/rssing.com.1596347.js" async> </script>