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.
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>