user1430050
user1430050

Reputation: 11

Summing up total for all products and their respective quantities ordered XSL

Currently getting a NaN error within XSLT. I want to get the total value or revenue for all products and their quantities ordered.

XML:

<customers>
<customer>
<customername>John</customername>
</customer>
<invoices>
 <invoice>
  <invoicenumber>01</invoicenumber>
  <products>
   <product>
    <productname>candy bar</productname>
    <productamount>6</productamount
    <productcost>2</productcost>
   </product>
  </products>
 </invoice>
 <invoice>
  <invoicenumber>02</invoicenumber>
  <products>
   <product>
    <productname>choco bar</productname>
    <productamount>3</productamount
    <productcost>1</productcost>
   </product>
  </products>
 </invoice>
</invoices>
<customer>
<customername>Fritz</customername>
</customer>
<invoices>
 <invoice>
  <invoicenumber>01</invoicenumber>
  <products>
   <product>
    <productname>Soda</productname>
    <productamount>3</productamount
    <productcost>2</productcost>
   </product>
  </products>
 </invoice>
 <invoice>
  <invoicenumber>02</invoicenumber>
  <products>
   <product>
    <productname>lollipop</productname>
    <productamount>5</productamount
    <productcost>1</productcost>
   </product>
  </products>
 </invoice>
</invoices>

And I have tried to adapt a Recursive template.

XSL:

Total:  
<xsl:call-template name="sumProducts">
<xsl:with-param name="pNodes" select="customers/customer/invoices/invoice/products"/>
<xsl:with-param name="pName1" select="././././././productamount"/>
<xsl:with-param name="pName2" select="././././././productcost"/>
</xsl:call-template>

<xsl:template name="sumProducts">
<xsl:param name="pNodes"/>
<xsl:param name="pName1"/>
<xsl:param name="pName2"/>
<xsl:param name="pAccum" select="0"/>
<xsl:choose>
<xsl:when test="not($pNodes)">
<xsl:value-of select="$pAccum"/>
</xsl:when>
<xsl:otherwise>
<xsl:call-template name="sumProducts">
<xsl:with-param name="pNodes" select="$pNodes[position() > 1]"/>
<xsl:with-param name="pName1" select="$pName1"/>
<xsl:with-param name="pName2" select="$pName2"/>
<xsl:with-param name="pAccum" select="$pAccum + $pNodes[1]/*[name()=$pName1] 
* pNodes[1]/*[name()=$pName2]"/>
</xsl:call-template>
</xsl:otherwise>
</xsl:choose>
</xsl:template>

Desired outcome would be Total = 26

Upvotes: 0

Views: 149

Answers (2)

Tim C
Tim C

Reputation: 70638

I can see a few problems with your XSLT. Firstly the setting of the pNodes parameter is incorrect

<xsl:with-param name="pNodes" select="customers/customer/invoices/invoice/products"/>

From looking at your XML, it needs to be as follows

<xsl:with-param name="pNodes" select="customers/invoices/invoice/products/product"/>

i.e. The invoices element is not within the customer element in your sample, and additionally you want to be summing the individual product elements.

Next, as Ian Roberts has mentioned, pName1 and pName2 look like they should be set to be element names. Currently you are just setting them to the value of whatever of the first occurrence of those elements.

<xsl:with-param name="pName1" select="'productamount'"/>
<xsl:with-param name="pName2" select="'productcost'"/> 

Finally, the setting of the pAccum parameter is wrong. You are currently doing this...

<xsl:with-param name="pAccum" 
   select="$pAccum + $pNodes[1]/*[name()=$pName1] * pNodes[1]/*[name()=$pName2]"/> 

But you have missed out a $ before the second pNodes variable. It should be this:

<xsl:with-param name="pAccum" 
   select="$pAccum + $pNodes[1]/*[name()=$pName1] * $pNodes[1]/*[name()=$pName2]"/> 

Here's the full XSLT, which should give you 26

<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
   <xsl:output method="xml" indent="yes"/>
   <xsl:template match="/">
      <xsl:call-template name="sumProducts">
         <xsl:with-param name="pNodes" select="customers/invoices/invoice/products/product"/>
         <xsl:with-param name="pName1" select="'productamount'"/>
         <xsl:with-param name="pName2" select="'productcost'"/>
      </xsl:call-template>
   </xsl:template>

   <xsl:template name="sumProducts">
      <xsl:param name="pNodes"/>
      <xsl:param name="pName1"/>
      <xsl:param name="pName2"/>
      <xsl:param name="pAccum" select="0"/>
      <xsl:choose>
         <xsl:when test="not($pNodes)">
            <xsl:value-of select="$pAccum"/>
         </xsl:when>
         <xsl:otherwise>
            <xsl:call-template name="sumProducts">
               <xsl:with-param name="pNodes" select="$pNodes[position() &gt; 1]"/>
               <xsl:with-param name="pName1" select="$pName1"/>
               <xsl:with-param name="pName2" select="$pName2"/>
               <xsl:with-param name="pAccum" select="$pAccum + $pNodes[1]/*[name()=$pName1]  * $pNodes[1]/*[name()=$pName2]"/>
            </xsl:call-template>
         </xsl:otherwise>
      </xsl:choose>
   </xsl:template>
</xsl:stylesheet>

Upvotes: 2

Ian Roberts
Ian Roberts

Reputation: 122394

Your recursive template looks OK, I think the problem is with your initial pName1 and pName2 parameter values. Try

<xsl:with-param name="pName1" select="'productamount'"/>
<xsl:with-param name="pName2" select="'productcost'"/>

Your template expects those parameters to be strings containing the names of the relevant elements, which is why you need the single quotes inside the double quoted select attributes.

Upvotes: 0

Related Questions