static void THK_tableOccenabledFromAOT01(Args _args)
{
#AOT
#properties
TreeNode TreeNode = TreeNode::findNode(#TablesPath);
str tableName;
str temporaryProperty;
sysdictTable m_dictTable;
int i,records,m;
dictionary m_dictionary = new dictionary();
#File
#WinAPI
#Avifiles
CommaIo commaIO;
SysOperationProgress sysProgress;
fileName fileName;
FileIOPermission permission;
int start;
;
if(!Box::yesNo("Are your sure lookup Occenabled table infomation?",dialogButton::No,"Occenabled Table"))
return;
fileName = WinAPI::getFolderPath(#CSIDL_Personal) + "\\" + "A02AvgPrice.csv";// + date2str(systemdateget(),321,2,0,2,0,2) + ".CSV";
permission = new FileIOPermission(fileName,#io_write);
permission.assert();
commaIo = new CommaIo(fileName,#io_write);
if(!commaIo)
{
warning("Please close your csv file and try again. Thanks!");
return;
}
if(commaIo.status() == IO_Status::Ok)
CommaIo.outFieldDelimiter(",");
else
return;
start = timeNow();
records = m_dictionary.tableCnt();
sysProgress = SysOperationProgress::newGeneral(#AviUpdate,"Waitting ... write CSV file",records);
CommaIo.writeExp(["Id","Table Id","Table Name","Table Label","Occenabled","utilElementType"]);
for (i = 1; i < records; i++)
{
m_dictTable = new sysdictTable(m_dictionary.tableCnt2Id(i));
sysProgress.incCount(1);
sysProgress.setText(strfmt("completed %1 [%2]: %3",i/ records * 100, i, m_dictTable.name()));
if(!m_dictTable.occEnabled())
{
m++;
CommaIo.writeExp([m,m_dictTable.id(),m_dictTable.name(),m_dictTable.label(),m_dictTable.occEnabled(),m_dictTable.utilElementType()]);
}
sysProgress.update(true);
}
if(m)
{
if (winAPI::fileExists(fileName))
{
winAPI::shellExecute(fileName);
}
}
CodeAccessPermission::revertAssert();
info(strfmt("It takes time %1",global::time2StrHMS(timenow() - start)));
info(strfmt("completed %1 records",m));
}
------------------
static void THK_tableOccenabledFromAOT02(Args _args)
{
#AOT
#properties
TreeNode TreeNode = TreeNode::findNode(#TablesPath);
str tableName;
AnyType occenabledProperty;
sysdictTable m_dictTable;
int i,records,m;
Dictionary dictionary = new dictionary();
#File
#WinAPI
#Avifiles
CommaIo commaIO;
SysOperationProgress sysProgress;
fileName fileName;
FileIOPermission permission;
int start;
;
if(!Box::yesNo("Are your sure lookup Occenabled table infomation?",dialogButton::No,"Occenabled Table"))
return;
fileName = WinAPI::getFolderPath(#CSIDL_Personal) + "\\" + "A02AvgPrice.csv";// + date2str(systemdateget(),321,2,0,2,0,2) + ".CSV";
permission = new FileIOPermission(fileName,#io_write);
permission.assert();
commaIo = new CommaIo(fileName,#io_write);
if(!commaIo)
{
warning("Please close your csv file and try again. Thanks!");
return;
}
if(commaIo.status() == IO_Status::Ok)
CommaIo.outFieldDelimiter(",");
else
return;
start = timeNow();
records = dictionary.tableCnt();
sysProgress = SysOperationProgress::newGeneral(#AviUpdate,"Waitting ... write CSV file",records);
CommaIo.writeExp(["Id","Table Id","Table Name","Table Label","Occenabled"]);
TreeNode = TreeNode.AOTfirstChild();
while (TreeNode)
{
i++;
tableName = findProperty(TreeNode.AOTgetProperties(), #PropertyName); //table - > Name
sysProgress.incCount(1);
sysProgress.setText(strfmt("completed %1 [%2 / %3]: %4",i/ records * 100, i,records, tableName));
occenabledProperty = findProperty(TreeNode.AOTgetProperties(),"Occenabled");
if(occenabledProperty == "No")
{
m++;
CommaIo.writeExp([m,"",tableName,"",occenabledProperty]);
}
TreeNode = TreeNode.AOTnextSibling();
sysProgress.update(true);
}
if(m)
{
if (winAPI::fileExists(fileName))
{
winAPI::shellExecute(fileName);
}
}
CodeAccessPermission::revertAssert();
info(strfmt("It takes time %1",global::time2StrHMS(timenow() - start)));
info(strfmt("completed %1 records",m));
}
2012年7月14日星期六
2012年7月13日星期五
How to resolve to Number sequnec lock
While working in multi user environment, if you are creating journal/journal
line through code then you might the have the number sequence locking problem.
For understanding the problem you can use following code.
X++ code with posting jounral
----------------------------------------------------------
static void checkNumberSeq(Args _args)
{
NumberSeq numberSeq;
Voucher releaseVoucher;
;
ttsbegin;
numberSeq = NumberSeq::newGetVoucher(VendParameters::numRefVendPaymentVoucher());
releaseVoucher = numberSeq.voucher();
NumberSeq::release(VendParameters::numRefVendPaymentVoucher().NumberSequence,releaseVoucher);
//ttscommit;
//ttsbegin;
numberSeq = NumberSeq::newGetVoucher(VendParameters::numRefVendPaymentVoucher());
releaseVoucher = numberSeq.voucher();
ttscommit;
}
---------------------------------------------------------
If you uncomment ttscommit/ttsbegin in the middle of this job first, it will work fine. If you comment them and run the job again, one NumberSequenceTable record gets locked, and the system hangs during second newGetVoucher() call.
This happens because standard Ax use pessimistic lock while generating new number.
To resolve this problem either you have to use newGetVoucher() out of ttsbegin/ttscommit scope.
Another way to resolve it (not thoroughly tested) is to modify method \Classes\NumberSeq\release as follows:
x++ code
-----------------------------------------------------------------------------------------------
public static boolean release(NumberSequenceCode _numberSequenceCode,
Num _num
//#N, 1001 10/01/2008- --> ,userConnection userConnection = null
//#N, 1001 10/01/2008- //numberSequenceTable = NumberSequenceTable::find(_numberSequenceCode,true);
if (prmIsDefault(userConnection))
{
userConnection = New userConnection();
}
userConnection.ttsbegin();
numberSequenceTable.setConnection(userConnection);
numberSequenceList.setConnection(userConnection);
select forUpdate firstOnly numberSequenceTable
index hint SeriesIdx
where numberSequenceTable.numberSequence == _numberSequenceCode;
//#N, 1001 10/01/2008- userConnection.ttsabort();
//#N, 1001 10/01/2008- >-- throw error(strFmt("@SYS26271",_numberSequenceCode));
}
if (!numberSequenceTable.checkBlocked())
{
//#N, 1001 10/01/2008- --> userConnection.ttsabort();
//#N, 1001 10/01/2008- >-- throw error("@SYS18447");
}
if (numberSequenceTable.continuous == NoYes::No)
ok = TRUE;
else
{
select forupdate firstonly numberSequenceList
index hint StatIdx
where numberSequenceList.numberSequence == _numberSequenceCode &&
numberSequenceList.num == _num;
if (numberSequenceList)
{
if (numberSequenceList.status == NumStatus::Free)
{
ok = TRUE;
}
else if (numberSequenceList.status == NumStatus::Active || numberSequenceList.status == NumStatus::Reserved) { if (numberSequenceList.transId)
{
numberSequenceList.status = NumStatus::Free;
numberSequenceList.transId = 0;
numberSequenceList.update();
select forupdate firstonly numberSequenceTTS
index hint TransIdIdx
where numberSequenceTTS.transId == numberSequenceList.transId;
if (numberSequenceTTS)
numberSequenceTTS.delete();
ok = TRUE;
}
else
{
xSession = new xSession(sessionId());
if (numberSequenceList.sessionId == xSession.masterSessionId() ? xSession.masterSessionId() : sessionId() &&
numberSequenceList.sessionLoginDate == xSession.loginDate() &&
numberSequenceList.sessionLoginTime == xSession.loginTime())
{
numberSequenceList.status = NumStatus::Free;
numberSequenceList.update();
ok = TRUE;
}
else
{
if (NumberSeqCleanUp::isProcessDead(numberSequenceList))
{
numberSequenceList.status = NumStatus::Free;
numberSequenceList.update();
ok = TRUE;
}
else
{
ok = FALSE;
}
}
}
}
}
else
{
numberSequenceList.numberSequence = _numberSequenceCode;
numberSequenceList.transId = 0;
numberSequenceList.status = NumStatus::Free;
numberSequenceList.num = _num;
numberSequenceList.insert();
ok = TRUE;
}
}
//#N, 1001 10/01/2008- --> userConnection.ttscommit();
//#N, 1001 10/01/2008- >-- ttscommit;
return ok;
}
-------------------------------------------------------------------------------------------
X++ code with posting jounral
----------------------------------------------------------
static void checkNumberSeq(Args _args)
{
NumberSeq numberSeq;
Voucher releaseVoucher;
;
ttsbegin;
numberSeq = NumberSeq::newGetVoucher(VendParameters::numRefVendPaymentVoucher());
releaseVoucher = numberSeq.voucher();
NumberSeq::release(VendParameters::numRefVendPaymentVoucher().NumberSequence,releaseVoucher);
//ttscommit;
//ttsbegin;
numberSeq = NumberSeq::newGetVoucher(VendParameters::numRefVendPaymentVoucher());
releaseVoucher = numberSeq.voucher();
ttscommit;
}
---------------------------------------------------------
If you uncomment ttscommit/ttsbegin in the middle of this job first, it will work fine. If you comment them and run the job again, one NumberSequenceTable record gets locked, and the system hangs during second newGetVoucher() call.
This happens because standard Ax use pessimistic lock while generating new number.
To resolve this problem either you have to use newGetVoucher() out of ttsbegin/ttscommit scope.
Another way to resolve it (not thoroughly tested) is to modify method \Classes\NumberSeq\release as follows:
x++ code
-----------------------------------------------------------------------------------------------
public static boolean release(NumberSequenceCode _numberSequenceCode,
Num _num
//#N, 1001 10/01/2008- --> ,userConnection userConnection = null
//#N, 1001 10/01/2008- //numberSequenceTable = NumberSequenceTable::find(_numberSequenceCode,true);
if (prmIsDefault(userConnection))
{
userConnection = New userConnection();
}
userConnection.ttsbegin();
numberSequenceTable.setConnection(userConnection);
numberSequenceList.setConnection(userConnection);
select forUpdate firstOnly numberSequenceTable
index hint SeriesIdx
where numberSequenceTable.numberSequence == _numberSequenceCode;
//#N, 1001 10/01/2008- userConnection.ttsabort();
//#N, 1001 10/01/2008- >-- throw error(strFmt("@SYS26271",_numberSequenceCode));
}
if (!numberSequenceTable.checkBlocked())
{
//#N, 1001 10/01/2008- --> userConnection.ttsabort();
//#N, 1001 10/01/2008- >-- throw error("@SYS18447");
}
if (numberSequenceTable.continuous == NoYes::No)
ok = TRUE;
else
{
select forupdate firstonly numberSequenceList
index hint StatIdx
where numberSequenceList.numberSequence == _numberSequenceCode &&
numberSequenceList.num == _num;
if (numberSequenceList)
{
if (numberSequenceList.status == NumStatus::Free)
{
ok = TRUE;
}
else if (numberSequenceList.status == NumStatus::Active || numberSequenceList.status == NumStatus::Reserved) { if (numberSequenceList.transId)
{
numberSequenceList.status = NumStatus::Free;
numberSequenceList.transId = 0;
numberSequenceList.update();
select forupdate firstonly numberSequenceTTS
index hint TransIdIdx
where numberSequenceTTS.transId == numberSequenceList.transId;
if (numberSequenceTTS)
numberSequenceTTS.delete();
ok = TRUE;
}
else
{
xSession = new xSession(sessionId());
if (numberSequenceList.sessionId == xSession.masterSessionId() ? xSession.masterSessionId() : sessionId() &&
numberSequenceList.sessionLoginDate == xSession.loginDate() &&
numberSequenceList.sessionLoginTime == xSession.loginTime())
{
numberSequenceList.status = NumStatus::Free;
numberSequenceList.update();
ok = TRUE;
}
else
{
if (NumberSeqCleanUp::isProcessDead(numberSequenceList))
{
numberSequenceList.status = NumStatus::Free;
numberSequenceList.update();
ok = TRUE;
}
else
{
ok = FALSE;
}
}
}
}
}
else
{
numberSequenceList.numberSequence = _numberSequenceCode;
numberSequenceList.transId = 0;
numberSequenceList.status = NumStatus::Free;
numberSequenceList.num = _num;
numberSequenceList.insert();
ok = TRUE;
}
}
//#N, 1001 10/01/2008- --> userConnection.ttscommit();
//#N, 1001 10/01/2008- >-- ttscommit;
return ok;
}
-------------------------------------------------------------------------------------------
2012年7月12日星期四
How to fix to report scaling and the report is empty message
When a report doesn't fit on a page, depending on it's properties Ax will resize the report. This is a powerful and very useful feature.
Now Ax will inform you that the report has been rescaled (Report is scaled xx percent to fit to page) and this message is generally not well received by users.
Users are annoyed by the message, they get it every time they run the report, they cannot do anything about it, they have to click to close the infolog, ...
Ax has a builtin feature to suppress this scaling message. You can modify the init method of your report, and add something like this:
this.printJobSettings().suppressScalingMessage(true);
this.printJobSettings().clientPrintJobSettings().suppressScalingMessage(true);
This is very effective and will do the job.
Only, this requires you to modify every report with these kind of messages.
A nicer way would be if we could switch it off in one place for all reports. Fortunately, this is possible as well.
Go to class SysReportRun, in the Run method, place following code before the call to super:
if(this.printJobSettings())
this.printJobSettings().suppressScalingMessage(true);
//this.printJobSettings().clientPrintJobSettings().suppressScalingMessage(true);
// VAR Changed on 06 Feb 2012 at 14:14:30 by JXie7519
void run(boolean onlyReport = false)
{
// If this report is a webReport the run a webReport.
if (webSession() && runBaseReport)
{
runBaseReport.runWebReport();
}
else
{
// When running the report and onlyReport = true then run the report.
if (!onlyReport && runBaseReport)
{
if (runBaseReport.prompt())
{
runBaseReport.run();
}
// If the prompt returns false the do not run the report.
// The RunBaseReport.Run method calls the ReportRun.run method with the parm onlyReport = true.
return;
}
}
this.buildPrintGrandTotal();
this.buildPrintOnlyTotals();
// VAR Changed on 06 Feb 2012 at 14:14:30 by JXie7519 Begin
if(this.printJobSettings().clientPrintJobSettings())
{
this.printJobSettings().clientPrintJobSettings().suppressScalingMessage(true);
this.suppressReportIsEmptyMessage(true);
}
else
{
this.printJobSettings().suppressScalingMessage(true);
this.suppressReportIsEmptyMessage(true);
}
// VAR Changed on 06 Feb 2012 at 14:14:30 by JXie7519 End
super();
}
Now we don't have to modify each and every report and our users are happy.
Note that you can still override the settings in your report. In some reports this is done by default, like SalesInvoice and SalesConfirm.
Now Ax will inform you that the report has been rescaled (Report is scaled xx percent to fit to page) and this message is generally not well received by users.
Users are annoyed by the message, they get it every time they run the report, they cannot do anything about it, they have to click to close the infolog, ...
Ax has a builtin feature to suppress this scaling message. You can modify the init method of your report, and add something like this:
this.printJobSettings().suppressScalingMessage(true);
this.printJobSettings().clientPrintJobSettings().suppressScalingMessage(true);
This is very effective and will do the job.
Only, this requires you to modify every report with these kind of messages.
A nicer way would be if we could switch it off in one place for all reports. Fortunately, this is possible as well.
Go to class SysReportRun, in the Run method, place following code before the call to super:
if(this.printJobSettings())
this.printJobSettings().suppressScalingMessage(true);
//this.printJobSettings().clientPrintJobSettings().suppressScalingMessage(true);
// VAR Changed on 06 Feb 2012 at 14:14:30 by JXie7519
void run(boolean onlyReport = false)
{
// If this report is a webReport the run a webReport.
if (webSession() && runBaseReport)
{
runBaseReport.runWebReport();
}
else
{
// When running the report and onlyReport = true then run the report.
if (!onlyReport && runBaseReport)
{
if (runBaseReport.prompt())
{
runBaseReport.run();
}
// If the prompt returns false the do not run the report.
// The RunBaseReport.Run method calls the ReportRun.run method with the parm onlyReport = true.
return;
}
}
this.buildPrintGrandTotal();
this.buildPrintOnlyTotals();
// VAR Changed on 06 Feb 2012 at 14:14:30 by JXie7519 Begin
if(this.printJobSettings().clientPrintJobSettings())
{
this.printJobSettings().clientPrintJobSettings().suppressScalingMessage(true);
this.suppressReportIsEmptyMessage(true);
}
else
{
this.printJobSettings().suppressScalingMessage(true);
this.suppressReportIsEmptyMessage(true);
}
// VAR Changed on 06 Feb 2012 at 14:14:30 by JXie7519 End
super();
}
Now we don't have to modify each and every report and our users are happy.
Note that you can still override the settings in your report. In some reports this is done by default, like SalesInvoice and SalesConfirm.
2012年7月5日星期四
How to using infolog.globalCache in Class
1)
// USR Changed on 05 七月 2012 at 17:07:36 by Admin
static void THK_SysGlobalCacheSetFromClass(Args _args)
{
SysGlobalCache globalCache = infolog.objectOnServer() ? appl.globalCache() : infolog.globalCache();
;
if (!globalCache.isSet(curUserId(), sessionId()))
{
infoLog.globalCache().set(curUserId(), sessionId(), "Split");
appl.globalCache().set(curUserId(), sessionId(), "Split");
}
try
{
//do something ... ...
}
catch(Exception::Error)
{
if (globalCache.isSet(curUserId(), sessionId()))
globalCache.remove(curuserid(), sessionid());
}
if (globalCache.isSet(curUserId(), sessionId()))
globalCache.remove(curuserid(), sessionid());
}
2)
// USR Changed on 05 七月 2012 at 17:07:40 by Admin
static void THK_SysGlobalCacheGetFromClass(Args _args)
{
anyType cacheValue;
SysGlobalCache globalCache = infolog.objectOnServer() ? appl.globalCache() : infolog.globalCache();
;
if(globalCache.isSet(curuserid(),sessionid()))
{
cacheValue = globalCache.get(curuserid(),sessionid());
}
}
// USR Changed on 05 七月 2012 at 17:07:36 by Admin
static void THK_SysGlobalCacheSetFromClass(Args _args)
{
SysGlobalCache globalCache = infolog.objectOnServer() ? appl.globalCache() : infolog.globalCache();
;
if (!globalCache.isSet(curUserId(), sessionId()))
{
infoLog.globalCache().set(curUserId(), sessionId(), "Split");
appl.globalCache().set(curUserId(), sessionId(), "Split");
}
try
{
//do something ... ...
}
catch(Exception::Error)
{
if (globalCache.isSet(curUserId(), sessionId()))
globalCache.remove(curuserid(), sessionid());
}
if (globalCache.isSet(curUserId(), sessionId()))
globalCache.remove(curuserid(), sessionid());
}
2)
// USR Changed on 05 七月 2012 at 17:07:40 by Admin
static void THK_SysGlobalCacheGetFromClass(Args _args)
{
anyType cacheValue;
SysGlobalCache globalCache = infolog.objectOnServer() ? appl.globalCache() : infolog.globalCache();
;
if(globalCache.isSet(curuserid(),sessionid()))
{
cacheValue = globalCache.get(curuserid(),sessionid());
}
}
How to using infolog.globalCache in Form
1)AOT\Forms\Jimmy_LabelLists\init()
public void init()
{
super();
LabelModuleId.text("SYS");
infolog.globalCache().set(FormStr(Jimmy_LabelLists),"RefreshCurrentForm",element);
}
2) AOT\Forms\Jimmy_LabelLists\canClose()
public boolean canClose()
{
boolean ret;
ret = super();
infolog.globalCache().remove(FormStr(Jimmy_LabelLists),"RefreshCurrentForm");
return ret;
}
3)AOT\Forms\Jimmy_LabelLists\button\
void clicked()
{
;
Jimmy_Label::Jimmy_FindUnUsedLabels();
//super();
}
AOT\Classes\Jimmy_Label\Jimmy_FindUnUsedLabels()
static void Jimmy_FindUnUsedLabels()
{
str 50 labelId;
int m,NumModule;
int maxLabel,LookModule,initTimeNow;
LabelModuleId labelModuleId;
LanguageId LanguageEN,LanguageCN,LanguageFR;
Dialog dialog = new Dialog("Update Labes");
DialogField dlgMax = dialog.addField(types::Integer,"Max Lable Num.","127510");
Container conValue;
#Macrolib.AviFiles
SysOperationProgress progress;
Jimmy_LabelLists LabelLists;
Jimmy_LabelModuleTable labelModuleTable,UpLabelTable;
FormRun fr;
FormStringControl formStringControl;
FormDataSource formDataSource;
int findLabelFromModuleId(LabelModuleId _LabelModuleId)
{
str LabelCN,LabelEN,LabelFR;
int i,numbers;
int maxLabelKey;
xRefNames xRefNames;
XRefReferences XRefReferences;
;
maxLabelKey = Jimmy_LabelModuleTable::find(_LabelModuleId).MaxLabelKey;
while (i <= maxLabel)
{
labelId = "@" + _LabelModuleId + int2str(i);
progress.incCount(1);
progress.setText(strfmt("正在更新 - %1%2 %3 - 进度 - %4% [已完成 %5 / %6]",
"@",
_LabelModuleId,int2str(i),
i / (labelModuleTable.MaxLabelKey ? labelModuleTable.MaxLabelKey : maxLabel) * 100,
m,
LookModule)
);
//this will show the operation progress
/*
//Find if the label id has an cross reference record
select recid from xRefNames
where xRefNames.Name == labelid
exists join XRefReferences
where xRefNames.RecId == XRefReferences.xRefNameRecId;*/
LabelCN = SysLabel::labelId2String(labelId,LanguageCN);
LabelEN = SysLabel::labelId2String(labelId,LanguageEN);
LabelFR = SysLabel::labelId2String(labelId,LanguageFR);
if(LabelFR == labelId)
LabelFR = "";
if(LabelEN == labelId)
LabelEN = "";
if(LabelCN == labelId)
LabelCN = "";
//If there is no record in cross reference then log it
if (//! xRefNames.RecId &&
(LabelCN || LabelEN || LabelFR) &&
//avoid logging labels that are already deleted (This is because of sequential check like IFC1, IFC2, IFC3 etc...)
LabelCN != labelId)
{
numbers++;
LabelLists.clear();
LabelLists.LabelId = labelId;
LabelLists.LabelEN = LabelEN;
LabelLists.LabelCN = LabelCN;
LabelLists.LabelFR = LabelFR;
LabelLists.KeyId = i;
LabelLists.LabelType = "@" + _LabelModuleId;
LabelLists.LabelModuleId = _LabelModuleId;
LabelLists.doInsert();
}
i++;
if(maxLabelKey && i > maxLabelKey)
break;
// progress.incCount();
progress.update(true);
}
return numbers;
}
;
dialog.doInit();
conValue = xSysLastValue::getValue(curext(),curuserid(),UtilElementType::ClassStaticMethod,funcname(),"");
if(conValue == conNull())
dlgMax.value(0);
else
dlgMax.value(conpeek(conValue,1));
if(!dialog.run())
return;
global::startLengthyOperation();
initTimeNow = timeNow();
maxLabel = dlgMax.value();
LanguageCN = "ZH-HANS";
LanguageEN = "EN-US";
LanguageFR = "FR";
select count(RecId) from labelModuleTable
where labelModuleTable.isActived == Noyes::Yes;
LookModule = int642int(labelModuleTable.RecId,false);
while select labelModuleTable
where labelModuleTable.isActived == Noyes::Yes
{
m++;
delete_From LabelLists
where LabelLists.LabelModuleId == labelModuleTable.LabelModuleId &&
LabelLists.KeyId <= maxLabel;
progress = new SysOperationProgress();
progress.setCaption(strfmt("Update[%1] - %2 - %3" ,
labelModuleTable.LabelModuleId,
labelModuleTable.MaxLabelKey ? labelModuleTable.MaxLabelKey : maxLabel,
funcname()));
progress.setAnimation(#AviUpdate);
if(labelModuleTable.MaxLabelKey)
progress.setTotal(labelModuleTable.MaxLabelKey);
else
progress.setTotal(maxLabel);
NumModule = findLabelFromModuleId(labelModuleTable.LabelModuleId);
info(strfmt("%1 - %2 [have %3 Labels]",m < 10 ? "0" + int2str(m) : int2str(m),labelModuleTable.LabelModuleId,NumModule));
}
ttsbegin;
while select forupdate UpLabelTable order by UpLabelTable.LabelModuleId
{
select firstonly LabelLists order by LabelLists.KeyId desc
where LabelLists.LabelModuleId == UpLabelTable.LabelModuleId;
if(LabelLists.KeyId > UpLabelTable.MaxLabelKey)
{
UpLabelTable.MaxLabelKey = LabelLists.KeyId;
UpLabelTable.update();
}
}
ttscommit;
if(infolog.globalCache().get(FormStr(Jimmy_LabelLists),"RefreshCurrentForm"))
{
fr = infolog.globalCache().get(FormStr(Jimmy_LabelLists),"RefreshCurrentForm");
formStringControl = fr.design().controlName("LabelModuleId");
formStringControl.text("SYS");
formDataSource = fr.dataSource("Jimmy_LabelLists");
if(formDataSource)
{
formDataSource.reread();
formDataSource.refresh();
formDataSource.executeQuery();
//fr.dataSource("Jimmy_LabelLists").research();
formDataSource.findRecord(Jimmy_LabelLists::find("SYS",127239));//127510
}
}
global::endLengthyOperation();
xSysLastValue::putValue([maxLabel],curext(),curUserId(),UtilElementType::ClassStaticMethod,funcname(),"");
info(strfmt("Total took to [ %1 ] times",global::time2StrHMS(timenow() - initTimeNow)));
}
public void init()
{
super();
LabelModuleId.text("SYS");
infolog.globalCache().set(FormStr(Jimmy_LabelLists),"RefreshCurrentForm",element);
}
2) AOT\Forms\Jimmy_LabelLists\canClose()
public boolean canClose()
{
boolean ret;
ret = super();
infolog.globalCache().remove(FormStr(Jimmy_LabelLists),"RefreshCurrentForm");
return ret;
}
3)AOT\Forms\Jimmy_LabelLists\button\
void clicked()
{
;
Jimmy_Label::Jimmy_FindUnUsedLabels();
//super();
}
AOT\Classes\Jimmy_Label\Jimmy_FindUnUsedLabels()
static void Jimmy_FindUnUsedLabels()
{
str 50 labelId;
int m,NumModule;
int maxLabel,LookModule,initTimeNow;
LabelModuleId labelModuleId;
LanguageId LanguageEN,LanguageCN,LanguageFR;
Dialog dialog = new Dialog("Update Labes");
DialogField dlgMax = dialog.addField(types::Integer,"Max Lable Num.","127510");
Container conValue;
#Macrolib.AviFiles
SysOperationProgress progress;
Jimmy_LabelLists LabelLists;
Jimmy_LabelModuleTable labelModuleTable,UpLabelTable;
FormRun fr;
FormStringControl formStringControl;
FormDataSource formDataSource;
int findLabelFromModuleId(LabelModuleId _LabelModuleId)
{
str LabelCN,LabelEN,LabelFR;
int i,numbers;
int maxLabelKey;
xRefNames xRefNames;
XRefReferences XRefReferences;
;
maxLabelKey = Jimmy_LabelModuleTable::find(_LabelModuleId).MaxLabelKey;
while (i <= maxLabel)
{
labelId = "@" + _LabelModuleId + int2str(i);
progress.incCount(1);
progress.setText(strfmt("正在更新 - %1%2 %3 - 进度 - %4% [已完成 %5 / %6]",
"@",
_LabelModuleId,int2str(i),
i / (labelModuleTable.MaxLabelKey ? labelModuleTable.MaxLabelKey : maxLabel) * 100,
m,
LookModule)
);
//this will show the operation progress
/*
//Find if the label id has an cross reference record
select recid from xRefNames
where xRefNames.Name == labelid
exists join XRefReferences
where xRefNames.RecId == XRefReferences.xRefNameRecId;*/
LabelCN = SysLabel::labelId2String(labelId,LanguageCN);
LabelEN = SysLabel::labelId2String(labelId,LanguageEN);
LabelFR = SysLabel::labelId2String(labelId,LanguageFR);
if(LabelFR == labelId)
LabelFR = "";
if(LabelEN == labelId)
LabelEN = "";
if(LabelCN == labelId)
LabelCN = "";
//If there is no record in cross reference then log it
if (//! xRefNames.RecId &&
(LabelCN || LabelEN || LabelFR) &&
//avoid logging labels that are already deleted (This is because of sequential check like IFC1, IFC2, IFC3 etc...)
LabelCN != labelId)
{
numbers++;
LabelLists.clear();
LabelLists.LabelId = labelId;
LabelLists.LabelEN = LabelEN;
LabelLists.LabelCN = LabelCN;
LabelLists.LabelFR = LabelFR;
LabelLists.KeyId = i;
LabelLists.LabelType = "@" + _LabelModuleId;
LabelLists.LabelModuleId = _LabelModuleId;
LabelLists.doInsert();
}
i++;
if(maxLabelKey && i > maxLabelKey)
break;
// progress.incCount();
progress.update(true);
}
return numbers;
}
;
dialog.doInit();
conValue = xSysLastValue::getValue(curext(),curuserid(),UtilElementType::ClassStaticMethod,funcname(),"");
if(conValue == conNull())
dlgMax.value(0);
else
dlgMax.value(conpeek(conValue,1));
if(!dialog.run())
return;
global::startLengthyOperation();
initTimeNow = timeNow();
maxLabel = dlgMax.value();
LanguageCN = "ZH-HANS";
LanguageEN = "EN-US";
LanguageFR = "FR";
select count(RecId) from labelModuleTable
where labelModuleTable.isActived == Noyes::Yes;
LookModule = int642int(labelModuleTable.RecId,false);
while select labelModuleTable
where labelModuleTable.isActived == Noyes::Yes
{
m++;
delete_From LabelLists
where LabelLists.LabelModuleId == labelModuleTable.LabelModuleId &&
LabelLists.KeyId <= maxLabel;
progress = new SysOperationProgress();
progress.setCaption(strfmt("Update[%1] - %2 - %3" ,
labelModuleTable.LabelModuleId,
labelModuleTable.MaxLabelKey ? labelModuleTable.MaxLabelKey : maxLabel,
funcname()));
progress.setAnimation(#AviUpdate);
if(labelModuleTable.MaxLabelKey)
progress.setTotal(labelModuleTable.MaxLabelKey);
else
progress.setTotal(maxLabel);
NumModule = findLabelFromModuleId(labelModuleTable.LabelModuleId);
info(strfmt("%1 - %2 [have %3 Labels]",m < 10 ? "0" + int2str(m) : int2str(m),labelModuleTable.LabelModuleId,NumModule));
}
ttsbegin;
while select forupdate UpLabelTable order by UpLabelTable.LabelModuleId
{
select firstonly LabelLists order by LabelLists.KeyId desc
where LabelLists.LabelModuleId == UpLabelTable.LabelModuleId;
if(LabelLists.KeyId > UpLabelTable.MaxLabelKey)
{
UpLabelTable.MaxLabelKey = LabelLists.KeyId;
UpLabelTable.update();
}
}
ttscommit;
if(infolog.globalCache().get(FormStr(Jimmy_LabelLists),"RefreshCurrentForm"))
{
fr = infolog.globalCache().get(FormStr(Jimmy_LabelLists),"RefreshCurrentForm");
formStringControl = fr.design().controlName("LabelModuleId");
formStringControl.text("SYS");
formDataSource = fr.dataSource("Jimmy_LabelLists");
if(formDataSource)
{
formDataSource.reread();
formDataSource.refresh();
formDataSource.executeQuery();
//fr.dataSource("Jimmy_LabelLists").research();
formDataSource.findRecord(Jimmy_LabelLists::find("SYS",127239));//127510
}
}
global::endLengthyOperation();
xSysLastValue::putValue([maxLabel],curext(),curUserId(),UtilElementType::ClassStaticMethod,funcname(),"");
info(strfmt("Total took to [ %1 ] times",global::time2StrHMS(timenow() - initTimeNow)));
}
How to handling Key strokes in AX
The task method in a form will serve the purpose the handling keystroke events to some extent.
Though we cannot catch all the keystrokes, we can handle a few of them:
For example,
public int task(int _taskId)
{
int ret;
#task
;
if (_taskId == #taskesc)
{
Box::okCancel("I am not going to listen to you and u can’t close me with a silly Escape button", DialogButton::Ok);
}
else
{
ret = super(_taskId);
}
return ret;
}
All the tasks defined in the task macro can be handled. Apart from those, other events are:
F1, Ctrl + N, Ctrl + C, Ctrl +v, etc … just put the following line in the task method to get these values:
Info (int2str (_taskId));
Though we cannot catch all the keystrokes, we can handle a few of them:
For example,
public int task(int _taskId)
{
int ret;
#task
;
if (_taskId == #taskesc)
{
Box::okCancel("I am not going to listen to you and u can’t close me with a silly Escape button", DialogButton::Ok);
}
else
{
ret = super(_taskId);
}
return ret;
}
All the tasks defined in the task macro can be handled. Apart from those, other events are:
F1, Ctrl + N, Ctrl + C, Ctrl +v, etc … just put the following line in the task method to get these values:
Info (int2str (_taskId));
Global Cache
Many times because of flawed implementation designs, we often need global variables. We may use a table with a key field and a container field for this purpose, but the simplest way of doing this will be using a Global Cache.
A global cache is an instance of class – SysGlobalCache, which is nothing but a Map containing Maps with Userid as Key. These Maps contain the actual value and a key.
In Ax, we have three(infact 4) Global Caches – Infolog.globalCache(), Appl.globalCache(), ClassFactory.GlobalCache().
How to use:
To Set a Value:
static void GlobalCacheSet(Args _args)
{
{
SysGlobalCache globalCache;
;
;
globalCache = ClassFactory.globalCache();
globalCache.set(curuserid(), 1, "One");
globalCache.set(curuserid(), 1, "One");
}
To Get the Value:
static void GlobalCacheGet(Args _args)
{
{
SysGlobalCache globalCache;
;
;
globalCache = ClassFactory.globalCache();
print globalCache.get(curuserid(), 1);
globalcache.remove(curuserid(), 1);
pause;
}
print globalCache.get(curuserid(), 1);
globalcache.remove(curuserid(), 1);
pause;
}
In the above the example, we can also use Infolog.globalCache() or Appl.globalCache().
Why do we have three Caches?
Its simple, the term "Caching" comes when there is something to do with Performance.
Infolog Object resides in Client and Application Object resides in Server.
To share your global variable across the network and accept the performance penalty of a client/server call, use the
To share your global variable across the network and accept the performance penalty of a client/server call, use the
infolog
variable (Info
class) or appl
variable (Application
class) instead of ClassFactory
.
ClassFactory is a special class, which has instances residing at both server and client. At run time, two instances of the ClassFactory class exist, and they share name classFactory. However confusing the implementation details might sound, this is a powerful concept. When you call a method on classFactory from code running on the client, you are calling the classFactory object on the client tier; when you call a method on classFactory from code running on the server, you are calling the classFactory object on the server tier. Remember this when debugging the ClassFactory class.
These four objects along with an instance of VersionControl will sustain for a duration of Dynamics Ax client session.
How to using query Multiple value
// USR Changed on 05 七月 2012 at 15:19:34 by Jimmy Xie
static void THK_queryMultipleValue(Args _args)
{
query q = new query();
queryRun qr;
queryBuildRange qbr;
queryBuildDataSource qbds;
TransDate AsofDate = systemdateget();
projId projId = "30001";
str criteria;
name projAdjustRefId = "01130850_001";
InventTrans InventTrans;
int i;
;
qbds = q.addDataSource(tablenum(InventTrans));
/*
qbds.addRange(fieldnum(InventTrans,DateFinancial)).value(queryRange(dateNull(),AsofDate));
qbds.addRange(fieldnum(InventTrans,TransType)).value(queryValue(InventTransType::Project));
qbds.addRange(fieldnum(InventTrans,ProjId)).value(projId);
qbds.addRange(fieldnum(InventTrans,projAdjustRefId)).value(projAdjustRefId);
*/
qbds.addSelectionField(fieldnum(InventTrans, Qty),SelectionField::Sum);
qbds.orderMode(orderMode::GroupBy);
qbds.addSortField(fieldnum(InventTrans,DateFinancial));
qbds.addSortField(fieldnum(InventTrans,ItemId));
//注意:
//条件务必保持有值,不然记录会为空;如果是字符串值必须加上双引号,枚举,日期值不需要
qbr = SysQuery::findOrCreateRange(qbds,fieldnum(InventTrans,DataAreaid));
criteria = strFmt(
'(%1) && (%1<=%2) && (%3==%4) && (%5=="%6") && (%7=="%8")',
fieldStr(InventTrans, DateFinancial), // %1 - ok!
Date2StrXpp(AsofDate), // %2 - ok!
fieldStr(InventTrans, TransType), //%3
any2int(InventTransType::Project), //%4
fieldStr(InventTrans, ProjId), //%5
projId , //6
fieldStr(InventTrans, projAdjustRefId),//%7 字符串
projAdjustRefId //8
);
qbr.value(criteria);
qr = new queryRun(q);
info(qr.query().dataSourceNo(1).toString());
while (qr.next())
{
InventTrans = qr.getNo(1);
i++;
info(strfmt("%1 - %2 - %3 [%4]",i,InventTrans.ItemId,InventTrans.DateFinancial,InventTrans.Qty));
}
}
static void THK_queryMultipleValue(Args _args)
{
query q = new query();
queryRun qr;
queryBuildRange qbr;
queryBuildDataSource qbds;
TransDate AsofDate = systemdateget();
projId projId = "30001";
str criteria;
name projAdjustRefId = "01130850_001";
InventTrans InventTrans;
int i;
;
qbds = q.addDataSource(tablenum(InventTrans));
/*
qbds.addRange(fieldnum(InventTrans,DateFinancial)).value(queryRange(dateNull(),AsofDate));
qbds.addRange(fieldnum(InventTrans,TransType)).value(queryValue(InventTransType::Project));
qbds.addRange(fieldnum(InventTrans,ProjId)).value(projId);
qbds.addRange(fieldnum(InventTrans,projAdjustRefId)).value(projAdjustRefId);
*/
qbds.addSelectionField(fieldnum(InventTrans, Qty),SelectionField::Sum);
qbds.orderMode(orderMode::GroupBy);
qbds.addSortField(fieldnum(InventTrans,DateFinancial));
qbds.addSortField(fieldnum(InventTrans,ItemId));
//注意:
//条件务必保持有值,不然记录会为空;如果是字符串值必须加上双引号,枚举,日期值不需要
qbr = SysQuery::findOrCreateRange(qbds,fieldnum(InventTrans,DataAreaid));
criteria = strFmt(
'(%1) && (%1<=%2) && (%3==%4) && (%5=="%6") && (%7=="%8")',
fieldStr(InventTrans, DateFinancial), // %1 - ok!
Date2StrXpp(AsofDate), // %2 - ok!
fieldStr(InventTrans, TransType), //%3
any2int(InventTransType::Project), //%4
fieldStr(InventTrans, ProjId), //%5
projId , //6
fieldStr(InventTrans, projAdjustRefId),//%7 字符串
projAdjustRefId //8
);
qbr.value(criteria);
qr = new queryRun(q);
info(qr.query().dataSourceNo(1).toString());
while (qr.next())
{
InventTrans = qr.getNo(1);
i++;
info(strfmt("%1 - %2 - %3 [%4]",i,InventTrans.ItemId,InventTrans.DateFinancial,InventTrans.Qty));
}
}
订阅:
博文 (Atom)