/// <summary> /// Gets infolog content /// </summary> /// <returns>infolog string</returns> public static str getErrorStr(container _cont) { SysInfologEnumerator enumerator; SysInfologMessageStruct myStruct; Exception exception; str error; enumerator = SysInfologEnumerator::newData(_cont); while (enumerator.moveNext()) { myStruct = new SysInfologMessageStruct(enumerator.currentMessage()); exception = enumerator.currentException(); error = strfmt('%1 %2', error, myStruct.message()); } return error; }
Showing posts with label InfoLog. Show all posts
Showing posts with label InfoLog. Show all posts
Saturday, November 26, 2022
Get Infolog as a string
Friday, June 12, 2020
General journals log update bugs
I found a couple questionable things in General journal log updating.
First, Log field limit of 255 characters. It is evidently not enough for relatively long information to be stored for eventual analysis.
Second, the way to update journal logs by copying lines from Infolog afterwards.
Anyway the standard methods meant to shorten the log content do not work properly. Let's see why.
And the method to find a voucher number can speak one language only.
So, we get a poorly built journal log.
It can be fixed by two extensions.
First, let's gather all lines in a set so that they would be present once only.
Second, teach the voucher searching method to speak any language and do not return rubbish instead of a real voucher number.
Now it looks much better and contains condensed information.
For storing more information, a new memo field can be a solution.
Hope, Microsoft will eventually come up with an optimized approach.
Good logging!
First, Log field limit of 255 characters. It is evidently not enough for relatively long information to be stored for eventual analysis.
Second, the way to update journal logs by copying lines from Infolog afterwards.
Anyway the standard methods meant to shorten the log content do not work properly. Let's see why.
And the method to find a voucher number can speak one language only.
So, we get a poorly built journal log.
It can be fixed by two extensions.
First, let's gather all lines in a set so that they would be present once only.
/// <summary> /// Extension of LedgerJournalCheckPost to fix a bug /// </summary> [ExtensionOf(classStr(LedgerJournalCheckPost))] final class LedgerJournalCheckPost_Extension { /// <summary> /// Updates the infolog for the given transaction. /// </summary> /// <param name = "_ledgerJournalTrans">The transaction.</param> public void updateTransInfoLog(LedgerJournalTrans _ledgerJournalTrans) { this.myUpdateTransInfoLog(_ledgerJournalTrans); // transLogPoint is already moved forward; therefore the next call will do no change to the log next updateTransInfoLog(_ledgerJournalTrans); } /// <summary> /// Updates the infolog for the given transaction. /// </summary> /// <param name = "_ledgerJournalTrans">The transaction.</param> private void myUpdateTransInfoLog(LedgerJournalTrans _ledgerJournalTrans) { #Define.UserTab('\t') Log logTxt; Integer x = transLogPoint; Integer y = infolog.num(0); str currentLine; str strLine; Voucher voucher; List list; ListEnumerator listEnumerator; Set setAllLines = new Set(Types::String); // <GEERU> Log tableLogTxt; #ISOCountryRegionCodes boolean countryRegion_RU = SysCountryRegionCode::isLegalEntityInCountryRegion([#isoRU]); // </GEERU> if (postingResults) { postingResults.parmLedgerPostingMessageLog(ledgerPostingMessageCollection); } while (x < y) { x++; // parse all the lines if there are some prefixes currentLine = infolog.text(x); list = strSplit(currentLine, #UserTab); listEnumerator = list.getEnumerator(); while (listEnumerator.moveNext()) { currentLine = listEnumerator.current(); if(setAllLines.in(currentLine)) { continue; } setAllLines.add(currentLine); // <GEERU> if (countryRegion_RU) { logTxt = currentLine + '\r\n'; tableLogTxt += logTxt; } else { // </GEERU> logTxt += currentLine + '\r\n'; // <GEERU> } // </GEERU> if (logTxt && postingResults != null) { if (_ledgerJournalTrans.Voucher == '') { voucher = LedgerJournalCheckPostResults::getVoucherFromLogText(currentLine); if (voucher == '') { // continue because calling the LedgerJournalCheckPostResults.updateErrorLog // method with a blank voucher has terrible performance and will not change the results continue; } } else { voucher = _ledgerJournalTrans.Voucher; } postingResults.updateErrorLog(voucher, logTxt); } } } // <GEERU> if (countryRegion_RU) { logTxt = tableLogTxt; } // </GEERU> if (logTxt) { tableErrorLog += logTxt; } transLogPoint = y; } }
Second, teach the voucher searching method to speak any language and do not return rubbish instead of a real voucher number.
/// <summary> /// Extension of LedgerJournalCheckPostResults to fix a bug /// </summary> [ExtensionOf(classStr(LedgerJournalCheckPostResults))] final class LedgerJournalCheckPostResults_Extension { /// <summary> /// See the description for the standard method /// </summary> /// <param name = "_logText">log text</param> /// <returns>Voucher text</returns> public static Voucher getVoucherFromLogText(Log _logText) { // this standard function does not works correctly next getVoucherFromLogText(_logText); return LedgerJournalCheckPostResults::myGetVoucherFromLogText(_logText); } /// <summary> /// Finds voucher numbers as it should be in all languages /// </summary> /// <param name = "_logText">Log text</param> /// <returns>Voucher text</returns> private static Voucher myGetVoucherFromLogText(Log _logText) { List list; ListEnumerator listEnumerator; Voucher voucher; str strLine; const str voucherText = '@GeneralLedger:Voucher'; int voucherLen = strLen(voucherText); list = strSplit(_logText, ','); listEnumerator= list.getEnumerator(); while (listEnumerator.moveNext()) { strLine = listEnumerator.current(); if (strContains(strLine, voucherText)) { // delete the word VOUCHER in any language as well as all the spaces around voucher = strLRTrim(strDel(strLine, 1, voucherLen)); break; } } return voucher; } }
Now it looks much better and contains condensed information.
For storing more information, a new memo field can be a solution.
Hope, Microsoft will eventually come up with an optimized approach.
Good logging!
Wednesday, March 6, 2019
Extended version of Universal Field Changer for Microsoft Dynamics AX2012
Oh yeah! The Field changer is still on the road! Now equipped with two updating options:
Feel free to use this surgeon's tool at your own risk! Grab it from here.
Feel free to use this surgeon's tool at your own risk! Grab it from here.
Labels:
AOT,
AX2012,
Excel,
InfoLog,
SQL,
temporary table,
update,
validateWrite,
xpo
Wednesday, March 2, 2016
Infolog in batch task history using SysOperation framework
If you add tasks to a batch job using SysOperation framework, like the following:
You need to set Execution mode to Synchronous while initializing your service class controller.
[...] while (mapIterator.more()) { if (!batchHeader) { batchHeader = this.getCurrentBatchHeader(); } controller = wblDirPartyMergeTaskController::construct(); [...] if (batchHeader) { batchInfo = controller.batchInfo(); batchHeader.addRuntimeTask(controller, this.getCurrentBatchTask().RecId); } else { setPrefix(batchInfoStr); controller.parmExecutionMode(SysOperationExecutionMode::Synchronous); controller.run(); } mapIterator.next(); } if (batchHeader) { batchHeader.save(); } }
You need to set Execution mode to Synchronous while initializing your service class controller.
// class wblDirPartyMergeTaskController extends SysOperationServiceController public void new() { super(); // default for controllers in these classes is synchronous execution // batch execution will be explicitly specified. The default for // SysOperationServiceController is ReliableAsynchronous execution this.parmExecutionMode(SysOperationExecutionMode::Synchronous); this.parmOther(newValue); }Otherwise you will see nothing in bath tasks history infolog.
Wednesday, February 8, 2012
How to go to the object from Infolog
Usually, it is more useful if the user can go directly to the object from an Infolog line.
For example, we need to see all the BOM without any active version. By the way, this is a good example, too, for what we cannot do by means of Advanced Filtre but quickly by coding a short job.
Now the user can opt for Show from the context menu or just double-click on the interesting line to open the related form.
For example, we need to see all the BOM without any active version. By the way, this is a good example, too, for what we cannot do by means of Advanced Filtre but quickly by coding a short job.
static void SIBOMwithNoActiveVersion(Args _args) { BOMTable bomTable; BOMVersion bomVersion; int nmbr, i; boolean printOnly = false; ; SetPrefix('BOMs without any active version'); Nmbr = 0; while select bomTable notexists join bomVersion where bomVersion.Active == NoYes::Yes && bomVersion.BOMId == bomTable.BOMId { nmbr++; info(strfmt("%1 %2, %3, %4, %5, %6, %7", nmbr, bomTable.BOMId, bomTable.Name, bomVersion.ItemId, bomVersion.Name, bomVersion.Active ),"", SysInfoAction_TableField::newBufferField(bomTable, fieldnum(bomTable, BOMId))); } }
Now the user can opt for Show from the context menu or just double-click on the interesting line to open the related form.
Tuesday, January 17, 2012
Universal Field Changer new version for Microsoft Dynamics AX2012
So, I took my old project from the case just to add a new feature: this time I would like to get all the table fields with their labels in the user's language.
Frankly seaking I was going to get it in a grid in order to export to Excel; but unfortunately I did not find a fast way to show my temporary table. Finally, I just use InfoLog in the comma separated format that can be used lately to open in Excel.
Enjoy, anyway!
Description:
Universal Field Changer class for Microsoft Dynamics AX2012:
- collects all the fields from all the tables in AOT in temporary tables;
- makes possible to change any values using filtres by table and field names and existing values;
- provides access to SQL query string;
- prints the field lists with labels in user's language;
- creates dynamically all the form controls and uses method overloading and can be used as a tutorial;
Frankly seaking I was going to get it in a grid in order to export to Excel; but unfortunately I did not find a fast way to show my temporary table. Finally, I just use InfoLog in the comma separated format that can be used lately to open in Excel.
Enjoy, anyway!
Description:
Universal Field Changer class for Microsoft Dynamics AX2012:
- collects all the fields from all the tables in AOT in temporary tables;
- makes possible to change any values using filtres by table and field names and existing values;
- provides access to SQL query string;
- prints the field lists with labels in user's language;
- creates dynamically all the form controls and uses method overloading and can be used as a tutorial;
Thursday, April 17, 2008
SysInfoAction and InfoLog
To do an action from InfoLog dialog you need to create SysInfoAction using one of SysInfoAction_xx classes or create your own descendant class.
For example, to allow the current user open E-mail parameters form by appropriate menu item:
Info('Check e-mail parameters,'',SysInfoAction_MenuFunction::newMenuItem('SysEmailParameters',MenuItemType::Display));
For example, to allow the current user open E-mail parameters form by appropriate menu item:
Info('Check e-mail parameters,'',SysInfoAction_MenuFunction::newMenuItem('SysEmailParameters',MenuItemType::Display));
Subscribe to:
Posts (Atom)