Skip to main content

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]



Comments

  1. Thanks for this post.

    are there some missing declarations?
    getting errors on 'selectedRec' and 'enumerator'

    ReplyDelete
    Replies
    1. is it possible to add custom parameter in customer account statement ext. I am trying to add but it does not show up.

      Delete
    2. Yes that's absolutely possible - you need to add param methods for your required parameters in contract class and call them in controller or service class.

      Delete
    3. You can declare these variables in class declaration as follows;
      RecId selectedRec;
      Enumerator enumerator;

      Delete
  2. You made such an interesting piece to read, giving every subject enlightenment for us to gain knowledge. Thanks for sharing the such information with us to read this... Buy Stripe Account

    ReplyDelete

Post a Comment

I will appreciate your comments !

Popular posts from this blog

The Dual Write implementation - Part 1 - Understand and Setup

What is Dual-write? Tightly couples – complete at one transaction level Near real time Bi-directional Master data and business documents – Customer records you are creating and modifying and at this document we are talking about sales orders or quotes and invoice. Master data could be reference data e.g. customer groups and tax information Why Dual-write and why not Data Integrator? Data Integrator is Manual or Scheduled One directional Now, Let's deep dive and understand what is required for Dual-write setup and from where to start. First thing first, check you have access to https://make.powerapps.com/ Choose right environment of CDS (CE) Make sure you have access to the environment too, click on gear icon and Admin Center  Look for required environment and Open it, you must have access as going forward you are going to configure dual write steps in the environment user the same user you are logged in now. Now, go back to power platform admin center and

D365FO: Entity cannot be deleted while dependent Entities for a processing group exist. Delete dependent Entities for a processing group and try again.

Scenario: There are times when you want to delete an entity from target entity list and when you do so, you face an error message which does not tell you where exactly the entity has been used.  "Entity cannot be deleted while dependent Entities for the processing group exist. Delete dependent Entities for a processing group and try again. " Solution: Browse the environment by appending this part  /?mi=SysTableBrowser&TableName=DMFDefinitionGroupEntity&cmp=USMF   at the end.  For example; if the environment URL is  https://daxture.sandbox.operations.dynamics.com then the complete URL will be https://daxture.sandbox.operations.dynamics.com/?mi=SysTableBrowser&TableName=DMFDefinitionGroupEntity&cmp=USMF Filter for Entity and it will give you the DefinitionGroup where the entity has been added or used in data management import/export projects. Get the DefinitionGroup name and search in the export/import projects under data management and either delete the whole

AX 2012 : Add/Remove Dynalink through X++

DynaLinks can be removed and added between datasources on form using this code; ClearDynaLinks() will remove all existing dynaLinks [these can be seen by right click on the click > Personalise > Query tab > under dataSource node AddDynalink() method has three parameters; 1. Source table field 2. Destination table 3. Destination table field The following code is implemented in the Click event of a button. void  clicked() {     SalesQuotationLine_ds.query().dataSourceNo( 1 ).clearDynalinks();     SalesQuotationLine_ds.query().dataSourceNo( 1 ).addDynalink( fieldNum (SalesQuotationLine, QuotationId),                                                          SAB_DocQuoteGroupRelation,                                                           fieldNum (SAB_DocQuoteGroupRelation,SalesQuotationId));            }