I have a list of nodes for which I need to compute a total then sum said total as a group. I have created the appropriate variables but when I use a sum() as my final output, it produces a concatenated string as opposed to a total value.
Here is the input XML:
<LineItems>
<LineItem>
<OrderLine>
<OrderQty>1</OrderQty>
<UnitPrice>105.28</UnitPrice>
<UnitPriceBasis>UM</UnitPriceBasis>
</OrderLine>
<LineItem>
<OrderLine>
<OrderQty>75</OrderQty>
<UnitPrice>2.88</UnitPrice>
<UnitPriceBasis>UM</UnitPriceBasis>
</OrderLine>
</LineItem>
<LineItem>
<OrderLine>
<OrderQty>3</OrderQty>
<UnitPrice>155.36</UnitPrice>
<UnitPriceBasis>UM</UnitPriceBasis>
</OrderLine>
</LineItem>
<LineItem>
<OrderLine>
<OrderQty>12</OrderQty>
<UnitPrice>1.64</UnitPrice>
<UnitPriceBasis>UM</UnitPriceBasis>
</OrderLine>
</LineItem>
<LineItem>
<OrderLine>
<OrderQty>2</OrderQty>
<UnitPrice>2.28</UnitPrice>
<UnitPriceBasis>UM</UnitPriceBasis>
</OrderLine>
</LineItem>
<LineItem>
<OrderLine>
<OrderQty>5</OrderQty>
<UnitPrice>3.6</UnitPrice>
<UnitPriceBasis>UM</UnitPriceBasis>
</OrderLine>
</LineItem>
<LineItem>
<OrderLine>
<OrderQty>1</OrderQty>
<UnitPrice>405.24</UnitPrice>
<UnitPriceBasis>UM</UnitPriceBasis>
</OrderLine>
</LineItem>
<LineItem>
<OrderLine>
<OrderQty>5</OrderQty>
<UnitPrice>79.04</UnitPrice>
<UnitPriceBasis>UM</UnitPriceBasis>
</OrderLine>
</LineItem>
<LineItem>
<OrderLine>
<OrderQty>1</OrderQty>
<UnitPrice>2.15</UnitPrice>
<UnitPriceBasis>UM</UnitPriceBasis>
</OrderLine>
</LineItem>
<LineItem>
<OrderLine>
<OrderQty>9</OrderQty>
<UnitPrice>2.15</UnitPrice>
<UnitPriceBasis>UM</UnitPriceBasis>
</OrderLine>
</LineItem>
</LineItems>
And here is what I have been using thus far:
<tpi:Fact name="TotalLinePrice" factType="Virtual">
<xsl:for-each-group select="/PurchaseOrder/LineItems/LineItem/OrderLine" group-starting-with="OrderLine[normalize-space(OrderQty)]">
<xsl:variable name="uPrice" as="node()*">
<xsl:for-each select="current-group()">
<xsl:variable name="unitPrice">
<xsl:copy-of select="xs:decimal(current-group()/UnitPrice)"/>
</xsl:variable>
<xsl:variable name="orderQty">
<xsl:copy-of select="xs:decimal(current-group()/OrderQty)"/>
</xsl:variable>
<xsl:variable name="calcPriceBasis">
<xsl:choose>
<xsl:when test="current-group()[UnitPriceBasis='TP']">
<xsl:copy-of select="round(($unitPrice div 1000))"/>
</xsl:when>
<xsl:when test="current-group()[UnitPriceBasis='HT']">
<xsl:copy-of select="round(($unitPrice div 1000))"/>
</xsl:when>
<xsl:when test="current-group()[UnitPriceBasis='HP']">
<xsl:copy-of select="round(($unitPrice div 100))"/>
</xsl:when>
<xsl:when test="current-group()[UnitPriceBasis='HTH']">
<xsl:copy-of select="round(($unitPrice div 100000))"/>
</xsl:when>
<xsl:when test="current-group()[UnitPriceBasis='PD']">
<xsl:copy-of select="round(($unitPrice div 12))"/>
</xsl:when>
<xsl:when test="current-group()[UnitPriceBasis='PN']">
<xsl:copy-of select="round(($unitPrice div 10))"/>
</xsl:when>
<xsl:when test="current-group()[UnitPriceBasis='TT']">
<xsl:copy-of select="round(($unitPrice div 10000))"/>
</xsl:when>
<xsl:otherwise>
<xsl:copy-of select="$unitPrice"/>
</xsl:otherwise>
</xsl:choose>
</xsl:variable>
<total>
<xsl:sequence select="format-number($calcPriceBasis * $orderQty, '0.00')"/>
</total>
</xsl:for-each>
</xsl:variable>
<xsl:value-of select="sum($uPrice)"/>
</xsl:for-each-group>
</tpi:Fact>
The output I am getting is:
"TOTALLINEPRICE":"105.28216466.0819.684.5618405.24395.22.1519.35"
which is each node of this calculation
<xsl:sequence select="format-number($calcPriceBasis * $orderQty, '0.00')"/>
appended to itself as a concatenated string. But I would hope to see
"TOTALLINEPRICE": "1456.84"
which would be the sum total of each calculated value.
TOTALLINEPRICE is a new node created inside of the XML much like adding
<TOTALLINEPRICE>
</TOTALLINEPRICE>
As you can see, I need to compute a different price basis based on an input code (UnitPriceBasis) within each nodes itself. I know that xslt is not a programmatic language and variables are technically immutable. But there has to be a way to do this.
For reference, I am using:
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="2.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:fo="http://www.w3.org/1999/XSL/Format" xmlns:xs="http://www.w3.org/2001/XMLSchema" xmlns:fn="http://www.w3.org/2005/xpath-functions" xmlns:tpi="http://www.spscommerce.net/tpi">
Where have I gone wrong?
CodePudding user response:
Consider the following simplified example:
XML
<items>
<item>
<qty>2</qty>
<price>10.00</price>
<group>A</group>
</item>
<item>
<qty>3</qty>
<price>5.00</price>
<group>A</group>
</item>
<item>
<qty>4</qty>
<price>8.00</price>
<group>A</group>
</item>
<item>
<qty>6</qty>
<price>3.00</price>
<group>B</group>
</item>
<item>
<qty>3</qty>
<price>9.00</price>
<group>B</group>
</item>
<item>
<qty>7</qty>
<price>8.00</price>
<group>C</group>
</item>
</items>
XSLT 2.0
<xsl:stylesheet version="2.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="/items">
<xsl:variable name="subtotals">
<xsl:for-each-group select="item" group-by="group">
<subtotal group="{current-grouping-key()}">
<xsl:value-of select="sum(current-group()/(price*qty))"/>
</subtotal>
</xsl:for-each-group>
</xsl:variable>
<output>
<xsl:copy-of select="$subtotals"/>
<total>
<xsl:value-of select="sum($subtotals/subtotal)"/>
</total>
</output>
</xsl:template>
</xsl:stylesheet>
Result
<?xml version="1.0" encoding="UTF-8"?>
<output>
<subtotal group="A">67</subtotal>
<subtotal group="B">45</subtotal>
<subtotal group="C">56</subtotal>
<total>168</total>
</output>
CodePudding user response:
It seems that want you wanted was actually this XSLT transformation (obtained from the one provided in the problem and fixed a few issues):
<xsl:stylesheet version="2.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:xs="http://www.w3.org/2001/XMLSchema" exclude-result-prefixes="xs">
<xsl:output omit-xml-declaration="yes" indent="yes"/>
<xsl:template match="/">
<Fact name="TotalLinePrice" factType="Virtual">
<xsl:variable name="uPrice">
<xsl:for-each-group select="/PurchaseOrder/LineItems/LineItem/OrderLine"
group-starting-with="OrderLine[normalize-space(OrderQty)]">
<xsl:for-each select="current-group()">
<xsl:variable name="unitPrice">
<xsl:copy-of select="xs:decimal(current-group()/UnitPrice)"/>
</xsl:variable>
<xsl:variable name="orderQty">
<xsl:copy-of select="xs:decimal(current-group()/OrderQty)"/>
</xsl:variable>
<xsl:variable name="calcPriceBasis">
<xsl:choose>
<xsl:when test="current-group()[UnitPriceBasis='TP']">
<xsl:copy-of select="round(($unitPrice div 1000))"/>
</xsl:when>
<xsl:when test="current-group()[UnitPriceBasis='HT']">
<xsl:copy-of select="round(($unitPrice div 1000))"/>
</xsl:when>
<xsl:when test="current-group()[UnitPriceBasis='HP']">
<xsl:copy-of select="round(($unitPrice div 100))"/>
</xsl:when>
<xsl:when test="current-group()[UnitPriceBasis='HTH']">
<xsl:copy-of select="round(($unitPrice div 100000))"/>
</xsl:when>
<xsl:when test="current-group()[UnitPriceBasis='PD']">
<xsl:copy-of select="round(($unitPrice div 12))"/>
</xsl:when>
<xsl:when test="current-group()[UnitPriceBasis='PN']">
<xsl:copy-of select="round(($unitPrice div 10))"/>
</xsl:when>
<xsl:when test="current-group()[UnitPriceBasis='TT']">
<xsl:copy-of select="round(($unitPrice div 10000))"/>
</xsl:when>
<xsl:otherwise>
<xsl:copy-of select="$unitPrice"/>
</xsl:otherwise>
</xsl:choose>
</xsl:variable>
<total>
<xsl:sequence select=
"xs:decimal(format-number($calcPriceBasis * $orderQty, '0.00'))"/>
</total>
</xsl:for-each>
</xsl:for-each-group>
</xsl:variable>
<xsl:copy-of select="sum($uPrice/*)"/>
</Fact>
</xsl:template>
</xsl:stylesheet>
When the above, corrected transformation is applied on the provided XML document (corrected to be made a well-formed XML document):
<PurchaseOrder>
<LineItems>
<LineItem>
<OrderLine>
<OrderQty>1</OrderQty>
<UnitPrice>105.28</UnitPrice>
<UnitPriceBasis>UM</UnitPriceBasis>
</OrderLine>
</LineItem>
<LineItem>
<OrderLine>
<OrderQty>75</OrderQty>
<UnitPrice>2.88</UnitPrice>
<UnitPriceBasis>UM</UnitPriceBasis>
</OrderLine>
</LineItem>
<LineItem>
<OrderLine>
<OrderQty>3</OrderQty>
<UnitPrice>155.36</UnitPrice>
<UnitPriceBasis>UM</UnitPriceBasis>
</OrderLine>
</LineItem>
<LineItem>
<OrderLine>
<OrderQty>12</OrderQty>
<UnitPrice>1.64</UnitPrice>
<UnitPriceBasis>UM</UnitPriceBasis>
</OrderLine>
</LineItem>
<LineItem>
<OrderLine>
<OrderQty>2</OrderQty>
<UnitPrice>2.28</UnitPrice>
<UnitPriceBasis>UM</UnitPriceBasis>
</OrderLine>
</LineItem>
<LineItem>
<OrderLine>
<OrderQty>5</OrderQty>
<UnitPrice>3.6</UnitPrice>
<UnitPriceBasis>UM</UnitPriceBasis>
</OrderLine>
</LineItem>
<LineItem>
<OrderLine>
<OrderQty>1</OrderQty>
<UnitPrice>405.24</UnitPrice>
<UnitPriceBasis>UM</UnitPriceBasis>
</OrderLine>
</LineItem>
<LineItem>
<OrderLine>
<OrderQty>5</OrderQty>
<UnitPrice>79.04</UnitPrice>
<UnitPriceBasis>UM</UnitPriceBasis>
</OrderLine>
</LineItem>
<LineItem>
<OrderLine>
<OrderQty>1</OrderQty>
<UnitPrice>2.15</UnitPrice>
<UnitPriceBasis>UM</UnitPriceBasis>
</OrderLine>
</LineItem>
<LineItem>
<OrderLine>
<OrderQty>9</OrderQty>
<UnitPrice>2.15</UnitPrice>
<UnitPriceBasis>UM</UnitPriceBasis>
</OrderLine>
</LineItem>
</LineItems>
</PurchaseOrder>
the seemingly wanted single result sum is produced:
<Fact name="TotalLinePrice" factType="Virtual">1651.5399999999997</Fact>