It had to happen. So let me explain.
We've seen the
mainOnServer trick used in a few classes now and it was time to copy/paste the concept. The idea is simple enough and the comment in the
PurchFormLetter class is crystal clear:
Main method is split to two parts to reduce the interaction between tiers. main method runs on the client. It gets parameters from Args class and passes them to mainOnServer method, which does the main job.
In my case it was importing 10 to 20,000 ledger entries from a text file. Not a simple import either as we had to translate values from another host system and perform numerous validations. It all made sense to read the text file into a
TextBuffer and send it across to the server tier.
The first problem occurred when attempting to
export the list of generated errors to Excel. I would be using a
TextBuffer to create a tab separated file with a list of table headers and then iterate over buffer fields of the problematic rows. The X++ method
performPushAndFormatting of the class
SysGridExportToExcel wasn't working as expected, mainly due to a problem with the clipboard. So... Here are two ways to clear the clipboard, neither of which was working - why was that?
TextBuffer tbErr = new TextBuffer();
;
tbErr.setText('');
// Clear clipboard 1/2
tbErr.toClipboard();
// Clear clipboard 2/2
WinApi::emptyclipboard();
I tried and tried and couldn't clear the clipboard on my machine for some reason. Finally I completely gave up on the
SysGridExportToExcel::performPushAndFormatting(...) idea and went for the approach of
generating a csv file and then letting the OS decide which application should open it. I adapted the idea to use a
TextBuffer as well, writing the contents to a file on the machine... Which also didn't work.
FileIoPermission _perm;
str tmpFileName;
;
tmpFileName = strfmt('%1ax_export\\%2',WinAPI::getTempPath(),"test.csv");
_perm = new FileIoPermission(tmpFileName,'RW');
_perm.assert();
tbErr.toFile(tmpFileName);
//Open CSV file in registered app
WinAPI::shellExecute(tmpFileName);
The code executes with no problems whatsoever until the last command which could never find the file we had generated. I ended up using Excel && Com (
example) to create the output but this could
take a very long time but there was still some niggling doubt in my mind as to what the source of the problem really was.
Agghhh! We were executing on the server all along! Simply by creating a
static client method it would have resolved my issues.
I'm just going to copy and paste my code below, hightlighting the bits you'd need to change were you to use it (at your own risk!). It's not generic enough to simply pass a query or buffer into a method, but adapt it as you see fit:
static client void exportErrorsToExcel(EVE_HRImportHistory importHistory)
{
QueryRun qr;
Common ljTrans; // EVE_DOMImportLedgerJournalTrans
int cnt = 0;
DictTable dt = new DictTable(tablenum(EVE_DOMImportLedgerJournalTrans));
FieldId fieldId;
SysDictField fieldDict;
int fieldNumDim = fieldNum(EVE_DOMImportLedgerJournalTrans, Dimension);
TextBuffer tbErr = new TextBuffer();
str sTab = '\t';
str sNL = '\r\n';
int iTab = strlen(sTab);
// Update Excel Output columns - 33
container moneyCols = [18,19];
// 0 = string, 1 = ??, 2 = money, 3 = date, 4 = ??
container formatCols = [0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,3,3,0,2,2,0,0,0,0,0,0,0,0,0,0,0,0];
// 1 == left 2 == right 3 == center
container alignmentCols = [1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,2,2,1,1,1,1,1,1,1,1,1,1,1,1,1];
;
// Add title rows
fieldId = dt.fieldNext(0);
while (fieldId)
{
if (!isSysId(fieldId))
{
// Heading Initialisations
fieldDict = new SysDictField(dt.id(), fieldId);
tbErr.appendText(fieldDict.label());
tbErr.appendText(sTab);
}
fieldId = dt.fieldNext(fieldId);
}
tbErr.delete(tbErr.size()-iTab+1,iTab); // Avoid last column
tbErr.appendText(sNL);
cnt++;
// Query
qr = EVE_DOMDirectDebitImporter::getImportHistoryBatch(importHistory.ID, true);
while(qr.next())
{
ljTrans = qr.getNo(1);
fieldId = dt.fieldNext(0);
while (fieldId)
{
if (!isSysId(fieldId))
{
if (fieldId != fieldNumDim) // Dimensions! Exclude from report :)
{
tbErr.appendText(strfmt("%1",ljTrans.(fieldId)));
} else {
tbErr.appendText("EXCLUIDO");
}
tbErr.appendText(sTab);
}
fieldId = dt.fieldNext(fieldId);
}
tbErr.delete(tbErr.size()-iTab+1,iTab);
tbErr.appendText(sNL);
cnt++;
}
tbErr.toClipboard();
SysGridExportToExcel::performPushAndFormatting(moneyCols, alignmentCols, formatCols, cnt);
tbErr = null;
}