[xsl] Re: Calculate balance

Subject: [xsl] Re: Calculate balance
From: Dimitre Novatchev <dnovatchev@xxxxxxxxx>
Date: Mon, 11 Mar 2002 11:33:06 -0800 (PST)
Using the scanl1 function from the functional programming library FXSL,
the solution to the problem is quite easy:

The following stylesheet:

runningBalance.xsl:
------------------
<xsl:stylesheet version="1.0" 
 xmlns:xsl="http://www.w3.org/1999/XSL/Transform";
 xmlns:funTypedSum="f:funTypedSum" 
 exclude-result-prefixes="xsl funTypedSum"
 >
  <xsl:import href="Fxsl\Msxsl\scanl.xsl"/>
  
  <xsl:output omit-xml-declaration="yes" indent="yes"/>
  
  <funTypedSum:funTypedSum/>
  
  <xsl:template match="/">
    <xsl:variable name="vTypedSum" 
                  select="document('')/*/funTypedSum:*[1]"/>
    <xsl:variable name="vList" 
            select="/report/ledgerbook/table/row/amount"/>
            
    <xsl:variable name="vOpeningBalance" 
                  select="/report/ledgerbook/openingbalance"/>
    
    <xsl:call-template name="scanl">
      <xsl:with-param name="pFun" select="$vTypedSum"/>
      <xsl:with-param name="pQ0" select="$vOpeningBalance"/>
      <xsl:with-param name="pList" select="$vList"/>
      <xsl:with-param name="pElName" select="'balance'"/>
    </xsl:call-template>
  </xsl:template>
  
  <xsl:template match="funTypedSum:*">
    <xsl:param name="pArg1"/>
    <xsl:param name="pArg2" select="/.."/>
    
    <xsl:variable name="vThisAmount" 
            select="$pArg2 * ((($pArg2/@type = 'C') * 2) - 1)"/>
    
    <xsl:value-of select="$pArg1 + $vThisAmount"/>
  </xsl:template>
</xsl:stylesheet>

when applied on the original xml source document:

runningBalance.xml:
------------------
<report>
  <date>
    <currentdate>11-Mar-2002</currentdate>
    <startdate>12-dec-02</startdate>
    <enddate>12-dec-02</enddate>
  </date>

  <ledgerbook>
    <openingbalance>-2000.0</openingbalance>

    <table>
      <row>
        <ledgerdate>12-DEC-2002</ledgerdate>
        <voucher>V1.1.3</voucher>
        <costcenter>Head Office</costcenter>
        <lccode />
        <description />
        <amount type="C">1000</amount>
      </row>

      <row>
        <ledgerdate>12-DEC-2002</ledgerdate>
        <voucher>V1.1.2</voucher>
        <costcenter>Head Office</costcenter>
        <lccode />
        <description />
        <amount type="C">1000</amount>
      </row>

      <row>
        <ledgerdate>12-DEC-2002</ledgerdate>
        <voucher>V1.2.1</voucher>
        <costcenter />
        <lccode />
        <description />
        <amount type="D">2000</amount>
      </row>

      <row>
        <ledgerdate>12-DEC-2002</ledgerdate>
        <voucher>V1.2.2</voucher>
        <costcenter />
        <lccode />
        <description />
        <amount type="D">2000</amount>
      </row>
    </table>
  </ledgerbook>
</report>



Produces this result:

<balance>-2000.0</balance>
<balance>-1000</balance>
<balance>0</balance>
<balance>-2000</balance>
<balance>-4000</balance>


Hope this helped.

Cheers,
Dimitre Novatchev.

Jeni Tennison <jeni at jenitennison dot com> wrote:

> Hi Shabbir,
> 
> >> Or if performance is really an issue, you could take a different
> >> approach in which you step through the rows one by one, with a
> >> template that applies templates to the next row and passes on the
> >> balance from each step. Let us know if you want to see an example
> >> of how that would work.
> >
> > Yes, I want to know that approch.
> 
> OK. Currently, you tell the processor to iterate over all the rows at
> once, with an xsl:for-each:
> 
>   <xsl:for-each select="report/ledgerbook/table/row">
>     <tr>
>       <td><xsl:value-of select="ledgerdate"/></td>
>       <td><xsl:value-of select="voucher"/></td>
>       <td><xsl:value-of select="costcenter"/></td>
>       <td><xsl:value-of select="lccode"/></td>
>       <td><xsl:value-of select="description"/></td>
>       <xsl:call-template name="amountplace"/>
>     </tr>
>   </xsl:for-each>
> 
> Rather than do that, you can manually step through them one by one.
> This enables you to pass parameters, keeping track of the current
> balance, from one row to the next.
> 
> Instead of the xsl:for-each, then, just apply templates to the first
> row in your table:
> 
>   <xsl:apply-templates select="report/ledgerbook/table/row[1]" />
> 
> Then create a template that matches row elements and takes a
> parameter, $balance, which is originally set to the openingbalance:
> 
> <xsl:template match="row">
>   <xsl:param name="balance" select="../../openingbalance" />
>   ...
> </xsl:template>
> 
> Within the template, you can work out the difference between this
> balance and the previous one as I showed in the last email, and
> therefore work out a new balance based on the old one:
> 
> <xsl:template match="row">
>   <xsl:param name="balance" select="../../openingbalance" />
>   <xsl:variable name="difference">
>     <xsl:choose>
>       <xsl:when test="amount/@type = 'D'">
>         <xsl:value-of select="amount * -1" />
>       </xsl:when>
>       <xsl:otherwise>
>         <xsl:value-of select="amount" />
>       </xsl:otherwise>
>     </xsl:choose>
>   </xsl:variable>
>   <xsl:variable name="newbalance" select="$balance + $difference" />
>   ...
> </xsl:template>
> 
> You can use this new balance within the tr element that you
> create:
> 
> <xsl:template match="row">
>   <xsl:param name="balance" select="../../openingbalance" />
>   <xsl:variable name="difference">
>     <xsl:choose>
>       <xsl:when test="amount/@type = 'D'">
>         <xsl:value-of select="amount * -1" />
>       </xsl:when>
>       <xsl:otherwise>
>         <xsl:value-of select="amount" />
>       </xsl:otherwise>
>     </xsl:choose>
>   </xsl:variable>
>   <xsl:variable name="newbalance" select="$balance + $difference" />
>   <tr>
>     <td><xsl:value-of select="ledgerdate"/></td>
>     <td><xsl:value-of select="voucher"/></td>
>     <td><xsl:value-of select="costcenter"/></td>
>     <td><xsl:value-of select="lccode"/></td>
>     <td><xsl:value-of select="description"/></td>
>     <xsl:call-template name="amountplace"/>
>     <td><xsl:value-of select="$newbalance" /></td>
>   </tr>
>   ...
> </xsl:template>
> 
> You can also use it as the value to be passed to the next row that
> you
> process. You need to apply templates to the immediately following row
> within the document, passing the value of the $newbalance variable as
> the value of the $balance parameter, as follows:
> 
> <xsl:template match="row">
>   <xsl:param name="balance" select="../../openingbalance" />
>   <xsl:variable name="difference">
>     <xsl:choose>
>       <xsl:when test="amount/@type = 'D'">
>         <xsl:value-of select="amount * -1" />
>       </xsl:when>
>       <xsl:otherwise>
>         <xsl:value-of select="amount" />
>       </xsl:otherwise>
>     </xsl:choose>
>   </xsl:variable>
>   <xsl:variable name="newbalance" select="$balance + $difference" />
>   <tr>
>     <td><xsl:value-of select="ledgerdate"/></td>
>     <td><xsl:value-of select="voucher"/></td>
>     <td><xsl:value-of select="costcenter"/></td>
>     <td><xsl:value-of select="lccode"/></td>
>     <td><xsl:value-of select="description"/></td>
>     <xsl:call-template name="amountplace"/>
>     <td><xsl:value-of select="$newbalance" /></td>
>   </tr>
>   <xsl:apply-templates select="following-sibling::row[1]">
>     <xsl:with-param name="balance" select="$newbalance" />
>   </xsl:apply-templates>
> </xsl:template>
> 
> Cheers,
> 
> Jeni
> 
> 
> 
> 
> __________________________________________________
> Do You Yahoo!?
> Try FREE Yahoo! Mail - the world's greatest free email!
> http://mail.yahoo.com/


__________________________________________________
Do You Yahoo!?
Try FREE Yahoo! Mail - the world's greatest free email!
http://mail.yahoo.com/

 XSL-List info and archive:  http://www.mulberrytech.com/xsl/xsl-list


Current Thread