View RSS Feed

Development Team Blog

Visual Report Writer and The Web (IV)

Rating: 3 votes, 5.00 average.
In this fourth blog the Customer List is the topic. Ehh, a customer list, isn't that too simple for a demo and a blog? Yes and no. A customer list running on the Web Order Customer table is in fact a simple report but as you discover below we have added a couple of features to make the report and integration more attractive. To see the here discussed report in action you should navigate to the live demo website (European Server, USA server). You are also advised to read the three previous blogs (1: The Solution, 2: Invoices Report, 3: The Cleanup).

The Report
The report is named Simple Customer List but contains a couple of enhancements to make it more attractive. The report contains two functions. One function is named PageNumber which simply return the text "Page:" combined with the special field {&Page Number}. Besides the fact that you can easily combine a static text and a variable it can be seen as simple. The second function is named StateName which shows the usage of ELFs. ELF is the abbreviation of Extended Library Function. With Visual Report Writer you can make use of functions written in a DLL (Dynamic Link Library) to perform special operations that are not in the product itself. The functions should return a value and in this case it returns the name of the customer state by finding a row in the codemast table of the workspace. This ELF is named CodeDescription and is delivered with the product in a DLL named CodeMast.Dll. The function code itself is:
Code:
return CodeDescription({?CodeMastPath},"STATES",{Customer.State})
The first parameter of this function call is a path to the codemast table. The codemast table maybe converted to SQL and then the value of the report parameter named CodeMastPath should point to an INT file. In this demo website the table is in the embedded DataFlex database format and thus points to a codemast.dat file on disk. With the usage of the parameter field it is possible to control the location of the table at integration level in a similar way as pbAutoLocateDFFiles operates.

Last but not least; a report should look nice, should be attractive. In this report we added a blue bar behind the column captions and the bar has a rounding in its corners. For the upcoming revision 3.0 we can add gradients to such an object to make it even more attractive.

The Integration
At report integration level we decided to add a couple of features available in and through the DataFlex 17.1 Web Application Framework.

First, the selection criteria and the results are separated from eachother to make more space for both of them and to show that it can be done. The selection criteria can be set on the first cWebTabPage object and the results are shown in an iFrame control on the second tab-page. The selections tab-page is divided into two panels to keep the controls reasonable in width on wider devices. The width of an input control enlarges if the width of the view enlarges and the piWidth of the cWebView object is not set to a specific width.

The first control on the tab-page is a cWebCombo control which is data-aware and automatically pulls the states from the codemast table via the DD validation table connection. The only change made in regards to this control is the cancellation of the field_defaults setting in the customer data dictionary object. Normally the state Florida is specified as a default state and to avoid the report by default only shows the customers of one state this is blanked out. You can still - and it is the purpose of having the cWebCombo - a state to limit the data found and presented. In the OnInitializeReport event of the cVRWReport object this is covered as:
Code:
WebGet psValue of oCustomerStateForm to sCustomerState
If (sCustomerState <> "") Begin
    Send AddFilter C_USEMAINVRWREPORTID "{Customer.State}" C_VRWEqual sCustomerState
End
The second control on the tab-page is a cWebForm control, not data-aware and "just" an input field. If the user enters a value here the rows from the table are filtered on including this value in the name column. In coding this is done as follows:
Code:
WebGet psValue of oNameContains to sNameContains
If (sNameContains <> "") Begin
    Get GetChecked of oContainsCaseInSensitive to bCaseInSensitive
    Move (SFormat ("return Instr({Customer.Name},'%1',1,%2)", sNameContains, bCaseInSensitive)) to sFilterFunction
    Set psFilterFunction C_USEMAINVRWREPORTID to sFilterFunction
End
Usage of functions inside a psFilterFunction adds power to the system BUT slows down the selection. Visual Report Writer currently (still on the wishlist) does not attempt to offload selection criteria if a function is used inside a psFilterFunction instruction. Offload means adding the selection criteria to the WHERE clause in an ODBC database report and jump-into index in a DataFlex embedded database data-source report. Note that selection criteria in a psFilterFunction where no functions are used can be offloaded to speed up the selection process. For an ODBC based report you can check if the selection criteria are offloaded by viewing the SQL statement created, available from the report designer tool as well as from integration level (ReportQuery function of cVRWReport).

The next control is again a cWebCombo control but not data-aware this time and its values are hard coded in the OnFill event of the control. The control allows you to limit the number of found customers by looking at the customer.status column. The following code is used to pass the user' choice to the report selection criteria.
Code:
WebGet psValue of oCustomerStatus to sStatus
If (sStatus <> '-') Begin
    Send AddFilter C_USEMAINVRWREPORTID "{Customer.Status}" C_VRWEqual sStatus
End
The next control is a horizontal line to serve both the purpose of creating some distance between the combo and the sliders (the next big thing) and having "just" a separator line. The object is instantiated from the cWebHorizontalLine class.
Code:
Object oSpacerLine is a cWebHorizontalLineEnd_Object
The next three controls are sliders (cWebSlider). The user can set the selection range for balance, purchase or credit limits to a upper and lower boundary value. The checkbox on the right hand side of the slider needs to be used to make the value active. This is done because the slider's ranges are set to a smaller range than the full set, just to show it can be done.

How code is behind the slider controls? First the object itself:
Code:
Object oPurchasesSlider is a cWebSlider
    Set piColumnSpan to 6
    Set psLabel to "Purchases Range:"
    Set peLabelAlign to alignRight
    Set pbShowRange to True
    Set pbShowValue to True
    Set pbRanged to True
End_Object
You can create this by drag-and-drop from the class palette and setting properties as pbShowRange, pbShowValue and pbRanged.

The OnLoad event is used to find the current highest and lowest values for the columns balance, credit limit and purchases. Because the credit limit and purchase columns are not part of an index we added two indexes to the customer data file to make the process of finding the highest and lowest value easier and faster. The OnLoad event of one of the sliders is:
Code:
Procedure OnLoad
    Integer iMinValue iMaxValue iFrom iTo

    Forward Send OnLoad

    Clear Customer
    Find Gt Customer by 3
    Move Customer.Balance to iMinValue

    Clear Customer
    Find Lt Customer by 3
    Move Customer.Balance to iMaxValue

    Move (iMinValue + (iMaxValue - iMinValue / 10)) to iFrom
    Move (iMaxValue - (iMaxValue - iMinValue / 10)) to iTo

    Set piMinValue to iMinValue
    Set piFrom to iFrom
    Set piMaxValue to iMaxValue
    Set piTo to iTo
End_Procedure
The code above sets the mimimum and maximum values of the sliders (piMinValue and piMaxValue) but also sets a "current" range which is 10% of the maximum minus the minimum value plus the minimum or the maximum, moving the range a little off the upper and lower values.

The slider control can show labels spread over the slider and this is done in the event OnFillLabels.
Code:
Procedure OnFillLabels
    Integer iMinValue iMaxValue iLow iMedium iHigh

    Get piMinValue to iMinValue
    Get piMaxValue to iMaxValue

    Move (iMaxValue - iMinValue * 0.05) to iLow
    Move (iMaxValue - iMinValue * 0.50) to iMedium
    Move (iMaxValue - iMinValue * 0.95) to iHigh

    Send AddLabel iLow "Low"
    Send AddLabel iMedium "Medium"
    Send AddLabel iHigh "High"
End_Procedure
Three labels are added at 5, 50 and 95% of the minimum and maximum values.

The ranges are - if made active via the checkbox - added to the selection criteria via the following code (again inside OnInitializeReport)
Code:
Get GetChecked of oUseBalanceFiltering to bUseValue
If (bUseValue) Begin
    WebGet piFrom of oBalanceSlider to iFrom
    WebGet piTo of oBalanceSlider to iTo
    Send AddFilter C_USEMAINVRWREPORTID "{Customer.Balance}" C_VRWGreaterThanOrEqual iFrom
    Send AddFilter C_USEMAINVRWREPORTID "{Customer.Balance}" C_VRWLessThanOrEqual iTo
End
In the report description above is mentioned that the report uses codemast.dll to return the name of the state. For this the path to the codemast table needed to be given and it was done via a parameter named CodeMastPath. The parameter is set in a method of its own (started from OnInitializeReport). In the following code the report is queried for the parameter ID of the named parameter CodeMastPath which is then used to set the path.
Code:
Procedure SetParameters
    Integer iParameter
    String sCodeMastPath

    Get ParameterIdByName C_USEMAINVRWREPORTID 'CodeMastPath' to iParameter
    Get_File_Path "CodeMast.Dat" to sCodeMastPath
    Set psParameterValue C_USEMAINVRWREPORTID iParameter to sCodeMastPath
End_Procedure
For deployment it is important to make sure the DLL can be loaded. In the web site this means the codemast.dll has to be copied to the same folder where webapp.exe is stored.

The report generation is done in a routine named GenerateReport which is started by the RunReport button in the commandbar used in the cWebView.
Code:
Procedure GenerateReport
    String sReportId sFile sUrl
    VRWPDFExportOptions PDFExportOptions
    Boolean bCanceled

    Get OpenReport to sReportId
    If (sReportId <> "") Begin
        Get DefaultPDFExportOptions to PDFExportOptions
        Set pPDFExportOptions to PDFExportOptions
        Get ReportCacheFileName ".pdf" to sFile
        If (sFile <> "") Begin
            Send ExportReport C_vrwPDF sFile

            Get pbCanceled to bCanceled
            If (not (bCanceled)) Begin
                Get DownloadURL of ghoWebResourceManager sFile to sUrl
                If (sUrl <> "") Begin
                    Send Show of oResultsTabPage
                    WebSet psUrl of oViewer to sUrl
                End
            End
        End

        Send CloseReport sReportId
    End
End_Procedure
The main difference between this code and the GenerateReport discussed in Invoices Report Blog is the instruction to switch to the results tab-page. The Send Show is responsible for switching tab-pages.

We hope that this blog - and the upcoming blogs - help you to create a very succesful business level web application using the 17.1 Web Framework and the Data Access Report Writer.

Comments