11/03/2016

Programmatically settle a payment journal trans against a specific open transaction.

In the following example, I used a customer invoice as an example of open transaction, the logic applies to vendor open transaction as well.
To make sure the amount can be fully settled, I checked if the payment amount matches the open transaction amount. According to the specific requirement, we can ajust the logic.
The key point of this code snippet is the use of CustVendOpenTransManager.

static void SettleCustOpenTrans(Args _args)
{
    CustVendOpenTransManager manager;
    LedgerJournalTrans ljt;
    CustTransOpen cto;
    CustTrans ct;
    ;
    ct = CustTrans::findFromInvoice('Invoice Number', 'Customer Account');

    // Get cust trans open.
    if (ct.RecId)
    {
        cto = CustTransOpen::findRefId(ct.RecId);

        // Check amount to make sure the open trans was not partially settled before.
        if (cto.AmountCur != ct.AmountCur)
            throw error('invoice has been partially paid');
    }
    else
    {
        throw error('invoice could not be found');
    }

    // Fetch the added LedgerJournalTrans.
    ljt = LedgerJournalTrans::find('Journal number', 'Jounal trans voucher', true);

    // Check if the journal trans is paying the invoice wholly.
    if (ljt.AmountCurCredit - ljt.AmountCurDebit != cto.AmountCur)
        throw error('this payment journal line is not paying the invoice wholly');


    ttsBegin;
    // Mark the invoice.
    manager = CustVendOpenTransManager::construct(ljt);
    manager.updateTransMarked(cto, true);

    // Update journal trans.
    ljt.MarkedInvoice = ct.Invoice;
    ljt.SettleVoucher = SettlementType::SelectedTransact;
    ljt.update();
    ttsCommit;

    info('done');
}


10/28/2016

Observations in sysoperation framework Synchronous execution mode

Observations in Synchronous execution mode.

Called from: Client
Run on: Server (If the service class is registered in AXClient service group, it will run on client, cause the executeOperationWithRunAs returns false in that case.)
Session: CIL (If the service class is registered in AXClient service group, it will run in an interpreter session, cause the executeOperationWithRunAs returns false in that case.)

Possible errors:
  • As I said above, normally synchronous service will be executed on server and in CIL session, but if it's executed on client and interpreter session, we cannot set 'Runon' of the service class to server any longer. Otherwise, we'll see a data contract error like this,
  • If the method is published as an AIF service entry (with [SysEntrypointAttribute]), and the 'Runon' is 'Call from', we'll see this 'uncheced' error when executing,

Conclusion,
In AX, we cannot call a method in a servie class in both synch and asynch mode, because of the 2 errors above and asynch mode needs the service to be registered in AXClient service group.



8/08/2016

How to run SSRS report from X++ code in AX? 2. SrsReportRunController

SrsReportRunController is new in AX2012. It extends the SysOperation framework.
Here's a sample.

SrsReportRunController 

SrsReportRunController controller;
;
//Initilize
controller = new SrsReportRunController();
//Set report name.
controller.parmReportName(ssrsReportStr(Test, PrecisionDesign1));

//Run report
controller.startOperation();

To set parameter

  • If the data source type is Report Data Provider, we need to use the a data contract instance to set parameter. See sample below,
//Initilize
controller = new SrsReportRunController();
//Set report name.
controller.parmReportName(ssrsReportStr(Test, PrecisionDesign1));
//Get data contract instance.
rdpContract = controller.parmReportContract().parmRdpContract() as TestDataContract;
//Set parameter value;
rdpContract.parmSalesId('so-000050');
controller.startOperation();

  • If the data source type is Query, and the dynamic filer is turned off, we can set the parameter thru a RDL contract instance.
//Initilize
controller = new SrsReportRunController();
//Set report name.
controller.parmReportName(ssrsReportStr(Test, PrecisionDesign1));
//Get data contract instance.
rdpContract = controller.parmReportContract().parmRdpContract() as TestDataContract;
//Set parameter value;
rdpContract.parmSalesId('so-000050');
controller.startOperation();

  • If the data source type is Query, and the dynamic filter is turned on, we need to get the query of the report and set range to that query.

//Initilize
controller = new SrsReportRunController();
//Set report name.
controller.parmReportName(ssrsReportStr(Test, PrecisionDesign1));
//Get data contract instance.
query = controller.parmReportContract().parmQueryContract().lookup('Dataset1_DynamicParameter');
//Set parameter value;
query.dataSourceNo(1).clearRange();
query.dataSourceNo(1).addRange(fieldNum(SalesTable, SalesId)).Value('so-000050');

controller.startOperation();

Remember: parmQueryContract() returns the map of the queries, and we can lookup by the parameter name

Run  multiple reports to screen

When printing multiple reports to screen in code, the previous printed report will block the next one, untill user closes the previous one, the next one starts rendering. This is anoying if user needs to print multiple reports to screen as a batch. The solution to this is pretty simple, we just need to create a class to extend SrsReportRunController and overwrite the methods dialogShow and dialogClose. See sample below,

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

    if (useReportViewerForm)
    {
        dialog.run();
        this.dialogPostRun();

        sysOperationDialog = dialog as SysOperationDialog;
        formRun = sysOperationDialog.formRun();
        formRun.detach();
    }
    else
    {
        super();
    }
}



protected void dialogClose()
{
    if(!useReportViewerForm)
    {
        super();
    }
}


8/05/2016

How to run SSRS report from X++ code in AX? 1. SRSReportRun

Since AX 2012 has been released for more than 5 years, this topic is not fresh any more. But it's always convenient to have that written down, so I can find the snipet of code just in case I forget.


SRSReportRun

SRSReportRun reportRun;
;
//Initialize reportRun with a specific report name and design.
reportRun = new SRSReportRun("Test.PrecisionDesign1");

//Set parameter, you can find the parameter name in visual studio
reportRun.reportParameter('Dataset1_SalesId').value('so-000050');

// If the property 'Dynamic filter' is set to yes, the parameter needs to set thru the report query. For example,
//query = reportRun.reportQueries().lookup(reportRun.currentQueryKey());
//query.dataSourceNo(1).clearRanges();
//query.dataSourceNo(1).addRange(fieldNum(SalesTable, SalesId)).value('so-000050');

//Run report, or use executeReport() (no dialog will pop up).
reportRun.run();



Pros:
  • Simple and straightforward.
  • When print multiple reports to screen together, each one runs asynchronously which means user will see multiple reports pop up to screen. SRSReportRunController doesn't do this, modification is required if we need to.

Cons:

  • When printing to screen, the parameter section cannot be completely hidden which makes it not that neat.

  • When there're multiple companies(legal entities) in AX, SRSReportRun always pass the default company of current user to report, even if the current company is different. So in this situation, SRSReportRun doesn't work. And I believe this is a bug.
Coclusion:
As SRSReportRun has a defect I don't see any solution to work arround, I would suggest not use it. More than that, in AX 2012, we can use another class which works with the SysOperation framework, the new framework compared to old version.

This is the link to how to run report by using SrsReportRunController.

8/02/2016

Issue fixed - In AX, some users can open a SSRS report, but no data populated in it.

Recently, there's an issue bugs me. The issue is for some users that they can execute a report, but the report has no data. But if I prompt the role of the user to admin, everything becomes normal. This must be a security setting related issue.
Later, I found the report was called from code, either by SRSReportRun or SrsReportRunController. And there's not menu items tied to that report, which means the role doesn't have the menu item of the report in its security enty point. After adding a menu item of that report and adding that menu item to the role, that user can run report well.
Seems not that complicated, but why it bugs me for many days? The reason is that I always thought if a user can open a report, that user must have privilege to execute that report, but I was wrong. User can see the parameters form, enter query criteria, but that doesn't mean the user has privilege to show data in that report. Cause so far, the security validation for report has not worked yet, not until user hits the button to render data.
To prove my conclusion, I rolled back to remove the menu item of the report I added, and I added the table of the report needs to load to that role permission. Then I executed the report with that user, everything is good.


  • So when setting security, the menu item entry point actually does 2 things,


  1. Enable the user of that role to see the menu item in menu.
  2. Enable the user of that role to operate with the data to which the object linked that menu item needs. Like this below.
  • If there's no menu item to that object, user is still able to execute that object from code, as long as the role of that user has permission to operate the data table of that object.

7/26/2016

Dynamics AX blog: X++ code to change financial dimension when assign...

Dynamics AX blog: X++ code to change financial dimension when assign...: Hi Friends, It is good to find the out of the box available API's in Microsoft Dynamics AX, which can take the burden of writing some ...



I just found this is really helpful to add site to an existing financial dimention to get a new financial dimension. I used it in building counting journal line!

5/23/2016

What to do with 'For more information about this error navigate to the report server on the local server machine, or enable remote errors' when printing SSRS report in AX.

Recently, I encountered an anoying error where printing a regular report from AX with the error message 'For more information about this error navigate to the report server on the local server machine, or enable remote errors'. So I just googled it as usual and tried to find a quick fix. What I found were all about just restart AOS, SSRS service, regenerate CIL and so forth, but none of them worked in my case. The dummy message must cover variety of causes.
Meanwhile I noticed the error popped up when loading data, not right after running the report, so I think probably it's caused by the data, and the error happened in AX loading data logic cannot be explicitly shown through SSRS exception handling.
I went to the Report Manager URL and found the report, then just directly excuted it in browser. The error message popped up with detail information for me to analysis the problem. Indeed, this was a data issue which caused a runtime error in data provider. Easy fix at the data and the report was good afterward.