1

我在逻辑上遇到问题,并会感谢任何帮助/提示。

我有<Deposits>元素和<Receipts>元素。但是,没有任何标识向什么存款支付了什么收据。

我正在尝试<Deposits>使用以下属性更新元素:

  • @DueAmont - 仍需支付的金额
  • @Status - 无论是已付、未付(部分付清)还是到期
  • @ReceiptDate - 支付这笔押金的最新收据日期

每笔押金都可以用一张或多张收据支付。也有可能发生,一张收据可以涵盖一笔或多笔存款。例如。如果有 3 个存款:

  1. 500
  2. 100
  3. 450

使用以下收据支付:

  1. 200
  2. 100
  3. 250

我想获得以下信息:
押金 1已全额支付(状态=已付,dueAmount=0,receiptNum=3。
押金 2已部分支付(状态=未结,dueAmount=50,receiptNum=3。
押金 3未支付(状态=到期,到期金额=450,收据编号=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"/>

我在代码中添加了注释,解释了我正在尝试做什么。我现在连续第三天盯着这段代码 - 看不出我做错了什么。请问有人可以帮我吗?:)

谢谢!

设置:
$deposits - 所有可用存款
$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() &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>
4

2 回答 2

2

有趣的问题。我相信更好的方法是添加一个参数来累积余额,这样您就不需要更新收据结构。我的版本如下。我已经在您提供的示例输入上对其进行了测试,并收到了预期的结果。

请注意,我将存款的模板模式从 更改MultiDepositsDeposits,因为您在示例输入中使用了后一个元素名称。

<!-- 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 &gt; 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 &gt;= @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 &gt; 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

    1运行) bal = 0;
          差异 = 0(bal) + 2200(dep1) - 200(recp1) = 2000
          (diff > 0 & more 收据)

    2运行) bal = 0-200(recp1) = -200;
          差异 = -200(bal) + 2200(dep1) - 2000(recp2) = 0
          (输出第一次存款:status = "paid",进行下一次存款)

    3运行) bal= -200 + 2200(dep1) = 2000;
          差异 = 2000(bal) + 1100(dep2) -2000(recp2) = 1100
          (diff > 0 & more 收据)

    4运行) bal= 2000 - 2000(recp2) = 0;
          差异 = 0(bal) + 1100(dep2) - 800(recp3) = 300
          (没有更多收据,输出第二笔存款:状态=“未结清”)
于 2010-04-20T18:55:09.157 回答
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 对您仍然有意义吗?您可能必须扩展它,可能通过以与我之前提供的收据模型相同的方式添加小计子元素。

我想说,如果你想处理这个问题,就假装你是用/在纸上做这一切的。这将阐明您需要如何在代码中执行此操作。

在查看了您的其他一些帖子后,我意识到您可能无法控制您获得的数据集。然而,在某些时候,您需要能够回答这个问题,“这些收据中的哪些金额用于哪些存款?” 在那之后,我不得不说,你尝试使用递归来解决这个问题可能只会让你感到困惑。任何递归方法都可以用循环代替。我期待看到您的最终解决方案是什么样的。

于 2010-04-20T16:14:13.533 回答