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;
}

Monday, December 29, 2014

Getting number of recrods present in a query

If you want to get the total number of records from a query run object, which can be used to set total count in a progress bar (progress indication framework).

Solution: Use SysQuery::countloop([QueryRun _queryRun]). this will return the total number of records in a query object

Wednesday, December 24, 2014

Generating SSRS report in PDF via code X++

Following is a sample code for generating SSRS report in PDF format using X++ code. This sample code generates Purchase order confirmation report in PDF format and saves it into specified location.


PSAPurchaseOrderController poConfirmationController;
PurchPurchaseOrderContract purchPOContract;
SRSReportExecutionInfo     reportExecutionInfo;
Args                       args = new Args();
SrsReportRunImpl           srsReportRun;
VendPurchOrderJour         vendPurchOrderJour;
ReportName                 reportName = ssrsReportStr(PSAPurchaseOrder, Report);

str                        fileName;
;

select firstOnly vendPurchOrderJour
order by RecId desc
where vendPurchOrderJour.PurchId == '';//Replace with PurchOrder Id

if (!vendPurchOrderJour)
return '';


args.record(vendPurchOrderJour);
poConfirmationController= PSAPurchaseOrderController::construct();
poConfirmationController.parmArgs(args);
poConfirmationController.parmReportName(reportName);
reportExecutionInfo = poConfirmationController.parmReportContract().parmReportExecutionInfo();
purchPOContract= poConfirmationController.parmReportContract().parmRdpContract();


purchPOContract.parmRecordId(vendPurchOrderJour.RecId); // Record id must be passed otherwise the report will be empty

poConfirmationController.parmArgs(args);srsReportRun = poConfirmationController.parmReportRun() as SrsReportRunImpl;


fileName = System.IO.Path::Combine(WinAPIServer::getTempPath(), strFmt('purchConfirmation_%1.pdf', _purchId));
poConfirmationController.parmReportRun(srsReportRun);
poConfirmationController.parmReportContract().parmPrintSettings().printMediumType(SRSPrintMediumType::File);
poConfirmationController.parmReportContract().parmPrintSettings().overwriteFile(true);


poConfirmationController.parmReportContract().parmPrintSettings().fileFormat(SRSReportFileFormat::PDF);
poConfirmationController.parmReportContract().parmPrintSettings().fileName(fileName);

poConfirmationController.runReport();

Tuesday, November 25, 2014

Creating a class object using X++

The following Job shows the code used for creating a "Class" using X++ code. This code may be used in scenarios like AIF service, Workflow generation etc processes where different objects like "Class" are created automatically using the similar code mentioned below.

static void BlogTestJob(Args _args)
{
    ClassBuild  classBuild;
    str         header;
    str         newClassName = "SampleClass";
    ;

    header = 'public class '+ newClassName;
    classBuild = new ClassBuild(newClassName);
    classBuild.addMethod('classdeclaration', header + '\n{\n}\n');
    classBuild.addMethod('main', 'public static void main(Args    _args)' + '\n{\n}\n');
    classBuild.classNode().AOTcompile(1);

    info('Job completed....');
}

Monday, November 24, 2014

Importing Sales order XML using X++

The following job will create Sales order using Document service through X++ code.

static void JobTestWebService_ImportXml(Args _args)
{
    //feed the xml that was modified after exporting through exportxml job
    XmlDocument xml = XmlDocument::newFile(@'C:\SORequest.xml');
    AxdSalesOrder salesOrder;

    try
    {
        salesOrder = new AxdSalesOrder();
        salesOrder.create(xml.xml(),  new AifEndPointActionPolicyInfo(), new AifConstraintList());
    }
    catch
    {
        throw error('Error in document service');
    }
    info(strFmt('Sales order created'));
}

Sample XML File

<?xml version="1.0" encoding="utf-8" ?>
<SalesOrder xmlns="http://schemas.microsoft.com/dynamics/2008/01/documents/SalesOrder">
<SalesTable class="entity">
  <CustAccount>1204</CustAccount>
  <DeliveryDate>2011-10-11</DeliveryDate>
  <ReceiptDateRequested>2011-11-11</ReceiptDateRequested>
  <PurchOrderFormNum>ABC-98654</PurchOrderFormNum>
<TableDlvAddr class="entity">
<Address>KEVIN DAVIS 13075 MANCHESTER RD STE 228.A  DES PERES, MO 631311878 </Address>
<City>DES PERES</City>
<CountryRegionId>USA</CountryRegionId>
<County>JEFFERSON</County>
<ISOcode>US</ISOcode>
<LocationName>KEVIN DAVIS</LocationName>
<State>MO</State>
<Street>13075 MANCHESTER RD STE 228.A </Street>
<ValidFrom>2014-11-11T00:00:00Z</ValidFrom>
<ValidTo>2014-11-11T00:00:00Z</ValidTo>
<ZipCode>631311878</ZipCode>
</TableDlvAddr>
  <SalesLine class="entity">
<ItemId>0001</ItemId>
<SalesQty>20</SalesQty>
<SalesPrice>82.25</SalesPrice>
<SalesUnit>Pcs</SalesUnit>
  </SalesLine>
</SalesTable>

</SalesOrder>

Exporting XML Sales order using AIF service

The following job will export the Sales order information into an XML format which can be used by document service in order to test Sales order creation using XML.

static void JobTestWebService_ExportXml(Args _args)
{
    AxdAddress address;
    AxdSalesOrder salesOrder;

    AifEntityKey    key;
    Map             map;

    //dummy variable
    AifPropertyBag bag;

    map = new Map(Types::Integer, Types::Container);
    map.insert(fieldnum(SalesTable, SalesId), ['SO-101327']);
    
    key = new AifEntityKey();
    key.parmTableId(tablenum(SalesTable));
    
    key.parmKeyDataMap(map);

    try
    {
        address = new AxdAddress();
        salesOrder = new AxdSalesOrder();
        info('XML output');
        info(salesOrder.read(key, null, new AifEndPointActionPolicyInfo(), new AifConstraintList(), bag));
    }
    catch
    {
        throw error('Error in document service outbound');
    }
}

Filter records on form using X++

This post will shows the steps required to filter records on form using parameters provided by the caller.

Step 1
Declare a class variable 
In the ClassDeclaration method of form declare a QueryBuildRange

class FormRun extends ObjectRun
{
    QueryBuildRange      abcQueryRange;
    ABC                  abcCode;
}

Step 2
Initialize this range in the init method of datasource after super() call.

public void init()
{
    super();
    abcQueryRange = this.query().dataSourceName('X').addRange(fieldnum(X, ABC));

}

Step 3
Now assign the filter value in the executeQuery method of same datasource before super() call.

public void executeQuery()
{
    if (abcCode)
    {
        abcQueryRange.value(queryValue(abcCode));
    }
    super();

}

The query filtration part is done, you now need to fetch the value of "abcCode" from the caller. For this you need to grab the values from argument passed by the caller in the "init" method form.


public void init()
{
    if (element.args().record())
    {
        if (element.args().record().TableId == tablenum(x))
        {
            abcCode
 = X::findRecId(element.args().record().RecId).abcCode;
        }
    }
    super();

}