Search code examples
xsltparameters

XSL How to update parameter value to calculate total


I have the following xml

<?xml version="1.0" encoding="UTF-8"?>
<Results>
  <form-type>orderform-A</form-type>
    <data>
      <form-data>
         <field>
           <name>productid-1</name>
           <value>Yes</value>
         </field>
         <field>
           <name>productid-3</name>
           <value>Yes</value>
         </field>
         <field>
           <name>productid-4</name>
           <value>Yes</value>
         </field>
      </form-data>
   </data>
</Results>

On orderform-A customer can order the following products

  • productid-1 (cost $5)
  • productid-2 (cost $5)
  • productid-3 (cost $5)
  • productid-4 (cost $5)

productid-4 if ordered along with productid-2 or productid-3 is offered for free. Note the order form will only list the products ordered.

Similarly I have the following order form which also allows customers to order multiple quantities of each product :

<?xml version="1.0" encoding="UTF-8"?>
<Results>
  <form-type>orderform-B</form-type>
    <data>
      <form-data>
        <field>
           <name>productid-w</name>
           <value>Yes</value>
        </field>
        <field>
           <name>productid-w-qty</name>
           <value>1</value>
         </field>
         <field>
            <name>productid-x</name>
            <value>Yes</value>
         </field>
         <field>
            <name>productid-x-qty</name>
            <value>1</value>
         </field>
         <field>
            <name>productid-y</name>
            <value>Yes</value>
         </field>
         <field>
            <name>productid-y-qty</name>
            <value>1</value>
         </field>
      </form-data>
    </data>
  </Results>

On orderform-B customer can order the following products

  • productid-w (cost $20)
  • productid-x (cost $10)
  • productid-y (cost $20)
  • productid-z (cost $10)

I want to calculate the total cost of each order so I've tried something as follows:

 <xsl:param name="form-type">
 <xsl:param name="total">0</xsl:param>

 <xsl:template match="/">
     <xsl:choose>
          <xsl:when test="$form-type = 'orderform-A'">
               <xsl:call-template name="orderform-A-total"/>
          </xsl:when>
          <xsl:when test="$form-type = 'orderform-B'">
                 <xsl:call-template name="orderform-B-total"/>
          </xsl:when>
    </xsl:choose>
</xsl:template>

<xsl:template name="orderform-A-total">
      <xsl:if test="count(//Results/data/form-data/field[name='productid-1']/value) > 0">
          <xsl:value-of select="$total + 5" />
      </xsl:if>
      <xsl:if test="count(//Results/data/form-data/field[name='productid-2']/value) > 0">
           <xsl:value-of select="$total + 5" />
      </xsl:if>
      <xsl:if test="count(//Results/data/form-data/field[name='productid-3']/value) > 0">
            <xsl:value-of select="$total + 5" />
       </xsl:if>
       <xsl:if test="(count(//Results/data/form-data/field[name='productid-4']/value > 0)">
             <xsl:value-of select="$total + 5" />
       </xsl:if>
       <xsl:value-of select="$total" /></value>
 </xsl:template>

 <xsl:template name="orderform-B-total">
           // TO DO
 </xsl:template>    

But this doesn't work. How do I calculate the total for the different types of order forms?


Solution

  • In your place I would probably do something like:

    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:variable name="pricelist">
        <item id="productid-1">5</item> 
        <item id="productid-2">5</item> 
        <item id="productid-3">5</item> 
        <item id="productid-4">5</item> 
    </xsl:variable>
    
    <xsl:key name="price" match="item" use="@id" />
    
    <xsl:template match="/">
        <total>
            <xsl:apply-templates/>
        </total>    
    </xsl:template>
    
    <xsl:template match="Results[form-type='orderform-A']">
        <xsl:variable name="charges">
            <xsl:apply-templates select="data/form-data/field"/>
        </xsl:variable>
        <xsl:value-of select="sum($charges/charge)" />
    </xsl:template>
    
    <xsl:template match="field">
        <charge>
            <xsl:value-of select="key('price', name, $pricelist)"/>
        </charge>   
    </xsl:template>
    
    
    <xsl:template match="Results[form-type='orderform-B']">
        <!-- TO DO -->
    </xsl:template>
     
    </xsl:stylesheet>
    

    To implement a rule such as:

    productid-4 if ordered along with productid-2 or productid-3 is offered for free

    you can add an empty template like:

    <xsl:template match="field[name='productid-4'][../field[name='productid-2' or name='productid-3']]"/>
    

    Note that there is no need for a form-type parameter when the input has a form-type element.


    Added:

    To handle the orderform-B variant, you could do:

    <xsl:template match="Results[form-type='orderform-B']">
        <xsl:variable name="charges">
            <xsl:apply-templates select="data/form-data/field[value='Yes']" mode="B"/>
        </xsl:variable>
        <xsl:value-of select="sum($charges/charge)" />
    </xsl:template>
    
    <xsl:template match="field" mode="B">
        <xsl:variable name="price" select="key('price', name, $pricelist)" />
        <xsl:variable name="qty" select="following-sibling::field[1]/value" />
        <charge>
            <xsl:value-of select="$price * $qty"/>
        </charge>   
    </xsl:template>
    

    And, of course, you must also add the items to the $pricelist variable.