Suma de cantidades de lista de materiales en XSLT

Estoy trabajando en un informe XSLT y necesito encontrar los totales de los componentes en una lista de materiales. El XML de entrada consta de elementos y enlaces. Para cada artículo, necesito obtener la cantidad de cada enlace donde se usa. Para complicar las cosas, necesito subir en la jerarquía y multiplicar las cantidades por las cantidades principales. El ejemplo solo muestra dos niveles, pero podría ser más profundo que eso. Por ejemplo, Item assy1 usa 5 assy2 y 1 tornillo Item. Cada artículo assy2 usa 2 tornillos. Entonces habría un total de 5 assy2 y 11 tornillos (1 usado por assy1 y cada assy2 usa 2 (2X5)). He descubierto cómo totalizar las cantidades de artículos, pero no cómo multiplicarlas en la jerarquía.

Aquí está el código fuente XML:

<items>
<item id="93516">
<attrs><attr name="FILE_ID">assy1</attr></attrs>
</item>
<item id="93515">
<attrs><attr name="FILE_ID">assy2</attr></attrs>
</item>
<item id="93514">
<attrs><attr name="FILE_ID">screw</attr></attrs>
</item>
</items>

<links>
<link source="93516" destination="93514">
<attrs><attr name="QUANTITY">5</attr></attrs>
</link>
<link source="93516" destination="93515">
<attrs><attr name="QUANTITY">1</attr></attrs>
</link>
<link source="93515" destination="93514">
<attrs><attr name="QUANTITY">2</attr></attrs>
</link>
</links>

Aquí hay un código que encontré y adapté para sumar las cantidades, pero no se multiplica por las cantidades principales:

<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">

<xsl:template match="/">
    <xsl:apply-templates select="/items/item"/>
</xsl:template>

<xsl:template match="item">
    <xsl:value-of select="attrs/attr[@name='FILE_ID']"/> - 
    <xsl:variable name="item_id" select="@id"/>
    <xsl:call-template name="bomQty">
        <xsl:with-param name="itemLinks" select="/links/link[@destination=$item_id]"/>
    </xsl:call-template> - 
</xsl:template>

<xsl:template name="bomQty">
  <xsl:param name="itemLinks"/>
    <xsl:choose>
      <xsl:when test="$itemLinks">
        <xsl:variable name="recursive_result">
          <xsl:call-template name="bomQty">
            <xsl:with-param name="itemLinks" select="$itemLinks[position() > 1]"/>
          </xsl:call-template>
        </xsl:variable>
        <xsl:value-of select="number($itemLinks[1]/attrs/attr[@name='QUANTITY']) + $recursive_result"/>
      </xsl:when>
      <xsl:otherwise><xsl:value-of select="0"/></xsl:otherwise>
  </xsl:choose>
</xsl:template>

</xsl:stylesheet>

Cualquier ayuda sería apreciada.

preguntado el 03 de mayo de 12 a las 17:05

1 Respuestas

Esta transformación:

<xsl:stylesheet version="1.0"
 xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
 xmlns:ext="http://exslt.org/common">
 <xsl:output method="text"/>
 <xsl:strip-space elements="*"/>

 <xsl:key name="kItemById" match="item" use="@id"/>

 <xsl:key name="kUses" match="@destination"
                       use="../@source"/>
 <xsl:key name="kLink" match="link"
           use="concat(@source, '+', @destination)"/>
 <xsl:key name="kelemByName" match="*/*" use="name()"/>

 <xsl:template match="/">

Bill of Materials for the production of one <xsl:text/>

 <xsl:text/> "<xsl:value-of select="/*/items/item[1]/*/*"/>":

  <xsl:variable name="vrtfPass1">
      <xsl:call-template name="generate">
       <xsl:with-param name="pId"
          select="/*/items/item[1]/@id"/>
      </xsl:call-template>
  </xsl:variable>

  <xsl:apply-templates mode="pass2" select=
                       "ext:node-set($vrtfPass1)/*"/>
 </xsl:template>

 <xsl:template name="generate">
  <xsl:param name="pId"/>
  <xsl:param name="pQty" select="1"/>

  <xsl:element name="{string(key('kItemById', $pId))}">
    <xsl:attribute name="qty">
      <xsl:value-of select="$pQty"/>
    </xsl:attribute>
    <xsl:for-each select="key('kUses', $pId)">
      <xsl:call-template name="generate">
        <xsl:with-param name="pId" select="."/>
        <xsl:with-param name="pQty" select=
        "key('kLink', concat($pId, '+', .))
              /attrs/attr[@name='QUANTITY']"/>
      </xsl:call-template>
    </xsl:for-each>
  </xsl:element>
 </xsl:template>

 <xsl:template match="/*" mode="pass2">
  <xsl:for-each select=
   "descendant::*
      [generate-id()
      =
       generate-id(key('kelemByName', name())[1])
      ]">

    <xsl:value-of select=
     "concat(name(), ': ')"/>

      <xsl:variable name="vrtfQuantities">
          <xsl:for-each select="key('kelemByName', name())">
              <xsl:call-template name="totalQuantity"/>
          </xsl:for-each>
      </xsl:variable>

            <xsl:value-of select=
             "concat(sum(ext:node-set($vrtfQuantities)/total), '&#xA;')"/>

  </xsl:for-each>
 </xsl:template>

 <xsl:template name="totalQuantity">
  <xsl:param name="pNode" select="."/>

  <xsl:choose>
   <xsl:when test="not($pNode/parent::*)">
    <total><xsl:value-of select="$pNode/@qty"/></total>
   </xsl:when>
   <xsl:otherwise>
    <xsl:variable name="vAncTotal">
     <xsl:call-template name="totalQuantity">
      <xsl:with-param name="pNode" select="$pNode/.."/>
     </xsl:call-template>
    </xsl:variable>

    <total><xsl:value-of select="$pNode/@qty * $vAncTotal"/></total>
   </xsl:otherwise>
  </xsl:choose>
 </xsl:template>

</xsl:stylesheet>

cuando se aplica en el documento XML proporcionado:

<bm>
    <items>
        <item id="93516">
            <attrs>
                <attr name="FILE_ID">assy1</attr>
            </attrs>
        </item>
        <item id="93514">
            <attrs>
                <attr name="FILE_ID">assy2</attr>
            </attrs>
        </item>
        <item id="93515">
            <attrs>
                <attr name="FILE_ID">screw</attr>
            </attrs>
        </item>
    </items>
    <links>
        <link source="93516" destination="93514">
            <attrs>
                <attr name="QUANTITY">5</attr>
            </attrs>
        </link>
        <link source="93516" destination="93515">
            <attrs>
                <attr name="QUANTITY">1</attr>
            </attrs>
        </link>
        <link source="93514" destination="93515">
            <attrs>
                <attr name="QUANTITY">2</attr>
            </attrs>
        </link>
    </links>
</bm>

produce el resultado correcto deseado:

Bill of Materials for the production of one  "assy1":

assy2: 5
screw: 11

contestado el 04 de mayo de 12 a las 05:05

Gracias, funcionó muy bien. Traté de votar la respuesta, pero no tengo suficientes puntos de reputación. - Cayos douglas

@DouglasKeys: Wow... Me alegro de que esta solución sea lo que necesitabas. En cuanto a votar a favor, todavía puedes aceptar la respuesta, haciendo clic en la marca de verificación junto a ella :) - dimitre novachev

No es la respuesta que estás buscando? Examinar otras preguntas etiquetadas or haz tu propia pregunta.