Friday, November 27, 2015

Launching parallel SSRS reports on screen

Recently I came across a requirement where one of our customer does not want the Check transaction to take another check page, if the number of settlements marked on Payment Journal lines exceeds the number which can be accommodated in single page. They wish to launch the details of transactions on separate report in parallel to check. However, we all know that SSRS report viewer in AX is launched as Dialog in default and hence it requires some modifications in controller class in order to avoid code blocking if any parallel report dialog is launched within report DP class.

In order to get rid of Dialog functionality of SSRS report viewer, see the following code below:

Override the "dialogShow" function in controller class

protected void dialogShow()
    {
          SysOperationDialog  sysOperationDialog;
            FormRun             formRun;

                dialog.run();
                  this.dialogPostRun();
                    sysOperationDialog = dialog as SysOperationDialog;
                      formRun = sysOperationDialog.formRun();

                          formRun.detach();
                        }

                        Override the "dialogClose" function in controller class

                        protected void dialogClose()
                        {
                            //super();
                        }
                              That's all what you need :)

                              Tuesday, October 20, 2015

                              Management Reporter Roles

                              The following tables shows the roles of AX which are transformed in Management reporter when data mart synchronizes.


                              Tuesday, September 29, 2015

                              Changing report design via code SSRS

                              It is one of the normal development requirements you may came across where you want to change the report design name on the basis of some logic. The following example shows you how to achieve this.

                              The modification is done in Controller class "outputReport" method, before "super" call you need to set the updated design name based on some condition and its done.

                              /// <summary>
                              ///    Executes the report for the print management setting that is currently loaded.
                              /// </summary>
                              /// <remarks>
                              ///    The <c>outReports</c> method loops over print management settings and calls this method for each
                              ///    print management setting loaded.
                              /// </remarks>
                              /// <exception cref="M:Exception::Error">
                              ///    The print management object has not been initialized.
                              /// </exception>
                              public void outputReport()
                              {
                                  reportDesign = 'MyReport.Report_DesignA';
                                  this.parmReportName(reportDesign);
                                  this.parmReportContract().parmReportName(reportDesign);
                                  formLetterReport.parmReportRun().settingDetail().parmReportFormatName(reportDesign);
                                  super();
                              }

                              Tuesday, September 22, 2015

                              Restoring missing DirPartyLocation info

                              I came into issue on one of the environment where somehow records of Vendor Contacts data was deleted from DirPartyLocation table and we need to restore the data without backup database as the backup database was way old to recover.

                              By looking into GAB framework which was introduced in AX 2012, I come to know that DirPartyTable  maintains Primary contacts and address information in the following fields.

                              • PrimaryAddressLocation
                              • PrimaryContactEmail
                              • PrimaryContactFax
                              • PrimaryContactPhone
                              • PrimaryContactTelex
                              • PrimaryContactURL

                              Friday, May 15, 2015

                              SSRS Reports Excel merge cell issue

                              I found various blogs and forums discussing the Excel merging cells issue while exporting SSRS report to Excel. I found a simple and logical reason behind the issue.


                              In the above design consider Report Header Textbox with Grid in the details part. If Header Textbox does not align with Grid columns, Excel will start merging the difference of columns which is shown by red line in the picture. However, if you get it align with Grid column Excel will start a new column as there will be no difference in columns indicated by green line.

                              Thursday, May 7, 2015

                              Cheque modification in AX 2012

                              Cheque modification is a most common requirements in Dynamics AX world. In this post I will discuss the values which are prepared before these values are dumped in SSRS Report framework. All cheque values including Recipient name and address, Amount in words, Date, Slip transactions etc.

                              The class which needs to be modify in order to customize the values is "CustVendCheque".

                              CustVendCheque class methods are generally modified to get the cheque values customized:
                              • getSlipText or (fillSlipTxt which is obsolete in AX 2012)
                              • output

                              getSlipText method is responsible for most of the data that is printed on the check slip lines; the key point here is that it is sent as a single block of text which leaves the developer very little room to actually modify the cheque without getting into a stream of circular reference errors. So, whatever we need to do as far as Text formatting (for check stubs) goes, we should get it done before this text (‘chequeSlipTxt’) leaves the class.

                              Friday, May 1, 2015

                              Financial dimension lookup

                              While developing in AX "lookups" are the most common requirements you may come across. However, sometimes you may have to link your customized lookup to non-conventional datasets such as "Financial Dimensions". 
                              This post will show you how to create custom lookup listing one of "Financial Dimension" on report dialog. For this requirement it requires "UI Builder" class.

                              This customized lookup function will be using "DimensionDefaultingLookup" from.

                              Contract Class declaration : 

                              [
                                  DataContractAttribute,  SysOperationContractProcessingAttribute(classStr(xxxxReportUIBuilder))    
                              ]
                              public class xxxxReportContract
                              {
                                  Name    dimensionValue;
                              }

                              Contract parameter :

                              [
                                  DataMemberAttribute(identifierStr(dimensionValue)),
                                  SysOperationLabelAttribute(literalstr('@SYS3794')),
                                  SysOperationDisplayOrderAttribute('1')
                              ]
                              public Name parmFinDimLocation(Name _dimensionValue = dimensionValue)
                              {
                                  dimensionValue = _dimensionValue;
                                  return dimensionValue;
                              }

                              Thursday, April 30, 2015

                              Reading excel file with progress bar

                              Reading an excel is most common requirement you may come across while developing in Axapta world. This post will show the code for reading an excel file with progress bar indicator.

                              static void excelReadWithProgressBarJob(Args _args)
                              {
                                  SysExcelApplication     application;
                                  SysExcelWorkbooks       workbooks;
                                  SysExcelWorkbook        workbook;
                                  SysExcelWorksheets      worksheets;
                                  SysExcelWorksheet       worksheet;
                                  SysExcelCells           cells;
                                  COMVariantType          type;
                                  int                     row, totalRowCount;
                                  int                     numberOfRecordsCleaned;
                                  int                     numberOfRecordsNotFound;
                                  Dialog                  dialog;
                                  DialogField             dialogField;
                                  Filename                filename;
                                  SysExcelRange           range;
                                  SysOperationProgress    progress = new SysOperationProgress();
                                  #Excel
                                  #AviFiles
                                  #define.Star('*')
                                  #define.Space(' ')
                                  
                                  VendAccount             vendAccount;
                                  VendTable               vendTableLocal;
                                  
                                  dialog = new Dialog("Caption of Dialog");
                                  dialogfield = dialog.addField(extendedTypeStr(FilenameOpen), "Excel file");
                                  dialog.filenameLookupFilter(["@SYS28576", #XLSX, "@SYS28576", "*.xls"]);
                                  
                                  if (dialog.run())
                                  {
                                      filename = (dialogfield.value());
                                  }
                                  else
                                  {
                                      return;
                                  }
                                  progress.setCaption("Caption for progress bar");
                                  progress.setAnimation(#AviUpdate);    
                                  
                                  application = SysExcelApplication::construct();
                                  workbooks = application.workbooks();
                                  
                                  try
                                  {
                                      workbooks.open(filename);
                                  }
                                  catch (Exception::Error)
                                  {
                                      throw error("File cannot be opened.");
                                  }
                                  
                                  workbook = workbooks.item(1);
                                  worksheets = workbook.worksheets();
                                  worksheet = worksheets.itemFromNum(1);
                                  cells = worksheet.cells();
                                  range = cells.range(#ExcelTotalRange);
                                  
                                  try
                                  {
                                      // Finds the row where the first contents is found.
                                      range = range.find(#Star, null, #xlFormulas, #xlWhole, #xlByRows, #xlPrevious);
                                      totalRowCount = range.row();
                                      progress.setTotal(totalRowCount);
                                  }
                                  catch (Exception::Error)
                                  {
                                      error("@SYS59926");
                                      totalRowCount = 0;
                                      progress.setTotal(totalRowCount);
                                  }
                                  
                                  do
                                  {
                                      row++;
                                      //Vendor account
                                      switch(cells.item(row, 1).value().variantType())
                                      {
                                          case COMVariantType::VT_BSTR:
                                              vendAccount = strFmt("%1", cells.item(row, 1).value().bStr());
                                              break;
                                          case COMVariantType::VT_DECIMAL, COMVariantType::VT_R4, COMVariantType::VT_R8:
                                              vendAccount = strFmt("%1", num2str(cells.item(row, 1).value().double(), -1, 0, DecimalSeparator::Auto, ThousandSeparator::None));
                                              break;
                                          case COMVariantType::VT_I1, COMVariantType::VT_I2, COMVariantType::VT_I4:
                                              vendAccount = strFmt("%1", cells.item(row, 1).value().int());
                                              break;
                                          case COMVariantType::VT_UI1, COMVariantType::VT_UI2, COMVariantType::VT_UI4:
                                              vendAccount = strFmt("%1", cells.item(row, 1).value().uLong());
                                              break;
                                           case COMVariantType::VT_EMPTY:
                                              vendAccount = '';
                                              break;
                                          default:
                                               throw error(strfmt('Unhandled variant type (%1).', cells.item(row, 3).value().variantType()));
                                      }
                                      vendTableLocal = VendTable::find(vendAccount);
                                      if (vendTableLocal)
                                      {
                                          numberOfRecordsCleaned++;
                                      }
                                      else
                                      {
                                          numberOfRecordsNotFound++;
                                      }
                                      type = cells.item(row + 1, 1).value().variantType();
                                      progress.setText(strfmt("Vendor : %1 (%2 / %3)", vendAccount, row, totalRowCount));
                                      progress.setCount(row, 1);
                                  }
                                  while (type != COMVariantType::VT_EMPTY);
                                  application.quit();
                                  info(strFmt('%1 / %2 number of records found', numberOfRecordsCleaned, row));
                                  info(strFmt('%1 / %2 number of records not found', numberOfRecordsNotFound, row));
                              }

                              Thursday, January 8, 2015

                              Changing Labels values using X++

                              I have come to a requirement where I need to change the values of labels defined in AX on run-time using X++. So, after some initial workout I found the solution to it.

                              For this solution you need to use SysLabelEdit class.
                              Click here SysLabelEdit class. 

                              static void LabelChangeJob(Args _args)
                              {
                                  SysLabelEdit sysLabelEditLocal;
                                  
                                  sysLabelEditLocal = new sysLabelEdit();
                                  info(strFmt('Before Label changes : %1', "@IQL112"));
                                  
                                  sysLabelEditLocal.labelModify('en-us',  sysLabelEditLocal.findLabel('en-us', 'Testing Label 1'), "Testing Label", "", SysLabelApplModule::None);
                                  
                                  info(strFmt('After Label changes : %1', "@IQL112"));
                              }

                              Output


                              Wednesday, January 7, 2015

                              Getting comments on workflow using X++

                              This blog post shows the code for extracting comments provided on workflow at the time of submission.

                              This method requires RecId of the record on which workflow was executed. I used this code for fetching comments provided at the time of submitting workflow on Purchase order.

                              public static Notes getComments(RecId     _purchTableRecId)
                              {
                                  WorkflowTrackingStatusTable     workflowtrackingstatustable;
                                  WorkflowTrackingTable           workflowtrackingtable;
                                  WorkflowTrackingCommentTable    workflowTrackingCommentTable;

                                  select firstOnly RecId from workflowtrackingstatustable
                                      join RecId from workflowtrackingtable
                                          where workflowtrackingstatustable.ContextRecId             == _purchTableRecId //RecId of the record
                                             && workflowtrackingstatustable.TrackingStatus            != WorkflowTrackingStatus::Cancelled
                                             && workflowtrackingtable.TrackingContext                 == workflowtrackingcontext::Workflow
                                             && workflowtrackingtable.TrackingType                    == workflowtrackingtype::Submission
                                             && workflowtrackingtable.WorkflowTrackingStatusTable     == workflowtrackingstatustable.RecId
                                      join Comment from workflowTrackingCommentTable
                                          where workflowTrackingCommentTable.WorkflowTrackingTable    == workflowtrackingtable.RecId;

                                  return workflowTrackingCommentTable.Comment;
                              }