Ever needed to debug parts of the workflow proces in Ax 2009?
We did yesterday.
Seems like some parts of the workflow process are ran by the workflow user (to be setup in the 'system service accounts' form under administration - security).
Which explains why breakpoints are ignored when debugging this stuff.
The guys from the Ax dev team @ MS have did foresee a handy way to bypass this obstacle.
Check out the #workflow macro, the 'WorkflowRunAsDebug' variable in it and the way the sysWorflowHandler::workflowRunAs method uses them.
By default 'WorkflowRunAsDebug' is set to 'false' which implies it uses the runas function (and the workflow execution account).
But if you set this WorkflowRunAsDebug to 'false', the code is executed using the current account (your own).
Do remember to recompile the sysWorflowHandler class (to apply changes in #workflow macro to the class)and to reset WorkflowRunAsDebug to false when releasing!
dinsdag 24 maart 2009
debugging Ax workflow
Posted by Sjakalaka 0 comments
vrijdag 30 november 2007
list properties for tables
The outcome of this method is a textfile with a number of properties listed for all tables in the AOT. Comes in handy when you want to group tables based on their properties for some reason.
static void TDY_TableProperties(Args _args)
{
#AOT
TreeNode treenode = TreeNode::findNode(#TablesPath + #AOTRootPath);
TreeNodeIterator itTables;
TreeNodeIterator itFields;
TreeNode tableNode;
TreeNode fieldNode;
TreeNode fieldsNode;
int cnt;
int layerMask;
int layerCounter;
str layerMaskStr;
DictEnum dictEnum = new DictEnum(enumnum(UtilEntryLevel));
container conOut;
AsciiIo outFile;
Map mapLayers = new Map(Types::Integer, types::Integer);
MapIterator miLayers;
#properties
;
itTables = treeNode.AOTiterator();
outFile = new AsciiIo('c:\\AxTableProp.txt', 'w');
outFile.outFieldDelimiter(';');
conOut = conIns(conout, ConLen(conOut) + 1, #PropertyTablegroup);
conOut = conIns(conout, ConLen(conOut) + 1, #PropertyLabel);
conOut = conIns(conout, ConLen(conOut) + 1, #PropertyLabel + " text");
conOut = conIns(conout, ConLen(conOut) + 1, #PropertyLabel + " text EN-US");
conOut = conIns(conout, ConLen(conOut) + 1, #PropertyConfigurationkey);
conOut = conIns(conout, ConLen(conOut) + 1, #PropertyName);
conOut = conIns(conout, ConLen(conOut) + 1, #PropertySecuritykey);
conOut = conIns(conout, ConLen(conOut) + 1, #PropertyTablecontents);
outFile.write(conOut);
tableNode = itTables.next();
while (tableNode)
{
conOut = conNull();
mapLayers = new Map(Types::Integer, types::Integer);
//conOut = conIns(conout, ConLen(conOut) + 1, tableNode.AOTname());
conOut = conIns(conout, ConLen(conOut) + 1, FindProperty(tableNode.AOTgetProperties(), #PropertyTablegroup));
conOut = conIns(conout, ConLen(conOut) + 1, FindProperty(tableNode.AOTgetProperties(), #PropertyLabel));
conOut = conIns(conout, ConLen(conOut) + 1, SysLabel::labelId2String2(FindProperty(tableNode.AOTgetProperties(), #PropertyLabel)));
conOut = conIns(conout, ConLen(conOut) + 1, SysLabel::labelId2String2(FindProperty(tableNode.AOTgetProperties(), #PropertyLabel), "EN-US"));
conOut = conIns(conout, ConLen(conOut) + 1, FindProperty(tableNode.AOTgetProperties(), #PropertyConfigurationkey));
conOut = conIns(conout, ConLen(conOut) + 1, FindProperty(tableNode.AOTgetProperties(), #PropertyName));
conOut = conIns(conout, ConLen(conOut) + 1, FindProperty(tableNode.AOTgetProperties(), #PropertySecuritykey));
conOut = conIns(conout, ConLen(conOut) + 1, FindProperty(tableNode.AOTgetProperties(), #PropertyTablecontents));
outFile.write(conOut);
tableNode = itTables.next();
cnt++;
print cnt;
}
outFile = Null;
}
Posted by Sjakalaka 0 comments
vrijdag 31 augustus 2007
info.language
If you're in a situation when you do not want to switch from client to server too often, but you do need to fetch the user's language constantly (like setting a language dependant description in the postLoad on a record), you might want to try this:
in the Application class:
- add a variable infoLangUser (str 5) in the classdeclaration
- add a method:
str 5 infoLangUser(str 5 _language = infoLangUser)
{
;
if (!prmIsDefault(_language))
infoLangUser = _language;
return infoLangUser;
}
- add the following line to the starupPost method in the Application class:
this.aceInfoLangUser(infolog.language());
- in the Global class, add a method:
static client server str 5 infoUserLanguage()
{
str 5 retValue;
;
if (!isRunningOnServer())
retValue = infolog.language();
else
retValue = appl.infoLangUser();
return retValue;
}
If you're on the server side and you need to fetch the language of the user, just call the infoUserLanguage method on the Global class. This will not cause any round-trips to the client because the method in the global class has this check build in that either sends you to the infolog class if you're running on the client, or to the application class (where the language is stored during startup) if you're running on the server.
This is especially useful in environments with a high network latency.
Posted by Sjakalaka 0 comments