That code works on common situation my colleauge found from web (don't know the source):
Dictionary dict;
int idx, lastIdx, totalTables;
TableId tableId;
Application application;
SysOperationProgress progress;
StackBase errorStack;
ErrorTxt errorTxt;
;
application = new Application();
dict = new Dictionary();
totalTables = dict.tableCnt();
progress = new SysOperationProgress();
progress.setTotal(totalTables);
progress.setCaption("@SYS90206");
errorStack = new StackBase(Types::String);
lastIdx = 3000;
try
{
for (idx = lastIdx+1; idx <= totalTables; idx++)
{
tableId = dict.tableCnt2Id(idx);
progress.setText(dict.tableName(tableId));
lastIdx = idx;
application.dbSynchronize(tableId, false, true, false);
progress.incCount();
}
}
catch (Exception::Error)
{
errorTxt = strFmt("Error on table: '%1' (%2) ", tableId, dict.tableName(tableId));
errorStack.push(errorTxt);
retry;
}
setPrefix("@SYS86407");
errorTxt = errorStack.pop();
while (errorTxt)
{
error(errorTxt);
errorTxt = errorStack.pop();
}
AX etiketine sahip kayıtlar gösteriliyor. Tüm kayıtları göster
AX etiketine sahip kayıtlar gösteriliyor. Tüm kayıtları göster
4 Ocak 2017 Çarşamba
12 Aralık 2016 Pazartesi
AX 2012 - Run Questionnaires with X++ and get Answer table answer Id
There are lots of pages about questionnaires at the web. So I won't tell anything about using and run
questionnaires from menus.
KMCollection - Questionnaire records table. kmCollectionId is key field.
KMVirtualNetworkAnswerTable - Answers table. kmVirtualNetworkAnswerTableId is key field. Result is saved at evaluation field as failed/passed or empty.
KMVirtualNetworkAnswerLine - Answers lines table.
Run Questionnaire with code:
KMQuestionnaireRun o = new CrsKMQuestionnaireRun();
o.set(KMQuestionnaireRunMode::All,myKMCollectionId, DirPersonUser::find(curUserId()).party());
o.run();
Run Answer form with code:
KMVirtualNetworkAnswerTable answerTable;
Args args = new Args();
RecId recId = DirPersonUser::find(curUserId()).party();
select firstOnly answerTable
where answerTable.kmVirtualNetworkAnswerTableId == this.AnswerId;
args.record(answerTable);args.caller(o);
new MenuFunction(menuItemDisplayStr(KMKnowledgeCollectorUserResults), MenuItemType::Display).run(args);
I extended KMQuestionnaireRun_Win32 class for save KMVirtualNetworkAnswerTable's ID field to my table at my caller form:
class myKMQuestionnaireRun extends KMQuestionnaireRun_Win32
{
recId myRecId;
}
I added a parm method for save caller table RecId:
RecId parmMyRecId(RecId _recId = myRecId)
{
myRecId =_recId;
return myRecId;
}
Derived end method of MQuestionnaireRun_Win32:
public void end(KMVirtualNetworkAnswerTableId _kmVirtualNetworkAnswerTableId)
{
myCallerTable tbl; //Questionnaries formunu çağıran tablo
super(_kmVirtualNetworkAnswerTableId);
if (_kmVirtualNetworkAnswerTableId == "")
return;
ttsBegin;
update_recordSet tbl setting AnswerId = _kmVirtualNetworkAnswerTableId
where tbl.RecId == myRecId;
ttsCommit;
}
And call from my table:
...
myKMQuestionnaireRun o = new myKMQuestionnaireRun();
myQTable c;
o.parmMyRecId(this.RecId);
o.set(KMQuestionnaireRunMode::All,this.KMCollectionId, DirPersonUser::find(curUserId()).party());
o.run();
...
questionnaires from menus.
KMCollection - Questionnaire records table. kmCollectionId is key field.
KMVirtualNetworkAnswerTable - Answers table. kmVirtualNetworkAnswerTableId is key field. Result is saved at evaluation field as failed/passed or empty.
KMVirtualNetworkAnswerLine - Answers lines table.
Run Questionnaire with code:
KMQuestionnaireRun o = new CrsKMQuestionnaireRun();
o.set(KMQuestionnaireRunMode::All,myKMCollectionId, DirPersonUser::find(curUserId()).party());
o.run();
Run Answer form with code:
KMVirtualNetworkAnswerTable answerTable;
Args args = new Args();
RecId recId = DirPersonUser::find(curUserId()).party();
select firstOnly answerTable
where answerTable.kmVirtualNetworkAnswerTableId == this.AnswerId;
args.record(answerTable);args.caller(o);
new MenuFunction(menuItemDisplayStr(KMKnowledgeCollectorUserResults), MenuItemType::Display).run(args);
I extended KMQuestionnaireRun_Win32 class for save KMVirtualNetworkAnswerTable's ID field to my table at my caller form:
class myKMQuestionnaireRun extends KMQuestionnaireRun_Win32
{
recId myRecId;
}
I added a parm method for save caller table RecId:
RecId parmMyRecId(RecId _recId = myRecId)
{
myRecId =_recId;
return myRecId;
}
Derived end method of MQuestionnaireRun_Win32:
public void end(KMVirtualNetworkAnswerTableId _kmVirtualNetworkAnswerTableId)
{
myCallerTable tbl; //Questionnaries formunu çağıran tablo
super(_kmVirtualNetworkAnswerTableId);
if (_kmVirtualNetworkAnswerTableId == "")
return;
ttsBegin;
update_recordSet tbl setting AnswerId = _kmVirtualNetworkAnswerTableId
where tbl.RecId == myRecId;
ttsCommit;
}
And call from my table:
...
myKMQuestionnaireRun o = new myKMQuestionnaireRun();
myQTable c;
o.parmMyRecId(this.RecId);
o.set(KMQuestionnaireRunMode::All,this.KMCollectionId, DirPersonUser::find(curUserId()).party());
o.run();
...
9 Ağustos 2016 Salı
AXAPTA - Invent on hand
//source: http://microsoft-dynamics-ax-erp.blogspot.com.tr/2012/07/find-inventonhand-in-axapta-x.html
static InventOnHand findOnHandByLocationId(ItemId _itemId, InventLocationId _inventLocationId = "")
{
InventDim inventDim;
InventDimParm inventDimParm;
InventOnHand inventOnHand = new InventOnHand();
;
if (_inventLocationId == "")
return InventOnhand::newItemId(_itemId);
//Take a combination of dimension , against which you want to find the stock
inventDim.InventLocationId = _inventLocationId;
//Set the flag for the selected dimensions as active
inventDimParm.initFromInventDim(inventDim);
//Initialize the inventSumDateDim with Date,item,dimension and dim parameter
inventOnHand = InventOnHand::newParameters(_itemid,
inventDim,
inventDimParm);
return inventOnHand;
}
or:
//http://daxtechies.blogspot.com/2013/03/to-find-stock-on-hand-in-ax-through-x.html
static InventOnHand findOnHandByLocationId(ItemId _itemId, InventLocationId _inventLocationId = "")
{
InventDim inventDim;
InventDimParm inventDimParm;
InventOnHand inventOnHand = new InventOnHand();
;
if (_inventLocationId == "")
return InventOnhand::newItemId(_itemId);
//Take a combination of dimension , against which you want to find the stock
inventDim.InventLocationId = _inventLocationId;
//Set the flag for the selected dimensions as active
inventDimParm.initFromInventDim(inventDim);
//Initialize the inventSumDateDim with Date,item,dimension and dim parameter
inventOnHand = InventOnHand::newParameters(_itemid,
inventDim,
inventDimParm);
return inventOnHand;
}
or:
//http://daxtechies.blogspot.com/2013/03/to-find-stock-on-hand-in-ax-through-x.html
InventSum inventsum;
Qty availableQty = 0;
;
select sum(PostedQty),
sum(Received),
sum(Deducted),
sum(Registered),
sum(Picked),
sum(ReservPhysical),
sum(Ordered),
sum(Arrived),
sum(ReservOrdered),
sum(OnOrder) from inventsum
where inventsum.ItemId == "KCYIWU001";
if (inventsum)
{
availableQty = inventsum.PostedQty
+ inventsum.Received
- inventsum.Deducted
+ inventsum.Registered
- inventSum.Picked
- inventSum.ReservPhysical
+ inventsum.Ordered
+ inventsum.Arrived
- inventsum.ReservOrdered
- inventsum.OnOrder;
}
8 Haziran 2016 Çarşamba
AXAPTA - Cancel a WMS picking list or cancel line of picking list
Cancel whole list:
Cancel a line:
WMSPickingRouteCancel::newWMSPickingRoute(wMSPickingRoute).run();
Cancel a line:
WMSPickingLineCancel::newWMSPickingLineCancel(wMSOrderTrans).run();
6 Mayıs 2016 Cuma
AXAPTA - Sign function doesn't work correctly
Sign function doesn't work correctly in AX 2009 and 2012. It should returns -1 for negative numbers, 0 for zero and +1 for positive numbers. But it gives -1 for negative and +1 for others.
Sign at Global:
static real sign(real num)
{
return num >= 0 ? 1 : -1;
}
My correction:
static int sign(real num)
{
if (num < 0)
return -1;
else
return num > 0 ? 1 : 0;
}
Sign at Global:
static real sign(real num)
{
return num >= 0 ? 1 : -1;
}
My correction:
static int sign(real num)
{
if (num < 0)
return -1;
else
return num > 0 ? 1 : 0;
}
5 Mayıs 2016 Perşembe
AXAPTA - Creating picking list
I created a class for me from found solution from a forum page :
class KRC_WMSOrder
{
WMSShipment wmsShipment;
SalesTable salesTable;
WMSPickingRoute wmsPickingRoute;
WMSPickingRouteLink wmsPickingRouteLink;
salesLine salesLine;
}
void createOrderLine(Qty _qty = salesLine.RemainSalesPhysical)
{
InventMovement inventMovement;
WMSOrder wmsOrder;
WMSOrderCreate orderCreate;
WMSOrderTrans wmsOrderTrans;
;
inventMovement = InventMovement::construct(salesLine);
orderCreate = WMSOrderCreate::newMovement(inventMovement,_qty);
orderCreate.parmMustBeWMSOrderControlled(true);
orderCreate.parmQty(_qty);
orderCreate.parmMaxQty(_qty);
orderCreate.run();
wmsOrder = orderCreate.parmWMSOrder();
wmsOrder.updateShipment(wmsShipment,_qty, wmsPickingRoute.PickingRouteID);
}
void createWmsPickingRoute()
{
;
wmsPickingRoute.clear();
wmsPickingRoute.initTypeOrderPick(wmsShipment, WMSExpeditionStatus::Activated,
WMSPickRequestTable::construct(salesTable),"", true);
wmsPickingRoute.ActivationDateTime = DateTimeUtil::utcNow();
wmsPickingRoute.insert();
}
void createWmsPickingRouteLink()
{
;
wmsPickingRouteLink.clear();
wmsPickingRouteLink.initFromSalesTable(salesTable);
wmsPickingRouteLink.initFromWMSPickingRoute(wmsPickingRoute);
wmsPickingRouteLink.insert();
}
void createWmsShipment()
{
;
wmsShipment.clear();
wmsShipment.initTypeOrderPick();
wmsShipment.insert();
}
SalesLine parmSalesLine(SalesLine _salesLine = salesLine)
{
;
salesLine = _salesLine;
return salesLine;
}
SalesTable parmSalesTable(SalesTable _salesTable = salesTable)
{
;
salesTable = _salesTable;
return salesTable;
}
void reserveItem(Qty _qty = salesLine.RemainSalesPhysical)
{
InventUpd_Reservation invUpdReservation;
;
invUpdReservation = InventUpd_Reservation::newInventDim(InventMovement::construct(salesLine,InventMovSubType::None),
salesLine.inventDim(),_qty,false);
invUpdReservation.updateNow();
}
Sample usage:
KRC_WMSOrder kWMS = new krc_wmsorder();
kWMS.createWmsShipment();
;
ttsbegin;
kWMS.parmSalesTable(salesTable);
kWMS.createWmsPickingRoute();
kWMS.createWmsPickingRouteLink();
kWMS.parmSalesLine(salesLine);
kWMS.reserveItem();
kWMS.createOrderLine();
ttscommit;
class KRC_WMSOrder
{
WMSShipment wmsShipment;
SalesTable salesTable;
WMSPickingRoute wmsPickingRoute;
WMSPickingRouteLink wmsPickingRouteLink;
salesLine salesLine;
}
void createOrderLine(Qty _qty = salesLine.RemainSalesPhysical)
{
InventMovement inventMovement;
WMSOrder wmsOrder;
WMSOrderCreate orderCreate;
WMSOrderTrans wmsOrderTrans;
;
inventMovement = InventMovement::construct(salesLine);
orderCreate = WMSOrderCreate::newMovement(inventMovement,_qty);
orderCreate.parmMustBeWMSOrderControlled(true);
orderCreate.parmQty(_qty);
orderCreate.parmMaxQty(_qty);
orderCreate.run();
wmsOrder = orderCreate.parmWMSOrder();
wmsOrder.updateShipment(wmsShipment,_qty, wmsPickingRoute.PickingRouteID);
}
void createWmsPickingRoute()
{
;
wmsPickingRoute.clear();
wmsPickingRoute.initTypeOrderPick(wmsShipment, WMSExpeditionStatus::Activated,
WMSPickRequestTable::construct(salesTable),"", true);
wmsPickingRoute.ActivationDateTime = DateTimeUtil::utcNow();
wmsPickingRoute.insert();
}
void createWmsPickingRouteLink()
{
;
wmsPickingRouteLink.clear();
wmsPickingRouteLink.initFromSalesTable(salesTable);
wmsPickingRouteLink.initFromWMSPickingRoute(wmsPickingRoute);
wmsPickingRouteLink.insert();
}
void createWmsShipment()
{
;
wmsShipment.clear();
wmsShipment.initTypeOrderPick();
wmsShipment.insert();
}
SalesLine parmSalesLine(SalesLine _salesLine = salesLine)
{
;
salesLine = _salesLine;
return salesLine;
}
SalesTable parmSalesTable(SalesTable _salesTable = salesTable)
{
;
salesTable = _salesTable;
return salesTable;
}
void reserveItem(Qty _qty = salesLine.RemainSalesPhysical)
{
InventUpd_Reservation invUpdReservation;
;
invUpdReservation = InventUpd_Reservation::newInventDim(InventMovement::construct(salesLine,InventMovSubType::None),
salesLine.inventDim(),_qty,false);
invUpdReservation.updateNow();
}
Sample usage:
KRC_WMSOrder kWMS = new krc_wmsorder();
kWMS.createWmsShipment();
;
ttsbegin;
kWMS.parmSalesTable(salesTable);
kWMS.createWmsPickingRoute();
kWMS.createWmsPickingRouteLink();
kWMS.parmSalesLine(salesLine);
kWMS.reserveItem();
kWMS.createOrderLine();
ttscommit;
24 Mart 2016 Perşembe
AXAPTA - Solve of turkish letter problem when send mail with sysmailer class
I found this solution from a blog which I cannot remember. Turkish letters (or another special letters with some languages) will not shown when send mail with sysMailer class if you won't add this red line:
SysEmailParameters parameters;
SysMailer mailer;
;
new InteropPermission(InteropKind::ComInterop).assert();
parameters = SysEmailParameters::find();
mailer = new SysMailer();
if (parameters.SMTPRelayServerName)
{
mailer.SMTPRelayServer(parameters.SMTPRelayServerName,
parameters.SMTPPortNumber,
parameters.SMTPUserName,
SysEmailParameters::password(),
parameters.NTLM);
}
else
{
mailer.SMTPRelayServer(parameters.SMTPServerIPAddress,
parameters.SMTPPortNumber,
parameters.SMTPUserName,
SysEmailParameters::password(),
parameters.NTLM);
}
mailer.fromAddress(fromAddr);
mailer.tos().appendAddress(toAddr);
mailer.subject(subject);
//solution for turkish letter problem (ÖöÇçĞğÜüŞşİı):
mailer.bodyCharSet("Windows-1254"); if(FileName)
mailer.attachments().add(FileName);
mailer.ccs().appendAddress(ccAddr);
mailer.bccs().appendAddress(bccAddr);
mailer.htmlBody(body);
mailer.sendMail();
}
CodeAccessPermission::revertAssert();
SysEmailParameters parameters;
SysMailer mailer;
;
new InteropPermission(InteropKind::ComInterop).assert();
parameters = SysEmailParameters::find();
mailer = new SysMailer();
if (parameters.SMTPRelayServerName)
{
mailer.SMTPRelayServer(parameters.SMTPRelayServerName,
parameters.SMTPPortNumber,
parameters.SMTPUserName,
SysEmailParameters::password(),
parameters.NTLM);
}
else
{
mailer.SMTPRelayServer(parameters.SMTPServerIPAddress,
parameters.SMTPPortNumber,
parameters.SMTPUserName,
SysEmailParameters::password(),
parameters.NTLM);
}
mailer.fromAddress(fromAddr);
mailer.tos().appendAddress(toAddr);
mailer.subject(subject);
//solution for turkish letter problem (ÖöÇçĞğÜüŞşİı):
mailer.bodyCharSet("Windows-1254"); if(FileName)
mailer.attachments().add(FileName);
mailer.ccs().appendAddress(ccAddr);
mailer.bccs().appendAddress(bccAddr);
mailer.htmlBody(body);
mailer.sendMail();
}
CodeAccessPermission::revertAssert();
2 Mart 2016 Çarşamba
AXAPTA - Find a table fields label value
There are two ways to find a table field's label value:
If fields label value derived from directly fields extended data type:
if (curext()=="krc" && this.RBOInventItemGroupId == "")
ret = checkfailed(strfmt("'%1' field cannot be empty!..",
new SysDictType(extendedTypeNum(RBORetailGroupId)).label()));
If there is a special label value dedicated at table level:
if (curext()=="krc" && this.RBOInventItemGroupId == "")
ret = checkfailed(strfmt("'%1' field cannot be empty!..",
new SysDictField(tablenum(WholeSalesCampaignGroup),
fieldnum(WholeSalesCampaignGroup,RBOInventItemGroupId)).label()));
If fields label value derived from directly fields extended data type:
if (curext()=="krc" && this.RBOInventItemGroupId == "")
ret = checkfailed(strfmt("'%1' field cannot be empty!..",
new SysDictType(extendedTypeNum(RBORetailGroupId)).label()));
If there is a special label value dedicated at table level:
if (curext()=="krc" && this.RBOInventItemGroupId == "")
ret = checkfailed(strfmt("'%1' field cannot be empty!..",
new SysDictField(tablenum(WholeSalesCampaignGroup),
fieldnum(WholeSalesCampaignGroup,RBOInventItemGroupId)).label()));
5 Şubat 2016 Cuma
AXAPTA - Prevent user to change filter from grid header
SysQuery::findOrCreateRange(InventDim_DS.query().dataSourceTable(tablenum(InventDim)),fieldnum(InventDim,InventSiteId)).status(RangeStatus::Locked);
26 Ocak 2016 Salı
AX 2009 - Run report at back ground as PDF output
Make Interactive property of report as No. It's easy make it as run for html with change PrintMedium enum's value.
DocuType docuType;
TextIo textIo;
FileIOPermission fioPermission;
#File
ReportRun report;
;
docuType = DocuType::find("Temp");
fileName = strfmt(@"%1My Test Report.PDF", docuType.ArchivePath);
fioPermission = new FileIOPermission(fileName ,"RW");
fioPermission.assert();
if(Global::isRunningOnServer())
{
if(WinAPIServer::fileExists(fileName))
WINAPIServer::deleteFile(fileName);
}
else
{
if(WinAPI::fileExists(fileName))
WINAPI::deleteFile(fileName);
}
report = new ReportRun(new Args(ReportStr(KRC_InventCoverReport)));
report.printJobSettings().setTarget(PrintMedium::File);
report.printJobSettings().preferredTarget(PrintMedium::File);
report.printJobSettings().format(PrintFormat::PDF);
report.printJobSettings().fileName(fileName);
report.query().interactive(false);
report.run();
DocuType docuType;
TextIo textIo;
FileIOPermission fioPermission;
#File
ReportRun report;
;
docuType = DocuType::find("Temp");
fileName = strfmt(@"%1My Test Report.PDF", docuType.ArchivePath);
fioPermission = new FileIOPermission(fileName ,"RW");
fioPermission.assert();
if(Global::isRunningOnServer())
{
if(WinAPIServer::fileExists(fileName))
WINAPIServer::deleteFile(fileName);
}
else
{
if(WinAPI::fileExists(fileName))
WINAPI::deleteFile(fileName);
}
report = new ReportRun(new Args(ReportStr(KRC_InventCoverReport)));
report.printJobSettings().setTarget(PrintMedium::File);
report.printJobSettings().preferredTarget(PrintMedium::File);
report.printJobSettings().format(PrintFormat::PDF);
report.printJobSettings().fileName(fileName);
report.query().interactive(false);
report.run();
22 Ocak 2016 Cuma
Axapta - Why sometimes MultiSelectionHelper and why sometimes traditional read?
That's the traditional way to read selected records from a grid:
c = CustTable_DS.getFirst(true);
while (c.RecId != 0)
{
info(c.Name);
c = CustTable_DS.getNext();
}
But there's a problem with this way. If user just wants to process records at line instead of select a couple of records unfortunately that record will be missed. That's the workaround for this symptom:
c = CustTable_DS.getFirst(true);
noSelected = true;
while (c.RecId != 0)
{
noSelected = false;
info(c.Name);
c = CustTable_DS.getNext();
}
if (noSelected)
info(CustTable.Name);
MultiSelectionHelper solves this problem:
MultiSelectionHelper helper = MultiSelectionHelper::construct();
CustTable c;
;
helper.parmDatasource(CustTable_ds);
c = helper.getFirst();
while (c.RecId != 0)
{
info(c.Name);
c = helper.getNext();
}
But there is a disadvantage for MultiSelectionHelper class too. If user wants to sort records at grid and wants to process with this sorted list we have to use traditional way again because unfortunately MultiSelectionHelper doesn't care about users sort.
Using MultiselectionHelper from a class:
public static void main(Args _args)
{
SMAServiceOrderTable serviceOrder;
MultiSelectionHelper helper;
FormRun caller = _args.caller();
FormDataSource SMAServiceOrderTable_DS;
SMAServiceOrderTable_DS = caller.dataSource();
helper = MultiSelectionHelper::createFromCaller(caller);
helper.createQueryRanges(SMAServiceOrderTable_DS.queryBuildDataSource(),fieldStr(SMAServiceOrderTable,RecId));
serviceOrder = helper.getFirst();
while(serviceOrder)
{
info(strFmt("%1",serviceOrder.RecId));
serviceOrder = helper.getNext();
}
}
c = CustTable_DS.getFirst(true);
while (c.RecId != 0)
{
info(c.Name);
c = CustTable_DS.getNext();
}
But there's a problem with this way. If user just wants to process records at line instead of select a couple of records unfortunately that record will be missed. That's the workaround for this symptom:
c = CustTable_DS.getFirst(true);
noSelected = true;
while (c.RecId != 0)
{
noSelected = false;
info(c.Name);
c = CustTable_DS.getNext();
}
if (noSelected)
info(CustTable.Name);
MultiSelectionHelper solves this problem:
MultiSelectionHelper helper = MultiSelectionHelper::construct();
CustTable c;
;
helper.parmDatasource(CustTable_ds);
c = helper.getFirst();
while (c.RecId != 0)
{
info(c.Name);
c = helper.getNext();
}
But there is a disadvantage for MultiSelectionHelper class too. If user wants to sort records at grid and wants to process with this sorted list we have to use traditional way again because unfortunately MultiSelectionHelper doesn't care about users sort.
Using MultiselectionHelper from a class:
public static void main(Args _args)
{
SMAServiceOrderTable serviceOrder;
MultiSelectionHelper helper;
FormRun caller = _args.caller();
FormDataSource SMAServiceOrderTable_DS;
SMAServiceOrderTable_DS = caller.dataSource();
helper = MultiSelectionHelper::createFromCaller(caller);
helper.createQueryRanges(SMAServiceOrderTable_DS.queryBuildDataSource(),fieldStr(SMAServiceOrderTable,RecId));
serviceOrder = helper.getFirst();
while(serviceOrder)
{
info(strFmt("%1",serviceOrder.RecId));
serviceOrder = helper.getNext();
}
}
Etiketler:
AX,
ax 2012,
AXAPTA,
getfirst,
getnext,
grid,
multi,
multiselect,
multiselectionhelper,
record,
select,
sort
19 Ocak 2016 Salı
Axapta - Add dimension based filter to a query
When we try to add filter based on StrFmt like this; dimension[2] = "0001" it gives error because of brackets. I found the solution from a forum page :
QueryBuildDataSource qbds = query.addDataSource(tablenum(EmplTable));
...
qbds.addRange(fieldid2Ext(fieldnum(EmplTable, Dimension),1)).value("600742");
QueryBuildDataSource qbds = query.addDataSource(tablenum(EmplTable));
...
qbds.addRange(fieldid2Ext(fieldnum(EmplTable, Dimension),1)).value("600742");
23 Aralık 2015 Çarşamba
Axapta - Add date and time to XPO export dialog form
With this feature XPO files will be version controlled. I got this useful for backup code from Eren Polat .
Add these code parts to SysExportDialog forms methods:
classDeclaration:
str sDate, sTime, sDateTime;
str sMilliSeconds;
init:
sMilliSeconds = int2str(winApi::getTickCount());
sDate = int2str(year(today())) + strReplace(num2str(mthofyr(today()),2,0,0,0), ' ', '0') + strReplace(num2str(dayofmth(today()),2,0,0,0), ' ', '0');
sTime = strfmt("%1:%2", time2str(timeNow(),1,1), substr(sMilliSeconds, strlen(sMilliSeconds)-2,2));
sTime = strReplace(sTime,":","");
sDateTime = strfmt("_%1%2",sDate,sTime);
run:
//element.updateBox(fileNameNext(strfmt('%1%2_%3%4', filePath, preFix, treeNode.treeNodeName(), #xpo)));
element.updateBox(fileNameNext(strfmt('%1%2_%3%4%5', filePath, preFix, treeNode.treeNodeName(), sDateTime, #xpo)));
Add these code parts to SysExportDialog forms methods:
classDeclaration:
str sDate, sTime, sDateTime;
str sMilliSeconds;
init:
sMilliSeconds = int2str(winApi::getTickCount());
sDate = int2str(year(today())) + strReplace(num2str(mthofyr(today()),2,0,0,0), ' ', '0') + strReplace(num2str(dayofmth(today()),2,0,0,0), ' ', '0');
sTime = strfmt("%1:%2", time2str(timeNow(),1,1), substr(sMilliSeconds, strlen(sMilliSeconds)-2,2));
sTime = strReplace(sTime,":","");
sDateTime = strfmt("_%1%2",sDate,sTime);
run:
//element.updateBox(fileNameNext(strfmt('%1%2_%3%4', filePath, preFix, treeNode.treeNodeName(), #xpo)));
element.updateBox(fileNameNext(strfmt('%1%2_%3%4%5', filePath, preFix, treeNode.treeNodeName(), sDateTime, #xpo)));
22 Aralık 2015 Salı
AX 2012 - Development with new WMS handheld terminal environment
Microsoft abandoned old WMS-II and put NEW WMS to AX 2012 which bought
from another company. There is a web working handheld terminal solution too with this NEW WMS. This handheld solution while works competely at web also it runs AX environmet too for easy development and debug. We can run it with WHSWorkExecute menu item at AX. Microsoft published two guide for WMS and TM. Handheld user creating and customizing described at WMS guide. So I passed this issue and go in development.
There isn't anything about development at guide. I used a blog about handheld development. I learned some things I'm sure I couldn't find out if didn't read this blog. :)
I assume you know issues described at WMS guide.
WMS handheld developments all works with X++ classes. Every new menu item is a class. All menu item classes are begins with WMSWorkExecuteDisplay name.
We assume develop our own menu item. If we don't develop our own menu item we can skip this step:
When you add your own work and/or indirect menu item you should add it to WHSWorkCreationProcess enum as an element. If your menu item is a work you have to also add it to WHSWorkExecuteMode enum too. According to blog which I mentioned before element name and label added WHSWorkExecuteMode should be same as element added WHSWorkCreationProcess, I never tested it, just followed. Our element name is Test.
Our class WMSWorkExecuteDisplayTest . Class should be extended from WHSWorkExecuteDisplay.
We will see a new menu item at Mobile device menu items ->General->Work creation process. There's one step to tie our class to this menu item; Add this switch/case code part to WHSWorkExecuteDisplay classes construct method:
case WHSWorkExecuteMode::MyTest : return WHSWorkExecuteDisplayTest::construct();
All menu items at handheld development work with their own displayForm method at their classes. I copied one of WMS classes and used my own work. displayForm method works as stages. It follows with a variable named step. First step is zero. The class I copied and used ends with stage 2 as save stage. You can follow stages as like as original classes with a switch/case block at displayForm.
We use buildControl for add a new control to our Web form:
ret += [this.buildControl(#RFText, #vendor, "@SYS14048", 1, pass.lookup(#vendor), extendedTypeNum(VendAccount), '', 0,false)];
If our macro #vendor doesn't exists at WHSRF macro library we should add it to WHRSF macro library. I just copy/paste method parameters from blog:
To check if our data mapped or not which we used at control we use exists method, use lookup method to get value. Usage is like that usually:
if (pass.exists(#vendor) && pass.lookup(#vendor) != '")
To add/chanhe data value use insert method:
pass.insert(#vendor,purchTable.OrderAccount);
By the way if you need extra parameters which are seem at system menu items but don't seem at user defined items (look at red circles),
you have to do small update at WHSRFMenuItem forms togglefields method:
if (whsrfMenuItemTable.WorkCreationProcess == WHSWorkCreationProcess::PurchaseOrderItemReceiving ||
whsrfMenuItemTable.WorkCreationProcess ==
...
...
//memre 22.12.2015
whsrfMenuItemTable.WorkCreationProcess == WHSWorkCreationProcess::PurchaseOrderLineReceivingWithVendor)
//memre
...
...
Meanwhile you might do a small update at WHSRFControlData classes processControl method. With a development at mine while original class can create LP (License Plate) number, mine couldn't. Reason was check at processControl look for enum values. This small update worked for me:
case #LicensePlateId:
...
...
else if (mode != WHSWorkExecuteMode::AdjustmentIn &&
mode != WHSWorkExecuteMode::ReportAsFinished &&
...
...
...
//memre 22.12.2015
mode != WHSWorkExecuteMode::PurchaseOrderLineReceivingWithVendor &&
//memre
mode != WHSWorkExecuteMode::PurchaseOrderItemReceivingAndLocate &&
...
...
That's all I learned to main principles of handheld development of new WMS. For more information you can look for this blog which I used too and WMS documents.
There isn't anything about development at guide. I used a blog about handheld development. I learned some things I'm sure I couldn't find out if didn't read this blog. :)
I assume you know issues described at WMS guide.
WMS handheld developments all works with X++ classes. Every new menu item is a class. All menu item classes are begins with WMSWorkExecuteDisplay name.
We assume develop our own menu item. If we don't develop our own menu item we can skip this step:
When you add your own work and/or indirect menu item you should add it to WHSWorkCreationProcess enum as an element. If your menu item is a work you have to also add it to WHSWorkExecuteMode enum too. According to blog which I mentioned before element name and label added WHSWorkExecuteMode should be same as element added WHSWorkCreationProcess, I never tested it, just followed. Our element name is Test.
Our class WMSWorkExecuteDisplayTest . Class should be extended from WHSWorkExecuteDisplay.
We will see a new menu item at Mobile device menu items ->General->Work creation process. There's one step to tie our class to this menu item; Add this switch/case code part to WHSWorkExecuteDisplay classes construct method:
case WHSWorkExecuteMode::MyTest : return WHSWorkExecuteDisplayTest::construct();
All menu items at handheld development work with their own displayForm method at their classes. I copied one of WMS classes and used my own work. displayForm method works as stages. It follows with a variable named step. First step is zero. The class I copied and used ends with stage 2 as save stage. You can follow stages as like as original classes with a switch/case block at displayForm.
We use buildControl for add a new control to our Web form:
ret += [this.buildControl(#RFText, #vendor, "@SYS14048", 1, pass.lookup(#vendor), extendedTypeNum(VendAccount), '', 0,false)];
If our macro #vendor doesn't exists at WHSRF macro library we should add it to WHRSF macro library. I just copy/paste method parameters from blog:
ame | Data type | Description |
_controlType | str | This tells the framework what type of HTML control to build for the
provided data. This must be one of the following macro definitions
(defined in WHSRF): #define.RFButton('button') #define.RFLabel('label') #define.RFText('text') #define.RFPassword('password') #define.RFListbox('listbox') #define.RFCombobox('combobox') #define.RFError('error') |
_name | str | Name used for the control in the rendered HTML. Also used as the key to store data persisted in the control within the WHSRFPassthrough Map object. |
_label | str | Text to display to the user in the UI for this control. |
_newLine | int | Field that specifies how this control should be rendered in the UI.
If a 1 is passed it means this control should be situated on a new line
in the HTML table. If a 0 is passed this control will remain on the same
line as the previous control(s). Note this only applied to the default display settings view page used to render the HTML pages. If you have customized the display settings view it is possible this no longer renders in the same way. |
_data | str | Data to display within the control (for example the data to include in a textbox control). Does not apply to label or button controls. |
_inputType | ExtendedTypeId | DataType of the field – important for data entry fields so the correct validation rules can be applied by the framework. For controls that do not require validation the macro #WHSRFUndefinedDataType can be used. |
_error | str | Indicates if this control contains validation errors – used to help the user understand which controls contain validation or input errors information. |
_defaultButton | int | Indicates the specified button should be executed as the default form submit – for example when the user presses the enter key in the UI. This is typically applied to the “OK” button in the forms. |
_enabled | boolean | Indicates if the control should be editable in the UI or if it should be displayed in a read-only mode. Optional – defaults to true. |
_selected | int | If set to 1 this will be the currently active control when the user comes to this page. Optional – defaults to empty string. |
_color | WHSRFColorText | Color to set the control textual data. Optional – defaults to WHSRFColorTest::Default. |
To check if our data mapped or not which we used at control we use exists method, use lookup method to get value. Usage is like that usually:
if (pass.exists(#vendor) && pass.lookup(#vendor) != '")
To add/chanhe data value use insert method:
pass.insert(#vendor,purchTable.OrderAccount);
By the way if you need extra parameters which are seem at system menu items but don't seem at user defined items (look at red circles),
you have to do small update at WHSRFMenuItem forms togglefields method:
if (whsrfMenuItemTable.WorkCreationProcess == WHSWorkCreationProcess::PurchaseOrderItemReceiving ||
whsrfMenuItemTable.WorkCreationProcess ==
...
...
//memre 22.12.2015
whsrfMenuItemTable.WorkCreationProcess == WHSWorkCreationProcess::PurchaseOrderLineReceivingWithVendor)
//memre
...
...
Meanwhile you might do a small update at WHSRFControlData classes processControl method. With a development at mine while original class can create LP (License Plate) number, mine couldn't. Reason was check at processControl look for enum values. This small update worked for me:
case #LicensePlateId:
...
...
else if (mode != WHSWorkExecuteMode::AdjustmentIn &&
mode != WHSWorkExecuteMode::ReportAsFinished &&
...
...
...
//memre 22.12.2015
mode != WHSWorkExecuteMode::PurchaseOrderLineReceivingWithVendor &&
//memre
mode != WHSWorkExecuteMode::PurchaseOrderItemReceivingAndLocate &&
...
...
That's all I learned to main principles of handheld development of new WMS. For more information you can look for this blog which I used too and WMS documents.
1 Aralık 2015 Salı
AX 2012 - Try - catch
There is a good article about try/catch. This code copied from that article:
void post(int amount)
{
#OCCRetryCount
MyPostings debit;
MyPostings credit;
System.Exception ex;
try
{
ttsBegin;
debit = MyPostings::find('debit', true);
debit.Amount += amount;
debit.update();
credit = MyPostings::find('credit', true);
credit.Amount -= amount;
credit.update();
ttsCommit;
}
catch (Exception::UpdateConflict)
{
if (appl.ttsLevel() != 0)
{
throw Exception::UpdateConflict;
}
if (xSession::currentRetryCount() >= #RetryNum)
{
throw Exception::UpdateConflictNotRecovered;
}
retry;
}
catch (Exception::DuplicateKeyException)
{
if (appl.ttsLevel() != 0)
{
throw Exception::DuplicateKeyException;
}
if (xSession::currentRetryCount() >= #RetryNum)
{
throw Exception::DuplicateKeyExceptionNotRecovered;
}
retry;
}
catch (Exception::Deadlock)
{
if (xSession::currentRetryCount() >= #RetryNum)
{
throw Exception::Deadlock;
}
retry;
}
catch(Exception::CLRError)
{
ex = ClrInterop::getLastException();
if (ex != null)
{
ex = ex.get_InnerException();
if (ex != null)
{
error(ex.ToString());
}
else
error(AifUtil::getClrErrorMessage());
}
else
error(AifUtil::getClrErrorMessage());
}
catch
{
warning(infolog.text());
if (appl.ttsLevel() != 0)
{
throw error("Something happened, that the logic was not designed to handle – please log a bug.");
}
error("Something bad happened - try again later");
}
}
void post(int amount)
{
#OCCRetryCount
MyPostings debit;
MyPostings credit;
System.Exception ex;
try
{
ttsBegin;
debit = MyPostings::find('debit', true);
debit.Amount += amount;
debit.update();
credit = MyPostings::find('credit', true);
credit.Amount -= amount;
credit.update();
ttsCommit;
}
catch (Exception::UpdateConflict)
{
if (appl.ttsLevel() != 0)
{
throw Exception::UpdateConflict;
}
if (xSession::currentRetryCount() >= #RetryNum)
{
throw Exception::UpdateConflictNotRecovered;
}
retry;
}
catch (Exception::DuplicateKeyException)
{
if (appl.ttsLevel() != 0)
{
throw Exception::DuplicateKeyException;
}
if (xSession::currentRetryCount() >= #RetryNum)
{
throw Exception::DuplicateKeyExceptionNotRecovered;
}
retry;
}
catch (Exception::Deadlock)
{
if (xSession::currentRetryCount() >= #RetryNum)
{
throw Exception::Deadlock;
}
retry;
}
catch(Exception::CLRError)
{
ex = ClrInterop::getLastException();
if (ex != null)
{
ex = ex.get_InnerException();
if (ex != null)
{
error(ex.ToString());
}
else
error(AifUtil::getClrErrorMessage());
}
else
error(AifUtil::getClrErrorMessage());
catch
{
warning(infolog.text());
if (appl.ttsLevel() != 0)
{
throw error("Something happened, that the logic was not designed to handle – please log a bug.");
}
error("Something bad happened - try again later");
}
}
AX 2012 - Add/update Customer and Vendor accounts
Customer account:
CustTable custTable;
DirParty dirParty;
DirPartyPostalAddressView dirPartyPostalAddressView;
DirPartyContactInfoView dirPartyContactInfo;
CustGroup custGroup;
DirPartyTable partyTable;
DirPartyLocation dirLocation;
LogisticsPostalAddress address;
LogisticsLocation logisticsLocation;
;
select firstOnly forUpdate custTable
where custTable.AccountNum == custAccount;
//-------------------- custtable --------------------------
if (custTable.RecId == 0)
{
custTable.initValue();
custTable.AccountNum = custAccount;
custTable.CustGroup = custGroupId;
custTable.VATNum = vatNum;
custTable.IdentificationNumber = identificationNumber;
custTable.TaxOfficeName_TR = taxOfficeName_TR;
custTable.Blocked = custBlocked;
custTable.insert(DirPartyType::Organization, custName);
}
else
{
custTable.CustGroup = custGroupId;
custTable.VATNum = vatNum;
custTable.TaxOfficeName_TR = taxOfficeName_TR;
custTable.IdentificationNumber = identificationNumber;
custTable.Blocked = custBlocked;
custTable.update();
update_recordSet partyTable setting name = custName, NameAlias = nameAlias
where partyTable.RecId == custTable.Party;
}
//-------------------- adres -----------------------------
dirParty = DirParty::constructFromCommon(custTable);
select firstonly dirPartyPostalAddressView
where dirPartyPostalAddressView.Party == custTable.Party &&
dirPartyPostalAddressView.LocationName == "Address";
dirPartyPostalAddressView.LocationName = "Adres";
dirPartyPostalAddressView.City = city;
dirPartyPostalAddressView.State = state;
dirPartyPostalAddressView.Street = street;
dirPartyPostalAddressView.IsPrimary = NoYes::Yes;
dirPartyPostalAddressView.CountryRegionId = countryRegionId;
dirParty.createOrUpdatePostalAddress(dirPartyPostalAddressView);
if (phone)
{
dirParty = DirParty::constructFromCommon(custTable);
select firstonly dirPartyContactInfo
where dirPartyContactInfo.Party == custTable.Party &&
dirPartyContactInfo.LocationName == "Phone";
dirPartyContactInfo.LocationName ="Phone";
dirPartyContactInfo.Locator = phone;
dirPartyContactInfo.IsPrimary = NoYes::Yes;
dirPartyContactInfo.Type = LogisticsElectronicAddressMethodType::Phone;
dirParty.createOrUpdateContactInfo(dirPartyContactInfo);
}
if (email)
{
select firstonly dirPartyContactInfo
where dirPartyContactInfo.Party == custTable.Party &&
dirPartyContactInfo.LocationName == "E-mail";
dirPartyContactInfo.LocationName ="E-mail";
dirPartyContactInfo.Locator = email;
dirPartyContactInfo.IsPrimary = NoYes::Yes;
dirPartyContactInfo.Type = LogisticsElectronicAddressMethodType::Email;
dirParty.createOrUpdateContactInfo(dirPartyContactInfo);
}
There's just one difference with vendor and customer creating is about separated creating DirParty record. If account is a person instead of organization, you have to use DirPersonName table instead of DirOrganization table:
VendTable vendTable;
DirParty dirParty;
DirPartyPostalAddressView dirPartyPostalAddressView;
DirPartyContactInfoView dirPartyContactInfo;
VendGroup vendGroup;
DirPartyTable partyTable;
DirPartyLocation dirLocation;
LogisticsPostalAddress address;
LogisticsLocation logisticsLocation;
DirOrganization organization;
;
select firstOnly forUpdate vendTable
where vendTable.AccountNum == vendAccount;
//-------------------- vendTable --------------------------
if (vendTable.RecId == 0)
{
organization.Name = vendName;
organization.NameAlias = nameAlias;
organization.LanguageId = CompanyInfo::languageId();
organization.insert();
vendTable.initValue();
vendTable.party = organization.recid;
vendTable.AccountNum = vendAccount;
vendTable.vendGroup = vendGroupId;
vendTable.VATNum = vatNum;
vendTable.Blocked = VendBlocked;
vendTable.TaxOfficeName_TR = taxOfficeName_TR;
vendTable.insert();
}
else
{
vendTable.vendGroup = vendGroupId;
vendTable.VATNum = vatNum;
vendTable.Blocked = VendBlocked;
vendTable.update();
update_recordSet organization setting name = vendName, NameAlias = nameAlias
where organization.RecId == vendTable.Party;
}
CustTable custTable;
DirParty dirParty;
DirPartyPostalAddressView dirPartyPostalAddressView;
DirPartyContactInfoView dirPartyContactInfo;
CustGroup custGroup;
DirPartyTable partyTable;
DirPartyLocation dirLocation;
LogisticsPostalAddress address;
LogisticsLocation logisticsLocation;
;
select firstOnly forUpdate custTable
where custTable.AccountNum == custAccount;
//-------------------- custtable --------------------------
if (custTable.RecId == 0)
{
custTable.initValue();
custTable.AccountNum = custAccount;
custTable.CustGroup = custGroupId;
custTable.VATNum = vatNum;
custTable.IdentificationNumber = identificationNumber;
custTable.TaxOfficeName_TR = taxOfficeName_TR;
custTable.Blocked = custBlocked;
custTable.insert(DirPartyType::Organization, custName);
}
else
{
custTable.CustGroup = custGroupId;
custTable.VATNum = vatNum;
custTable.TaxOfficeName_TR = taxOfficeName_TR;
custTable.IdentificationNumber = identificationNumber;
custTable.Blocked = custBlocked;
custTable.update();
update_recordSet partyTable setting name = custName, NameAlias = nameAlias
where partyTable.RecId == custTable.Party;
}
//-------------------- adres -----------------------------
dirParty = DirParty::constructFromCommon(custTable);
select firstonly dirPartyPostalAddressView
where dirPartyPostalAddressView.Party == custTable.Party &&
dirPartyPostalAddressView.LocationName == "Address";
dirPartyPostalAddressView.LocationName = "Adres";
dirPartyPostalAddressView.City = city;
dirPartyPostalAddressView.State = state;
dirPartyPostalAddressView.Street = street;
dirPartyPostalAddressView.IsPrimary = NoYes::Yes;
dirPartyPostalAddressView.CountryRegionId = countryRegionId;
dirParty.createOrUpdatePostalAddress(dirPartyPostalAddressView);
if (phone)
{
dirParty = DirParty::constructFromCommon(custTable);
select firstonly dirPartyContactInfo
where dirPartyContactInfo.Party == custTable.Party &&
dirPartyContactInfo.LocationName == "Phone";
dirPartyContactInfo.LocationName ="Phone";
dirPartyContactInfo.Locator = phone;
dirPartyContactInfo.IsPrimary = NoYes::Yes;
dirPartyContactInfo.Type = LogisticsElectronicAddressMethodType::Phone;
dirParty.createOrUpdateContactInfo(dirPartyContactInfo);
}
if (email)
{
select firstonly dirPartyContactInfo
where dirPartyContactInfo.Party == custTable.Party &&
dirPartyContactInfo.LocationName == "E-mail";
dirPartyContactInfo.LocationName ="E-mail";
dirPartyContactInfo.Locator = email;
dirPartyContactInfo.IsPrimary = NoYes::Yes;
dirPartyContactInfo.Type = LogisticsElectronicAddressMethodType::Email;
dirParty.createOrUpdateContactInfo(dirPartyContactInfo);
}
There's just one difference with vendor and customer creating is about separated creating DirParty record. If account is a person instead of organization, you have to use DirPersonName table instead of DirOrganization table:
VendTable vendTable;
DirParty dirParty;
DirPartyPostalAddressView dirPartyPostalAddressView;
DirPartyContactInfoView dirPartyContactInfo;
VendGroup vendGroup;
DirPartyTable partyTable;
DirPartyLocation dirLocation;
LogisticsPostalAddress address;
LogisticsLocation logisticsLocation;
DirOrganization organization;
;
select firstOnly forUpdate vendTable
where vendTable.AccountNum == vendAccount;
//-------------------- vendTable --------------------------
if (vendTable.RecId == 0)
{
organization.Name = vendName;
organization.NameAlias = nameAlias;
organization.LanguageId = CompanyInfo::languageId();
organization.insert();
vendTable.initValue();
vendTable.party = organization.recid;
vendTable.AccountNum = vendAccount;
vendTable.vendGroup = vendGroupId;
vendTable.VATNum = vatNum;
vendTable.Blocked = VendBlocked;
vendTable.TaxOfficeName_TR = taxOfficeName_TR;
vendTable.insert();
}
else
{
vendTable.vendGroup = vendGroupId;
vendTable.VATNum = vatNum;
vendTable.Blocked = VendBlocked;
vendTable.update();
update_recordSet organization setting name = vendName, NameAlias = nameAlias
where organization.RecId == vendTable.Party;
}
Kaydol:
Kayıtlar (Atom)