Full ZKGrails App: Calling Data Breakout tabs Wednesday, May 29, 2013

This .zul page is dramatically simpler, despite the complexity of the page. The simplicity reflects my improved understanding of how to programmaticly build the ZKGrails component within the Grails composer for this .zul page.



Note: disregard the extra closing tags displayed in the ZUML. Blogger automatically, adds those.

The front-end ZUML code for the complex breakouts tab has been reduced to a single tabbox and tabpanel tag.

See the Grails composer "IntlBillAuditToolComposer.groovy" below.

Note: this .zul page is called from the main .zul page, rendered inside it via an "include" component like this:

    
  
Including the page significantly simplified the design, allowing the content to be easily divided across .zul pages and grails composers. This avoided having one huge grails composer.


    
        

            

                   
               

            
             
              
    


Composer Details


Here is the start of our main "afterCompose()" method, and our call for the data we will need for our data models.

Note: it is feasible to optimize the code to avoid a second call to the DB to retrieve the same data.
def afterCompose = { window ->
        
        /**
         * Prepare date range for 1 year of data
         */
        Calendar cal = Calendar.getInstance()
        cal.add(Calendar.HOUR, +8)
        Calendar cal2 = Calendar.getInstance()
        cal2.add(Calendar.YEAR, -1)
        Date today = cal.getTime()
        Date lastYear = cal2.getTime()

        /**
         * Build Query and get results
         */
        def query = VendorStats.where{
            period in (lastYear .. today)
            vendor.intl == true // Just Domestic
        }
        def results = query.list()
Here we prepare for our later percentage calculations by calling a subroutine which totals our data.
/**
         * Get the totals calculated for all the calls and minutes
         * for
         * L = Lcr (Data from the LCR)
         * A = Actual (Data from the Vendor's bill)
         */
        def totals = getTotals( results )
        def total_L = totals[0]
        def total_A = totals[1]
Here are the details of the "getTotals()" subroutine.
/**
     * Total all the calls and minutes for later percent calculations
     * It takes a collection of "VendorStats"
     * L = LCR value
     * A = Actual bill value
     */
    def getTotals( data ){
        def L = [:]
        def A = [:]
        
        data.each { vs ->
            def month = new SimpleDateFormat("yyyy-MM").format(vs.period)
            def mon = new SimpleDateFormat("MMM").format(vs.period)
            def L_label = "LCR"
            def A_label = "Actual"

            if ( ! L[month] ){ L[month] =   [ "label":L_label, "month":mon, "minutes":0, "cost":0 ] }
            if ( ! A[month] ){ A[month] =   [ "label":A_label, "month":mon, "minutes":0, "cost":0 ] }
         
            if (vs.lcr){ //LCR
                def m = vs.minutes
                def c = vs.cost
                L[month]["minutes"] = L[month]["minutes"] + m
                L[month]["cost"] = L[month]["cost"] + c
                
            } else { //Actual
                def m = vs.minutes
                def c = vs.cost
                A[month]["minutes"] = A[month]["minutes"] + m
                A[month]["cost"] = A[month]["cost"] + c     
            }            
        }
        return [L, A]
    }  
}
Here we compile our main data model in so we can build our various ZK components.
/**
         * Intialize the 4 classes of "Vendor Data" (VD)
         * 
         * I = Int'l value
         * D = Domestic value
         * L = LCR value
         * A = Actual bill value
         */
        def VD = [:]
        def VD_label = "Vendor Data"
        results.each { vs ->
            def vendorName = vs.vendor.name
            def month = new SimpleDateFormat("yyyy-MM").format(vs.period)
            def mon = new SimpleDateFormat("MMM").format(vs.period)
            def L_label = "LCR"
            def A_label = "Actual"
            def L = [:]
            def A = [:]
            
            try {
                if ( ! VD."${month}" ){
                    VD."${month}" = [:]
                }
                if ( ! VD."${month}"."${vendorName}" ){
                    VD."${month}"."${vendorName}" = [:]
                }

                if ( vs.lcr ){ //LCR
                    def lcr = true
                    def cst = vs.cost
                    def min = vs.minutes
                    def sumCost = total_L[month]["cost"]                       
                    def sumMin = total_L[month]["minutes"]
                    def percentCost = cst / sumCost
                    def percentMinutes = min / sumMin
                    L = ["month":month, "mon":mon, "lcr":lcr, 
                         "label2":L_label, "vendorName":vendorName, 
                         "label":VD_label, "cost":cst, "minutes":min, 
                         "percentCost":percentCost, "percentMinutes":percentMinutes, 
                         "sumCost":sumCost, "sumMin":sumMin ]

                    if ( ! VD."${month}"."${vendorName}"."L" ){
                        VD."${month}"."${vendorName}"."L" = [:]
                    }                

                    VD."${month}"."${vendorName}"."L" = L

                } else { //Actual
                    def lcr = false              
                    def cst = vs.cost
                    def min = vs.minutes
                    def sumCost = total_A[month]["cost"]                       
                    def sumMin = total_A[month]["minutes"]
                    def percentCost = cst / sumCost
                    def percentMinutes = min / sumMin                           
                    A = ["month":month, "mon":mon, "lcr":lcr,
                         "label2":A_label, "vendorName":vendorName, 
                         "label":VD_label, "cost":cst, "minutes":min, 
                         "percentCost":percentCost, "percentMinutes":percentMinutes, 
                         "sumCost":sumCost, "sumMin":sumMin ]

                    if ( ! VD."${month}"."${vendorName}"."A" ){
                        VD."${month}"."${vendorName}"."A" = [:]
                    }                     

                    VD."${month}"."${vendorName}"."A" = A
                }
            } catch( NullPointerException e ){
                println "caught: " + e
            }
        }
Here we dynamically build and append our tabbox component with all the needed tabs.
$('#tabbox01').append {
            tabs {
                total_L.each{ m, v ->
                    tab {
                        label = m
                    }
                }
            }    
        }
Here we dynamically build and append our our tabpanels for each month if the data exists. We also fill each of those tabpanels with the rows of data for the appropriate month.
$('#tabpanels01').append {
    total_L.each { m, v ->
        tabpanel {
            grid {
            vflex = 1
            auxhead {
                auxheader {
                    colspan=1
                }
                auxheader {
                    label="LCR"; colspan=2
                }
                auxheader {
                    label="Percentages of Totals"; colspan=2                            
                }                             
                auxheader {
                    label="Actual"; colspan=2
                }
                auxheader {
                    label="Percentages of Totals"; colspan=2                             
                } 
            }

            def list = ["Vendors","Minutes","Cost","% Minutes",
                        "% Cost","Minutes","Cost","% Minutes","% Cost"]
                        
            columns {
                for (i in list){
                    column {
                        label=i; sort="auto"; align="center"
                    }
                }                           
            }

            String moneyPat = "\$##,###.##"
            String minutesPat = "##,###"
            String percentPat = "##.##%"

            def red = "color:red"
. . .(con't) $('#tabpanels01'.append {} call . . .

We build the rows components.
rows {
    VD[m].each { i, j ->
        def vendorName =  i
        def minutes =  j."L"."minutes"
        def cost =  j."L"."cost"
        def percentCost =  j."L"."percentCost"
        def percentMinutes  =  j."L"."percentMinutes"
        def minutes2 =  j."A"."minutes"
        def cost2 =  j."A"."cost"
        def percentCost2 =  j."A"."percentCost"
        def percentMinutes2 =  j."A"."percentMinutes"
                                        
        /**
         * Format values for display
         */                           
        minutes = textFormatService.numberFormatter( minutes, minutesPat )
        cost = textFormatService.numberFormatter( cost, moneyPat )
        
        def form_percentCost = textFormatService.numberFormatter( percentCost, percentPat )
        
        def form_percentMinutes = 
            textFormatService.numberFormatter( percentMinutes, percentPat )

        minutes2 = textFormatService.numberFormatter( minutes2, minutesPat )
        
        cost2 = textFormatService.numberFormatter( cost2, moneyPat )
        
        def form_percentCost2 = 
            textFormatService.numberFormatter( percentCost2, percentPat )
        
        def form_percentMinutes2 = 
            textFormatService.numberFormatter( percentMinutes2, percentPat )
                                       
        row {
            label( value:vendorName )
            label( value:minutes )
            label( value:cost )

            /**
             * Color values red if it is > 100%
             */
            if( percentCost > 1 ){
                label( value:form_percentCost, style:red )
            } else {
                label( value:form_percentCost )
            }
            if( percentMinutes > 1 ){
                label( value:form_percentMinutes, style:red )
            } else {
                label( value:form_percentMinutes )  
            }                                           

            label( value:minutes2 )
            label( value:cost2 )

            /**
             * Color values red if it is > 100%
             */                                    
            if( percentCost2 > 1 ){
                label( value:form_percentCost2, style:red )
            } else {
                label( value:form_percentCost2 )
            }
            if( percentMinutes > 1 ){
                label( value:form_percentMinutes2, style:red )
            } else {
                label( value:form_percentMinutes2 )  
            }                                    

        } //row

0 comments: