I'm stuck with recursion, was wondering if anyone can help me out with it.
I have <Receipts> and <Deposits> elements, that are not verbose, i.e. that a <Receipts> element doesn't have an attribute to show what <Deposit> it is towards. I need to figure out <Deposits> "still amount due" and when a last receipt towards it was paid if any.
I'm trying to do it with the following code:
The idea was to take 1st deposit and see if there are receipts. If the deposit isn't fully paid and there are more receipts - call that recorsive function with all the same params except now count in following receipt.
If there aren't any more receipts or deposit is payed - process it correctly (add required attributes). Otherwise proceed with 2nd deposit. And so on.
However, the XSLT crashes with error message that "a processor stack has overflowed - possible cause is infinite template recursion"
I would really appreciate any help/teps... I'm not that great with recursion and can't understand why mine here doesn't work.
Thanks! :)
<!-- Accumulate all the deposits with #DueAmount attribute -->
<xsl:variable name="depositsClassified">
<xsl:call-template name="classifyDeposits">
<!-- a node-list of all Deposits elements ordered by DueDate Acs -->
<xsl:with-param name="depositsAll" select="$deposits"/>
<xsl:with-param name="depositPrevAmount" select="'0'"/>
<!-- a node-list of all Receipts elements ordered by ReceivedDate Acs -->
<xsl:with-param name="receiptsAll" select="$receiptsAsc"/>
<xsl:with-param name="receiptCount" select="'1'"/>
</xsl:call-template>
</xsl:variable>
<xsl:template name="classifyDeposits">
<xsl:param name="depositsAll"/>
<xsl:param name="depositPrevAmount" select="'0'"/>
<xsl:param name="receiptsAll"/>
<xsl:param name="receiptCount"/>
<xsl:if test="$depositsAll">
<!-- Do required operations for the 1st deposit -->
<xsl:variable name="depositFirst" select="$depositsAll[1]"/>
<xsl:variable name="receiptSum">
<xsl:choose>
<xsl:when test="$receiptsAll">
<xsl:value-of select="sum($receiptsAll[position() <= $receiptCount]/#ActionAmount)"/>
</xsl:when>
<xsl:otherwise>0</xsl:otherwise>
</xsl:choose>
</xsl:variable>
<xsl:variable name="diff" select="$depositPrevAmount + $depositFirst/#DepositTotalAmount - $receiptSum"/>
<xsl:choose>
<xsl:when test="$diff > 0 and
$receiptCount < $receiptsQuantityOf">
<xsl:call-template name="classifyDeposits">
<xsl:with-param name="depositsAll" select="$depositsAll"/>
<xsl:with-param name="depositPrevAmount" select="$depositPrevAmount"/>
<xsl:with-param name="receiptsAll" select="$receiptsAll"/>
<xsl:with-param name="receiptCount" select="$receiptCount + 1"/>
</xsl:call-template>
</xsl:when>
<xsl:otherwise>
<!-- Record changes to the deposit (#DueAmount and receipt ReceivedDate) -->
<xsl:apply-templates select="$depositFirst" mode="defineDeposit">
<xsl:with-param name="diff" select="$diff"/>
<xsl:with-param name="latestReceiptForDeposit" select="$receiptsAll[position() = $receiptCount]"/>
</xsl:apply-templates>
<!-- Recursive call to the next deposit -->
<xsl:call-template name="classifyDeposits">
<xsl:with-param name="depositsAll" select="$depositsAll[position() > 1]"/>
<xsl:with-param name="depositPrevAmount" select="$depositPrevAmount + $depositFirst/#DepositTotalAmount"/>
<xsl:with-param name="receiptsAll" select="$receiptsAll"/>
<xsl:with-param name="receiptCount" select="'1'"/>
</xsl:call-template>
</xsl:otherwise>
</xsl:choose>
</xsl:if>
</xsl:template>
<!-- Determine deposit's status, due amount and payment received date if any -->
<xsl:template match="Deposits" mode="defineDeposit">
<xsl:param name="diff"/>
<xsl:param name="latestReceiptForDeposit"/>
<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="receipt" select="$latestReceiptForDeposit"/>
</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="receipt" select="$latestReceiptForDeposit"/>
</xsl:apply-templates>
</xsl:when>
<xsl:otherwise/>
</xsl:choose>
</xsl:template>
<xsl:template match="Deposits" mode="addAttrs">
<xsl:param name="status"/>
<xsl:param name="dueAmount"/>
<xsl:param name="receipt" select="''"/>
<!-- Constract a new MultiDeposits element with required info -->
<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="$receipt">
<xsl:attribute name="latestReceiptDate">
<xsl:value-of select="$receipt/#ActionDate"/>
</xsl:attribute>
</xsl:if>
<xsl:copy-of select="./*"/>
</xsl:copy>
</xsl:template>
Your named template classifyDeposits is definitely recursing infinitely. It has no base case. In the <xsl:choose> section you have two conditions: <xsl:when> and <xsl:otherwise>, however you call classifyDeposits in both conditions.
So no matter what you pass to this template, it will call itself. You need to have a base case: a condition in which the template doesn't call itself.
In pseudocode, you're essentially doing this:
function recursive
if (A>B) then
call recursive
else
call recursive
You need to add another branch, somewhere, that does not call itself.
function recursive
if (C=0) then
return
else if (A>B) then
call recursive
else
call recursive
Of course, just having the condition isn't good enough. It needs to be reachable.
One thing i've learned about concerning recursion is that you need to make sure that you have a condition that will trigger when you are done.
example:
decrement_me_until_zero(int i)
{
if(i ==0) return; //we're done here, roll on back up!
else
{
i = i-1;
decrement_me_until_zero(i);
return;
}
}
I don't know xlst at all, but this can be a simple reason why recursion can go into an infinite loop. Consider instead:
decrement_me_until_zero(int i)
{
if(i ==0) return; //we're done here, roll on back up!
else
{
decrement_me_until_zero(i);
i=i-1; //oops! we decrement after we pass the variable down!
return;
}
}
I hope that helps
Related
Closed. This question is not reproducible or was caused by typos. It is not currently accepting answers.
This question was caused by a typo or a problem that can no longer be reproduced. While similar questions may be on-topic here, this one was resolved in a way less likely to help future readers.
Closed 8 years ago.
Improve this question
Problem in the cases in the below XSLt code, even though variables are defined, it shows undefined variable.
First choose is to test wheather the Year is for Leap year or non Leap Year.
Second choose only finds the cases for number of Days of the month.
<xsl:template name="CalDateNTime">
<xsl:param name="pDate"/>
<xsl:param name="pMonth"/>
<xsl:param name="pYear"/>
<xsl:choose>
<!-- Leap Year -->
<xsl:when test="($pYear mod 4=0 and $pYear mod 100 !=0) or $pYear mod 400 =0" >
<xsl:value-of select="$pDate"/>
<xsl:value-of select="$rDate"/>
<xsl:value-of select="$pMonth"/>
<xsl:choose>
<xsl:when test="$pMonth=1 and $rDate=32">
<xsl:variable name="rDate" select="1"/>
<xsl:variable name="rMonth" select="2"/>
</xsl:when>
<xsl:when test="$pMonth=2 and $rDate=30">
<xsl:variable name="rDate" select="1"/>
<xsl:variable name="rMonth" select="3"/>
</xsl:when>
<xsl:when test="$pMonth=3 and $rDate=32">
<xsl:variable name="rDate" select="1"/>
<xsl:variable name="rMonth" select="4"/>
</xsl:when>
</xsl:choose>
</xsl:when>
<!-- Non - Leap Year -->
<xsl:otherwise>
<xsl:variable name="jan" select="31"/>
<xsl:variable name="feb" select="28"/>
</xsl:otherwise>
</xsl:choose>
</xsl:template>
XSLT variables only exist within their parent.
<xsl:when test="$pMonth=1 and $rDate=32">
<xsl:variable name="rDate" select="1" />
<xsl:variable name="rMonth" select="2" />
</xsl:when>
These two variables are out of scope immediately (at the </xsl:when>).
You must structure your program differently. Put the <xsl:choose> into the variable.
I can only assume what your partial sample code is supposed to do. My assumption is: It should calculate the day after a given date.
Here's an alternative implementation:
<xsl:template name="CalDateNTime">
<xsl:param name="pDate" />
<xsl:param name="pMonth" />
<xsl:param name="pYear" />
<xsl:variable name="dayNum" select="'312831303130313130313031'" />
<xsl:variable name="isLeap" select="($pYear mod 4 = 0 and $pYear mod 100 != 0) or ($pYear mod 400 = 0)" />
<!-- determine index into the $dayNum string -->
<xsl:variable name="idx" select="($pMonth - 1) * 2" />
<xsl:variable name="maxDate" select="substring($dayNum, $idx + 1, 2)" />
<xsl:variable name="isSameMonth" select="($pDate < $maxDate) or ($pDate = $maxDate and $pMonth = 2 and $isLeap)" />
<!-- calculate following day, month, year values -->
<xsl:variable name="nDate">
<xsl:choose>
<xsl:when test="$isSameMonth"><xsl:value-of select="$pDate + 1" /></xsl:when>
<xsl:otherwise>1</xsl:otherwise>
</xsl:choose>
</xsl:variable>
<xsl:variable name="nMonth">
<xsl:choose>
<xsl:when test="$isSameMonth"><xsl:value-of select="$pMonth" /></xsl:when>
<xsl:when test="$pMonth < 12"><xsl:value-of select="$pMonth + 1" /></xsl:when>
<xsl:otherwise>1</xsl:otherwise>
</xsl:choose>
</xsl:variable>
<xsl:variable name="nYear">
<xsl:choose>
<xsl:when test="not($isSameMonth) and $nMonth = 1"><xsl:value-of select="$pYear + 1" /></xsl:when>
<xsl:otherwise><xsl:value-of select="$pYear" /></xsl:otherwise>
</xsl:choose>
</xsl:variable>
<!-- build output string -->
<xsl:value-of select="concat($nYear, '-')" />
<xsl:if test="$nMonth < 10">0</xsl:if>
<xsl:value-of select="concat($nMonth, '-')" />
<xsl:if test="$nDate < 10">0</xsl:if>
<xsl:value-of select="$nDate" />
</xsl:template>
Usage
<xsl:call-template name="CalDateNTime">
<xsl:with-param name="pDate" select="28" />
<xsl:with-param name="pMonth" select="2" />
<xsl:with-param name="pYear" select="2000" />
</xsl:call-template>
Result
2000-02-29
I have the variable x which is a number. I have a line. ("<name>James</name>") I need to print this sentence number x times. Can I do it in an easy way? without being complex?
If you are using XSLT 2.0 then you can do this ...
<xsl:for-each select="for $i in 1 to $x return $i">
<name>James</name>
</xsl:for-each>
The following is untested...
<xsl:call-template name="show">
<xsl:with-param name="text"><name>James</name></xsl:with-param>
<xsl:with-param name="count">50</xsl:with-param>
</xsl:call-template>
<xsl:template name="show">
<xsl:param name="text"/>
<xsl:param name="count"/>
<xsl:value-of select="$text"/>
<xsl:if test="number($count)>0">
<xsl:call-template name="show">
<xsl:with-param name="text" select="$text"/>
<xsl:with-param name="count" select="number($count)-1"/>
</xsl:call-template>
</xsl:if>
</xsl:template>
Updated to have <name> and </name>.
Here is an XmlPlayground of the above working
You could adding the following somewhere in your stylesheet:
<mydata>
<x/><x/><x/><x/> <!-- to print four times -->
</mydata>
then
<xsl:for-each select="document()//mydata/x">
<name>James</name>
</xsl:for-each>
This takes advantage of the ability to include your own data in an XSLT program, and access it through the document function (no argument indicates the stylesheet itself).
What is the best way to loop in XSLT from 1 to 60?
I research in net, there are some templates to do this, is there any other way for example like a built-in function?
In XSLT 2.0,
<xsl:for-each select="1 to 60">...</xsl:for-each>
But I guess that you must be using XSLT 1.0, otherwise you wouldn't be asking.
In XSLT 1.0 you should use recursion: a template that calls itself with a counter that's incremented on each call, and the recursion terminates when the required value is reached.
Alternatively there's a workaround in XSLT 1.0: provided your source document contains at least 60 nodes, you can do
<xsl:for-each select="(//node())[60 >= position()]">...</xsl:for-each>
The problem with simple recursion when processing long sequences is that often the space for the call stack becomes insufficient and the processing ends due to stack overflow. This typically happens with sequence length >= 1000.
A general technique to avoid this (implementable with any XSLT processor, even if it doesn't recognize tail-recursion) is DVC (Divide and Conquer) style recursion.
Here is an example of a transformation that successfully prints the numbers from 1 to 1000000 (1M):
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="text"/>
<xsl:template match="/">
<xsl:call-template name="displayNumbers">
<xsl:with-param name="pStart" select="1"/>
<xsl:with-param name="pEnd" select="1000000"/>
</xsl:call-template>
</xsl:template>
<xsl:template name="displayNumbers">
<xsl:param name="pStart"/>
<xsl:param name="pEnd"/>
<xsl:if test="not($pStart > $pEnd)">
<xsl:choose>
<xsl:when test="$pStart = $pEnd">
<xsl:value-of select="$pStart"/>
<xsl:text>
</xsl:text>
</xsl:when>
<xsl:otherwise>
<xsl:variable name="vMid" select=
"floor(($pStart + $pEnd) div 2)"/>
<xsl:call-template name="displayNumbers">
<xsl:with-param name="pStart" select="$pStart"/>
<xsl:with-param name="pEnd" select="$vMid"/>
</xsl:call-template>
<xsl:call-template name="displayNumbers">
<xsl:with-param name="pStart" select="$vMid+1"/>
<xsl:with-param name="pEnd" select="$pEnd"/>
</xsl:call-template>
</xsl:otherwise>
</xsl:choose>
</xsl:if>
</xsl:template>
</xsl:stylesheet>
When applied on any XML document (not used) this transformation produces the wanted result -- all the numbers from 1 to 1000000.
You can use/adapt this transformation for any task that needs to "do something N times".
Very simple check inside the foreach-loop
<xsl:if test="$maxItems > position()">
do something
</xsl:if>
Based on Dimitre Novatchev's answer.
Example:
<xsl:variable name="maxItems" select="10" />
<xsl:variable name="sequence" select="any-sequence"/>
<xsl:for-each select="$sequence">
<!-- Maybe sort first -->
<xsl:sort select="#sort-by" order="descending" />
<!-- where the magic happens -->
<xsl:if test="$maxItems > position()">
do something
</xsl:if>
</xsl:for-each>
The basic example for V1.0 using recursion would it be like this:
<xsl:template match="/">
<Root>
<!-- Main Call to MyTemplate -->
<xsl:call-template name="MyTemplate" />
</Root>
</xsl:template>
<xsl:template name="MyTemplate">
<xsl:param name="index" select="1" />
<xsl:param name="maxValue" select="60" />
<MyCodeHere>
<xsl:value-of select="$index"/>
</MyCodeHere>
<!-- < represents "<" for html entities -->
<xsl:if test="$index < $maxValue">
<xsl:call-template name="MyTemplate">
<xsl:with-param name="index" select="$index + 1" />
<xsl:with-param name="total" select="$maxValue" />
</xsl:call-template>
</xsl:if>
</xsl:template>
XSLT works based on templates and you'll need a template do run that loop.
You'll need to build a template receiving start and end values and, inside it, make a recursive call computing with start + 1. When $start equals $end, you do return your template, without another call.
In practice: http://www.ibm.com/developerworks/xml/library/x-tiploop/index.html
This must be easy, but I am not able to figure it out.
Aim is to replace '95' with '3F' if Substring length before '95' is an even number. If Substring length before '95' is not an even number, take the value of sub-string alongwith the value of '9' in '95' and start looking for occurrence of 95 again recursively to replace the value.
Final string will have value replaced with '3F'
A string value is assigned to 'inputHex' variable. The same is passed to the 'inputHexStr' for invoking a template replace95. In the given string first value of '95' occurs at position 134 and 135. therefore the condition not(string-length(substring-before($inputHexStr,$from)) mod 2 = 0) gets invoked. Problem is... in the next invocation to 'replace95' template with the remaining value of string, the string is not accessible and debug shows as a nodeset.
Why is that ?
Update - for the input xml as <x/> the next iteration is changing the context to the root node of the given xml file and the value of inputHexStr is shown as nodeset instead of string.
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">
<xsl:output method="text"/>
<xsl:template match="/">
<xsl:variable name="inputHex"
select="'3C3F786D6C2076657273696F6E3D22312E302220656E636F64696E673D227574662D3822203F3E3C783E74686973206973206120626164206368617261637465722019520737472696E6720646174613C2F7895'"/>
<xsl:variable name="hexResult">
<xsl:call-template name="replace95">
<xsl:with-param name="inputHexStr" select="$inputHex"/>
</xsl:call-template>
</xsl:variable>
<xsl:value-of select="$hexResult"/>
</xsl:template>
<xsl:template name="replace95">
<xsl:param name="inputHexStr" select="."/>
<xsl:param name="from" select="'95'"/>
<xsl:param name="to" select="'3F'"/>
<xsl:choose>
<xsl:when test="not(contains($inputHexStr,$from))">
<xsl:value-of select="$inputHexStr"/>
</xsl:when>
<xsl:otherwise>
<xsl:if test="string-length(substring-before($inputHexStr,$from)) mod 2 = 0">
<xsl:value-of select="substring-before($inputHexStr,$from)"/>
<xsl:value-of select="$to"/>
<xsl:call-template name="replace95">
<xsl:with-param name="inputHexstr"
select="substring-after($inputHexStr,$from)"/>
<xsl:with-param name="from" select="$from"/>
<xsl:with-param name="to" select="$to"/>
</xsl:call-template>
</xsl:if>
<xsl:if test="not(string-length(substring-before($inputHexStr,$from)) mod 2 = 0)">
<xsl:variable name="no95part"
select="substring($inputHexStr,1,string-length(substring-before($inputHexStr,$from))+1)"/>
<xsl:value-of select="$no95part"/>
<xsl:variable name="no95Length" select="string-length($no95part)"/>
<xsl:call-template name="replace95">
<xsl:with-param name="inputHexstr" select="substring($inputHexStr,$no95Length+1)"/>
<xsl:with-param name="from" select="$from"/>
<xsl:with-param name="to" select="$to"/>
</xsl:call-template>
</xsl:if>
</xsl:otherwise>
</xsl:choose>
</xsl:template>
</xsl:stylesheet>
I think this is because of a typo when you recursively call your templates with parameters.
<xsl:with-param name="inputHexstr" select=...
You have a lower case s here. XSLT is case-sensitive, so it should be a capital S
<xsl:with-param name="inputHexStr" select=...
Note that, it might be worth combining your two xls:if statements into a single xsl:choose to avoid extra calculations on the remainder
<xsl:choose>
<xsl:when test="not(contains($inputHexStr,$from))">...</xsl:when>
<xsl:when test="string-length(substring-before($inputHexStr,$from)) mod 2 = 0">...</xsl:when>
<xsl:otherwise>...</xsl:otherwise>
</xsl:choose>
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.
Actual XML:
<Deposits DepositDate="2010-04-07T00:00:00" DepositTotalAmount="500.0000" NoOfPeople="10.0000" PerPerson="50.00"/>
<Deposits DepositDate="2010-04-12T00:00:00" DepositTotalAmount="100.0000" NoOfPeople="10.0000" PerPerson="10.00"/>
<Deposits DepositDate="2010-04-26T00:00:00" DepositTotalAmount="450.0000" NoOfPeople="10.0000" PerPerson="45.00"/>
<Receipts Amount="200.00" PaymentType="Cheque" Comment="" ReceiptAmount="200.00" ActionDate="2010-04-07T11:01:47" PayingInSlipNumber="" IsCard="No" IsRefund="No"/>
<Receipts Amount="100.00" PaymentType="Cheque" Comment="" ReceiptAmount="100.00" ActionDate="2010-04-11T11:01:47" PayingInSlipNumber="" IsCard="No" IsRefund="No"/>
<Receipts Amount="250.00" PaymentType="Cheque" Comment="" ReceiptAmount="250.00" ActionDate="2010-04-20T11:01:47" PayingInSlipNumber="" IsCard="No" IsRefund="No"/>
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>
Interesting question. I believe a better approach is to add a parameter to accumulate the balance, so that you will not need to update the receipts structure. My version follows. I have tested it on the sample input you provided, and received the expected result.
Please note that I changed the template pattern for deposits from MultiDeposits to Deposits, since you used the latter element name in your sample input.
<!-- Accumulate all the deposits with #Status, #DueAmount and #ReceiptDate
attributes. Provide all deposits and receipts. -->
<xsl:variable name="depositsClassified">
<xsl:call-template name="classifyDeposits">
<xsl:with-param name="depositsAll" select="$deposits"/>
<xsl:with-param name="receiptsAll" select="$receiptsAsc"/>
</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="balance" select="0"/>
<!-- If there are deposits to proceed -->
<xsl:if test="$depositsAll">
<!-- Get the 1st deposit -->
<xsl:variable name="deposit" select="$depositsAll[1]"/>
<!-- Get the 1st receipt. -->
<xsl:variable name="receipt" select="$receiptsAll[1]"/>
<!-- Calculate difference. -->
<xsl:variable
name="diff"
select="$balance + $deposit/#DepositTotalAmount
- $receipt/#ReceiptAmount"/>
<xsl:choose>
<!-- Deposit isn't paid fully and there are more receipts.
Move on to the next receipt, with updated balance. -->
<xsl:when test="($diff > 0) and $receiptsAll[2]">
<xsl:call-template name="classifyDeposits">
<xsl:with-param name="depositsAll" select="$depositsAll"/>
<xsl:with-param
name="receiptsAll"
select="$receiptsAll[position() != 1]"/>
<xsl:with-param
name="balance"
select="$balance - $receipt/#ReceiptAmount"/>
</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="receipt" select="$receipt"/>
</xsl:apply-templates>
<!-- Recursive call for the next deposit. -->
<xsl:call-template name="classifyDeposits">
<xsl:with-param
name="depositsAll"
select="$depositsAll[position() != 1]"/>
<xsl:with-param name="receiptsAll" select="$receiptsAll"/>
<xsl:with-param
name="balance"
select="$balance + $deposit/#DepositTotalAmount"/>
</xsl:call-template>
</xsl:otherwise>
</xsl:choose>
</xsl:if>
</xsl:template>
<!-- Output deposit's status and due amount -->
<xsl:template match="Deposits" mode="defineDeposit">
<xsl:param name="diff"/>
<xsl:param name="receipt"/>
<xsl:copy>
<xsl:copy-of select="#*"/>
<xsl:choose>
<xsl:when test="$diff >= #DepositTotalAmount">
<xsl:attribute name="Status">due</xsl:attribute>
<xsl:attribute name="DueAmount">
<xsl:value-of select="#DepositTotalAmount"/>
</xsl:attribute>
</xsl:when>
<xsl:when test="$diff > 0">
<xsl:attribute name="Status">outstanding</xsl:attribute>
<xsl:attribute name="DueAmount">
<xsl:value-of select="$diff"/>
</xsl:attribute>
</xsl:when>
<xsl:otherwise>
<xsl:attribute name="Status">paid</xsl:attribute>
<xsl:attribute name="DueAmount">0</xsl:attribute>
<xsl:attribute name="ReceiptDate">
<xsl:value-of select="$receipt/#ActionDate"/>
</xsl:attribute>
</xsl:otherwise>
</xsl:choose>
<xsl:copy-of select="node()"/>
</xsl:copy>
</xsl:template>
As an example, here's how a recursion develops for the input:
Deposit 1 = 2200, Deposit 2 = 1100
Receipt 1 = 200, Receipt 2 = 2000, Receipt 3 = 800
1Run) bal = 0;
diff = 0(bal) + 2200(dep1) - 200(recp1) = 2000
(diff > 0 & more receipts)
2Run) bal = 0-200(recp1) = -200;
diff = -200(bal) + 2200(dep1) - 2000(recp2) = 0
(output first deposit: status = "paid", proceed to next deposit)
3Run) bal= -200 + 2200(dep1) = 2000;
diff = 2000(bal) + 1100(dep2) -2000(recp2) = 1100
(diff > 0 & more receipts)
4Run) bal= 2000 - 2000(recp2) = 0;
diff = 0(bal) + 1100(dep2) - 800(recp3) = 300
(no more receipts, output second deposit: status = "outstanding")
However there isn't any identification what receipt was paid toward what deposit.
This is the key to your problem. You have to have a way to associate the receipts with the deposits UP FRONT. Imagine if you were working with paper receipts, how would you handle this? You'd have to ask the person who gave you the receipt how much was intended for which deposit, and then once you found that out you record it on the receipt. Once you know this and reflect it in the way you represent receipts, you can build the xslt to grab those bits out. Unfortunately I can't help you with the xslt for that, but imagine that each receipt has child element for each partition. like:
<RECEIPTS total=500 blah blah blah>
<subtotal deposit=1 amount=100>
<subtotal deposit=2 amount=300>
</RECEIPTS>
then as you loop through, grab the children of the receipt, loop through each subtotal and add it to the appropriate counter for the deposit sum.
Also, I noticed, from your desired output, what happens if more than one receipt is applied towards a deposit? how do you represent that? currently you have
Deposit 2 is partly paid (status=outstanding, dueAmount=50, receiptNum=3
what if deposit 2 was partially paid with 2 receipts, is the attribute receiptNum still going to have meaning for you? you might have to extend this, maybe by adding subtotal children elements in the same manner as the receipts model I offered earlier.
Id say if you want to get a handle on this, pretend you were doing this all with/on paper. That would shed light on how you need to do it in code.
After looking at some of your other posts, I realize that you may not be in control of the dataset you get. At some point however, you need to be able to answer the question, "Which amounts of these receipts go to which deposits?" After that, I have to say, your attempts at using recursion to solve this problem might be serving only to confuse you. Any recursion method can be replaced with a loop instead. I look forward to seeing what your final solution looks like.