How can I identify an Axapta class Name from the class ID? - axapta

Please can someone help me make sense of the Batch madness?
I'm trying to debug an Axapta 3.0 implementation that has about 50 Batch Jobs. Most of the batched classes do not implement the description() method, so when you look at the Batch List form (Basic>>Inquiries>>Batch list) the description field is blank. You can see the Batch Group and the Start Time, etc. but you can't tell which class is actually being called.
The Batch table contains a hidden field called ClassNum which identifies the ID property of the class. Can anyone tell me how I can find the corresponding class from the ID? Once I've identified the culprits I can add descriptions.
I tried using the standard Find function on the AOT but it doesn't pick them up.
Any suggestions would be most welcome!
Many thanks,
Mike

Jay's answer provides two comprehensive solutions.
I've just discovered that the global class ClassId2Name does the same thing, so you can simply have:
display str Classname()
{
return ClassId2Name(this.ClassNum);
}

There atleast two ways to do this, you can use the DictClass class:
display ClassName className()
{
DictClass dictClass = new DictClass(this.ClassNum);
;
if(dictClass!=null)
return dictClass.name();
return '';
}
Or using the UtilIdElements table:
display ClassName className()
{
UtilIdElements utilIdElements;
;
select utilIdElements where utilIdElements.id==this.ClassNum && utilIdElements.recordType==UtilElementType::Class;
if(utilIdElements)
return utilIdElements.name;
return '';
}

Alternative to get ClassName if ClassNum is not available.
display str Classname()
{
return classId2Name(ClassIdGet(this));
}

Related

onAfterWrite method called twice with DataObjectManager

I am using dataobjectmanager with a many_many relationship (I can't use manymanydataobjectmanager for this) between owner and car. Whenever a new car is created I iterate through all instances of owner and add it's id to a linked table along with the new car ID.
My problem is that the code for doing this is within the onafterwrite method and is called twice. I'm not sure why. I've also noticed that for my three owners it is creating rows in the linked table oddly. The first two IDs will be in order then it will stick one. So it'll be rows 1, 2 and 4 with no 3.
This is my onAfterWrite method
public function onAfterWrite() {
if(Permission::check('ADMIN')){
$Pages = DataObject::get('Owner');
foreach($Pages as $owner) {
DB::query("INSERT INTO Owner_Cars (OwnerID, CarID) VALUES ('". $owner->ID . "', '" . $this->ID . "')");
}
}
else {
echo "Failure";
return false;
}
return parent::onAfterWrite();
}
I'd appreciate any advice you could give me.
Thanks
The onAfterWrite() method is probably called twice because write() is called twice. The most common reason that this happens is that:
A write() happens to generate the ID
Then another write() happens as part of saving related records that rely on that ID.
In general, I don't think you can rely on onAfterWrite() being called once: write() is supposed to be designed so that it can be called any old number of times and will only actually affect the database if there are changes to be made.
You would need need to make your code call the necessary DELETE, INSERT, and/or UPDATE statements to be compatible with this. You might, for example:
Select all the Owner_Cars records where OwnerID = $owner->ID
Delete any not in the $Pages list
Insert any from the $Page list that aren't already in Owner_Cars
One other suggestion I would make, if you can, is to try out SilverStripe 3. SilverStripe 3's GridField handles this kind of stuff more robustly out of the box and you might find it easier to build your app on that.

Dynamics AX 2009: Add a field to InventJournalTrans, propagate to InventTrans

I need to add an additional field to InventJournalTrans, that after posting will show up in the InventTrans table. The field is a reference column to a record in another table. What method(s) do I need to modify to make this behavior happen?
Currently, I have already added the fields to both tables and modified the Form to allow the user to enter and save the new field. I just can't seem to find the bottom of the rabbit hole on where the actual posting to InventTrans is occurring.
Ideally, it should just be a:
inventTrans.ReasonRefRecId = inventJournalTrans.ReasonRefRecId;
assignment statement before the
inventTrans.insert();
call. Anybody have a clue on where this is at?
The link above does contain the solution -- I have included the code from that page in case that page disappears or no longer becomes available. Thanks to gl00mie for answering on that site and providing this answer.
You should create a new InventMovement method like this:
public MyNewFieldType myNewField()
{
return MyNewFieldType::DefaultValue; // suppose your new field is an enum
}
Then modify \Classes\InventMovement\initInventTransFromBuffer
void initInventTransFromBuffer(InventTrans _inventTrans, InventMovement _movement_orig)
{
// ... append this line to the end of whatever else is already in this method
_inventTrans.MyNewField = this.myNewField();
}
And finally overload the new method in the InventMov_Journal class:
public MyNewFieldType myNewField()
{
return inventJournalTrans.MyNewField;
}

Can I create a column of nvarchar(MAX) using FluentMigrator?

Using FluentMigrator, the default creation of a Column using .AsString() results in an nvarchar(255). Is there a simple way (before I modify the FluentMigrator code) to create a column of type nvarchar(MAX)?
You could create an extension method to wrap .AsString(Int32.MaxValue) within .AsMaxString()
e.g.
internal static class MigratorExtensions
{
public static ICreateTableColumnOptionOrWithColumnSyntax AsMaxString(this ICreateTableColumnAsTypeSyntax createTableColumnAsTypeSyntax)
{
return createTableColumnAsTypeSyntax.AsString(int.MaxValue);
}
}
OK, I found it. Basically, use .AsString(Int32.MaxValue). Pity there's not a .AsMaxString() method, but I guess it's easy enough to put in...
You can use AsCustom("nvarchar(max)") and pack it to extension
If you often create columns/tables with the same settings or groups of columns, you should be creating extension methods for your migrations!
For example, nearly every one of my tables has CreatedAt and UpdatedAt DateTime columns, so I whipped up a little extension method so I can say:
Create.Table("Foos").
WithColumn("a").
WithTimestamps();
I think I created the Extension method properly ... I know it works, but FluentMigrator has a LOT of interfaces ... here it is:
public static class MigrationExtensions {
public static ICreateTableWithColumnSyntax WithTimestamps(this ICreateTableWithColumnSyntax root) {
return root.
WithColumn("CreatedAt").AsDateTime().NotNullable().
WithColumn("UpdatedAt").AsDateTime().NotNullable();
}
}
Similarly, nearly every one of my tables has an int primary key called 'Id', so I think I'm going to add Table.CreateWithId("Foos") to always add that Id for me. Not sure ... I actually just started using FluentMigrator today, but you should always be refactoring when possible!
NOTE: If you do make helper/extension methods for your migrations, you should never ever ever change what those methods do. If you do, someone could try running your migrations and things could explode because the helper methods you used to create Migration #1 works differently now than they did earlier.
Here is the code for creating columns incase it helps you create helper methods: https://github.com/schambers/fluentmigrator/blob/master/src/FluentMigrator/Builders/Create/Column/CreateColumnExpressionBuilder.cs
How about extending like this:
public static class StringMaxMigratorExtensions
{
public static ICreateTableColumnOptionOrWithColumnSyntax AsStringMax(this ICreateTableColumnAsTypeSyntax createTableColumnAsTypeSyntax)
{
return createTableColumnAsTypeSyntax.AsCustom("nvarchar(max)");
}
public static IAlterColumnOptionSyntax AsStringMax(this IAlterColumnAsTypeSyntax alterColumnAsTypeSyntax)
{
return alterColumnAsTypeSyntax.AsCustom("nvarchar(max)");
}
}

groovy swingbuilder bindable list and tables

Is there a way to bind data to a list and/or a table using the groovy swing builder bind syntax? I could only find simple examples that bind simple properties like strings and numbers to a text field, label, or button text.
Had a look round, and the best I could see was using GlazedLists rather than standard Swing lists
http://www.jroller.com/aalmiray/entry/glazedlists_groovy_not_your_regular
There is a GlazedList plugin. And this article is very helpful. The Griffon guys swear by GlazedLists.
I just did something like this--it's really not that hard to do manually. It's still a work in progress, but if it helps anyone I can give what I have. So far it binds the data in both directions (Updating the data updates the component, editing the table updates the data and sends a notification to any propertyChangeListeners of the "Row")
I used a class to define one row of a table. You create this class to define the nature of your table. It looks something like this:
class Row
{
// allows the table to listen for changes and user code to see when the table is edited
#Bindable
// The name at the top of the column
#PropName("Col 1")
String c1
#Bindable
// In the annotation I set the default editable to "false", here I'll make c2 editable.
// This annotation can (and should) be expanded to define more column properties.
#PropName(value="Col 2", editable=true)
String c2
}
Note that once the rest of the code is packaged up in a class, this "Row" class is the ONLY thing that needs to be created to create a new table. You create instances of this class for each row, add them to the table and you are completely done--no other gui work aside from getting the table into a frame.
This class could include quite a bit more code--I intend to have it contain a thread that polls a database and updates the bound properties, then the table should pick up the changes instantly.
In order to provide custom column properties I defined an annotation that looks like this (I plan to add more):
#Retention(RetentionPolicy.RUNTIME)
#Target(ElementType.FIELD)
public #interface PropName {
String value();
boolean editable() default false
}
The rest is a class that builds the table. I keep it in a separate class so it can be reused (by instantiating it with a different "Row" class)
NOTE: I butchered this as I pasted it in so it may not run without a little work (braces probably). It used to include a frame which I removed to just include the table. You need to wrap the table returned from getTable() in a frame..
public class AutoTable<T>
{
SwingBuilder builder // This way external code can access the table
def values=[] // holds the "Row" objects
PropertyChangeListener listener={repaint()} as PropertyChangeListener
def AutoTable(Class<T> clazz)
{
builder = new SwingBuilder()
builder.build{
table(id:'table') {
tableModel(id:'tableModel') {
clazz.declaredFields.findAll{
it.declaredAnnotations*.annotationType().contains(PropName.class)}.each {
def annotation=it.declaredAnnotations.find{it.annotationType()==PropName.class
}
String n=annotation.value()
propertyColumn(header:n, propertyName:it.name, editable:annotation.editable())
}
}
tableModel.rowsModel.value=values
}
}
// Use this to get the table so it can be inserted into a container
def getTable() {
return builder.table
}
def add(T o) {
values.add(o)
o.addPropertyChangeListener(listener)
}
def remove(T o) {
o.removePropertyChangeListener(listener)
values.remove(o)
}
def repaint() {
builder.doLater{
builder.table.repaint();
}
}
}
There is probably a way to do this without the add/remove by exposing a bindable list but it seemed like more work without a lot of benifit.
At some point I'll probably put the finished class up somewhere--if you have read this far and are still interested, reply in a comment and I'll be sure to do it sooner rather than later.

Creating Type Safe Collections in Flex

I'm trying to create a collection class in Flex that is limited to housing a specific type of data that i am using (an interface). I have chosen not to extend the ArrayCollection class as it's too generic and doesn't really give me the compile time safety that i'm after. In it's simplistic form my collection contains an array and i manage how objects are added and removed, etc.
What i really want to be able to do is use these collections in for each loops. It definitely doesn't seem as straight forward as say c# where you just implement IEnumerable and IEnumerator (or just using the generic Collection). Is there a way to do this in action script and if so any info on how it is achieved?
Cheers
You need to extend the Flash Proxy class. Extending Proxy allows you to alter how 'get' and 'set' work, as well as 'for..in' and 'for..each' loops. You can find more details on the Livedocs.
Here's an example for your issue:
package
{
import flash.utils.Proxy;
import flash.utils.flash_proxy;
public class EnumerableColl extends Proxy
{
private var _coll:Array;
public function EnumerableColl()
{
super();
_coll = [ 'test1', 'test2', 'test3' ];
}
override flash_proxy function nextNameIndex( index:int ):int
{
if ( index >= _coll.length ) return 0;
return index + 1;
}
override flash_proxy function nextValue( index:int ):*
{
return _coll[ index - 1];
}
}
}
Take a look at Vector<>. That is about as best as you can go for a typed collection in Flex (4 onwards). However, you will need to implement your own class otherwise. One way, it seems, is to use the Iterator Pattern.
Also, take a look at this SO post.

Resources