Мне нужно было бы рассчитать общую стоимость товаров в посылках (сумма) для отправки, используя данные из подэлементов. Я пытаюсь обойти проблему в исходной системе, выполняя некоторые вычисления с помощью XSLT (доступна только версия 1.0). Проблема выглядит следующим образом:
<Shipment>
...
<Parcels>
<Parcel>
<Lenght>10</Lenght>
<Width>20</Width>
<Height>30</Height>
<Weight>10</Weight>
<Products>
<Product>
<ItemNo>12345</ItemNo>
<Description>Shirt</Description>
<OrderedQty>4</OrderedQty>
<DeliveredQty>2</DeliveredQty>
<LineValue>400</LineValue>
<Currency>EUR</Currency>
</Product>
<Product>
<ItemNo>54321</ItemNo>
<Description>Trousers</Description>
<OrderedQty>1</OrderedQty>
<DeliveredQty>1</DeliveredQty>
<LineValue>50</LineValue>
<Currency>EUR</Currency>
</Product>
</Products>
</Parcel>
<Parcel>
<Lenght>15</Lenght>
<Width>25</Width>
<Height>35</Height>
<Weight>5</Weight>
<Products>
<Product>
<ItemNo>23456</ItemNo>
<Description>Jacket</Description>
<OrderedQty>2</OrderedQty>
<DeliveredQty>1</DeliveredQty>
<LineValue>300</LineValue>
<Currency>EUR</Currency>
</Product>
</Products>
</Parcel>
</Parcels>
</Shipment>
Система, формирующая информацию о товаре для каждой посылки, будет неправильно рассчитывать LineValue при частичной доставке. В приведенном выше примере XML элемент 12345 (Рубашка) в первой посылке имеет значение UnitValue, равное 100, и система генерирует значение LineValue, равное 400 (OrderedQty * UnitValue). Однако было доставлено только 2/4 товара, поэтому правильное LineValue должно быть DeliveredQty * 100 = 200. Поскольку у меня нет реального значения единицы в XML, необходимо выполнить некоторые вычисления в каждом элементе Product. это я могу сделать с некоторыми переменными:
<xsl:for-each select = "Products/Product">
<xsl:variable name = "OrderedQty">
<xsl:value-of select = "OrderedQty"/>
</xsl:variable>
<xsl:variable name = "DeliveredQty">
<xsl:value-of select = "DeliveredQty"/>
</xsl:variable>
<xsl:variable name = "LineValue">
<xsl:value-of select = "LineValue"/>
</xsl:variable>
<xsl:variable name = "UnitValue">
<xsl:value-of select = "$LineValue div $OrderQty"/>
</xsl:variable>
<xsl:variable name = "RealLineValue">
<xsl:value-of select = "$DeliveredQty * $UnitValue"/>
</xsl:variable>
</xsl:for-each>
Итак, я получаю правильное значение единицы и новое рассчитанное значение строки для каждого продукта. ОК... НО... (вот здесь возникает настоящая проблема):
Как я могу выполнить расчет суммы на уровне отгрузки путем суммирования вычисленных значений $RealLineValues из каждого элемента продукта вместе?
Я попытался использовать рекурсивный шаблон, который нашел в StackOverflow:
<xsl:template name = "sum">
<xsl:param name = "nodes"/>
<xsl:param name = "sum" select = "0"/>
<xsl:variable name = "current" select = "$nodes[1]"/>
<xsl:if test = "$current">
<xsl:call-template name = "sum">
<xsl:with-param name = "nodes" select = "$nodes[position() > 1]"/>
<xsl:with-param name = "sum" select = "$sum + $current/$RealLineValue"/>
</xsl:call-template>
</xsl:if>
<xsl:if test = "not($current)">
<xsl:value-of select = "$sum"/>
</xsl:if>
</xsl:template>
И вызывая этот шаблон с верхнего уровня, но очевидно, что переменная $RealLineValue не может использоваться таким образом.
Как вы можете видеть из моих испытаний, я не очень хорошо разбираюсь в XSLT, поэтому любая помощь будет очень признательна :)
Если вы можете предоставить решение для вычисления «sum(LineValue div OrderedQty) * DeliveredQty)» для каждого продукта в каждой посылке на уровне отгрузки, используя XSLT 1.0, это было бы СУПЕР!
И да, результат должен быть 2*(400/4) + 1(50/1) + 1(300/2) = 200 + 50 + 150 = 400
Заранее ОГРОМНОЕ спасибо.
Если вы хотите использовать рекурсивный шаблон, вы можете сделать:
XSLT 1.0
<xsl:stylesheet version = "1.0"
xmlns:xsl = "http://www.w3.org/1999/XSL/Transform">
<xsl:output method = "xml" version = "1.0" encoding = "UTF-8" indent = "yes"/>
<xsl:template match = "/Shipment">
<sum>
<xsl:call-template name = "sum">
<xsl:with-param name = "nodes" select = "//Product"/>
</xsl:call-template>
</sum>
</xsl:template>
<xsl:template name = "sum">
<xsl:param name = "nodes"/>
<xsl:param name = "sum" select = "0"/>
<xsl:variable name = "current" select = "$nodes[1]"/>
<xsl:choose>
<xsl:when test = "$current">
<xsl:variable name = "UnitValue" select = "$current/LineValue div $current/OrderedQty"/>
<xsl:variable name = "RealLineValue" select = "$current/DeliveredQty * $UnitValue"/>
<xsl:call-template name = "sum">
<xsl:with-param name = "nodes" select = "$nodes[position() > 1]"/>
<xsl:with-param name = "sum" select = "$sum + $RealLineValue"/>
</xsl:call-template>
</xsl:when>
<xsl:otherwise>
<xsl:value-of select = "$sum"/>
</xsl:otherwise>
</xsl:choose>
</xsl:template>
</xsl:stylesheet>
Альтернативный подход см.: XSL, как вычислить сумму произведения значений атрибутов каждого элемента
Вы меня опередили :) Выглядит в основном идентично моему решению.
Смотрите также: stackoverflow.com/questions/20915982