Set Dynamic field property in Dynamic query.
Keep daxing!!
Set Dynamic field property in Dynamic query.
Keep daxing!!
Today we see how to get multi-select lookup in the report dialog. This is used to sys operation Framework also. We have to fetch selected records in dp class or service class. For this, I wrote logic in the below classes.
Contract Class :
[ DataContractAttribute, SysOperationContractProcessingAttribute(classstr(TestUIBuilderClass)) ] class TestContractClass { List vendAccountList; [ DataMemberAttribute("Vendor"), AifCollectionTypeAttribute("Vendor", Types::String), SysOperationLabelAttribute(literalstr("Vend account")), SysOperationHelpTextAttribute(literalstr("Vend account.")), //SysOperationGroupMemberAttribute('Group'), SysOperationDisplayOrderAttribute('1') ] public List parmVendorList(List _vendAccountList = vendAccountList) { vendAccountList = _vendAccountList; return vendAccountList; } }
-------------------------------------------------------------------------------------------------------------
UI Builder Class:
class TestUIBuilderClass extends SrsReportDataContractUIBuilder { TestContractClass myContractClass; DialogField dialogField; container con; public void build() { // super(); // Add group to dialog //Dialog dialogObject = this.dialog(); // dialogObject.addGroup('Group'); myContractClass = this.dataContractObject() as TestContractClass; dialogField = this.addDialogField(methodStr(TestContractClass, parmVendorList), TestContractClass); } public void postBuild() //or postRun() we can use any method. { super(); myContractClass = this.dataContractObject() as TestContractClass; dialogField = this.bindInfo().getDialogField(TestContractClass, methodStr(TestContractClass, parmVendorList)); dialogField.registerOverrideMethod(methodStr(FormStringControl, lookup), methodStr(TestUIBuilderClass, VendorLookup), this); //dialogField.registerOverrideMethod(methodStr(FormStringControl, modified), methodStr(TestUIBuilderClass, Vendormodified), this); // modified method. //if (dialogField) //{ // dialogField.lookupButton(2); //} } public void VendorLookup(FormStringControl _control) { Query query = new Query(); QueryBuildDataSource vendQBD; // QueryBuildFieldList fieldList; vendQBD = query.addDataSource(tableNum(VendTable)); //qbds1 = qbds.addDataSource(tableNum(DirPartyTable)); //qbds1.relations(True); //qbds1.fields().clearFieldList(); //fieldList = qbds.fields(); //fieldList.addField(fieldNum(DirPartyTable, Name)); //fieldList.dynamic(QueryFieldListDynamic::No); vendQBD.addSelectionField(fieldNum(VendTable, AccountNum)); SysLookupMultiSelectGrid::lookup(query, _control, _control, _control, con); } }
-------------------------------------------------------------------------------------------------------------
DP Class
[ SRSReportQueryAttribute (querystr(Myquery)), SRSReportParameterAttribute(classstr(TestContractClass)) ] class TestDPClass extends SRSReportDataProviderBase //SrsReportDataProviderPreProcess { MyTable myTable; List vendorList; Vendtable vendTable; [SRSReportDataSetAttribute(tableStr('MyTable'))] public MyTable getTempMyTable() { select MyTable; return MyTable; } private Query buildQuery(Query _query,List _vendorList) { ListIterator listIterator = new ListIterator(_vendorList); while(listIterator.more()) { _query.dataSourceTable(tablenum(VendTable)).addRange(fieldnum(VendTable, AccountNum)).value(queryValue(listIterator.value())); listIterator.next(); } return _query; } public void processReport() { QueryRun queryRun; TestContractClass contract = this.parmDataContract() as TestContractClass; Query query = this.parmQuery(); ; vendorList = contract.parmVendorList(); queryRun = new QueryRun(vendorList.empty() ? query : this.buildQuery(this.parmQuery(), vendorList)); while(queryRun.next()) { vendTable = queryRun.get(tablenum(VendTable)); this.insertIntoTempTable(); } } private void insertIntoTempTable() { myTable.AccountNum = vendTable.AccountNum; myTable.VendGroup = vendTable.VendGroup; myTable.insert(); } }
Keep Daxing!!
Settle customer Invoice through X++.
Keep Daxing!!
Update the vendor open trans to mark and update the amount using x++.
Keep Daxing!!
checks if the invoice is already marked with some payment or not using X++
Keep Daxing!!
Hi guys, Today we see how to Auto Settle Vendor Invoice/Payment from the same location In D365FO using X++.
I have a requirement like first I have to fetch the invoice records of vendor and I have to settle that invoice with payment voucher which is from the same location. Example: INV001(from HYD) with PAY001(from Hyd).
VendTable VendTable;
VendTrans invVendTrans, payVendTrans; CustVendTransData custVendTransData; LedgerJournalTrans ledgerJournalTrans, payLedgerJournalTrans; CustVendOpenTransManager manager; VendTransOpen vendTransOpen; DimensionAttributeValueSetStorage invDimensionAttributeValueSetStorage ; DimensionAttribute invDimensionAttribute; DimensionValue dimensionValue; date payDate = mkDate(30,08,2021); ; #define.DimensionName("Location") // Find the oldest unsettled invoice while select invVendTrans order by TransDate asc where invVendTrans.AccountNum == VendAccount && invVendTrans.AmountMST <= 0 && //invVendTrans.TransType == LedgerTransType::Vend && invVendTrans.TransDate <= payDate && invVendTrans.TransType != LedgerTransType::Settlement && !invVendTrans.closed join ledgerJournaltrans where ledgerJournaltrans.Voucher == invVendTrans.Voucher && (ledgerJournalTrans.AccountType == LedgerJournalACType::Vend || ledgerJournalTrans.OffsetAccountType == LedgerJournalACType::Vend) { try { invDimensionAttributeValueSetStorage = dimensionAttributeValueSetStorage::find(ledgerJournaltrans.DefaultDimension); invDimensionAttribute = dimensionAttribute::findbyname(#DimensionName); dimensionValue = invDimensionAttributeValueSetStorage.getDisplayValueByDimensionAttribute(invDimensionAttribute.recId); if (!dimensionValue) { continue; } ttsBegin; VendTable = VendTable::find(invVendTrans.AccountNum); DimensionAttributeValueSetItem dimensionAttributeValueSetItem; DimensionAttributeValue dimensionAttributeValue; DimensionAttribute dimensionAttribute; // Find the oldest unsettled payment of same location while select firstonly payVendTrans order by TransDate asc where payVendTrans.AccountNum == vendTable.AccountNum && payVendTrans.AmountMST >= 0 && //payVendTrans.TransType == LedgerTransType::Payment && payVendTrans.TransDate <= payDate && payVendTrans.TransType != LedgerTransType::Settlement && !payVendTrans.closed join payLedgerJournalTrans where payLedgerJournalTrans.Voucher == payVendTrans.Voucher join dimensionAttributeValueSetItem where dimensionAttributeValueSetItem.DimensionAttributeValueSet == payLedgerJournalTrans.DefaultDimension || dimensionAttributeValueSetItem.DimensionAttributeValueSet == payLedgerJournalTrans.OffsetDefaultDimension // We can use DefaultDimension from vend Trans table also. //join dimensionAttributeValueSetItem // where dimensionAttributeValueSetItem.DimensionAttributeValueSet == payVendTrans.DefaultDimension join dimensionAttributeValue where dimensionAttributeValue.RecId == dimensionAttributeValueSetItem.DimensionAttributeValue && dimensionAttributeValue.DisplayValue == dimensionValue join dimensionAttribute where dimensionAttribute.RecId == dimensionAttributeValue.DimensionAttribute && dimensionAttribute.Name == #DimensionName join vendTransOpen where vendTransOpen.RefRecId == payVendTrans.RecId && vendTransOpen.AccountNum == payVendTrans.AccountNum { manager = custvendopentransmanager::construct(vendtable); manager.updatetransmarked(vendtransopen,true); // manager.updateSettleAmount(vendTransOpen, invVendTrans.AmountMST); // manager.settleMarkedTrans(); custVendTransData = CustVendTransData::construct(invVendTrans); custVendTransData.markForSettlement(VendTable); custVendTransData = CustVendTransData::construct(payVendTrans); custVendTransData.markForSettlement(VendTable); // Settle all marked transactions //VendTrans::settleTransact(VendTable, null, true, SettleDatePrinc::DaysDate, systemdateget()); VendTrans::settleTransact(VendTable, null, true, SettleDatePrinc::SelectDate, systemdateget()); invVendTrans.reread(); if (invVendTrans.Closed) { break; } } ttscommit; } catch (Exception::Error) { ttsAbort; continue; } catch (Exception::Deadlock) { ttsAbort; continue; } catch (Exception::Warning) { CLRInterop::getLastException(); continue; } catch (Exception::CLRError) { ttsAbort; CLRInterop::getLastException(); continue; } }
Keep daxing!!
To day we see how to auto settle vendor transactions using x++.
VendTable VendTable;
VendTrans invVendTrans, payVendTrans; CustVendTransData custVendTransData; CustVendOpenTransManager manager; VendTransOpen vendTransOpen; try { ttsBegin; VendTable = VendTable::find(AccountNum); // Find the oldest unsettled invoice select firstonly invVendTrans order by TransDate asc where invVendTrans.AccountNum == VendTable.AccountNum && invVendTrans.TransType == LedgerTransType::Vend && !invVendTrans.closed; // Find the oldest unsettled payment select firstonly payVendTrans order by TransDate asc where payVendTrans.AccountNum == VendTable.AccountNum && payVendTrans.TransType == LedgerTransType::Payment && !payVendTrans.closed; select firstonly vendTransOpen where vendTransOpen.RefRecId == payVendTrans.RecId && vendTransOpen.AccountNum == payVendTrans.AccountNum; if (invVendTrans.RecId && payVendTrans.RecId && vendTransOpen.RecId) { manager = CustVendOpenTransManager::construct(VendTable); manager.updateTransMarked(vendTransOpen,true); // manager.updateSettleAmount(vendTransOpen, Amount); //manager.settleMarkedTrans(); custVendTransData = CustVendTransData::construct(invVendTrans); custVendTransData.markForSettlement(VendTable); custVendTransData = CustVendTransData::construct(payVendTrans); custVendTransData.markForSettlement(VendTable); // Settle all marked transactions //VendTrans::settleTransact(VendTable, null, true, SettleDatePrinc::DaysDate, systemdateget()); VendTrans::settleTransact(VendTable, null, true, SettleDatePrinc::SelectDate, systemdateget()); } ttscommit; } catch (Exception::Error) { ttsAbort; continue; } catch (Exception::Deadlock) { ttsAbort; continue; } catch (Exception::Warning) { CLRInterop::getLastException(); continue; } catch (Exception::CLRError) { ttsAbort; CLRInterop::getLastException(); continue; }Keep daxing!!
I got a requirement like I have to import vendors in to my custom table. I write the below code to cmp the reqirement.
using System.IO; using OfficeOpenXml; using OfficeOpenXml.ExcelPackage; using OfficeOpenXml.ExcelRange; class ImportVendorsForAutoSettlement { /// <summary> /// excelimport used to import the invoice numbers. /// </summary> public void excelImport() { System.IO.Stream stream; DialogGroup dlgUploadGroup; FileUploadBuild fileUploadBuild; FormBuildControl formBuildControl; AutoSettlementVendorList autoSettlementVendorList; VendAccount vendAccount; Dialog dialog = new Dialog("ImportDataFromExcel"); ; dlgUploadGroup = dialog.addGroup("@SYS54759"); formBuildControl = dialog.formBuildDesign().control(dlgUploadGroup.name()); fileUploadBuild = formBuildControl.addControlEx(classstr(FileUpload), "Upload"); fileUploadBuild.style(FileUploadStyle::MinimalWithFilename); fileUploadBuild.fileTypesAccepted('.xlsx'); if (dialog.run() && dialog.closedOk()) { FileUpload fileUploadControl = dialog.formRun().control(dialog.formRun().controlId("Upload")); FileUploadTemporaryStorageResult fileUploadResult = fileUploadControl.getFileUploadResult(); if (fileUploadResult != null && fileUploadResult.getUploadStatus()) { stream = fileUploadResult.openResult(); using (ExcelPackage Package = new ExcelPackage(stream)) { int rowCount, i; Package.Load(stream); ExcelWorksheet worksheet = package.get_Workbook().get_Worksheets().get_Item(1); OfficeOpenXml.ExcelRange range = worksheet.Cells; rowCount = worksheet.Dimension.End.Row - worksheet.Dimension.Start.Row + 1; try { for (i = 2; i<= rowCount; i++) { ttsbegin; vendAccount = range.get_Item(i, 1).value; //supplierID = range.get_Item(i, 2).value; // transType = range.get_Item(i, 3).value; autoSettlementVendorList = AutoSettlementVendorList::find(vendAccount, true); if (!autoSettlementVendorList && vendAccount) { autoSettlementVendorList.clear(); autoSettlementVendorList.VendAccount = vendAccount; autoSettlementVendorList.insert(); } else if (autoSettlementVendorList && autoSettlementVendorList.IsProcess) { autoSettlementVendorList.IsProcess = false; autoSettlementVendorList.update(); } ttscommit; } } catch(Exception::Error) { info("Error"); } } Info("Sucessed"); } else { error("Error"); } } } }
Keep Daxing!!
Reset Number sequence using x++
Keep daxing!!
Customer creating new record in table. Once he deleted the last record of table and he inserting a new record at that time he want to apply last deleted record number to newly created record.
I wrote the code in table level deleted method.
public void delete() { NumberSequenceTable numSeqTable; MyId myId; ; myId= MyTable.MyId; numSeqTable = NumberSequenceTable::find( SalesParameters::numRefMyId().NumberSequence); super(); if (numSeqTable.Continuous) { NumberSeq::release(SalesParameters::numRefMyId().NumberSequence, myId); } else { if (NumberSeq::numInsertFormat(numSeqTable.NextRec - 1, numSeqTable.Format) == myId) { ttsbegin; numSeqTable = NumberSequenceTable::find( numSeqTable.NumberSequence, true); numSeqTable.NextRec--; numSeqTable.doUpdate(); ttscommit; } } }
Ref :
https://stackoverflow.com/questions/35247361/how-to-recover-a-number-sequence-just-used
Keep daxing!!
Today we discuss, how to roll back number sequence if any validation failed in D365FO.
If number sequence is creating without using NumberSeq class. we can use
ttsbegin;
// our logic
ttscommit;
If any validation failed the system roll back everything.
If we creating number sequence using NumberSeq class by abort() method we can recover that number. This only works if in the same transaction scope as NumberSeq and if the number sequence is continuous. if not, both calls are no-operations.
NumberSeq numSeq = NumberSeq::newGetNum(Parameters::numRefMyId(), true);
// NumberSeq numSeq = NumberSeq::newGetNumFromId(Table.AutoNumberSequenceTable, true); MyTable myTable; ; try { myTable.clear(); myTable.myId = numSeq.num(); if (myTable.validateWrite()) { myTable.insert(); numSeq .used(); } } catch (Exception::Error) { numSeq .abort(); }Keep daxing!!
I got a requirement in order to create Multiple number sequence based on type for newly created purchase order in D365FO.
The similar requirement is already exists in fixed assets. Same way I followed here.
1. I created new table with 3 fields.
Purch table method:
[ExtensionOf(tableStr(PurchTable))] final class PurchTable_Extension { public NumberSeq initPONumberSeq(PurchaseType _purchaseType = " ") { NumberSeq purchaseOrderNumberSeq = PurchaseTypeTable::getNumberSeqForType( _purchaseType ? _purchaseType : this.PurchaseType); return purchaseOrderNumberSeq; } }
PurchaseTypeTable(My table) method:
public class PurchaseTypeTable extends common { public static NumberSeq getNumberSeqForType(PurchaseType _purchaseType) { PurchaseTypeTable purchaseTypeTable = PurchaseTypeTable::find(_purchaseType); NumberSeq purchaseOrderSeq; PurchTable purchTable; if (purchaseTypeTable.AutoNumberSequenceTable) { purchaseOrderSeq = NumberSeq::newGetNumFromId(purchaseTypeTable.AutoNumberSequenceTable, true); } return purchaseOrderSeq; } }
Form close method:
[ExtensionOf(formStr(PurchCreateOrder))] final class PurchCreateOrderForm_Extension { public numberseq purchaseordernumberseq; public PurchId purchId; public void close() { if (!purchtable.recid && purchaseordernumberseq) { purchaseordernumberseq.abort(); } } }
Form data source write method:
Keep daxing!!
Here I am generating the report through code and sending that report to cusomer using X++.
class ConfirmSalesOrdersService extends SysOperationServiceBase { public void cnfOrdersAndSendMail() { SalesTable salesTable; SalesFormLetter salesFormLetter; CustConfirmJour custConfirmJour; while select salesTable where salesTable.DocumentStatus == DocumentStatus::None && salesTable.SalesType == SalesType::Sales && salesTable.AutoOrderCnfirm == NoYes::Yes { if(salesTable.RecId) { try { salesFormLetter = SalesFormLetter::construct(DocumentStatus::Confirmation); salesFormLetter.update(salesTable, DateTimeUtil::getSystemDate(DateTimeUtil::getUserPreferredTimeZone()), SalesUpdate::All); select firstonly custConfirmJour where custConfirmJour.SalesId == salesTable.SalesId; this.getReport(custConfirmJour.RecId,salesTable.customerEmail(),salesTable.SalesId); } catch { //Error("%1",infolog.text()); this.getErrorStr(); continue; } } } } public void getReport(RecId recId,Email custEmail,SalesId salesId) { Filename fileName = strFmt("%1_%2",salesId,"ConfirmationJournal.pdf"); SrsReportRunController controller = new SrsReportRunController(); SalesConfirmContract contract = new SalesConfirmContract(); SRSPrintDestinationSettings settings; Array arrayFiles; System.Byte[] reportBytes = new System.Byte[0](); SRSProxy srsProxy; SRSReportRunService srsReportRunService = new SrsReportRunService(); Microsoft.Dynamics.AX.Framework.Reporting.Shared.ReportingService.ParameterValue[] parameterValueArray; Map reportParametersMap; SRSReportExecutionInfo executionInfo = new SRSReportExecutionInfo(); contract.parmRecordId(recId); // Provide details to controller and add contract controller.parmArgs(new Args()); controller.parmReportName(ssrsReportStr(SalesConfirm, Report)); controller.parmShowDialog(false); controller.parmLoadFromSysLastValue(false); controller.parmReportContract().parmRdpContract(contract); // Provide printer settings settings = controller.parmReportContract().parmPrintSettings(); settings.printMediumType(SRSPrintMediumType::File); settings.fileName(fileName); settings.fileFormat(SRSReportFileFormat::PDF); // Below is a part of code responsible for rendering the report controller.parmReportContract().parmReportServerConfig(SRSConfiguration::getDefaultServerConfiguration()); controller.parmReportContract().parmReportExecutionInfo(executionInfo); srsReportRunService.getReportDataContract(controller.parmreportcontract().parmReportName()); srsReportRunService.preRunReport(controller.parmreportcontract()); reportParametersMap = srsReportRunService.createParamMapFromContract(controller.parmReportContract()); parameterValueArray = SrsReportRunUtil::getParameterValueArray(reportParametersMap); srsProxy =SRSProxy::constructWithConfiguration(controller.parmReportContract().parmReportServerConfig()); // Actual rendering to byte array reportBytes = srsproxy.renderReportToByteArray(controller.parmreportcontract().parmreportpath(), parameterValueArray, settings.fileFormat(), settings.deviceinfo()); // You can also convert the report Bytes into an xpp BinData object if needed container binData; Binary binaryData; System.IO.MemoryStream mstream = new System.IO.MemoryStream(reportBytes); binaryData = Binary::constructFromMemoryStream(mstream); // download file //System.IO.StreamReader sReader = new System.IO.StreamReader(stream); //fileContent = sReader.ReadToEnd(); //File::SendStringAsFileToUser(fileContent, fileName); // System.IO.File::WriteAllBytes("C:\\backup\\"+fileName,reportBytes); // You can save the file on your local instance for verification if(binaryData) { binData = binaryData.getContainer(); } System.Byte[] binData1; System.IO.Stream stream1; // Turn the Bytes into a stream for(int i = 0; i < conLen(binData); i++) { binData1 = conPeek(binData,i+1); stream1 = new System.IO.MemoryStream(binData1); } this.sendEmail(custEmail,"Order Confirmation",stream1,salesId,fileName);//custEmail } public void sendEmail(Description _emailTo, Description _Subject,System.IO.MemoryStream stream1,SalesId _salesId,FileName _filename) { SysMailerSMTP mailer = new SysMailerSMTP(); SysMailerMessageBuilder builder = new SysMailerMessageBuilder(); SysEmailParameters parameters = SysEmailParameters::find(); Filename fileName = _filename; //you will have to setup the below parameters in sysadmin module if (parameters.SMTPRelayServerName) { mailer.SMTPRelayServer(parameters.SMTPRelayServerName, parameters.SMTPPortNumber, parameters.SMTPUserName, SysEmailParameters::password(), parameters.SMTPUseNTLM); } else { warning("SERVER NOT FOUND"); } builder.setFrom(SysEmailParameters::find().SMTPUserName); builder.addTo(_emailTo); builder.setSubject(_Subject); builder.setBody(strFmt("%1,%2","Order id:",_salesId)); if(stream1 != null) { builder.addAttachment(stream1, fileName); } SysMailerFactory::getNonInteractiveMailer().sendNonInteractive(builder.getMessage()); } private str getErrorStr() { SysInfologEnumerator enumerator; SysInfologMessageStruct msgStruct; Exception exception; str error; ; enumerator = SysInfologEnumerator::newData(infolog.cut()); while (enumerator.moveNext()) { msgStruct = new SysInfologMessageStruct(enumerator.currentMessage()); exception = enumerator.currentException(); error = strfmt("%1 - %2", error, msgStruct.message()); } return error; } }
Keep Daxing!!
I got a requirement like I have to add signature image to company and I have to populate in my custom report.
Steps:
1.Added "container field" in table.
2.Added New fast tab, buttons and Image control in form as shown below.
3. Written the code in below class. By Event handler methods I written the code.
public class OMLegalEntityEventHandler { // Change button clicked method { FormDataSource companyInfo_ds = sender.formRun().dataSource(1) as FormDataSource; CompanyInfo companyInfo = companyInfo_ds.cursor() as CompanyInfo; str imageFilePathName; ; imageFilePathName = OMLegalEntityEventHandler::uploadImageFile(); if (imageFilePathName) { ttsbegin; companyInfo.selectForUpdate(true); companyInfo.CompanySignature = ImageReference::GetPackedBinaryData(imageFilePathName); companyInfo.update(); ttscommit; } companyInfo_ds.research(true); } //Remove button clicked method { FormDataSource companyInfo_ds = sender.formRun().dataSource(1) as FormDataSource; CompanyInfo companyInfo = companyInfo_ds.cursor() as CompanyInfo; ; ttsbegin; companyInfo.selectForUpdate(true); companyInfo.CompanySignature = conNull(); companyInfo.update(); ttscommit; companyInfo_ds.research(true); } // Data source activate method { CompanyInfo companyInfo = sender.cursor() as CompanyInfo; FormWindowControl logoImage = sender.formRun().design().controlName(formControlStr(OMLegalEntity, DashboardSignatureImage)) as FormWindowControl; Image _image; ; if (companyInfo.CompanySignature) { _image = new Image(); _image.setData(companyInfo.CompanySignature); logoImage.image(_image); } else { logoImage.image(null); } } // File upload from this method public static str uploadImageFile() { FormRun visualForm; FileUpload fileUploadControl; str imageFilePathName; ; visualForm = classFactory::formRunClassOnClient(new Args(formstr(SysGetFileFromUser))); visualForm.init(); visualForm.design().caption("@ApplicationPlatform:GetFileImageCaption"); fileUploadControl = visualForm.design().controlName('FileUpload1'); visualForm.run(); visualForm.wait(); FileUploadTemporaryStorageResult fileUploadResult = fileUploadControl.getFileUploadResult(); if (fileUploadResult != null && fileUploadResult.getUploadStatus()) { imageFilePathName = fileUploadResult.getDownloadUrl(); } return imageFilePathName; } }
Keep daxing!!
One Table is declared in the form 'class declaration' method (Global). I want to access that table buffer in my form control extension class(COC). For achieving that I used the below code.
[ExtensionOf(formControlStr(MyForm, MyControl))]
public final class MyFormMyControl_Extension { public boolean modified() { // FormCheckBoxControl control = any2Object(this) as FormCheckBoxControl; // FormRun control = tax.formRun(); FormRun FormRun = salesTable_ds.Formrun(); boolean ret; MyTable myTable = element.myTable;Keep daxing!!