Tuesday, September 2, 2014

RDP based SSRS report

A RDP based SSRS report consist of the following:
Temporary table
Data contract class
UI builder class
RDP class
Controller class
SSRS report
Output menu item

Step 1: Temporary table
Create a new inmemory temporary table which will be used in the RDP class.


Step 2: Data contract class.

  1. Class declaration
The following should be set before the class declaration.
[
   DataContractAttribute,
   SysOperationContractProcessingAttribute(classStr(xxxReportUIBuilder)),
 SysOperationGroupAttribute('Parameter',"Parameter",'1'),
   SysOperationGroupAttribute('Select',"Select",'2')

]

Where xxxReportUIBuilder is the UI builder class name.
SysOperationGroupAttribute should be used if we need to group parameters and the order in which the group should be shown.

If validation is required, The class should implement SysOperationValidatable
public class LF_BookedSalesReportContract implements SysOperationValidatable

Variables should be declared which are required as parameters.
TransDate           fromDate;
TransDate           toDate;
DimensionValue      branch;
hcmWorkerRecId      employee;


  1. Add parm methods for the variables declared.
[
DataMemberAttribute('FromDate'),
SysOperationLabelAttribute(literalstr("@SYS24050")),
SysOperationGroupMemberAttribute('Parameter'),
SysOperationDisplayOrderAttribute('1')

]
public TransDate parmFromDate(TransDate _fromDate = fromDate)
{
   fromDate = _fromDate;
   return fromDate;
}

SysOperationGroupMemberAttribute should be used identify the group name and SysOperationDisplayOrderAttribute should be used to set the display order of the parameter in the group.

///
/// Gets or sets the value of the datacontract parameter ToDate.
///
///
/// The new value of the datacontract parameter ToDate; optional.
///
///
///  The current value of datacontract parameter ToDate
///
[
   DataMemberAttribute('ToDate'),
   SysOperationLabelAttribute(literalstr("@SYS80934"))
]
public TransDate parmToDate(TransDate _toDate = toDate)
{
   toDate = _toDate;
   return toDate;
}

  1. Validate variables.

public boolean validate()
{
   boolean isValid = true;

if (this.parmToDate() && this.parmFromDate() > this.parmToDate())
   {
       // Invalid date interval
        isValid = checkFailed("@SYS91020");
   }
       if(isValid )
       {
           return true;
       }
       else
       {
           return false;
       }

   }

To validate dimensions:
   DimensionAttribute              dimAttribute;
   DimensionAttributeDirCategory   dimAttributeDirCategory;
   DimensionFinancialTag           DimensionFinancialTag;

   void checkDimension(DimensionValue  _value, Name    _dimname)
   {
       dimAttribute = DimensionAttribute::findByName(_dimname);
       select RecId from DimensionFinancialTag where DimensionFinancialTag.Value == _value
         join DirCategory from dimAttributeDirCategory
         where dimAttributeDirCategory.DimensionAttribute == dimAttribute.recId &&
            dimAttributeDirCategory.DirCategory == DimensionFinancialTag.FinancialTagCategory ;
       if (!DimensionFinancialTag.RecId)
           isValid = checkFailed(strfmt("%1 value %2 does not exist.",_dimname,_value));
   }

   department = this.parmDepartment();
   if(department)
   {
checkDimension(department,"Departments"); checkDimension(department,"Departments");
   }



Step 3: UI builder class.
  1. Class declaration
Should extend SrsReportDataContractUIBuilder
Declare a variable for the contract class created above.
xxxDPContract rdpContract

  1. Post run method
This method can be overridden and can be used to register overridden method for controls like the lookup, modified method etc. Also can be used to set the labels for the controls.

public void postRun()
{
   DialogField dialogField;
   Dialog dialogLocal = this.dialog();
   ;

   super();
dialogLocal.caption("Report Name");

// This method should be called in order to handle events on dialogs. This method should not be called in case of multi select control overridden.
   dialogLocal.dialogForm().formRun().controlMethodOverload(false);
   rdpContract = this.dataContractObject();
   //Set default values in the contract
rdpContract.parmToDate(systemDateGet());
   rdpContract.parmSalesEmpType(LF_SalesEmpType::LF_SalesResponsible);
   dialogField = this.bindInfo().getDialogField(this.dataContractObject(),
methodStr(LF_BookedSalesReportContract, parmToDate));
   dialogField.value(systemDateGet());

   dialogField = this.bindInfo().getDialogField(this.dataContractObject(),
methodStr(LF_BookedSalesReportContract, parmSalesChannel));
   dialogField.registerOverrideMethod(methodstr(FormStringControl, lookup),
methodstr(LF_BookedSalesReportUIBuilder,  lookupSalesChannel), this);

}

lookup method overridded:
public void lookupSalesChannel(FormStringControl _control)
{
    SysTableLookup          sysTableLookup;
    Query                   query;
    QueryBuildDataSource    qbdsdimFinancialTag,qbdsSPSContract;
    DialogField             dlgFldSalesProjType;

    dlgFldSalesProjType = this.bindInfo().getDialogField(this.dataContractObject(),
methodStr(LF_BookedSalesReportContract, parmSalesProjType));
    if (dlgFldSalesProjType.value() == LF_SalesProjType::LF_SmallProjSales)
    {
        sysTableLookup  = SysTableLookup::newParameters(tableNum(DimensionFinancialTag), _control);
        sysTableLookup.addLookupfield(fieldNum(DimensionFinancialTag, Value));
        sysTableLookup.addLookupfield(fieldNum(DimensionFinancialTag, Description));

        query   = new query();
        qbdsdimFinancialTag = query.addDataSource(tableNum(DimensionFinancialTag));
        qbdsSPSContract = qbdsdimFinancialTag.addDataSource(tableNum(LF_SmallProjSalesData));
       qbdsSPSContract.addLink(fieldNum(DimensionFinancialTag,RecId),fieldNum(LF_SmallProjSalesData,LF_SalesChannelRecId));
        qbdsSPSContract.joinMode(JoinMode::ExistsJoin);

        sysTableLookup.parmQuery(Query);
        sysTableLookup.performFormLookup();
    }
}

Example of modified method
public boolean modifiedCustAcct(FormStringControl _control)
{
   CustTable               custTable;
   LogisticsPostalAddress  deliveryAddress;
   DialogField             dialogField;


   dialogField = this.bindInfo().getDialogField(this.dataContractObject(), methodStr(LF_ShippingLabelContract, parmCustAccount));

   if(dialogField.value())
   {
       select firstonly custTable where custTable.AccountNum == dialogField.value();
       if (custTable)
       {
           deliveryAddress = LogisticsLocationEntity::findPostalAddress(custTable,LogisticsLocationRoleType::Delivery);
           if (deliveryAddress)
               this.setAddressFields(deliveryAddress);
           else
               this.setAddressFields(custTable.postalAddress());

           dialogField = this.bindInfo().getDialogField(this.dataContractObject(), methodStr(LF_ShippingLabelContract, parmCustName));
           dialogField.value(custTable.name());
           contract.parmCustName(custTable.name());
       }
   }
   return true;
}

Example to clear control values.
public void preBuild()
{
   FormBuildGroupControl       formBuildGroupControl;


   super();
   contract = this.dataContractObject();



   contract.parmJobNum("");
   contract.parmCustName("");
   

   formBuildGroupControl = dialog.curFormGroup();
   formBuildGroupControl.columns(2);
}

Step 4: RDP class.
  1. Class declaration
The following should be set before the class declaration.
[
   SRSReportParameterAttribute(classstr(xxxReportContract)),
SRSReportQueryAttribute(queryStr(QueryName)) //used when query is also part of the rdp.

]

Where xxxReportContract is the Data Contract class name.

The class should extends SRSReportDataProviderBase //SrsReportDataProviderPreProcess //
SrsReportDataProviderPreProcess can be used for debugging the rdp class.

In the class declartion, declare a variable for the tmp which will be sent to the SSRS report, and for the queryrun.
LF_BookedContractSalesTmp    bookedContractSalesTmp;
QueryRun                 queryRun;



  1. Process report
Write the logic to insert the data into the temp table
   LF_ScheduledDollarsBranchContract      dataContract; // Get the query from the runtime using a dynamic query.

   dataContract = this.parmDataContract();
   projFromDate = dataContract.parmProjectFromDate();
   queryRun = new SysQueryRun(this.parmQuery());

   //Add more filters to the query based on the user selected values
    refer the setQueryRanges() below.

   while (queryRun.next()
   {


   }


Example to modify the query.
private void setQueryRanges(ProjResponsibleFinancialWorker _projectAccountant)
{
   QueryBuildDataSource qbdsProjTable, qbdsContractLineItems, qbdsProjInvTeamTable;
   qbdsProjTable = queryrun.query().dataSourceTable(tablenum(ProjTable));
   qbdsContractLineItems = qbdsProjTable.addDataSource(tableNum(PSAContractLineItems));
   qbdsContractLineItems.addSelectionField(fieldnum(PSAContractLineItems, FeeProjId));
   qbdsContractLineItems.addSelectionField(fieldnum(PSAContractLineItems, ChangeOrderNum));
   qbdsContractLineItems.addSelectionField(fieldnum(PSAContractLineItems, LineDesc));
   qbdsContractLineItems.addSelectionField(fieldnum(PSAContractLineItems, LineValue));
   qbdsContractLineItems.addSelectionField(fieldnum(PSAContractLineItems, CreatedDateTime));
   //qbdsContractLineItems.addSelectionField(fieldnum(PSAContractLineItems, ProjInvoiceProjId));
   qbdsContractLineItems.addRange(fieldnum(PSAContractLineItems,LF_ContractLineStatusId )).value("TM");
   qbdsContractLineItems.addRange(fieldnum(PSAContractLineItems,LF_ContractLineStatusId )).value("PN");
   qbdsContractLineItems.addRange(fieldnum(PSAContractLineItems,LF_ContractLineStatusId )).value("P$");
   qbdsContractLineItems.addRange(fieldnum(PSAContractLineItems,LF_ContractLineStatusId )).value(SysQuery::valueEmptyString());
   qbdsContractLineItems.addRange(fieldnum(PSAContractLineItems, FeeProjId)).value(SysQuery::valueNotEmptyString());
   qbdsContractLineItems.addSortField(fieldNum(PSAContractlineitems,FeeProjId));
   qbdsContractLineItems.addSortField(fieldNum(PSAContractlineitems,ChangeOrderNum));
   qbdsContractLineItems.addLink(fieldNum(ProjTable, ProjId), fieldNum(PSAContractLineItems, FeeProjId));
   qbdsContractLineItems.joinMode(JoinMode::InnerJoin);

   qbdsProjInvTeamTable = qbdsContractLineItems.addDataSource(tablenum(LF_ProjInvoiceTeamTable));
   qbdsProjInvTeamTable.addSelectionField(fieldnum(LF_ProjInvoiceTeamTable, LF_ProjectAcct));
   qbdsProjInvTeamTable.addLink(fieldNum(PSAContractLineItems, ProjInvoiceProjId), fieldNum(LF_ProjInvoiceTeamTable, LF_ProjInvoiceId));
   if (_projectAccountant)
   {
       qbdsProjInvTeamTable.joinMode(JoinMode::InnerJoin);
       qbdsProjInvTeamTable.addRange(fieldNum(LF_ProjInvoiceTeamTable,LF_ProjectAcct)).value(queryValue(_projectAccountant));
   }
   else
   {
       qbdsProjInvTeamTable.joinMode(JoinMode::OuterJoin);
   }
}



  1. method to set the tmp table
This method should set the report dataset attribute to the temporary table

[
   SRSReportDataSetAttribute("scheduledDollarBySchedulerTmp")
]
public LF_ScheduledDollarBySchedulerTmp getScheduledDollarBySchedulerTmp()
{
   select * from scheduledDollarBySchedulerTmp;

   return  scheduledDollarBySchedulerTmp;
}


Step 4: Controller class.
This class should extend SrsReportRunController. In the class declaration, declare a macro for the report name

#define.ReportName('LF_BookedSalesReport.Report')

Create a main method to call the SSRS report.
public static void main(Args _args)
{
   LF_BookedSalesReportController controller  = new LF_BookedSalesReportController();
   controller.parmReportName(#ReportName);
   controller.parmArgs(_args);
   controller.startOperation();
}

Override heh prePromptModifyContract to make changes to query filter.
protected void prePromptModifyContract()
{
   ProjTable   projTableLocal;
   //super();
   //add a range in the report query
   if(this.parmArgs().record())
   {
       projTableLocal = this.parmArgs().record();
       SrsReportHelper::addParameterValueRangeToQuery(this.getFirstQuery(),tableNum(ProjTable),fieldNum(ProjTable, ProjId),SysQuery::value(projTableLocal.projId));
   }
}



Step 5: SSRS Report.
Create a SSRS report in the VS report designer. Create an RDP based precision report and the dataset should use the rdp class created before.

Step 6: Output menu item.


Step 7: Add Output menu item to menu.