我遇到了逻辑上的麻烦,并会感激任何帮助/提示。
我有<Deposits>
个元素和<Receipts>
个元素。但是,没有任何标识证明哪些收据是支付给存款的。
我正在尝试使用以下属性更新<Deposits>
元素:
每笔存款都可以用一张或多张收据支付。也可能发生,1张收据可能包含一笔或多笔存款。例如。如果有3个存款:
使用以下收据支付:
我想获得以下信息:
存款1 已全额付款(状态=已付款,dueAmount = 0,receiptNum = 3。)
部分支付存款2 (状态=未结,dueAmount = 50,receiptNum = 3。)
存款3 未付款(status = due,dueAmount = 450,receiptNum = NAN。
实际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"/>
我在代码中添加了注释,解释了我正在尝试做的事情。我现在盯着这个代码第3天不停 - 看不出我做错了什么。请有人帮我吗? :)
谢谢!
设置:
$ deposit - 所有可用存款
$ receiptsAsc - 按@ActionDate
代码:
<!-- 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>
答案 0 :(得分:2)
有趣的问题。我认为更好的方法是添加一个参数来累积余额,这样您就不需要更新收据结构了。我的版本如下。我已根据您提供的样本输入对其进行了测试,并收到了预期的结果。
请注意,由于您在示例输入中使用了后一个元素名称,因此我将存款的模板模式从MultiDeposits
更改为Deposits
。
<!-- 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>
作为一个例子,这里是输入的递归方式:
存款1 = 2200,存款2 = 1100
收据1 = 200,收据2 = 2000,收据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")
答案 1 :(得分:1)
然而,没有任何标识证明哪些收据是支付给存款的。
这是您的问题的关键。您必须有办法将收据与存款UP FRONT相关联。想象一下,如果您正在处理纸质收据,您将如何处理?您必须询问给您收据的人是多少用于存款,然后一旦您发现存款,就将其记录在收据上。一旦你知道这一点并以表示收据的方式反映它,你就可以构建xslt以获取这些位。不幸的是我无法帮助你使用xslt,但想象每个收据都有每个分区的子元素。像:
<RECEIPTS total=500 blah blah blah>
<subtotal deposit=1 amount=100>
<subtotal deposit=2 amount=300>
</RECEIPTS>
然后当你循环时,抓住收据的子项,遍历每个小计并将其添加到适当的计数器中以存入金额。
另外,我注意到,根据您所需的输出,如果存款中有多张收据,会发生什么?你怎么代表那个?目前你有
Deposit 2 is partly paid (status=outstanding, dueAmount=50, receiptNum=3
如果存款2部分支付2张收据怎么办,属性receiptNum对你有意义吗?您可能需要对此进行扩展,可能通过添加小计子元素,方法与我之前提供的收据模型相同。
我想说如果你想对此进行处理,假装你在纸上做这件事。这将阐明你需要如何在代码中完成它。
在查看了其他一些帖子之后,我意识到您可能无法控制所获得的数据集。然而,在某些时候,你需要能够回答这个问题,“这些收据中有哪些收据存入哪些存款?”在那之后,我不得不说,你尝试使用递归来解决这个问题可能只会让你感到困惑。任何递归方法都可以用循环替换。我期待看到你的最终解决方案是什么样的。