понедельник, 6 августа 2012 г.

XSL Grouping data for sum, avg, etc

It it easy to calculate summary values at the end of document using something like this:
<fo:table-cell border="solid 1px black" text-align="right" font-weight="bold">
    <fo:block>
        <xsl:value-of   select="format-number(sum(./yes-report/object-array/big-decimal[position() = 2]), '##0.00')"/>
    </fo:block>
</fo:table-cell>
But sometimes need to have intermediate results for some data group, for example annual monthly based sales report per each departments or payments operation via different payment gateways with different currencies. Given example point to fact, than need to have nested group. "Well know" method to acheive this - use Steve Muench method:

1. Define grouping key
<xsl:key name="currencyGroup" match="org.yes.cart.payment.persistence.entity.impl.CustomerOrderPaymentEntity"
             use="concat(orderCurrency, transactionOperation, transactionGatewayLabel)"/>
use concatination to define complex key instead one node key.

2. Add appropriate sorting
<xsl:for-each select="//org.yes.cart.payment.persistence.entity.impl.CustomerOrderPaymentEntity[generate-id(.)=generate-id(key('currencyGroup', concat(orderCurrency, transactionOperation, transactionGatewayLabel))[1])]">
     <xsl:sort select="orderCurrency"/>
     <xsl:sort select="transactionOperation"/>
     <xsl:sort select="transactionGatewayLabel"/>


3. Calculate summary at the end of each group
<xsl:if test="position() = last()">
        <fo:table-row>
            <fo:table-cell border="solid 1px black"
                           number-columns-spanned="11" font-weight="bold">
                <fo:block>Summary</fo:block>
            </fo:table-cell>
            <fo:table-cell border="solid 1px black" text-align="right"
                           font-weight="bold">
                <fo:block>
                    <xsl:value-of
                            select="format-number(sum(
                        key('currencyGroup', concat(orderCurrency, transactionOperation, transactionGatewayLabel))/paymentAmount
                        ), '##0.00')"/>
                </fo:block>
            </fo:table-cell>
        </fo:table-row>
        <fo:table-row>
            <fo:table-cell border="none"
                           number-columns-spanned="12" font-weight="bold">
                <fo:block><fo:leader /></fo:block>
            </fo:table-cell>
        </fo:table-row>
    </xsl:if>


</xsl:for-each>


Result example with xsl-fo is following:


Source files located here paymentreport.zip including xml, xslf