validationvariablesxsltgrouping

How validate a value through a position to avoid a header print in xslt?


am new in xslt and I am creating a PDF from an xml code using xslt 1.0.

My source data is the below one.

<STATEMENT>
    <STATEMENT_AGING>
        <AGING>
            <AGING_LABEL>Current</AGING_LABEL>
            <AGING_AMOUNT>$28,927.43</AGING_AMOUNT>
        </AGING>
        <AGING>
            <AGING_LABEL>1 - 30</AGING_LABEL>
            <AGING_AMOUNT>$0.00</AGING_AMOUNT>
        </AGING>
        <AGING>
            <AGING_LABEL>31 - 60</AGING_LABEL>
            <AGING_AMOUNT>$0.00</AGING_AMOUNT>
        </AGING>
        <AGING>
            <AGING_LABEL>61 - 90</AGING_LABEL>
            <AGING_AMOUNT>$0.00</AGING_AMOUNT>
        </AGING>
        <AGING>
            <AGING_LABEL>91 - 120</AGING_LABEL>
            <AGING_AMOUNT>$0.00</AGING_AMOUNT>
        </AGING>
        <AGING>
            <AGING_LABEL>Over 120</AGING_LABEL>
            <AGING_AMOUNT>$0.00</AGING_AMOUNT>
        </AGING>
    </STATEMENT_AGING>
    <GROUP 
           ID='1'
           label=''>
        <GROUP_LABEL />
        <GROUP_NAME>SoleraStatementSection</GROUP_NAME>
        <GROUP_TYPE>LABEL</GROUP_TYPE>
        <GROUP_HIDE_FLAG>0</GROUP_HIDE_FLAG>
        <GROUP_HEADER_ROW>
            <COL 
                 headerAlign='start'
                 headerFormat='text'
                 width='2cm'>Doc. #</COL>
            <COL 
                 headerAlign='start'
                 headerFormat='text'
                 width='1cm'>Doc. Type</COL>
            <COL 
                 headerAlign='start'
                 headerFormat='text'
                 width='1cm'>Doc. Date</COL>
            <COL 
                 headerAlign='start'
                 headerFormat='text'
                 width='1cm'>Due Date</COL>
            <COL 
                 headerAlign='center'
                 headerFormat='text'
                 width='1cm'>Currency</COL>
            <COL 
                 headerAlign='end'
                 headerFormat='currency'
                 width='1cm'>Original Amount</COL>
            <COL 
                 headerAlign='end'
                 headerFormat='currency'
                 width='1cm'>Balance</COL>
            <COL 
                 headerAlign='start'
                 headerFormat='number'
                 width='1cm'>ChildAccount</COL>
        </GROUP_HEADER_ROW>
        <GROUP_DATA_ROW>
            <COL>2120-000023134</COL>
            <COL>Invoice</COL>
            <COL>2024-05-13T00:23:14.205918-05:00</COL>
            <COL>2024-06-12T05:00:00.000000+00:00</COL>
            <COL>USD</COL>
            <COL>$13,247.46</COL>
            <COL>$13,247.46</COL>
            <COL>Price Acura</COL>
        </GROUP_DATA_ROW>
        <GROUP_DATA_ROW>
            <COL>2120-000023135</COL>
            <COL>Invoice</COL>
            <COL>2024-05-13T00:36:37.008144-05:00</COL>
            <COL>2024-06-12T05:00:00.000000+00:00</COL>
            <COL>USD</COL>
            <COL>$13,247.37</COL>
            <COL>$13,247.37</COL>
            <COL>Price Acura</COL>
        </GROUP_DATA_ROW>
        <GROUP_DATA_ROW>
            <COL>2120-000023136</COL>
            <COL>Invoice</COL>
            <COL>2024-05-13T00:51:32.838201-05:00</COL>
            <COL>2024-06-12T05:00:00.000000+00:00</COL>
            <COL>USD</COL>
            <COL>$2,432.60</COL>
            <COL>$2,432.60</COL>
            <COL>Price Honda</COL>
        </GROUP_DATA_ROW>
    </GROUP>
    <GROUP ID='2'
           label=''>
        <GROUP_LABEL />
        <GROUP_NAME>CreditData</GROUP_NAME>
        <GROUP_TYPE>LABEL</GROUP_TYPE>
        <GROUP_HIDE_FLAG>0</GROUP_HIDE_FLAG>
        <GROUP_HEADER_ROW>
            <COL 
                 headerAlign='start'
                 headerFormat='text'
                 width='0cm'>Credit #</COL>
            <COL 
                 headerAlign='start'
                 headerFormat='text'
                 width='0cm'>Credit Type</COL>
            <COL 
                 headerAlign='start'
                 headerFormat='text'
                 width='0cm'>Credit Date</COL>
            <COL 
                 headerAlign='center'
                 headerFormat='text'
                 width='0cm'>Currency</COL>
            <COL 
                 headerAlign='end'
                 headerFormat='currency'
                 width='0cm'>Credit Amount</COL>
            <COL 
                 headerAlign='end'
                 headerFormat='currency'
                 width='0cm'>Unallocated Amnt</COL>
            <COL 
                 headerAlign='start'
                 headerFormat='text'
                 width='0cm'>ChildAccount</COL>
        </GROUP_HEADER_ROW>
    </GROUP>
    <GROUP ID='3'
           label=''>
        <GROUP_LABEL />
        <GROUP_NAME>PaymentSection</GROUP_NAME>
        <GROUP_TYPE>LABEL</GROUP_TYPE>
        <GROUP_HIDE_FLAG>0</GROUP_HIDE_FLAG>
        <GROUP_HEADER_ROW>
            <COL 
                 headerAlign='start'
                 headerFormat='text'
                 width='0cm'>Payment #</COL>
            <COL 
                 headerAlign='start'
                 headerFormat='text'
                 width='0cm'>Payment Type</COL>
            <COL 
                 headerAlign='start'
                 headerFormat='text'
                 width='0cm'>Payment Date</COL>
            <COL 
                 headerAlign='center'
                 headerFormat='text'
                 width='0cm'>Currency</COL>
            <COL 
                 headerAlign='end'
                 headerFormat='currency'
                 width='0cm'>Payment Amount</COL>
            <COL 
                 headerAlign='end'
                 headerFormat='currency'
                 width='0cm'>Unallocated Amnt</COL>
            <COL 
                 headerAlign='start'
                 headerFormat='text'
                 width='0cm'>ChildAccount</COL>
        </GROUP_HEADER_ROW>
    </GROUP>
</STATEMENT>

The thing is that in the GROUPID 1 I'm trying to group the data by custName or col[8] but I want to print the header everytime this custName change. I tried to store the value into an variable but once the loop comes up again, the value is lost, so my IF am trying to use to evalute is not working.

Does anyone has an idea of how can I manage this, because I saw a lot of answers with diff methods but I don't understand quite well all of them because my source xml has "generic" labels intead of real label names.

PS. I tried with a choose and if condition but none of them are working.

    <xsl:template name= "DetailSectionG1">
        <fo:block space-before="8mm" font-weight="bold" color="{$blue-color}" keep-together.within-page="always" keep-with-next="always">
            <fo:table width="100%" border-collapse="collapse" table-layout="fixed">
                <fo:table-column column-width="{$left-margin}"/>
                <fo:table-column column-width="proportional-column-width(1)"/>
                <fo:table-column column-width="{$right-margin}"/>
                <fo:table-body>
                    <fo:table-row>
                        <fo:table-cell>
                            <fo:block/>
                        </fo:table-cell>
                        <fo:table-cell>
                            <fo:block font-size="{$large-font-size}" padding-top="3 * {$padding}" padding-bottom="{$padding}">
                                Statement Details
                            </fo:block>
                        </fo:table-cell>
                    </fo:table-row>
                </fo:table-body>
            </fo:table>
        </fo:block>
        <xsl:choose>
            <xsl:when test="GROUP[@ID=1]/GROUP_DATA_ROW/COL[1] !=''">
                <fo:block>
                    <fo:table width="100%" border-collapse="collapse" table-layout="fixed">
                        <fo:table-column column-width="{$data-left-margin}"/>
                        <fo:table-column column-width="30mm"/>
                        <fo:table-column column-width="25mm"/>
                        <fo:table-column column-width="25mm"/>
                        <fo:table-column column-width="25mm"/>
                        <fo:table-column column-width="22mm"/>
                        <fo:table-column column-width="27mm"/>
                        <fo:table-column column-width="25mm"/>
                        <fo:table-column column-width="{$data-right-margin}"/>
                        <xsl:for-each select="/STATEMENT/GROUP[@ID=1]/GROUP_DATA_ROW[generate-id(.)=generate-id(key('groupDataRow', concat(COL[position() = 8],'+',COL[position() = 1])))]">
                            <xsl:variable name="vGroup" select="key('kAllFields', concat(COL[position() = 8],'+',COL[position() = 1]))"/>
                            <fo:table-body>
                                <xsl:choose>
                                    <xsl:when test="custName != COL[position() = 8] or position() = 1">
                                        <fo:table-row>
                                            <fo:table-cell>
                                                <fo:block/>
                                            </fo:table-cell>
                                            <fo:table-cell>
                                                <fo:block font-weight="bold" color="{$blue-color}" keep-together.within-page="always" keep-with-next="always" padding-bottom="{$padding}"/>
                                                    <!--xsl:value-of select="COL[position() = 8]"/-->
                                                <!--/fo:block-->
                                            </fo:table-cell>
                                        </fo:table-row>
                                        <fo:table-row>
                                            <fo:table-cell>
                                                <fo:block/>
                                            </fo:table-cell>
                                            <xsl:call-template name="HeaderCell">
                                                <xsl:with-param name="position">1</xsl:with-param>
                                                <xsl:with-param name="color" select="$blue-color"/>
                                            </xsl:call-template>
                                            <xsl:call-template name="HeaderCell">
                                                <xsl:with-param name="position">2</xsl:with-param>
                                                <xsl:with-param name="color" select="$blue-color"/>
                                            </xsl:call-template>
                                            <xsl:call-template name="HeaderCell">
                                                <xsl:with-param name="position">3</xsl:with-param>
                                                <xsl:with-param name="color" select="$blue-color"/>
                                            </xsl:call-template>
                                            <xsl:call-template name="HeaderCell">
                                                <xsl:with-param name="position">4</xsl:with-param>
                                                <xsl:with-param name="color" select="$blue-color"/>
                                            </xsl:call-template>
                                            <xsl:call-template name="HeaderCell">
                                                <xsl:with-param name="position">5</xsl:with-param>
                                                <xsl:with-param name="color" select="$blue-color"/>
                                            </xsl:call-template>
                                            <xsl:call-template name="HeaderCell">
                                                <xsl:with-param name="position">6</xsl:with-param>
                                                <xsl:with-param name="color" select="$blue-color"/>
                                            </xsl:call-template>
                                            <xsl:call-template name="HeaderCell">
                                                <xsl:with-param name="position">7</xsl:with-param>
                                                <xsl:with-param name="color" select="$blue-color"/>
                                            </xsl:call-template>
                                        </fo:table-row>
                                    </xsl:when>
                                    <xsl:otherwise/>
                                </xsl:choose>
                                <!--xsl:if test="{$acctName} != COL[position() = 8] or position() = 1"> </xsl:if-->
                                <fo:table-row>
                                    <fo:table-cell>
                                        <fo:block/>
                                    </fo:table-cell>
                                    <xsl:call-template name="Cell">
                                        <xsl:with-param name="position">1</xsl:with-param>
                                    </xsl:call-template>
                                    <xsl:call-template name="Cell">
                                        <xsl:with-param name="position">2</xsl:with-param>
                                    </xsl:call-template>
                                    <fo:table-cell>
                                        <fo:block color="{$base-color}" padding-bottom="{$small-padding}">
                                            <xsl:attribute name="end-indent">1mm</xsl:attribute>
                                            <xsl:attribute name="text-align">
                                                <xsl:value-of select="../GROUP_HEADER_ROW[1]/COL[position() = 3]/@headerAlign"/>
                                            </xsl:attribute>
                                            <xsl:call-template name="getDate">
                                                <xsl:with-param name="value" select="COL[position()=3]"/>
                                            </xsl:call-template>
                                        </fo:block>
                                    </fo:table-cell>
                                    <fo:table-cell>
                                        <fo:block color="{$base-color}" padding-bottom="{$small-padding}">
                                            <xsl:attribute name="end-indent">1mm</xsl:attribute>
                                            <xsl:attribute name="text-align">
                                                <xsl:value-of select="../GROUP_HEADER_ROW[1]/COL[position() = 4]/@headerAlign"/>
                                            </xsl:attribute>
                                            <xsl:call-template name="getDate">
                                                <xsl:with-param name="value" select="COL[position()=4]"/>
                                            </xsl:call-template>
                                        </fo:block>
                                    </fo:table-cell>
                                    <xsl:call-template name="Cell">
                                        <xsl:with-param name="position">5</xsl:with-param>
                                    </xsl:call-template>
                                    <xsl:call-template name="Cell">
                                        <xsl:with-param name="position">6</xsl:with-param>
                                    </xsl:call-template>
                                    <xsl:call-template name="Cell">
                                        <xsl:with-param name="position">7</xsl:with-param>
                                    </xsl:call-template>
                                </fo:table-row>
                                <xsl:variable name="custName">
                                    <xsl:value-of select="COL[position() = 8]"/>
                                </xsl:variable>
                            </fo:table-body>
                        </xsl:for-each>
                    </fo:table>
                </fo:block>
            </xsl:when>
            <xsl:otherwise>
                <fo:block keep-together.within-page="always" keep-with-next="always">
                    <fo:table width="100%" border-collapse="collapse" table-layout="fixed">
                        <fo:table-column column-width="{$left-margin}"/>
                        <fo:table-column column-width="proportional-column-width(1)"/>
                        <fo:table-column column-width="{$right-margin}"/>
                        <fo:table-body>
                            <fo:table-row>
                                <fo:table-cell>
                                    <fo:block/>
                                </fo:table-cell>
                                <fo:table-cell>
                                    <fo:block color="{$base-color}" padding-bottom="{$small-padding}" end-indent="1mm">
                                        No invoices to display
                                    </fo:block>
                                </fo:table-cell>
                            </fo:table-row>
                        </fo:table-body>
                    </fo:table>
                </fo:block>
            </xsl:otherwise>
        </xsl:choose>
    </xsl:template>

My PDF expectation is this one.

[Expectation Output] (https://i.sstatic.net/GPAPwxHQ.png)

For now the validation I am trying is not working and the acctName and header table is showing only once.

Current Output


Solution

  • You seem to be struggling with grouping, note that grouping is much easier in XSLT 2 or later where you can do e.g.

      <xsl:template match="GROUP[@ID = 1]">
        <xsl:for-each-group select="GROUP_DATA_ROW" group-by="COL[8]">
          <group key="{current-grouping-key()}">
            <xsl:sequence select="current-group()"/>
          </group>
        </xsl:for-each-group>
      </xsl:template>
    

    and get e.g.

    <group key="Price Acura">
       <GROUP_DATA_ROW>
          <COL>2120-000023134</COL>
          <COL>Invoice</COL>
          <COL>2024-05-13T00:23:14.205918-05:00</COL>
          <COL>2024-06-12T05:00:00.000000+00:00</COL>
          <COL>USD</COL>
          <COL>$13,247.46</COL>
          <COL>$13,247.46</COL>
          <COL>Price Acura</COL>
       </GROUP_DATA_ROW>
       <GROUP_DATA_ROW>
          <COL>2120-000023135</COL>
          <COL>Invoice</COL>
          <COL>2024-05-13T00:36:37.008144-05:00</COL>
          <COL>2024-06-12T05:00:00.000000+00:00</COL>
          <COL>USD</COL>
          <COL>$13,247.37</COL>
          <COL>$13,247.37</COL>
          <COL>Price Acura</COL>
       </GROUP_DATA_ROW>
    </group>
    <group key="Price Honda">
       <GROUP_DATA_ROW>
          <COL>2120-000023136</COL>
          <COL>Invoice</COL>
          <COL>2024-05-13T00:51:32.838201-05:00</COL>
          <COL>2024-06-12T05:00:00.000000+00:00</COL>
          <COL>USD</COL>
          <COL>$2,432.60</COL>
          <COL>$2,432.60</COL>
          <COL>Price Honda</COL>
       </GROUP_DATA_ROW>
    </group>
    

    so you have the two groups and the right data for each group easily, I haven't made any attempt to spew out the wanted XSL-FO but it should be easy once you have the grouping solved.

    Note that XSLT 3, the current version of XSLT, is supported on a wide variety of platforms, in the browser (https://www.saxonica.com/download/javascript.xml) and for Node.js (https://www.npmjs.com/package/saxon-js) you can use SaxonJS 2 (2.6 is the current release), for Java you will find Saxon HE on Maven https://mvnrepository.com/artifact/net.sf.saxon/Saxon-HE or on Github, for Python there is SaxonCHE (https://pypi.org/project/saxonche/), for .NET there is Saxon .NET HE (https://www.nuget.org/packages/Saxon-HE, https://www.nuget.org/packages/SaxonHE12s9apiExtensions).

    Nevertheless, if you are working in a restricted environment to be forced with XSLT 1, use a key

      <xsl:key name="group" match="GROUP[@ID = 1]/GROUP_DATA_ROW" use="COL[8]"/>
      
      <xsl:template match="GROUP[@ID = 1]">
        <xsl:for-each select="GROUP_DATA_ROW[generate-id() = generate-id(key('group', COL[8])[1])]">
          <group key="{COL[8]}">
            <xsl:copy-of select="key('group', COL[8])"/>
          </group>
        </xsl:for-each>
      </xsl:template>