Microsoft_MVP_banner

AX 2012 : Generate customer account statement as PDF through X++

Customer account statement in AX 2012 R3 can be generated from Accounts Receivable > Reports > External > Customer account statement. However, if we need to generate this report through code – how can we achieve this? I created a new query in AOT with only dataSource ‘CustTable’, as I want to loop on selected customers only. I created a class extending from RunBase (did not want to touch existing report and class for customer account statement) being considering it a custom process of generating customer account statement using X++ code opposed to what exists in system. New query looks like this with three ranges, these ranges will show on dialog form Created a new class with following methods which are common for every dialog class; main construct new pack unpack dialog getFromDialog run showQueryValues queryRun I will explain important methods from the class which I think need discussion. QueryRun method – this method will be called from Pack() method from this line of code return [#CurrentVersion,#CurrentList,this.queryRun().pack()]; public QueryRun queryRun() {     QueryRun                ret;     QueryBuildDataSource    qbds;     CustTable               custTable = CustTable::findRecId(selectedRec);     if (!queryRun)     {         query   =   new query(queryStr(FF_CustAccountStatement));         qbds = query.dataSourceTable(tableNum(custTable));         if(selectedRec)         {             SysQuery::findOrCreateRange(qbds, fieldNum(custTable, AccountNum)).value(custTable.AccountNum);             SysQuery::findOrCreateRange(qbds, fieldNum(custTable, CustGroup)).value(custTable.CustGroup);             SysQuery::findOrCreateRange(qbds, fieldNum(custTable, AccountStatement)).value(SysQuery::value(custTable.AccountStatement));         }         else         {             SysQuery::findOrCreateRange(qbds, fieldNum(custTable, AccountNum)).value(SysQuery::valueUnlimited());             SysQuery::findOrCreateRange(qbds, fieldNum(custTable, CustGroup)).value(SysQuery::valueUnlimited());             SysQuery::findOrCreateRange(qbds, fieldNum(custTable, AccountStatement)).value(SysQuery::valueUnlimited());         }         queryRun    =   new QueryRun(query);     }     ret = queryRun;     return ret; } This method generates customer account statement for each provided customer in query. This method is called from run() method; while (queryRun.next())     {         custTable    =   queryRun.get(tableNum(custTable));         //this.setParameters(custTable);         filename    =   WinAPI::getTempPath()+”Customer “+ custTable.AccountNum+ ” Customer Account Statement.pdf”;         this.createCustAccountStatement(custTable, filename);     } public void createCustAccountStatement(CustTable _custTable,FileName _filename) {     CustAccountStatementExtController      controller = new CustAccountStatementExtController();     SRSPrintDestinationSettings            printSettings;     CustAccountStatementExtContract        Contract;     controller.parmReportName(PrintMgmtDocType::construct(PrintMgmtDocumentType::CustAccountStatement).getDefaultReportFormat());     controller.parmLoadFromSysLastValue(true);     controller.parmShowDialog(false);     printSettings = controller.parmReportContract().parmPrintSettings();     printSettings.printMediumType(SRSPrintMediumType::File);     printSettings.fileFormat(SRSReportFileFormat::PDF);     printSettings.overwriteFile(true);     printSettings.fileName(_filename);     Contract = controller.parmReportContract().parmRdpContract() as CustAccountStatementExtContract;     enumerator = controller.parmReportContract().parmQueryContracts().getEnumerator();     enumerator.moveNext();     query = enumerator.currentValue();     query.dataSourceTable(tableNum(CustTable)).addRange(fieldNum(CustTable, AccountNum)).value(queryValue(_custTable.AccountNum));     Contract.parmAgingBucket(agingBucket);     Contract.parmAgingBucketPrintDescription(AgingBucketPrintDescription);     Contract.parmAgingPeriod(agingPeriod);     Contract.parmDayMonth(dayMonth);     Contract.parmFromDate(fromDate);     Contract.parmIncludeReversed(includeReversed);     Contract.parmManualSetup(manualSetup);     Contract.parmOnlyOpen(onlyOpen);     Contract.parmPrintAging(printAging);     Contract.parmPrintAmountGiro(printAmountGiro);     Contract.parmPrintCreditLimit(printCreditLimit);     Contract.parmPrintGiro(printGiro);     Contract.parmPrintingDirection(printingDirection);     Contract.parmPrintNonZero(printNonZero);     Contract.parmPrintPaymentSchedule(printPaymentSchedule);     Contract.parmPrintType(printType);     Contract.parmSpecifyDueToDate(specifyDueToDate);     Contract.parmToDate(toDate);           controller.startOperation(); } It generates report in user’s temp folder for each selected customer on below screen [this is custom screen created by a class which I referred above]

AX 2012 – sysOperation Framework implementation [Example]

This post demonstrates how to create batch classes using Business Operation Framework (or sysOperation). We used to use RunBaseBatch framework to implement batch functionality before this new concept of AX services was taken in place. Compensations of BOF (or sysOperation) framework over RunBaseBatch are very well explained here http://daxmusings.codecrib.com/2011/08/from-runbase-to-sysoperation-business.html.  Key points RunBase/RunBaseBatch Vs sysOperation Frameworks The sysOperation framework is an implementation of the MVC pattern (Model-View-Controller) Model – parameters [contract class] View – Dialog [contract class or an optional UI builder class] Controller – Process (the code that runs in the essence of pattern) The framework influence the Services framework, whereas a service in AX has a data contract with some special attributes DataMember attributes in contract class take the entered values in parameters (dialog fields) to the process (aka “operations”) sysOperationFramework simplifies the pack/unpack of variables taking advantage of attributes, this was pretty nasty in RunBaseBatch framework Let’s jump into example now, we are going to create a batch looks like this; Create a new Contract Class General concept of contract class: Contract class in basically an AX class with special attributes as you can see mentioned at the top of this class declaration. This class acts as a model of MVC (model-view-controller) pattern.  In runbase/runbasebatch framework this model has member variables and pack/unpack (aka serialization).  These member variables are defined in runbase/runbasebatch class declaration with pack/unpack variables handle under currentVersion and localVersion macros. Specific to this this example: This class has two attributes; DataContractAttribute which is making it special class to leverage with AX services and the other attribute is referring to UIBuilder class. More on UIBuilder class explains well in this post follows. [     DataContractAttribute,     SysOperationContractProcessingAttribute(classstr(FF_ExampleUIBuilder)) ] class FF_ExampleContract implements SysOperationValidatable {     FromDate                fromDate;     ToDate                  toDate;     NoYesId                 todaysDate;     MainAccountNum          mainAccount; } [DataMemberAttribute] public MainAccountNum parmMainAccount(MainAccountNum _mainAccount = mainAccount) {     mainAccount = _mainAccount;     return mainAccount; } [DataMemberAttribute] public FromDate parmFromDate(ExchangeRateImportFromDate _fromDate = fromDate) {     fromDate = _fromDate;     return fromDate; } [DataMemberAttribute] public ToDate parmToDate(ExchangeRateImportToDate _toDate = toDate) {     toDate = _toDate;     return toDate; } [DataMemberAttribute] public NoYesId parmTodaysDate(NoYesId _todaysDate = todaysDate) {     todaysDate = _todaysDate;     return todaysDate; } public boolean validate() {     boolean ok = true;     if (mainAccount == “”)     {         ok = checkFailed(“Main account cannot be blank”);     }     if (todaysDate == NoYes::No)     {         if (fromDate == dateNull())         {             ok = checkFailed(“From date cannot be blank”);         }         if (toDate == dateNull())         {             ok = checkFailed(“To date cannot be blank”);         }     }     return ok; } Create a new UIBuider class; This class is only required when you want to play with added parameter (data member attributes) in contract class. Like in this example I will be enabling/disabling parameters based on other parameter values, this class can also be used to build custom lookups and perform some custom logic only to play with parameters and their values. class FF_ExampleUIBuilder extends SysOperationAutomaticUIBuilder {     #define.MainAccount(‘mainAccount’)     #define.TodaysDate(‘todaysDate’)     #define.FromDate(‘fromDate’)     #define.ToDate(‘toDate’)     FormComboBoxControl                 mainAccountControl;     FormCheckBoxControl                 todaysDateControl;     FormDateControl                     fromDateControl;     FormDateControl                     toDateControl; } public void build() {     DialogGroup                             fromToDateRangeGroup;     DialogField                             dialogField;     SAB_RetailConsolidateContract           dataContract;     dialog = this.dialog();     dataContract = this.dataContractObject();     dialogField = dialog.addField(extendedtypestr(MainAccountNum));     mainAccountControl = dialogField.control();     mainAccountControl.text(dataContract.parmMainAccount());     mainAccountControl.name(#MainAccount);     dialogField = dialog.addField(extendedtypestr(NoYesId));     todaysDateControl = dialogField.fieldControl();     todaysDateControl.value(NoYes::No);     todaysDateControl.name(#TodaysDate);     todaysDateControl.label(“Today”);     fromToDateRangeGroup = dialog.addGroup(“Date range”);     fromToDateRangeGroup.columns(2);     fromToDateRangeGroup.widthMode(FormWidth::ColumnWidth);     fromToDateRangeGroup.caption(”);     dialogField = dialog.addField(extendedtypestr(FromDate));     fromDateControl = dialogField.control();     fromDateControl.name(#FromDate);     fromDateControl.allowEdit(true);     fromDateControl.dateValue(systemDateGet());     fromToDateRangeGroup.addFieldname(#ToDate);     dialogField = dialog.addField(extendedtypestr(ToDate));     toDateControl = dialogField.control();     toDateControl.name(#ToDate);     toDateControl.allowEdit(true);     toDateControl.dateValue(systemDateGet());     fromToDateRangeGroup.addFieldname(#ToDate); } /// <summary> /// Provides form logic related to the date type selected. /// </summary> /// <param name=”_importDateTypeControl”> /// The <c>FormComboBoxControl</c> control for the date type. /// </param> public boolean dateControlSelectionChg(FormCheckBoxControl _importDateTypeControl) {     FormDateControl fromDateControlLocal;     FormDateControl toDateControlLocal;     boolean ret;     fromDateControlLocal = this.dialog().dialogForm().runControl(#FromDate);     toDateControlLocal =  this.dialog().dialogForm().runControl(#ToDate);     ret = _importDateTypeControl.modified();     if (_importDateTypeControl.checked() == NoYes::Yes)     {         fromDateControlLocal.dateValue(dateNull());         toDateControlLocal.dateValue(dateNull());         fromDateControlLocal.allowEdit(false);         toDateControlLocal.allowEdit(false);     }     else     {         fromDateControlLocal.dateValue(systemDateGet());         toDateControlLocal.dateValue(systemDateGet());         fromDateControlLocal.allowEdit(true);         toDateControlLocal.allowEdit(true);     }     return  ret; } /// <summary> /// Called after the user interface has been built to allow for method overrides. /// </summary> public void postRun() {     super();     dialog.dialogForm().formRun().controlMethodOverload(false);     todaysDateControl.registerOverrideMethod(methodstr(FormCheckBoxControl, modified), methodstr(FF_ExampleUIBuilder, dateControlSelectionChg), this); } Create a new Controller class; This class as a controller class of the MVC pattern and runs the business logic. In this example it is setting dialog title, retrieving dialog values from contract class and from Main method it is calling service class with provided method name and execution mode. class FF_ExampleController extends SysOperationServiceController {     #define.MainAccount(‘mainAccount’)     #define.TodaysDate(‘todaysDate’)     #define.ToDate(‘toDate’)     #define.FromDate(‘fromDate’) } public ClassDescription caption() {     ClassDescription ret;     // This is used to identify the batch job within batch processing     ret = “Example sysOperation Framework”;     return ret; } public void getFromDialog() {     FormComboBoxControl                 mainAccountControl;     FormCheckBoxControl                 todaysDateControl;     FormDateControl                     fromDateControl;     FormDateControl                     toDateControl;     DialogForm                          theDialogForm;     FF_ExampleContract                  FF_ExampleContract;     theDialogForm = this.dialog().dialogForm();     super();     mainAccountControl = theDialogForm.runControl(#MainAccount);     todaysDateControl = theDialogForm.runControl(#TodaysDate);     fromDateControl = theDialogForm.runControl(#toDate);     toDateControl = theDialogForm.runControl(#fromDate);     FF_ExampleContract = this.getDataContractObject(classStr(FF_ExampleContract));     if (FF_ExampleContract)     {         // Set the values in data contract         FF_ExampleContract.parmMainAccount(mainAccountControl.text());         FF_ExampleContract.parmTodaysDate(todaysDateControl.value());         FF_ExampleContract.parmToDate(fromDateControl.dateValue());         FF_ExampleContract.parmFromDate(toDateControl.dateValue());     } } public LabelType parmDialogCaption(LabelType _dialogCaption = “”) {     LabelType caption;     // This appears as the window name     caption = “Example for SysOperation Framework”;     return caption; } public static FF_ExampleController construct() {     return new FF_ExampleController(); } public static void main(Args args) {     FF_ExampleController                        controller;     identifierName                              className;     identifierName                              methodName;     SysOperationExecutionMode                   executionMode;     [className, methodName, executionMode] = SysOperationServiceController::parseServiceInfo(args);     controller = new FF_ExampleController(className, methodName, executionMode);     if (controller.prompt())     {         controller.run();     } } Create a new Service class; This class is an AX service class with [SysEntryPointAttribute] attribute at the top of the method which will be called from action menu item. class FF_ExampleService {      } [SysEntryPointAttribute] public void transactionConsolidation(FF_ExampleContract FF_ExampleContract) {     FromDate                            fromDate       = FF_ExampleContract.parmFromDate();     ToDate                              toDate         = FF_ExampleContract.parmToDate();     NoYesId                             todaysDate     = FF_ExampleContract.parmTodaysDate();    

Import demo data into AX 2012 R3 – Test Data Transfer Tool

To import demo data into AX 2012 R3 version, Test Data Transfer Tool was put in place. The Test Data Transfer Tool (beta) has been moved. It is now available from the Downloadable tools section of Microsoft Dynamics Lifecycle Services. Download the test data transfer tool from the below link; https://lcs.dynamics.com/v2/LocalTools/ Steps to import data are well described here https://community.dynamics.com/ax/b/alirazatechblog/archive/2014/05/03/how-to-import-contoso-demo-data-to-dynamics-ax-2012-r3 Test Data Transfer Tool – Video Microsoft Dynamics AX 2012 Test Data Transfer Tool (Beta) Updated 7th Jan 2014 Video

AX 7 – Technical Insights

AX 7 is impending with a radical technical and structural change in the world of Microsoft ERP, Dynamics AX, in contrast to what we got in current version AX 2012. This is a soft indication to all developers to get ready to adopt this change nippily. There is a lot to learn on AX 7 development while supportingworking with current versions of Dynamics AX 🙁 Back to file system; Yes, with AX 7 we are back to file system. Any change (technical) we make in application is saved into file (XML file) at your disk. Before we talk about the main artefacts involved in AX 7 development and the approach we will be using while developing in AX 7 world. Let’s put some glance on Development environment how will it looks like, what we (developer) will have in our DEV box. Visual Studio will be used for all development stuff; MS developed visual studio extensions using standard Visual Studio extensions to enable the development of X++ code and AX 7 metadata in Visual Studio.  Metadata API that communicate from visual studio with the XML files that are the source code on disk. Your source and metadata are the set of XML files now. Visual Studio user interface communicates with them with an API called Metadata API.  Build from Visual Studio: When you build from Visual Studio, it compiles into assemblies and other binaries that runtime uses and during this whole process you are working against files. At this point in time there is no need for AOS to be running during design and compilation.  Debug: Use standard visual studio paradigms to run and debug the code with F5 and Ctrl F5. Runtime: It is the AOS and batch manager service which are running locally into your DEV box, the AOS is actually a web service in IIS. Database: This is a standard SQL server database in your box,  Deployment Package: It is a major change in AX 7 and I will post more in my future posts.  For now just to understand how it works, let’s assume you have completed the development or a stage of development of a certain task into your Development box and now wan to move these changes to Production. You will need to create a package (your changes) what its called Deployment Package. These can be created onto your development box and also can be created on your build automation box.  These Deployment Packages are compiled binary versions of your models and packages that can be deployed on the cloud. The cloud can be a test, UAT or production environment. 

AX 2012: File existance check

Multiple ways to check file existance in AX 2012; Client side check, does not work with batch job WinAPI::fileExists(_fileName); Server side check which works for run base batch too public boolean getFileExists(filepath _fileName) {    System.IO.FileInfo   fileInfo;    new InteropPermission(InteropKind::ClrInterop).assert();    fileInfo = new System.IO.FileInfo(_fileName);    return fileInfo.get_Exists(); }

AX 2012 – How to retrieve meeting (appointment) status from Outlook

Microsoft Dynamics AX 2012 provides a very convenient way to synchronize outlook tasks, contacts and appointments back into AX.  MSDN topics describe the integration in details. https://technet.microsoft.com/en-us/library/aa498242.aspx https://technet.microsoft.com/en-us/library/gg230659.aspx Let’s discuss the main motive of this post; How can you retrieve the meeting (appointment) status from outlook back to AX 2012. Before I show you where and what code need to add or change, I will show the existing functionality how it works. Go to Home > Periodic > Microsoft outlook synchronization > Synchronize Here you can see, be default it tries to sync Contacts, Tasks and Appointments within provided date range. I will focus on Appointments for this post and sync Appointments only, Let’s assume I have following meeting (appointment) response in my outlook, remember it was a meeting invitation which I sent from AX and now I have their responses back into my outllook. For below email the response is Accepted. Upon Sync Appointments it creates Activities in AX and can be access from Home > Common > Activities > All activities. The last (right most) column is showing the meeting response, AX default functionality (Sync process) does not bring this meeting response back into AX. Here is the code change which needs to plug in into existing class to achieve this function. Class: SmmOutlookSync_Appointment Method: synchronizeAppointmentsOutlookToAxapta() This method is retreiving the meeting status; // Faisal.F, read meeting response; // Accepted = 3; Tentative = 2; Declined = 4 [based on macro smmMSOutlook2002ObjectModelConstants] private str FCM_getMeetingResponse(COM  _outlookItem) {     COM     recip, recips;     str     meetingStatus;     if ( _outlookItem.Recipients() != null)     {         recips = outlookItem.Recipients();         if (recips.Count() >= 1)         {             recip = recips.Item(1);             switch(recip.MeetingResponseStatus())             {                 case 2:                     meetingStatus = “Tentative”;                     break;                 case 3:                     meetingStatus = “Accepted”;                     break;                 case 4:                     meetingStatus = “Declined”;                     break;                 default:                     meetingStatus = “None”;                     break;             }         }     }     return meetingStatus;    }

Calling/Opening AX form through X++

Sample piece of code to open AX form through X++ static void OpenForm_ThroughCode(Args _args) {     Args                            args;     Object                          formRun;     // open form     args = new Args();     args.name(formstr(FormName));     formRun = classfactory.formRunClass(args);     formRun.init();     formRun.run();     formRun.wait(); } If you want to pass a record to open a form args = new Args(); args.record(ProjTable::find(‘PR00001’)); args.name(formstr(FormName)); formRun = classfactory.formRunClass(args); formRun.init(); formRun.run(); formRun.wait(); How to retrieve these args on caller form’s init() public void init() {     ProjTable   projTableLocal;        super();        projTableLocal = element.args().record();    }

How to implement Runbase form in AX

Today I had a requirement that allow users to run Outlook synchronization process in batch rather sync it manually using out of the box functionality. Let’s have a look on existing functionality and then extend it to achieve the requirement of running it in batch. Existing functionality in AX Home> Periodic > Synchronize [AOT form name is smmOutlookSyncrhonization] As you can see someone has to manually sync outlook emails to AX acvitities or viceversa. Extended functionality in AX Let’s implement this functionality in batch so users can set recurrence the Sync. Create a new class; class SyncOutLookEmails_RunbaseForm extends RunBaseBatch {     // Packed     TransDate            testDate; // I am using this variable for own testing purpose     #define.CurrentVersion(2)     #define.Version1(2)     #localmacro.CurrentList         testDate     #endmacro } public Object dialog() {     DialogRunbase   dialog = Dialog::newFormnameRunbase(formstr(smmOutlookSyncrhonization),this); ;     return dialog; } public boolean getFromDialog() {     boolean ret;     ret = super();     return ret; } protected void new() {     super(); } public container pack() {     return [#CurrentVersion,#CurrentList]; } public boolean runsImpersonated() {     return true; } public boolean showQueryValues() {     return true; } public boolean unpack(container _packedClass) {     boolean     ret;     Version     version = RunBase::getVersion(_packedClass);     ;     switch (version)     {         case #CurrentVersion:             [version, #CurrentList] = _packedClass;             ret = true;             break;         default:             ret = false;             break;     }     return ret; } server static SyncOutLookEmails_RunbaseForm construct() {     return new SyncOutLookEmails_RunbaseForm(); } server static void main(Args args) {     SyncOutLookEmails_RunbaseForm   syncOutLookEmails_RunbaseForm = SyncOutLookEmails_RunbaseForm::construct();     if (syncOutLookEmails_RunbaseForm.prompt())         syncOutLookEmails_RunbaseForm.run();  } Run this class by pressing F5, Ooopsss!!! it throws an error A DialogStartGrp group is missing from the form smmOutlookSyncrhonization. In this group dialog controls are added. This requires to add a group on the form and this has to have under a tab/tab page, let’s do this; How the current form looks in AOT How it should look like to use it for batch class, created above. Added new Tab Added new Tab page Added dialogStartGrp under tab page Moved exisiting groups under tab page Set caption “General” to tab page. It requires few more methods at form level to make it working in batch public class FormRun extends ObjectRun {     HcmWorker                   hcmWorker;     OutlookUserSetup            outlookUserSetup;     TransDate                   synchronizeFromDate;     TransDate                   synchronizeToDate;         SyncOutLookEmails_RunbaseForm   syncOutLookEmails_RunbaseForm; } public void init() {     synchronizeFromDate = systemdateget();     synchronizeToDate = systemdateget();         syncOutLookEmails_RunbaseForm = element.args().caller().runbase();     super();     if (!smmAxaptaOutlookMapping::isOutlookMappingSetupCompleted())     {         smmAxaptaOutlookMapping::createDefaultSetup();     }     // Find worker connected to the current logged ion     hcmWorker = HcmWorker::find(HcmWorker::userId2Worker(curuserid()));     if (hcmWorker)     {         outlookUserSetup = OutlookUserSetup::findByWorker(hcmWorker.RecId);         // Calculate synchronization period based on the employee setup parameters         activityFromDate.dateValue(systemdateget() – outlookUserSetup.SmmSynchronizeDaysBack);         activityToDate.dateValue(systemdateget() + outlookUserSetup.SmmSynchronizeDaysForward);     }     else     {         // No employee is mapped to the current user. Set mapping in Employee option form.         error(“@SYS80637”);         element.close();     } } void closeOk() {     DialogRunbase dialog = element.args().caller(); ;     dialog.updateServer();     if (syncOutLookEmails_RunbaseForm.checkCloseDialog())         super(); } //AOSRunMode::Client RunBase runBase() {     return syncOutLookEmails_RunbaseForm; } Run class again by pressing F5 Target achieved!!! You can run the form with batch Happy Dax!ng !!!

Sales quotation GST through X++

X++ code to achieve this; static void SalesTax_Per_SalesQuotation(Args _args) {     SalesQuotationTable     salesQuotationTable;     SalesQuotationTotals    salesQuotationTotals;     container               cont;         salesQuotationTotals = SalesQuotationTotals::construct(SalesQuotationTable::find(“QUOT000022”));     salesQuotationTotals.calc();     cont         = salesQuotationTotals.displayFieldsCurrency(salesQuotationTotals.currencyCode());     info(strFmt(“Sales quotation total GST: %1”, conpeek(cont, TradeTotals::posTaxTotal()))); }

FaisalFareed@2025. All rights reserved

Design by T3chDesigns