Thursday, April 29, 2010

Retrieving Control Values from an Existing Form Instantiated in X++

  
In one of my recent requirements, I needed to instantiate the smmEmailDistribution Form from a custom form that I had created, once control was returned to the custom form, then I needed to pull the values of various elements that the user had modified on the smmEmailDistribution form. How this is done was not immediately clear, and after spending hours attempting to find an example of this type of functionality in my reference books and via general googling, I eventually made a post on the Dynamics User Group forum. Oddly enough however, shortly after posting my question (less than an hour), I discovered the answer on my own by good old trial-and-error. The code below was placed in the clicked() method of a button on my custom Form, and does exactly what my need required:


void clicked()
{


FormStringControl strEdit;
Args args;
FormRun formRun;


;


args = new args();


args.name(formstr(smmEmailDistribution));
formRun = classFactory.formRunClass(Args);
formRun.init();
formrun.run();
formrun.wait();


// Get the Email1 control from smmEmailDistribution
strEdit = formrun.design().controlName("Email1");


super();


}

Tuesday, April 27, 2010

Obtaining the Next Number Sequence via X++

Recently, I needed write a method that would create a line in a custom table for each customer residing in a temporary table (which was actually part of the final process that I briefly mentioned in my last post “Programmatically Inserting Data into a Grid”)

My actual custom Table contained a field that was based on a Number Sequence. Thus in my process, when I programmatically inserted a new record into my custom Table, per customer, I needed to generate the next number sequence for each record. A little digging led me here which revealed some of the code that I was looking for.

On the clicked() method of my “Process” button I essentially did the following:


void clicked()
{
tbl_Complaint complaintReport;


NumberSequenceReference nsr;
NumberSeq ns;
;


while select temp
{
ttsbegin;


complaintReport.CustNumber = tmt.CustNumber;
complaintReport.Name = tmt.Name;


nsr = tbl_Complaint::numRefComplaintId();
ns = NumberSeq::newGetNum(nsr, false, true);


complaintReport.ComplaintReportId = ns.num();


complaintReport.insert();


ttscommit;


}

super();
}
It should be noted, that in my initial run of the code, I did not include the ttsbegin and ttscommit lines. This however threw the following error:

“System does not support setup 'continuous' of number sequence”
Once again, after doing a little digging, it was here that I learned of my options to resolve this error, which for my needs was solved by implementing transaction scope.

Monday, April 26, 2010

Programmatically Inserting Data into a Grid

During many .NET based projects, I have utilized DataGrids in both Windows Form and Web based applications to hold temporary non-bound data used primarily for user verification purposes. More recently, I was faced with a similar requirement in a needed AX modification, but due to “limitations” of the AX Grid control, I was forced to re-think my approach.

The problem: there does not appear to be any way to programmatically add data to a grid; and if there actually is, I have yet to find it. So it seems, that grids must always be bound to a data source. The way around this so-called “limitation”, is to utilize a Temporary Table. A Temporary Table would be used because, unlike standard tables, it does not map to the a relational database, and will only contain data when an active record buffer is attached to it. Temporary record buffers are isolated to the user, thus if two different users are accessing the Temporary Table, they are actually pointed to different datasets. Therefore, if User (A) and User (B) are using the same form at the same time, there would not be a conflict in data integrity.

In my required modification, a form was needed to allow a user to choose from a list of customers, giving them the ability to add their selections to a grid. Once the user verified that the gird contained all of the customers they required, they could then process this list with a click of the mouse. For this particular tutorial, I will explain the basic method that I have discovered to accomplishing this task of “programmatically inserting data into a grid”.

Let’s say we want a Grid to hold a selected list of Customer Names & Numbers. What we would want to do, is create a Table in the AOT, and mark it as Temporary, containing two string fields; Name, CustNumber. We would then want to set the Data Source of our Grid to this Table, and then make a few X++ modifications

Do the following in the AOT:
1. Create a new Table, and name it TempCustList
2. Add two string fields: CustNumber, and Name
3. Right-Click the Table, and select Properties
4. Set the Temporary property to “Yes”
5. Create a new Form, and name it FrmCustList
6. Drag-and-Drop the TempCustList Table into the Form’s Data Sources
7. Expand down to the Form’s Design node.
8. Right-Click the Design node and select: New ControlGrid
9. Right-Click the Grid, and select Properties
10. Set the Name property to “GridCustList
11. Set the Data Source property to “TempCustList
12. Perform the following:

In the FrmCustList Form where we will be using the Temporary Table, we will want to declare the record buffer so that we can access the data in the temporary table anywhere in the form without losing it (as long as our Form remains open). For example, If you declare the Temporary Table within a mouse click method on a form’s button for example, and insert data into the Temporary Table, the data will be lost, as soon as the method completes.

public class FormRun extends ObjectRun
{
TempCustList temp;
}
13. On the Form, add a Button and override it’s click() method with the following code:

void clicked()
{

// Test data, replace with a query to pull actual customer info, based on selection…
temp.Name = "Joe Smith";
temp.CustNumber = "CUST-0001";

// Insert into the Temp table
temp.insert();

// Refresh the Data Source, which then refreshes the Grid…
TempCustList.setTmpData(temp);
TempCustList_ds.executeQuery();

super();
}
The above code, will obviously add the same line to the Grid every time it is clicked, however the scope of this tutorial was simply to show you how to add data to a grid “programmatically”. A simple modification to the code would allow you to utilize the example for your needs.

Wednesday, April 21, 2010

Creating a new Number Sequence for an Existing Module, and Putting it to Use!

When I first learned how to create a Number Sequence in my Dynamics AX 2009 Development IV Class (80014) in February of 2010, I left with more confusion about the topic than it should have, and I dreaded coming back to work with the prospect of actually having to implement new Number Sequences. However, once I pulled together the precise steps from various books, online documents, and flat out trial-and-error, the task turned out to not be quite as bad as I had originally left the class thinking. I was however dumbfounded as to why no single source (that I was able to find) outlined the exact steps from creating a new Number Sequence to putting it to use in one clean tutorial.

NOTE: Please bear with me in this tutorial; I am going to break naming convention best practices, so that my example is clearer.

The first thing you need to do is determine which module you want/need to create the new number sequence for. For this example, I am going create a new number sequence for the “Accounts Receivable” module. Then take the following steps:

Creating a new Number Sequence for the Accounts Receivable Module

1. Create your Extended Data Type that will be used in your table as your Number Sequence field. For this example we will create a string based Extended Data Type called edt_ComplaintId (with a Label property = “Complaint Report”)

2. Next, since we are using the “Accounts Receivable” module, we must modify the proper NumberSeqReference_XXXX Class. Accounts Receivable, actually maps to Customer (or Cust), so we need to make a modification to the NumberSeqReference_Customer class.
We are only concerned with the loadModule() method, and will simply need to add the following code to it:

numRef.dataTypeId = typeId2ExtendedTypeId(typeid(edt_ComplaintId)); numRef.referenceHelp = "Unique key for the Complaint Report"; numRef.wizardContinuous = true;
numRef.wizardManual = NoYes::No;
numRef.wizardAllowChangeDown = NoYes::No;
numRef.wizardAllowChangeUp = NoYes::No;
numRef.wizardHighest = 999999;
this.create(numRef);
3. After compiling and saving your changes, the next step is to use the Number Sequence Wizard to set it up. Click Basic > Setup > Number sequences > Number sequences to open the form where you can manage your Number Sequences.

4. Click on the “Wizardbutton, which then should initialize the wizard.

NOTE: If you receive an error at this point telling you “The application already has the required number sequences”, this means that you either did not add the definition to an established NumberSeqReference_XXXX Class' (in this example, the NumberSeqReference_Customer Class) loadModule() method, or you have already run the Wizard and set it up.
5. Once you arrive on the Wizard Welcome screen, click the Next > button.

6. On the following screen, verify that the Module is “Accounts receivable” and the Reference is “Complaint Report” (which is the label of our Extended Data Type). The Number Sequence code is just an auto-generated value that you can leave as is (Remember this code, so that you can find it in your list of Number Sequences, because you are going to want to make a couple changes to it). Once you have verified the above, click the Next > button.

7. The next screen just gives you an overview, click the Finish button.

8. You should then see your Number Sequence in the list.

9. You will probably want to change the Format to something such as “CR-######”

10. At this point, your Number Sequence is ready for use!


Using the Number Sequence

1. Create a Table and name it tbl_Complaint.

2. Add the edt_ComplaintId Extended Data Type as one of the fields (drag and drop the Extended Data Type directly into the fields of the Table) and name this field ComplaintId.

3. Add the following Method to the tbl_Complaint:

static client server NumberSequenceReference numRefComplaintId()
{
return NumberSeqReference::findReference(typeId2ExtendedTypeId(typeid(edt_ComplaintId)));
}
4. Create a Form called frm_Complaint.

5. Add the tbl_Complaint table to the form’s Data Sources, and name the Data Source ds_Complaint.

6. Add the following Methods to the Form:

public class FormRun extends ObjectRun
{
NumberSeqFormHandler numberSeqFormHandler;
}


NumberSeqFormHandler numberSeqFormHandler()
{
if (!numberSeqFormHandler)
{
numberSeqFormHandler = NumberSeqFormHandler::newForm(Tmt_CallReport::numRefComplaintId().NumberSequence, element, ds_Complaint.dataSource(), fieldnum(tbl_Complaint, ComplaintId));
}
return numberSeqFormHandler;
}
7. Add the following Methods to the ds_Complaint Data Source:

public void create(boolean _append = false)
{
element.numberSeqFormHandler().formMethodDataSourceCreatePre();
super(_append);
element.numberSeqFormHandler().formMethodDataSourceCreate();
}


public void delete()
{
element.numberSeqFormHandler().formMethodDataSourceDelete();
super();
}


public void write()
{
super();
element.numberSeqFormHandler().formMethodDataSourceWrite();
}
8. Your Number Sequence should now function!

Friday, April 9, 2010

Solving: “Error in Url property error”

My latest task was to create a new Role Center for Dynamics AX 2009; which in its most basic form, is simply a webpage on the SharePoint Enterprise Portal that is linked in AX. After creating my Web Part page in the SharePoint EP, I went back to the AX Client to setup the Role Center.

In AX, I did the following steps:

1. Opened the AOT
2. Scrolled down and expanded the “Web” node
3. Expanded the “Web Menu Items” node
4. Right-Clicked the “URLs” node
5. Clicked “New URL” menu item
6. Right-Clicked the new URL item, and selected the “Properties” menu item
7. Filled in the Name and Label properties
8. Clicked the […] button for the URL property

At this point I received an odd error message:

Error in Url property error

After a bit of digging it turns out that a Microsoft patch is required called “Software Update for Web Folders (KB907306)”. This needs to be installed on the machine where the AX client is running. After downloading, and installing the patch, I restarted the AX client, got back to my URL item in the AOT, and the […] button then functioned properly.