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)));
23 Aralık 2015 Çarşamba
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;
}
30 Kasım 2015 Pazartesi
AX 2012 - Add/update product
//http://daxtechies.blogspot.com.tr/2013/12/ax2012-r2-creating-product-or-product.html
if ( InventTable::find(itemId).ItemId != "" )
this.updateProduct();
else
this.newProduct();
itemRecId = EcoResProduct::findByDisplayProductNumber(itemId).RecId;
this.updateInventTable();
private void newProduct(EcoResProductSubtype _subType = EcoResProductSubtype::Product)
{
EcoResProductService erProdSvc;
EcoResEcoResProduct ecoResProd;
EcoResEcoResProduct_Product_Master prodMaster;
EcoResEcoResProduct_Translation translation;
EcoResEcoResProduct_Identifier identifier;
EcoResEcoResProduct_ProductDimGroup prodDimGroup;
EcoResEcoResProduct_Product_Distinct distMaster;
EcoResEcoResProduct_StorageDimGroup storDimGroup;
EcoResEcoResProduct_TrackingDimGroup tracDimGroup;
InventTable inventTable;
erProdSvc = EcoResProductService::construct();
ecoResProd = new EcoResEcoResProduct();
if (_subType == EcoResProductSubtype::ProductMaster) //varianted product
{
prodMaster = new EcoResEcoResProduct_Product_Master();
prodMaster.parmDisplayProductNumber(itemId);
prodMaster.parmProductType(EcoResProductType::Item);
prodMaster.parmSearchName(nameAlias);
prodMaster.parmVariantConfigurationTechnology(EcoResVariantConfigurationTechnologyType::PredefinedVariants);
translation = prodMaster.createTranslation().addNew();
identifier = prodMaster.createIdentifier().addNew();
prodDimGroup = prodMaster.createProductDimGroup().addNew();
prodDimGroup.parmProduct(itemId);
prodDimGroup.parmProductDimensionGroup(dimGroup);
storDimGroup = prodMaster.createStorageDimGroup().addNew();
tracDimGroup = prodMaster.createTrackingDimGroup().addNew();
}
if (_subType == EcoResProductSubtype::Product) //product
{
distMaster = new EcoResEcoResProduct_Product_Distinct();
distMaster.parmDisplayProductNumber(itemId);
distMaster.parmProductType(EcoResProductType::Item);
distMaster.parmSearchName(nameAlias);
translation = distMaster.createTranslation().addNew();
identifier = distMaster.createIdentifier().addNew();
storDimGroup = distMaster.createStorageDimGroup().addNew();
tracDimGroup = distMaster.createTrackingDimGroup().addNew();
}
translation.parmDescription(itemName);
translation.parmLanguageId(SystemParameters::getSystemLanguageId());
translation.parmName(itemName);
storDimGroup.parmProduct(itemId);
storDimGroup.parmStorageDimensionGroup(storeGroup);
tracDimGroup.parmProduct(itemId);
tracDimGroup.parmTrackingDimensionGroup(trackingGroup);
identifier.parmProductNumber(itemId);
if (_subType == EcoResProductSubtype::ProductMaster) //varianted product
ecoResProd.createProduct().add(prodMaster);
if (_subType == EcoResProductSubtype::Product) //product
ecoResProd.createProduct().add(distMaster);
erProdSvc.create(ecoResProd);
EcoResProductReleaseManagerBase::releaseProduct(EcoResProduct::findByProductNumber(itemId).RecId,CompanyInfo::find().RecId);
}
private void updateProduct()
{
InventTableModule inventModule;
EcoResProductTranslation translation;
update_recordSet inventModule setting unitId = unitId
where inventModule.ItemId == itemId;
update_recordSet translation
setting Description = itemName,
Name = itemName
where translation.Product == itemRecId &&
translation.LanguageId == SystemParameters::getSystemLanguageId();
}
private void updateInventTable()
{
InventTable inventTable;
InventModelGroupItem modelGroup;
update_recordSet inventTable
setting NetWeight = netWeight,
TaraWeight = taraWeight,
GrossDepth = depth,
GrossWidth = width,
GrossHeight = height,
UnitVolume = volume
where
inventTable.Product == itemRecId;
if (inventModelGroupId == "")
delete_from modelGroup
where modelGroup.ItemId == itemId && modelGroup.ItemDataAreaId == curext() &&
modelGroup.ModelGroupDataAreaId == curext();
else
{
select firstOnly forUpdate modelGroup
where modelGroup.ItemId == itemId && modelGroup.ItemDataAreaId == curext() &&
modelGroup.ModelGroupDataAreaId == curext();
modelGroup.ItemId = itemId;
modelGroup.ItemDataAreaId = curext();
modelGroup.ModelGroupDataAreaId = curext();
modelGroup.ModelGroupId = inventModelGroupId;
if (modelGroup.RecId == 0)
modelGroup.insert();
else
modelGroup.update();
}
}
if ( InventTable::find(itemId).ItemId != "" )
this.updateProduct();
else
this.newProduct();
itemRecId = EcoResProduct::findByDisplayProductNumber(itemId).RecId;
this.updateInventTable();
private void newProduct(EcoResProductSubtype _subType = EcoResProductSubtype::Product)
{
EcoResProductService erProdSvc;
EcoResEcoResProduct ecoResProd;
EcoResEcoResProduct_Product_Master prodMaster;
EcoResEcoResProduct_Translation translation;
EcoResEcoResProduct_Identifier identifier;
EcoResEcoResProduct_ProductDimGroup prodDimGroup;
EcoResEcoResProduct_Product_Distinct distMaster;
EcoResEcoResProduct_StorageDimGroup storDimGroup;
EcoResEcoResProduct_TrackingDimGroup tracDimGroup;
InventTable inventTable;
erProdSvc = EcoResProductService::construct();
ecoResProd = new EcoResEcoResProduct();
if (_subType == EcoResProductSubtype::ProductMaster) //varianted product
{
prodMaster = new EcoResEcoResProduct_Product_Master();
prodMaster.parmDisplayProductNumber(itemId);
prodMaster.parmProductType(EcoResProductType::Item);
prodMaster.parmSearchName(nameAlias);
prodMaster.parmVariantConfigurationTechnology(EcoResVariantConfigurationTechnologyType::PredefinedVariants);
translation = prodMaster.createTranslation().addNew();
identifier = prodMaster.createIdentifier().addNew();
prodDimGroup = prodMaster.createProductDimGroup().addNew();
prodDimGroup.parmProduct(itemId);
prodDimGroup.parmProductDimensionGroup(dimGroup);
storDimGroup = prodMaster.createStorageDimGroup().addNew();
tracDimGroup = prodMaster.createTrackingDimGroup().addNew();
}
if (_subType == EcoResProductSubtype::Product) //product
{
distMaster = new EcoResEcoResProduct_Product_Distinct();
distMaster.parmDisplayProductNumber(itemId);
distMaster.parmProductType(EcoResProductType::Item);
distMaster.parmSearchName(nameAlias);
translation = distMaster.createTranslation().addNew();
identifier = distMaster.createIdentifier().addNew();
storDimGroup = distMaster.createStorageDimGroup().addNew();
tracDimGroup = distMaster.createTrackingDimGroup().addNew();
}
translation.parmDescription(itemName);
translation.parmLanguageId(SystemParameters::getSystemLanguageId());
translation.parmName(itemName);
storDimGroup.parmProduct(itemId);
storDimGroup.parmStorageDimensionGroup(storeGroup);
tracDimGroup.parmProduct(itemId);
tracDimGroup.parmTrackingDimensionGroup(trackingGroup);
identifier.parmProductNumber(itemId);
if (_subType == EcoResProductSubtype::ProductMaster) //varianted product
ecoResProd.createProduct().add(prodMaster);
if (_subType == EcoResProductSubtype::Product) //product
ecoResProd.createProduct().add(distMaster);
erProdSvc.create(ecoResProd);
EcoResProductReleaseManagerBase::releaseProduct(EcoResProduct::findByProductNumber(itemId).RecId,CompanyInfo::find().RecId);
}
private void updateProduct()
{
InventTableModule inventModule;
EcoResProductTranslation translation;
update_recordSet inventModule setting unitId = unitId
where inventModule.ItemId == itemId;
update_recordSet translation
setting Description = itemName,
Name = itemName
where translation.Product == itemRecId &&
translation.LanguageId == SystemParameters::getSystemLanguageId();
}
private void updateInventTable()
{
InventTable inventTable;
InventModelGroupItem modelGroup;
update_recordSet inventTable
setting NetWeight = netWeight,
TaraWeight = taraWeight,
GrossDepth = depth,
GrossWidth = width,
GrossHeight = height,
UnitVolume = volume
where
inventTable.Product == itemRecId;
if (inventModelGroupId == "")
delete_from modelGroup
where modelGroup.ItemId == itemId && modelGroup.ItemDataAreaId == curext() &&
modelGroup.ModelGroupDataAreaId == curext();
else
{
select firstOnly forUpdate modelGroup
where modelGroup.ItemId == itemId && modelGroup.ItemDataAreaId == curext() &&
modelGroup.ModelGroupDataAreaId == curext();
modelGroup.ItemId = itemId;
modelGroup.ItemDataAreaId = curext();
modelGroup.ModelGroupDataAreaId = curext();
modelGroup.ModelGroupId = inventModelGroupId;
if (modelGroup.RecId == 0)
modelGroup.insert();
else
modelGroup.update();
}
}
16 Kasım 2015 Pazartesi
AXAPT - Change company with code
It's easy to change company with code. But there are two small points to care; if select/SQL used, buffer table name should be make null at every reuse. IF query used query should be create again in every loop. Query.reset() doesn't work for clear query:
Query:
changeCompany(dataArea.Id)
{
qRun = new QueryRun(q);
while (qRun.next())
{
custTable = qRun.get(tablenum(CustTable));
...
}
}
Select/SQL:
changeCompany(dataArea.Id)
{
custTable = null;
subSegmentGroup = null;
while select AccountNum from custTable
...
Query:
changeCompany(dataArea.Id)
{
qRun = new QueryRun(q);
while (qRun.next())
{
custTable = qRun.get(tablenum(CustTable));
...
}
}
Select/SQL:
changeCompany(dataArea.Id)
{
custTable = null;
subSegmentGroup = null;
while select AccountNum from custTable
...
13 Kasım 2015 Cuma
AXAPTA - Security check with X++ code
Sometimes we need security check with code (Display method etc...):
SecurityKeySet securityKeys = new SecurityKeySet();
;
securityKeys.loadUserRights(curuserid());
if (securityKeys.access(securitykeynum("KRC_CrossCompany")) == AccessType::NoAccess)
chkCrsCompany.value(NoYes::No);
SecurityKeySet securityKeys = new SecurityKeySet();
;
securityKeys.loadUserRights(curuserid());
if (securityKeys.access(securitykeynum("KRC_CrossCompany")) == AccessType::NoAccess)
chkCrsCompany.value(NoYes::No);
12 Kasım 2015 Perşembe
AXAPTA - Add extra action to info function with SysInfoAction class
There're two ways to call a form with this class:
info(strfmt("Sales order created: %1",salesTable.SalesId),"",
SysInfoAction_TableField::newBuffer(salesTable));
Difference with this way with up is while this way sends record data to form, upper way doesn't:
SysInfoAction_FormRun infoAction = SysInfoAction_FormRun::newFormName(formStr(SalesTable));
;
infoAction.parmCallerBuffer(salestable);
info(strfmt("Satış siparişi oluşturuldu: %1",salesTable.SalesId),"",
infoaction);
info(strfmt("Sales order created: %1",salesTable.SalesId),"",
SysInfoAction_TableField::newBuffer(salesTable));
Difference with this way with up is while this way sends record data to form, upper way doesn't:
SysInfoAction_FormRun infoAction = SysInfoAction_FormRun::newFormName(formStr(SalesTable));
;
infoAction.parmCallerBuffer(salestable);
info(strfmt("Satış siparişi oluşturuldu: %1",salesTable.SalesId),"",
infoaction);
16 Ekim 2015 Cuma
AXAPTA - Pack-unpack at forms
ClassDeclaration method:
Container packedQuery;
SysQueryRun qRun;
str dummy;
#DEFINE.CurrentVersion(1)
#LOCALMACRO.CurrentList
dummy,
packedQuery
#ENDMACRO
Other methods:
container pack()
{
;
dummy = txtDummy.valuestr();
return [#CurrentVersion,#CurrentList];
}
public boolean unpack(container _packedClass)
{
int version = conPeek(_packedClass,1);
switch (version)
{
case #CurrentVersion:
[version,#CurrentList] = _packedClass;
break;
default:
return false;
}
return true;
}
public void init()
{
;
xSysLastValue::getLast(this);
super();
txtDummy(dummy);
element.initQuery();
}
public void close()
{
super();
xSysLastValue::saveLast(this);
}
void initParmDefault()
{
}
private IdentifierName lastValueDesignName()
{
return '';
}
private IdentifierName lastValueElementName()
{
return this.name();
}
private UtilElementType lastValueType()
{
return UtilElementType::Form;
}
private UserId lastValueUserId()
{
return curuserid();
}
public dataAreaId lastValueDataAreaId()
{
return curExt();
}
This method is not mandatory, but you may write like this if you would like to use query:
void initQuery()
{
Query q;
QueryBuildDataSource qbds;
QueryBuildDataSource qbds2;
QueryBuildRange qRange;
;
if (packedQuery)
qRun = new SysQueryRun(packedQuery);
else
{
q = new query();
qbds = q.addDataSource(tablenum(EmplTable));
qRun = new SysQueryRun(q);
}
qRun.promptLoadLastUsedQuery(false);
}
This one is for select button of query, so of course not mandatory too:
void clicked()
{
;
super();
if (qRun.prompt())
packedQuery = qRun.pack();
}
Container packedQuery;
SysQueryRun qRun;
str dummy;
#DEFINE.CurrentVersion(1)
#LOCALMACRO.CurrentList
dummy,
packedQuery
#ENDMACRO
Other methods:
container pack()
{
;
dummy = txtDummy.valuestr();
return [#CurrentVersion,#CurrentList];
}
public boolean unpack(container _packedClass)
{
int version = conPeek(_packedClass,1);
switch (version)
{
case #CurrentVersion:
[version,#CurrentList] = _packedClass;
break;
default:
return false;
}
return true;
}
public void init()
{
;
xSysLastValue::getLast(this);
super();
txtDummy(dummy);
element.initQuery();
}
public void close()
{
super();
xSysLastValue::saveLast(this);
}
void initParmDefault()
{
}
private IdentifierName lastValueDesignName()
{
return '';
}
private IdentifierName lastValueElementName()
{
return this.name();
}
private UtilElementType lastValueType()
{
return UtilElementType::Form;
}
private UserId lastValueUserId()
{
return curuserid();
}
public dataAreaId lastValueDataAreaId()
{
return curExt();
}
This method is not mandatory, but you may write like this if you would like to use query:
void initQuery()
{
Query q;
QueryBuildDataSource qbds;
QueryBuildDataSource qbds2;
QueryBuildRange qRange;
;
if (packedQuery)
qRun = new SysQueryRun(packedQuery);
else
{
q = new query();
qbds = q.addDataSource(tablenum(EmplTable));
qRun = new SysQueryRun(q);
}
qRun.promptLoadLastUsedQuery(false);
}
This one is for select button of query, so of course not mandatory too:
void clicked()
{
;
super();
if (qRun.prompt())
packedQuery = qRun.pack();
}
12 Ekim 2015 Pazartesi
AXAPTA - Add right click menu popup to form control
You can do this with overwrite control's showContextMenu method:
public int showContextMenu(int _menuHandle)
{
int ret;
int bar1,bar2;
PopupMenu pMenu = PopupMenu::create(_menuHandle,element.hWnd());
;
bar1 = pMenu.insertItem("Selection 1");
bar2 = pMenu.insertItem("Selection 2");
ret = pMenu.draw();
switch (ret)
{
case -1 :
break;
case bar1:
...
case bar2:
...
default :
break;
}
return ret;
}
public int showContextMenu(int _menuHandle)
{
int ret;
int bar1,bar2;
PopupMenu pMenu = PopupMenu::create(_menuHandle,element.hWnd());
;
bar1 = pMenu.insertItem("Selection 1");
bar2 = pMenu.insertItem("Selection 2");
ret = pMenu.draw();
switch (ret)
{
case -1 :
break;
case bar1:
...
case bar2:
...
default :
break;
}
return ret;
}
8 Ekim 2015 Perşembe
Axapta - Send and receive dataset
I took this code part from Fatih Demirci's developments.
Send dataset:
System.Data.DataTable dataTable;
System.Data.DataColumnCollection dataTableColumns;
System.Data.DataRowCollection DataRowCollection;
System.Data.DataRow tmpRow;
System.Data.DataSet dataset = new System.Data.DataSet();
System.Data.DataTableCollection dataTableCollection;
dataTable = new System.Data.DataTable();
dataTableColumns = dataTable.get_Columns();
//Describe lines
dataTableColumns.Add("ACCOUNTNO", System.Type::GetType("System.String"));
dataTableColumns.Add("ITEMNO", System.Type::GetType("System.String"));
dataTableColumns.Add("TOACCOUNTNUM", System.Type::GetType("System.String"));
dataTableColumns.Add("RETURNREASONIDD", System.Type::GetType("System.String"));
dataTableColumns.Add("QUANTITY", System.Type::GetType("System.Double"));
dataTableColumns.Add("DESCRIPTION", System.Type::GetType("System.String"));
//bir satır al
DataRowCollection = dataTable.get_Rows();
tmpRow = dataTable.NewRow();
//fill lines
tmpRow.set_Item("ACCOUNTNO","MU_014600");
tmpRow.set_Item("ITEMNO","200.05.01.0306");
tmpRow.set_Item("TOACCOUNTNUM","MU_014600");
tmpRow.set_Item("RETURNREASONIDD","");
tmpRow.set_Item("QUANTITY",11);
tmpRow.set_Item("DESCRIPTION","");
DataRowCollection.Add(tmpRow);
dataTable.AcceptChanges();
//At this time if opposite side needs data table, it's ready
//In the other case if wants dataset, tie to dataset
dataTableCollection = dataSet.get_Tables();
dataTableCollection.Add(datatable);
//now dataset ready too
Receive dataset:
System.Data.DataTable dataTable;
System.Data.DataTableCollection dataTableCollection;
System.Data.DataColumnCollection dataColumnCollection;
System.Data.DataRowCollection dataRowCollection;
System.Data.DataRow dataRow;
System.Data.DataColumn dataColumn;
System.Data.DataTable returnData;
;
dataTableCollection = dataSet.get_Tables();
totalTable = dataTableCollection.get_Count();
for(i = 0; i < totalTable; i ++)
{
dataTable = dataTableCollection.get_Item(i);
dataColumnCollection = dataTable.get_Columns();
DataRowCollection = dataTable.get_Rows();
totalRow = dataRowCollection.get_Count();
totalCol = dataColumnCollection.get_Count();
for( j = 0; j < totalRow; j ++)
{
dataRow = dataRowCollection.get_Item(j);
custAccount = dataRow.get_Item("ACCOUNTNO");
itemId = dataRow.get_Item("ITEMNO");
toCustAccount = dataRow.get_Item("TOACCOUNTNUM");
strtext = dataRow.get_Item("RETURNREASONIDD");
krc_DefectiveId = str2int64(strtext);
strtext = dataRow.get_Item("QUANTITY");
_b2bdesc = dataRow.get_Item("DESCRIPTION");
...
Send dataset:
System.Data.DataTable dataTable;
System.Data.DataColumnCollection dataTableColumns;
System.Data.DataRowCollection DataRowCollection;
System.Data.DataRow tmpRow;
System.Data.DataSet dataset = new System.Data.DataSet();
System.Data.DataTableCollection dataTableCollection;
dataTable = new System.Data.DataTable();
dataTableColumns = dataTable.get_Columns();
//Describe lines
dataTableColumns.Add("ACCOUNTNO", System.Type::GetType("System.String"));
dataTableColumns.Add("ITEMNO", System.Type::GetType("System.String"));
dataTableColumns.Add("TOACCOUNTNUM", System.Type::GetType("System.String"));
dataTableColumns.Add("RETURNREASONIDD", System.Type::GetType("System.String"));
dataTableColumns.Add("QUANTITY", System.Type::GetType("System.Double"));
dataTableColumns.Add("DESCRIPTION", System.Type::GetType("System.String"));
//bir satır al
DataRowCollection = dataTable.get_Rows();
tmpRow = dataTable.NewRow();
//fill lines
tmpRow.set_Item("ACCOUNTNO","MU_014600");
tmpRow.set_Item("ITEMNO","200.05.01.0306");
tmpRow.set_Item("TOACCOUNTNUM","MU_014600");
tmpRow.set_Item("RETURNREASONIDD","");
tmpRow.set_Item("QUANTITY",11);
tmpRow.set_Item("DESCRIPTION","");
DataRowCollection.Add(tmpRow);
dataTable.AcceptChanges();
//At this time if opposite side needs data table, it's ready
//In the other case if wants dataset, tie to dataset
dataTableCollection = dataSet.get_Tables();
dataTableCollection.Add(datatable);
//now dataset ready too
Receive dataset:
System.Data.DataTable dataTable;
System.Data.DataTableCollection dataTableCollection;
System.Data.DataColumnCollection dataColumnCollection;
System.Data.DataRowCollection dataRowCollection;
System.Data.DataRow dataRow;
System.Data.DataColumn dataColumn;
System.Data.DataTable returnData;
;
dataTableCollection = dataSet.get_Tables();
totalTable = dataTableCollection.get_Count();
for(i = 0; i < totalTable; i ++)
{
dataTable = dataTableCollection.get_Item(i);
dataColumnCollection = dataTable.get_Columns();
DataRowCollection = dataTable.get_Rows();
totalRow = dataRowCollection.get_Count();
totalCol = dataColumnCollection.get_Count();
for( j = 0; j < totalRow; j ++)
{
dataRow = dataRowCollection.get_Item(j);
custAccount = dataRow.get_Item("ACCOUNTNO");
itemId = dataRow.get_Item("ITEMNO");
toCustAccount = dataRow.get_Item("TOACCOUNTNUM");
strtext = dataRow.get_Item("RETURNREASONIDD");
krc_DefectiveId = str2int64(strtext);
strtext = dataRow.get_Item("QUANTITY");
_b2bdesc = dataRow.get_Item("DESCRIPTION");
...
3 Eylül 2015 Perşembe
Axapta - Query prompt prevent "Select query - query last used"
When you run a query and restore values at Axapta you would be see values are not you restored. Reason would be "last used query" at "select query". To prevent this effect you have to use SysQueryRun instead of QueryRun. SysQueryRun is extended version of QueryRun.
This code prevents "last used query":
mySysQueryRun.promptLoadLastUsedQuery(false);
This code prevents "last used query":
mySysQueryRun.promptLoadLastUsedQuery(false);
2 Eylül 2015 Çarşamba
Axapta - Transfer order partial receive
This's so easy with a way which I learned from a forum page :
inventTranstransferLine.QtyReceiveNow = 50;
When you post the transfer order, your 50 pcs items will be receive.
inventTranstransferLine.QtyReceiveNow = 50;
When you post the transfer order, your 50 pcs items will be receive.
Etiketler:
accept,
AX,
AXAPTA,
partial,
qtyreceivenow,
receive,
transfer order
27 Ağustos 2015 Perşembe
AXAPTA - Save query to a table field and restore than
I used Mirko Bonello's blog for this sample.
Create a container field at table. Mine is InventTableQuery:
void createAndSaveQueryInTable()
{
QueryRun SysqueryRun;
;
if (this.InventTableQuery)
queryRun = new QueryRun(this.InventTableQuery);
else
queryRun = new QueryRun(queryStr('InventTable'));
qrun.promptLoadLastUsedQuery(false);
if (queryRun.prompt())
this.InventTableQuery = queryRun.pack();
}
We used SysQueryRun instead for QueryRun and set qrun.promptLoasLastUsedQuery(false) for don't screw like me because of "Why I get same query values for every record?" question... :)
Create a container field at table. Mine is InventTableQuery:
void createAndSaveQueryInTable()
{
QueryRun SysqueryRun;
;
if (this.InventTableQuery)
queryRun = new QueryRun(this.InventTableQuery);
else
queryRun = new QueryRun(queryStr('InventTable'));
qrun.promptLoadLastUsedQuery(false);
if (queryRun.prompt())
this.InventTableQuery = queryRun.pack();
}
We used SysQueryRun instead for QueryRun and set qrun.promptLoasLastUsedQuery(false) for don't screw like me because of "Why I get same query values for every record?" question... :)
Kaydol:
Kayıtlar (Atom)