juls_pro_37: XSLT 1.0 Sum

Hi,

bräuchte für den Knoten "Total/AllowanceOrCharge_DEEE" ein neues Segment "Amount" mit der Summe von "Item/TotalQuantity" * "AllowanceOrCharge_Line/Amount" wenn unter "Item/AllowanceOrCharge_Line" der "Code" = "TX".

Sprich (75 * 2,08) + (5*2,08) = 166,40

XML:

<?xml version="1.0" encoding="utf-8" standalone="yes"?>
<SALESINVOICE>  
  <HeaderInformation>
    <InvoiceNumber>Test</InvoiceNumber>    
  </HeaderInformation>
  <LineInformation>
    <Item>
      <LineNum>1</LineNum>
      <TotalQuantity>75.00</TotalQuantity>  
      <AllowanceOrCharge_Line>
        <Code>TX</Code>
        <Amount>2.08</Amount>        
      </AllowanceOrCharge_Line>
    </Item>  
    <Item>
      <LineNum>2</LineNum>
      <TotalQuantity>5.00</TotalQuantity>  
      <AllowanceOrCharge_Line>
        <Code>TX</Code>       
        <Amount>2.08</Amount>
      </AllowanceOrCharge_Line>
    </Item>
  </LineInformation> 
  <Totals>
    <AllowanceOrCharge_DEEE>
        <Code>TX</Code>
    </AllowanceOrCharge_DEEE>
  </Totals>
</SALESINVOICE>

XSLT:

<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
  <xsl:output method="xml" version="1.0" encoding="UTF-8" standalone="yes" indent="yes"/>
  <xsl:strip-space elements="*" />
    
    <xsl:template match="AllowanceOrCharge_DEEE">     
        <xsl:copy>
         <!--copy all other nodes-->
         <xsl:apply-templates select="@* | node()"/>  
          <xsl:for-each select="//Item/AllowanceOrCharge_Line">
            <xsl:if test="./Code = 'TX'">           
            <Amount>
                <xsl:value-of select="sum((./TotalQuantity * ./AllowanceOrCharge_Line/Amount))"/>               
            </Amount>
              </xsl:if>              
        </xsl:for-each> 
        </xsl:copy>
    </xsl:template>

  <!-- Identity-Template für die nicht explizit benannten Elemente -->
  <xsl:template match="@* | node()">
    <xsl:copy>
      <xsl:apply-templates select="@* | node()"/>
    </xsl:copy>
  </xsl:template>

</xsl:stylesheet>

Korrekt:

<?xml version="1.0" encoding="utf-8" standalone="yes"?>
<SALESINVOICE>  
  <HeaderInformation>
    <InvoiceNumber>Test</InvoiceNumber>    
  </HeaderInformation>
  <LineInformation>
    <Item>
      <LineNum>1</LineNum>
      <TotalQuantity>75.00</TotalQuantity>  
      <AllowanceOrCharge_Line>
        <Code>TX</Code>
        <Amount>2.08</Amount>        
      </AllowanceOrCharge_Line>
    </Item>  
    <Item>
      <LineNum>2</LineNum>
      <TotalQuantity>5.00</TotalQuantity>  
      <AllowanceOrCharge_Line>
        <Code>TX</Code>       
        <Amount>2.08</Amount>
      </AllowanceOrCharge_Line>
    </Item>
  </LineInformation> 
  <Totals>
    <AllowanceOrCharge_DEEE>
        <Code>TX</Code>
        <Amount>166.40</Amount>
    </AllowanceOrCharge_DEEE>
  </Totals>
</SALESINVOICE>

danke & lg Julian

akzeptierte Antworten

  1. Hallo Julian,

    bräuchte für den Knoten "Total/AllowanceOrCharge_DEEE" ein neues Segment "Amount" mit der Summe von "Item/TotalQuantity" * "AllowanceOrCharge_Line/Amount" wenn unter "Item/AllowanceOrCharge_Line" der "Code" = "TX".

    Sprich (75 * 2,08) + (5*2,08) = 166,40

    Soll es mehrere amount-Elemente unterhalb von AllowanceOrCharge_DEEE geben oder genau eins?

    Die sum()-Funktion greift nur über Knotenmengen, also Element- oder Attributinhalte eines Namens. Berechnungen kann man innerhalb der Klammern nicht durchführen.

    Insofern braucht man hier ein rekursives Template, welches die einzelnen Teilergebnisse zusammenführt. Falls es mehrere amount-Elemente geben soll, dann würden die jeweiligen Einzelberechnungen ohne sum() reichen.

    Grüße,
    Thomas

    1. Danke Thomas, dass du dir auch dieses Thema anhängst. :)

      Danke auch für die Erklärung.

      Es soll genau ein einziges "Amount" unter "AllowanceOrCharge_DEEE" mit dem Gesamtwert geben.

      LG Julian

      1. Hallo Julian,

        Es soll genau ein einziges "Amount" unter "AllowanceOrCharge_DEEE" mit dem Gesamtwert geben.

        XSLT-1.0-Variante mit xsl:call-template:

        <?xml version="1.0" encoding="UTF-8"?>
        <xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
          <xsl:output method="xml" version="1.0" encoding="UTF-8" standalone="yes" indent="yes"/>
          <xsl:strip-space elements="*"/>
        
          <xsl:template match="AllowanceOrCharge_DEEE">
            <xsl:copy>
              <!--copy all other nodes-->
              <xsl:apply-templates select="@* | node()"/>
              <xsl:variable name="tx_anz" select="count(//Item[AllowanceOrCharge_Line[Code = 'TX']])"/>
              <xsl:if test="$tx_anz >= 1">
                <Amount>
                  <xsl:call-template name="calc_amount">
                    <xsl:with-param name="summe" select="0"/>
                    <xsl:with-param name="i" select="1"/>
                    <xsl:with-param name="max" select="$tx_anz"/>
                  </xsl:call-template>
                </Amount>
              </xsl:if>
            </xsl:copy>
          </xsl:template>
        
          <!-- Identity-Template für die nicht explizit benannten Elemente -->
          <xsl:template match="@* | node()">
            <xsl:copy>
              <xsl:apply-templates select="@* | node()"/>
            </xsl:copy>
          </xsl:template>
        
          <xsl:template name="calc_amount">
            <xsl:param name="summe"/>
            <xsl:param name="i"/>
            <xsl:param name="max"/>
        
            <xsl:choose>
              <xsl:when test="$i &lt;= $max">
                <xsl:variable name="produkt" select="//Item[AllowanceOrCharge_Line[Code = 'TX']][$i]/TotalQuantity * //Item[AllowanceOrCharge_Line[Code = 'TX']][$i]/AllowanceOrCharge_Line/Amount"/>
                <xsl:call-template name="calc_amount">
                  <xsl:with-param name="summe" select="$summe + $produkt"/>
                  <xsl:with-param name="i" select="$i + 1"/>
                  <xsl:with-param name="max" select="$max"/>
                </xsl:call-template>
              </xsl:when>
              <xsl:otherwise>
                <xsl:value-of select="format-number($summe, '#.00')"/>
              </xsl:otherwise>
            </xsl:choose>
          </xsl:template>
        
        </xsl:stylesheet>
        

        Mit XSLT 2.0/3.0 und for-in-return gestaltet es sich deutlich kompakter. Die einzelnen aufzusummierenden Werte werden darüber innerhalb von sum(…) zunächst vorberechnet:

        <?xml version="1.0" encoding="UTF-8"?>
        <xsl:stylesheet version="2.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
          <xsl:output method="xml" version="1.0" encoding="UTF-8" standalone="yes" indent="yes"/>
          <xsl:strip-space elements="*"/>
        
          <xsl:template match="AllowanceOrCharge_DEEE">
            <xsl:copy>
              <!--copy all other nodes-->
              <xsl:apply-templates select="@* | node()"/>
              <xsl:variable name="tx_anz" select="count(//Item[AllowanceOrCharge_Line[Code = 'TX']])"/>
              <xsl:if test="$tx_anz >= 1">
                <Amount>
                  <xsl:value-of select="format-number(sum(for $item in //Item[AllowanceOrCharge_Line[Code = 'TX']]
                    return $item/TotalQuantity * $item/AllowanceOrCharge_Line/Amount), '#.00')"/>
                </Amount>
              </xsl:if>
            </xsl:copy>
          </xsl:template>
        
          <!-- Identity-Template für die nicht explizit benannten Elemente -->
          <xsl:template match="@* | node()">
            <xsl:copy>
              <xsl:apply-templates select="@* | node()"/>
            </xsl:copy>
          </xsl:template>
        
        </xsl:stylesheet>
        

        Das Ergebnis ist jeweils:

        […]
        <Totals>
          <AllowanceOrCharge_DEEE>
            <Code>TX</Code>
            <Amount>166.40</Amount>
          </AllowanceOrCharge_DEEE>
        </Totals>
        […]
        

        Grüße,
        Thomas

        1. WOOOW, danke!!! das hätt ich nie hinbekommen!!! danke danke

        2. Hi Thomas,

          im online tool lässt sich der code einwandfrei verarbeiten. Aber in meinem ERP erhalte ich folg. Error:

          XSLT compile error. '<', hexadecimal value 0x3C, is an invalid attribute character. Line 50, position 26.

          <xsl:when test="$i & lt;= $max">

          Kannst du mir hier evtl. noch helfen?

          Danke & LG Julian

          1. wenn ich < oder <= verwende kommt auch derselbe Fehler. Komischerweise bei > klappt die Ausgabe?

            Wie es aussieht funktioniert auch: <xsl:when test="$max >= $i">

            Mir ist nur nicht klar, warum es andersrum nicht klappt? <xsl:when test="$i <= $max">

          2. Hallo Julian,

            im online tool lässt sich der code einwandfrei verarbeiten. Aber in meinem ERP erhalte ich folg. Error:

            XSLT compile error. '<', hexadecimal value 0x3C, is an invalid attribute character. Line 50, position 26.

            <xsl:when test="$i & lt;= $max">

            Wo kommt das Leerzeichen zwischen & und lt;= her? Stand nicht in meinem Code.

            < muss als &lt; maskiert werden, das >-Zeichen kann als &gt; stehen, muss aber meistens nicht. > ist nur innerhalb von <![CDATA[ ... ]]>-Abschnitten kritisch, wenn darin ]]> selbst vorkommt (dann als ]]&gt; notieren).

            Grüße,
            Thomas

            1. sry, dass Leereichen ist mir reingerutscht - war schon etwas spät. :)

              Habe den erwähnten Ausschnitt des Codes auf "<xsl:when test="$max >= $i">" ausgebessert und es funktioniert.

              Passt das auch aus deiner Sicht oder habe ich was übersehen?

              Egal ob ich "<xsl:when test="$i &lt;= $max">" oder "<xsl:when test="$i <= $max">"versucht habe, kam derselbe Error.

              LG Julian

              1. Hallo Julian,

                sry, dass Leereichen ist mir reingerutscht - war schon etwas spät. :)

                Habe den erwähnten Ausschnitt des Codes auf "<xsl:when test="$max >= $i">" ausgebessert und es funktioniert.

                Passt das auch aus deiner Sicht oder habe ich was übersehen?

                Egal ob ich "<xsl:when test="$i &lt;= $max">" oder "<xsl:when test="$i <= $max">"versucht habe, kam derselbe Error.

                Dann hat wohl der verwendete XSLT-Prozessor eine Macke. "$i &lt;= $max" ist völlig ok (<= nach den XML-Refeln natürlich nicht). Die umgekehrte größer/gleich-Bedingung ist auch legitim, aber prüfe mal die verwendete Umgebung.

                Grüße,
                Thomas

                1. danke danke, somit hab ich dieses Problem zumindest gelöst dank deiner Hilfe :)

                  Hab leider wenig Ahnung wie ich das Prüfen bzw. Beheben könnte. Ich arbeite da in einem ERP mit einem integrierten Tool.

                  Kennst du dich da aus, wenn ich Details dazu liefere? Soll ich einen neuen Eintrag im Forum machen?

                  Habe diese Error-Meldung öfters in Google gefunden und meistens wurde einfach (wie in meinem Fall) der Code ein bisschen umgeschrieben. :)

                  LG Julian

                  1. Hallo Julian,

                    Hab leider wenig Ahnung wie ich das Prüfen bzw. Beheben könnte. Ich arbeite da in einem ERP mit einem integrierten Tool.

                    Kennst du dich da aus, wenn ich Details dazu liefere? Soll ich einen neuen Eintrag im Forum machen?

                    Habe diese Error-Meldung öfters in Google gefunden und meistens wurde einfach (wie in meinem Fall) der Code ein bisschen umgeschrieben. :)

                    Offenbar ein Software-spezifisches Problem. Also besser den Hersteller fragen, warum XML-Standards nicht korrekt unterstützt werden. Falls möglich, setze einen konformen externen XSLT-Prozessor wie Saxon-HE ein.

                    Grüße,
                    Thomas

                    1. alles klar, danke dir auf jeden Fall für deine mega Unterstützung!!!!

                      LG Julian