XSLT 1.0 help with recursion logic
- by DashaLuna
Hello guys,
I'm having troubles with the logic and would apprecite any help/tips.
I have <Deposits> elements and <Receipts> elements. However there isn't any identification what receipt was paid toward what deposit.
I am trying to update the <Deposits> elements with the following attributes:
@DueAmont - the amount that is still due to pay
@Status - whether it's paid, outstanding (partly paid) or due
@ReceiptDate - the latest receipt's date that was paid towards this deposit
Every deposit could be paid with one or more receipts. It also could happen, that 1 receipt could cover one or more deposits. For example. If there are 3 deposits:
500
100
450
That are paid with the following receipts:
200
100
250
I want to get the following info:
Deposit 1 is fully paid (status=paid, dueAmount=0, receiptNum=3.
Deposit 2 is partly paid (status=outstanding, dueAmount=50, receiptNum=3.
Deposit 3 is not paid (status=due, dueAmount=450, receiptNum=NAN.
I've added comments in the code explaining what I'm trying to do. I am staring at this code for the 3rd day now non stop - can't see what I'm doing wrong. Please could anyone help me with it? :)
Thanks!
Set up:
$deposits - All the available deposits
$receiptsAsc - All the available receipts sorted by their @ActionDate
Code:
<!-- Accumulate all the deposits with @Status, @DueAmount and @ReceiptDate attributes Provide all deposits, receipts and start with 1st receipt -->
<xsl:variable name="depositsClassified">
<xsl:call-template name="classifyDeposits">
<xsl:with-param name="depositsAll" select="$deposits"/>
<xsl:with-param name="receiptsAll" select="$receiptsAsc"/>
<xsl:with-param name="receiptCount" select="'1'"/>
</xsl:call-template>
</xsl:variable>
<!-- Recursive function to associate deposits' total amounts with overall receipts paid
to determine whether a deposit is due, outstanding or paid. Also determine what's the due amount and latest receipt towards the deposit for each deposit -->
<xsl:template name="classifyDeposits">
<xsl:param name="depositsAll"/>
<xsl:param name="receiptsAll"/>
<xsl:param name="receiptCount"/>
<!-- If there are deposits to proceed -->
<xsl:if test="$depositsAll">
<!-- Get the 1st deposit -->
<xsl:variable name="deposit" select="$depositsAll[1]"/>
<!-- Calculate the sum of all receipts up to and including currenly considered -->
<xsl:variable name="receiptSum">
<xsl:choose>
<xsl:when test="$receiptsAll">
<xsl:value-of select="sum($receiptsAll[position() <= $receiptCount]/@ReceiptAmount)"/>
</xsl:when>
<xsl:otherwise>0</xsl:otherwise>
</xsl:choose>
</xsl:variable>
<!-- Difference between deposit amount and sum of the receipts calculated
above -->
<xsl:variable name="diff" select="$deposit/@DepositTotalAmount - $receiptSum"/>
<xsl:choose>
<!-- Deposit isn't paid fully and there are more receipts/payments exist.
So consider the same deposit, but take next receipt into calculation as
well -->
<xsl:when test="($diff > 0) and ($receiptCount < count($receiptsAll))">
<xsl:call-template name="classifyDeposits">
<xsl:with-param name="depositsAll" select="$depositsAll"/>
<xsl:with-param name="receiptsAll" select="$receiptsAll"/>
<xsl:with-param name="receiptCount" select="$receiptCount + 1"/>
</xsl:call-template>
</xsl:when>
<!-- Deposit is paid or we ran out of receipts -->
<xsl:otherwise>
<!-- process the deposit. Determine its status and then update
corresponding attributes -->
<xsl:apply-templates select="$deposit" mode="defineDeposit">
<xsl:with-param name="diff" select="$diff"/>
<xsl:with-param name="receiptNum" select="$receiptCount"/>
</xsl:apply-templates>
<!-- Recursively call the template with the rest of deposits excluding the first. Before hand update the @ReceiptsAmount. For the receipts before current it is now 0, for the current is what left in the $diff, and simply copy over receipts after current one. -->
<xsl:variable name="receiptsUpdatedRTF">
<xsl:for-each select="$receiptsAll">
<xsl:choose>
<!-- these receipts was fully accounted for the current deposit. Make them 0 -->
<xsl:when test="position() < $receiptCount">
<xsl:copy>
<xsl:copy-of select="./@*"/>
<xsl:attribute name="ReceiptAmount">0</xsl:attribute>
</xsl:copy>
</xsl:when>
<!-- this receipt was partly/fully(in case $diff=0) accounted for the current deposit. Make it whatever is in $diff -->
<xsl:when test="position() = $receiptCount">
<xsl:copy>
<xsl:copy-of select="./@*"/>
<xsl:attribute name="ReceiptAmount">
<xsl:value-of select="format-number($diff, '#.00;#.00')"/>
</xsl:attribute>
</xsl:copy>
</xsl:when>
<!-- these receipts weren't yet considered - copy them over -->
<xsl:otherwise>
<xsl:copy-of select="."/>
</xsl:otherwise>
</xsl:choose>
</xsl:for-each>
</xsl:variable>
<xsl:variable name="receiptsUpdated" select="msxsl:node-set($receiptsUpdatedRTF)/Receipts"/>
<!-- Recursive call for the next deposit. Starting counting receipts from the current one. -->
<xsl:call-template name="classifyDeposits">
<xsl:with-param name="depositsAll" select="$deposits[position() != 1]"/>
<xsl:with-param name="receiptsAll" select="$receiptsUpdated"/>
<xsl:with-param name="receiptCount" select="$receiptCount"/>
</xsl:call-template>
</xsl:otherwise>
</xsl:choose>
</xsl:if>
</xsl:template>
<!-- Determine deposit's status and due amount -->
<xsl:template match="MultiDeposits" mode="defineDeposit">
<xsl:param name="diff"/>
<xsl:param name="receiptNum"/>
<xsl:choose>
<xsl:when test="$diff <= 0">
<xsl:apply-templates select="." mode="addAttrs">
<xsl:with-param name="status" select="'paid'"/>
<xsl:with-param name="dueAmount" select="'0'"/>
<xsl:with-param name="receiptNum" select="$receiptNum"/>
</xsl:apply-templates>
</xsl:when>
<xsl:when test="$diff = ./@DepositTotalAmount">
<xsl:apply-templates select="." mode="addAttrs">
<xsl:with-param name="status" select="'due'"/>
<xsl:with-param name="dueAmount" select="$diff"/>
</xsl:apply-templates>
</xsl:when>
<xsl:when test="$diff < ./@DepositTotalAmount">
<xsl:apply-templates select="." mode="addAttrs">
<xsl:with-param name="status" select="'outstanding'"/>
<xsl:with-param name="dueAmount" select="$diff"/>
<xsl:with-param name="receiptNum" select="$receiptNum"/>
</xsl:apply-templates>
</xsl:when>
<xsl:otherwise/>
</xsl:choose>
</xsl:template>
<!-- Add new attributes (@Status, @DueAmount and @ReceiptDate) to the
deposit element -->
<xsl:template match="MultiDeposits" mode="addAttrs">
<xsl:param name="status"/>
<xsl:param name="dueAmount"/>
<xsl:param name="receiptNum" select="''"/>
<xsl:copy>
<xsl:copy-of select="./@*"/>
<xsl:attribute name="Status"><xsl:value-of select="$status"/></xsl:attribute>
<xsl:attribute name="DueAmount"><xsl:value-of select="$dueAmount"/></xsl:attribute>
<xsl:if test="$receiptNum != ''">
<xsl:attribute name="ReceiptDate">
<xsl:value-of select="$receiptsAsc[position() = $receiptNum]/@ActionDate"/>
</xsl:attribute>
</xsl:if>
<xsl:copy-of select="./*"/>
</xsl:copy>
</xsl:template>