views:

262

answers:

2

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:

  1. 500
  2. 100
  3. 450

That are paid with the following receipts:

  1. 200
  2. 100
  3. 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() &lt;= $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 &gt; 0) and ($receiptCount &lt; 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() &lt; $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 &lt;= 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 &lt; ./@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>
+1  A: 

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.

Joshua
@Joshua: Thanks for the reply. I'm sorry that my explanation wasn't clear to start with. I've replyied to @Tomalak in comments to the question. I hope comments make it a bit clearer what I needed. In that respect @markusk solution is what I was looking for. Thanks again for taking time and posting, I really appreciate it.
DashaLuna
No worries, and you're welcome! It makes sense now.
Joshua
+2  A: 
markusk
@markusk: Thank you sooo much! It works like magic! Sorry about the <MultiDeposits> confusion, it should be <Deposits> as you rightly spotted. I really like this solution with the balance, rather than my way too complicated one with rewriting <Receipts> elements. I'm a bit confused how the $balance in the recursive call to next deposit works (i.e. <xsl:with-param name="balance" select="$balance + $deposit/@DepositTotalAmount"/>). I've been working with the following set up: Deposit 1 = 2200, Deposit 2 = 1100 and Receipt 1 = 200, Receipt 2 = 2000, Receipt 3 = 800. [Please see next comment]
DashaLuna
1Run) bal = 0; diff = 0(bal) + 2200(dep) - 200(recp) = 2000 (it's > 0 diff = -200(bal) + 2200(dep) - 2000(recp) = 0 (proceed to next deposit)3Run) bal= -200 + 2200(dep) = 2000; diff = 2000(bal) + 1100(dep) - 200(recp) = 2900 (it's > 0 diff = 1800(bal) + 1100(dep) - 2000(recp) = 900 (it's > 0 diff = -200(bal) +1100 (dep) -800 (rec) = 100 (no more deposits)[Please see next comment]
DashaLuna
So I end up with 100, instead of 300 that they still owe for the 2nd deposit. I'm confused where I'm going wrong here. Would really appreciate if you could point it out. Sorry if it's unclear, I've tried my best with the example.Many thanks!!
DashaLuna
@DashaLuna: There's an error in your summary for 3Run), diff should be 1100 and not 2900. I have updated my answer with your sample input. The call `<xsl:with-param name="balance" select="$balance + $deposit/@DepositTotalAmount"/>` ensures that when a deposit is flagged as "paid", the deposit amount is counted against the current receipt when calculating the difference for the next deposit.
markusk
@markusk: Thanks for the example! I see where I was doing it wrong. When proceeding to the next deposit, I would start from the 1st receipt of initial ever node-set, but not the resulting one from the previous step (where we go through receipts and accumulate them in `$balance` when a deposit isn't fully paid and there are receipts left). Erm, hope that makes sense.Thank you soo much for taking time and posting the answer and comments! You're a genius :)
DashaLuna
@DashaLuna: Happy to help, I'm glad my code was of use to you. :-)
markusk