2010-04-20 5 views
1

У меня возникли проблемы с логикой и я буду благодарен за любую помощь/советы.XSLT 1.0 help с логикой рекурсии

У меня есть <Deposits> элементов и <Receipts> элементов. Однако нет никакой идентификации того, какая квитанция была выплачена в отношении того, какой депозит.

Я пытаюсь обновить <Deposits> элементы со следующими атрибутами:

  • @DueAmont - сумма, которая до сих пор из-за платить
  • @Status - будь то платный, выдающее (частично оплачены) или из-за
  • @ReceiptDate - последняя дата поступления, которая была выплачена в отношении этого месторождения

Каждый депозит может быть оплачен одним или мор e квитанций. Также может случиться так, что 1 квитанция может покрыть один или несколько депозитов. Например. Если есть 3 месторождения:

Это оплачиваются следующие поступления:

Я хочу, чтобы получить следующую информацию:
депозит 1 полностью оплачиваемую (статус = платный, dueAmount = 0, receiptNum = 3.
Депозит 2 частично оплачены (статус = выдающийся, dueAmount = 50, receiptNum = 3.
Депозит 3 не оплачен (статус = должное, 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"/> 

Я добавил комментарии в код, объясняющий, что я пытаюсь сделать. Я смотрю на этот код на третий день, теперь не останавливаюсь - не вижу, что я делаю неправильно. Пожалуйста, может кто-нибудь мне помочь? :)

Спасибо!

Установка:
$ депозиты - Все доступные депозиты
$ 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> 
+0

@ Даша: Объяснение вашего XML - это хорошо, но * фактический * XML (даже если он уменьшен в соответствии с той частью, которую вы объясняете) намного приятнее. Слова всегда неоднозначны, код не так много. – Tomalak

+0

@Tomalak хорошо пункт. Я также добавил XML. – DashaLuna

+0

@ Даша: Как вы определяете, какая квитанция «принадлежит» какому депозиту? Или это просто вопрос «заполнения» всех депозитов поступлениями, в порядке возрастания даты? – Tomalak

ответ

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 &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

 
    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") 
+0

@markusk: Спасибо, много! Это работает как волшебство! Извините за путаницу , это должно быть , как вы правильно заметили. Мне очень нравится это решение с балансом, а не слишком сложным с переписыванием элементов. Я немного смущен, как работает баланс $ в рекурсивном вызове на следующий депозит (т. Е. ). Я работал со следующей настройкой: Депозит 1 = 2200, Депозит 2 = 1100 и Квитанция 1 = 200, Квитанция 2 = 2000, Квитанция 3 = 800. [См. Следующий комментарий] – DashaLuna

+0

1Run) bal = 0; diff = 0 (bal) + 2200 (dep) - 200 (recp) = 2000 (это> 0 и более квитанций) 2Run) bal = 0-200 (recp) = -200; diff = -200 (bal) + 2200 (dep) - 2000 (recp) = 0 (перейти к следующему депозиту) 3Run) bal = -200 + 2200 (dep) = 2000; diff = 2000 (bal) + 1100 (dep) - 200 (recp) = 2900 (это> 0 и более квитанций) 4Run) bal = 2000-200 (rec) = 1800; diff = 1800 (bal) + 1100 (dep) - 2000 (recp) = 900 (это> 0 и более квитанций) 5Run) bal = 1800 - 2000 (rec) = -200; diff = -200 (bal) +1100 (dep) -800 (rec) = 100 (больше никаких депозитов) [См. следующий комментарий] – DashaLuna

+0

Итак, у меня на 100, а не 300, что они по-прежнему должны за второе депозит. Я смущен, где я ошибаюсь. Было бы очень полезно, если бы вы могли указать на это.Извините, если это неясно, я попытался изо всех сил в этом примере. Большое спасибо! – DashaLuna

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 еще будет иметь смысл для вас? вам может потребоваться продлить это, возможно, добавив промежуточные дочерние элементы таким же образом, как и модель квитанций, предложенная мной ранее.

Id скажите, хотите ли вы получить ручку на этом, притворитесь, что делаете все это с/на бумаге. Это пролило бы свет на то, как вам нужно это делать в коде.

После просмотра некоторых ваших других сообщений я понимаю, что вы не можете контролировать набор данных, который вы получаете. Однако в какой-то момент вы должны уметь ответить на вопрос: «Какие суммы этих поступлений идут на какие депозиты?» После этого, я должен сказать, ваши попытки использовать рекурсию для решения этой проблемы могут служить только для того, чтобы смутить вас. Любой метод рекурсии может быть заменен на цикл. Я с нетерпением жду, как будет выглядеть ваше окончательное решение.

+0

@ Joshua: Спасибо за ответ. Извините, что мои объяснения были непонятными для начала. Я ответил @Tomalak в комментариях к вопросу. Я надеюсь, что комментарии сделают это немного яснее, что мне нужно. В этом отношении решение @markusk - это то, что я искал. Еще раз спасибо за то, что вы потратили время и публикации, я очень благодарен. – DashaLuna

+0

Не беспокойтесь, и вы всегда рады! Теперь я чувствую, что это очень важно. – Joshua

 Смежные вопросы

  • Нет связанных вопросов^_^