Dynamic Table - Too Large for Area - Script to combine extra rows

I have a need to limit the number of rows in a dynamic table so it will fit on the page. All of the extra rows need to be added together and then displayed as the last row.

All of the columns are currency or numbers except for the description column and a date column. The description and date columns on the combined row would become static values.

For example the page will fit 3 rows (plus the last one), but there are 6 rows.

Row1          10          2          5
Row2          10          4          5
Row3          10          6          5
Row4          10          8          5
Row5          10         10          5
Row6          10         12          5

Rows 1 through 3 would be displayed like normal, but rows 4 through 6 would be added together and displayed as one row.

Row1          10          2          5
Row2          10          4          5
Row3          10          6          5
Row456        30         30         15

Any ideas on how this can be done?

Is the space on the page for the detail table always the same or will it changes? I.E. Is your detail table in a fixed position?

In case the answer on @hamelj’s question is “yes” then I assume that you can add for example the following HTML code (in Source mode) to your Print Context Section:

<table id="example" cellpadding="0" cellspacing="0" style=" width: 100%;">
	<tbody>
		<tr>
			<td style="width: 25%;"><br></td>
			<td style="width: 25%;"><br></td>
			<td style="width: 25%;"><br></td>
			<td style="width: 25%;"><br></td>
		</tr>
	</tbody>
</table>

And use a Standard Script such as:
Name: Example
Selector: table#example tbody

var result = "";
var totals = {
	rowNumber: "",
	currencyA: 0,
	currencyB: 0,
	currencyC: 0
};

if ("detail" in record.tables) {
	for (var i = 0; i < record.tables["detail"].length; i++) {
		var rowNumber = record.tables["detail"][i].fields["rowNumber"];
		var currencyA = record.tables["detail"][i].fields["currencyA"];
		var currencyB = record.tables["detail"][i].fields["currencyB"];
		var currencyC = record.tables["detail"][i].fields["currencyC"];
	
		if (i < 3) {
			result += "<tr>";
			result += ("<td style=\"width: 25%;\">Row no. " + rowNumber + "</td>");
			result += ("<td style=\"width: 25%;\">" + currencyA + "</td>");
			result += ("<td style=\"width: 25%;\">" + currencyB + "</td>");
			result += ("<td style=\"width: 25%;\">" + currencyC + "</td>");
			result += "</tr>";
		} else {
			if (totals.rowNumber.length > 0) {
				if (typeof rowNumber == "number") totals.rowNumber += ", ";
			}
			
			if (typeof rowNumber == "number") totals.rowNumber += rowNumber;
			if (typeof currencyA == "number") totals.currencyA += currencyA;
			if (typeof currencyB == "number") totals.currencyB += currencyB;
			if (typeof currencyC == "number") totals.currencyC += currencyC;
		}
	}
}

if (totals.rowNumber.length > 0) {
	result += "<tr>";
	result += ("<td style=\"width: 25%;\">Row no. " + totals.rowNumber + "</td>");
	result += ("<td style=\"width: 25%;\">" + totals.currencyA + "</td>");
	result += ("<td style=\"width: 25%;\">" + totals.currencyB + "</td>");
	result += ("<td style=\"width: 25%;\">" + totals.currencyC + "</td>");
	result += "</tr>";
}

results.html(result);

In this example I have assumed that the record fields “rowNumber”, “currencyA”, “currencyB” and “currencyC” are integers (Data Type).

The maximum number of rows is determined by the condition used by the if-statement if (i < 3).

Yes, the top position will be static, but the number of rows will be dependent on the data.

In my real data there are 8 columns.

  • Description - String
  • Start Date - Date
  • End Date - Date
  • Hours - Float
  • Rate - Currency
  • Multiple - Float
  • Current - Currency
  • YTD - Currency

For the extra lines added together, the Description will be set to a static value and the 2 dates will be left blank. For the currency and float values, how do I format them correctly. Currency with 2 digits after the decimal and with float up to 2 digits. I’m playing around with toPrecision, but with mixed results.

This is where I’m at.

I added this HTML to a new section.

<table id="example" class="table-xxxxxx-grid" cellpadding="0" cellspacing="0" style=" width: 100%;">
    <thead>
        <tr>
            <td style="width: 32.98%;">Description</td>
            <td style="text-align: center; width: 8.63%;">Start Date</td>
            <td style="text-align: center; width: 8.68%;">End Date</td>
            <td style="text-align: right; width: 8.11%;">Hours</td>
            <td style="text-align: right; width: 8.51%;">Rate</td>
            <td style="text-align: right; width: 9.66%;">Multiple</td>
            <td style="text-align: right; width: 9.98%;">Current</td>
            <td style="text-align: right; width: 13.45%;">Year to Date</td>
        </tr>
    </thead>
    <tbody>
        <tr>
            <td></td>
            <td></td>
            <td></td>
            <td></td>
            <td></td>
            <td></td>
            <td></td>
            <td></td>
        </tr>
    </tbody>
</table>

Added a standard script
image

var result = "";
var totals = {
        description: "",
        startdate: "",
        enddate: "",
        rate: 0,
        hours: 0,
        multiple: 0,
        current: 0,
        ytd: 0
};

if ("earnings" in record.tables) {
        for (var i = 0; i < record.tables["earnings"].length; i++) {
               var description = record.tables["earnings"][i].fields["BASE_BALANCE_NAME"];
               var startdate = record.tables["earnings"][i].fields["ORIGINATING_START_DATE"];
               var enddate = record.tables["earnings"][i].fields["ORIGINATING_END_DATE"];
               var rate = record.tables["earnings"][i].fields["EARNINGS_RATE"];
               var hours = record.tables["earnings"][i].fields["EARNINGS_HOURS"];
               var multiple = record.tables["earnings"][i].fields["EARNINGS_MULTIPLE"];
               var current = record.tables["earnings"][i].fields["EARNINGS_AMOUNT"];
               var ytd = record.tables["earnings"][i].fields["EARNINGS_HOURS_YEAR"];

               if (i < 6) {
                       result += "<tr>";
                       result += ("<td style=\"text-align: left; width: 32.98%;\">" + description + "</td>");
                       result += ("<td style=\"text-align: left; width: 8.63%;\">" + startdate + "</td>");
                       result += ("<td style=\"text-align: left; width: 8.68%;\">" + enddate + "</td>");
                       result += ("<td style=\"text-align: right; width: 8.11%;\">" + rate + "</td>");
                       result += ("<td style=\"text-align: right; width: 8.51%;\">" + hours.toPrecision(2) + "</td>");
                       result += ("<td style=\"text-align: right; width: 9.66%;\">" + multiple + "</td>");
                       result += ("<td style=\"text-align: right; width: 9.98%;\">" + current + "</td>");
                       result += ("<td style=\"text-align: right; width: 13.45%;\">" + ytd + "</td>");
                       result += "</tr>";
               } else {
                       if (totals.description.length > 0) {
                               if (typeof description == "number") totals.description += ", ";
                       }
                       
                       if (typeof description == "number") totals.description += description;
                       if (typeof startdate == "number") totals.startdate += startdate;
                       if (typeof enddate == "number") totals.enddate += enddate;
                       if (typeof rate == "number") totals.rate += rate;
                       if (typeof hours == "number") totals.hours += hours;
                       if (typeof multiple == "number") totals.multiple += multiple;
                       if (typeof current == "number") totals.current += current;
                       if (typeof ytd == "number") totals.ytd += ytd;
               }
        }
}

if (totals.description.length == 0) {
        result += "<tr>";
        result += ("<td style=\"text-align: left; width: 32.98%;\">" + totals.description + "</td>");
        result += ("<td style=\"text-align: left; width: 8.63%;\">" + totals.startdate+ "</td>");
        result += ("<td style=\"text-align: left; width: 8.68%;\">" + totals.enddate+ "</td>");
        result += ("<td style=\"text-align: right; width: 8.11%;\">" + totals.rate + "</td>");
        result += ("<td style=\"text-align: right; width: 8.51%;\">" + totals.hours.toPrecision(2) + "</td>");
        result += ("<td style=\"text-align: right; width: 9.66%;\">" + totals.multiple + "</td>");
        result += ("<td style=\"text-align: right; width: 9.98%;\">" + totals.current + "</td>");
        result += ("<td style=\"text-align: right; width: 13.45%;\">" + totals.ytd + "</td>");
        result += "</tr>";
}

results.html(result);

Also would this be correct if (totals.description.length == 0) { instead of if (totals.description.length > 0) { that I changed to get it to display the extra line?

I believe I’ve figured out the formatting piece. I had inadvertently mixed up rate and hours data versus the column headings so things were not being applied as expected. I’m using formatter.currencyNoSymbol(rate) to handle the formatting.

var result = "";
var totals = {
        description: "",
        startdate: "",
        enddate: "",
        hours: 0,
        rate: 0,
        multiple: 0,
        current: 0,
        ytd: 0
};

if ("earnings" in record.tables) {
        for (var i = 0; i < record.tables["earnings"].length; i++) {
               var description = record.tables["earnings"][i].fields["BASE_BALANCE_NAME"];
               var startdate = record.tables["earnings"][i].fields["ORIGINATING_START_DATE"];
               var enddate = record.tables["earnings"][i].fields["ORIGINATING_END_DATE"];
               var hours = record.tables["earnings"][i].fields["EARNINGS_HOURS"];
               var rate = record.tables["earnings"][i].fields["EARNINGS_RATE"];
               var multiple = record.tables["earnings"][i].fields["EARNINGS_MULTIPLE"];
               var current = record.tables["earnings"][i].fields["EARNINGS_AMOUNT"];
               var ytd = record.tables["earnings"][i].fields["EARNINGS_HOURS_YEAR"];

               if (i < 6) {
                       result += "<tr>";
                       result += ("<td style=\"text-align: left; width: 32.98%;\">" + description + "</td>");
                       result += ("<td style=\"text-align: left; width: 8.63%;\">" + startdate + "</td>");
                       result += ("<td style=\"text-align: left; width: 8.68%;\">" + enddate + "</td>");
                       result += ("<td style=\"text-align: right; width: 8.11%;\">" + hours + "</td>");
                       result += ("<td style=\"text-align: right; width: 8.51%;\">" + formatter.currencyNoSymbol(rate) + "</td>");              
                       result += ("<td style=\"text-align: right; width: 9.66%;\">" + multiple + "</td>");
                       result += ("<td style=\"text-align: right; width: 9.98%;\">" + formatter.currencyNoSymbol(current) + "</td>");
                       result += ("<td style=\"text-align: right; width: 13.45%;\">" + ytd + "</td>");
                       result += "</tr>";
               } else {
                       if (totals.description.length > 0) {
                               if (typeof description == "number") totals.description += ", ";
                       }
                       
                       if (typeof description == "number") totals.description += description;
                       if (typeof startdate == "number") totals.startdate += startdate;
                       if (typeof enddate == "number") totals.enddate += enddate;
                       if (typeof hours == "number") totals.hours += hours;
                       if (typeof rate == "number") totals.rate += rate;
                       if (typeof multiple == "number") totals.multiple += multiple;
                       if (typeof current == "number") totals.current += current;
                       if (typeof ytd == "number") totals.ytd += ytd;
               }
        }
}

if (totals.description.length == 0) {
        result += "<tr>";
        result += ("<td style=\"text-align: left; width: 32.98%;\">" + totals.description + "</td>");
        result += ("<td style=\"text-align: left; width: 8.63%;\">" + totals.startdate + "</td>");
        result += ("<td style=\"text-align: left; width: 8.68%;\">" + totals.enddate + "</td>");
        result += ("<td style=\"text-align: right; width: 8.11%;\">" + totals.hours + "</td>");
        result += ("<td style=\"text-align: right; width: 8.51%;\">" + formatter.currencyNoSymbol(totals.rate) + "</td>");      
        result += ("<td style=\"text-align: right; width: 9.66%;\">" + totals.multiple + "</td>");
        result += ("<td style=\"text-align: right; width: 9.98%;\">" + formatter.currencyNoSymbol(totals.current) + "</td>");
        result += ("<td style=\"text-align: right; width: 13.45%;\">" + totals.ytd + "</td>");
        result += "</tr>";
}

results.html(result);

Now to not display the extra line when there less lines than the limit.

This is where I’ve landed. Do you see anything troubling with it?

I need get some clarification from the business owners of what they want regarding the addition on the rate and multiple. To me simple addition does not make sense on these two columns. The hours, current and ytd columns make sense, but the others should be left blank or have a static value in my opinion.

var result = "", maxRows = 14;
var totals = {
        description: "",
        startdate: "",
        enddate: "",
        hours: 0,
        rate: 0,
        multiple: 0,
        current: 0,
        ytd: 0
};

if ("earnings" in record.tables) {
        for (var i = 0; i < record.tables["earnings"].length; i++) {
               var description = record.tables["earnings"][i].fields["BASE_BALANCE_NAME"];
               var startdate = record.tables["earnings"][i].fields["ORIGINATING_START_DATE"];
               var enddate = record.tables["earnings"][i].fields["ORIGINATING_END_DATE"];
               var hours = record.tables["earnings"][i].fields["EARNINGS_HOURS"];
               var rate = record.tables["earnings"][i].fields["EARNINGS_RATE"];
               var multiple = record.tables["earnings"][i].fields["EARNINGS_MULTIPLE"];
               var current = record.tables["earnings"][i].fields["EARNINGS_AMOUNT"];
               var ytd = record.tables["earnings"][i].fields["EARNINGS_HOURS_YEAR"];

               if (i < maxRows) {
                       result += "<tr>";
                       result += ("<td style=\"text-align: left; width: 32.98%;\">" + description + "</td>");
                       result += ("<td style=\"text-align: left; width: 8.63%;\">" + startdate + "</td>");
                       result += ("<td style=\"text-align: left; width: 8.68%;\">" + enddate + "</td>");
                       result += ("<td style=\"text-align: right; width: 8.11%;\">" + hours + "</td>");
                       result += ("<td style=\"text-align: right; width: 8.51%;\">" + formatter.currencyNoSymbol(rate) + "</td>");              
                       result += ("<td style=\"text-align: right; width: 9.66%;\">" + multiple + "</td>");
                       result += ("<td style=\"text-align: right; width: 9.98%;\">" + formatter.currencyNoSymbol(current) + "</td>");
                       result += ("<td style=\"text-align: right; width: 13.45%;\">" + ytd + "</td>");
                       result += "</tr>";
               } else {
                       if (totals.description.length > 0) {
                               if (typeof description == "number") totals.description += ", ";
                       }
                       
                       if (typeof description == "number") totals.description += description;
                       if (typeof startdate == "number") totals.startdate += startdate;
                       if (typeof enddate == "number") totals.enddate += enddate;
                       if (typeof hours == "number") totals.hours += hours;
                       if (typeof rate == "number") totals.rate += rate;
                       if (typeof multiple == "number") totals.multiple += multiple;
                       if (typeof current == "number") totals.current += current;
                       if (typeof ytd == "number") totals.ytd += ytd;
               }
        }
}
if (record.tables["earnings"].length > maxRows){
//if (totals.description.length == 0) {
        result += "<tr>";
        result += ("<td style=\"text-align: left; width: 32.98%;\">" + "Other Earnings - Combined for reporting" + "</td>");
        result += ("<td style=\"text-align: left; width: 8.63%;\">" + "" + "</td>");
        result += ("<td style=\"text-align: left; width: 8.68%;\">" + "" + "</td>");
        result += ("<td style=\"text-align: right; width: 8.11%;\">" + totals.hours + "</td>");
        result += ("<td style=\"text-align: right; width: 8.51%;\">" + formatter.currencyNoSymbol(totals.rate) + "</td>");      
        result += ("<td style=\"text-align: right; width: 9.66%;\">" + totals.multiple + "</td>");
        result += ("<td style=\"text-align: right; width: 9.98%;\">" + formatter.currencyNoSymbol(totals.current) + "</td>");
        result += ("<td style=\"text-align: right; width: 13.45%;\">" + totals.ytd + "</td>");
        result += "</tr>";
}

results.html(result);

Hi @UomoDelGhiaccio,

The last shared JavaScript code looks fine to me.

The only question you have to think about is if the height of the “combined” table row might change. The reason why I am asking this is because I assume that the height of the specific table row will increase when the content of the TD-element “description” doesn’t fit on one line.

The length of the description poses the largest risk. I have not seen the other columns approach line wrapping. Perhaps truncating the Description something like this when I extract the values in the datamapper?

string.substring(0, length);

@Marten,

I just discovered that they told me the wrong location to collect the Current and YTD values. The data is an XML file and is located in a different node structure than the rest of the columns. Structured like this example.

<NODE_EARNINGS>
    <RECORD_TYPE>A</RECORD_TYPE>
    <CURRENT>123.45</CURRENT>
    <YTD>678.90</YTD>
</NODE_EARNINGS>
<NODE_EARNINGS>
    <RECORD_TYPE>B</RECORD_TYPE>
    <CURRENT>12.34</CURRENT>
    <YTD>567.89</YTD>
</NODE_EARNINGS>
<NODE_HOURS>
    <RECORD_BASE>B</RECORD_BASE>
    <START>10-01-2022</START>
    <END>10-14-2022</END>
    <RATE>31.02</RATE>
    <MULTIPLE>1</MULTIPLE>
</NODE_HOURS>
<NODE_HOURS>
    <RECORD_BASE>A</RECORD_BASE>
    <START>10-01-2022</START>
    <END>10-14-2022</END>
    <RATE>15.53</RATE>
    <MULTIPLE>1</MULTIPLE>
</NODE_HOURS>

In the data mapper it’s easy enough to add a repeat and create another detail table. There will be a string value in <RECORD_TYPE> that matches in both two detail tables to identify the two records.

Given this new twist, what would be the best approach? Do I try to do this in the datamapper or in the dynamic table script?

The other thing to consider is that these XML files are really huge with lots of records and lots of nodes per record.

Hi @UomoDelGhiaccio,

You would like to combine the child nodes of the NODE_EARNINGS node with the child nodes of the NODE_HOURS node when the node RECORD_TYPE does contain the same value as the node RECORD_BASE, is that correct? In case this is correct then I assume that the best option will be to solve this on the Data Mapping Configuration side.

Yes, so from this data sample.

<NODE_EARNINGS> 
    <RECORD_TYPE>A</RECORD_TYPE> 
    <CURRENT>123.45</CURRENT> 
    <YTD>678.90</YTD> 
</NODE_EARNINGS> 
<NODE_EARNINGS> 
    <RECORD_TYPE>B</RECORD_TYPE> 
    <CURRENT>12.34</CURRENT> 
    <YTD>567.89</YTD> 
</NODE_EARNINGS> 
<NODE_HOURS> 
    <RECORD_BASE>B</RECORD_BASE> 
    <START>10-01-2022</START> 
    <END>10-14-2022</END> 
    <RATE>31.02</RATE> 
    <MULTIPLE>1</MULTIPLE> 
</NODE_HOURS> 
<NODE_HOURS> 
    <RECORD_BASE>A</RECORD_BASE> 
    <START>10-01-2022</START> 
    <END>10-14-2022</END> 
    <RATE>15.53</RATE> 
    <MULTIPLE>1</MULTIPLE> 
</NODE_HOURS>

The resulting data model would be something like this.

DETAIL_TABLE[0]
    RECORD_TYPE: B
    CURRENT: 12.34
    YTD: 567.89</YTD> 
    START: 10-01-2022
    END: 10-14-2022
    RATE: 31.02
    MULTIPLE: 1
DETAIL_TABLE[1]
    RECORD_TYPE: A 
    CURRENT: 123.45 
    YTD: 678.90 
    START: 10-01-2022 
    END: 10-14-2022  
    RATE: 15.53 
    MULTIPLE: 1

I’m extracting both into separate detail tables (i.e, Expenses and Expenses_Amts), then using an action I loop though the Expenses detail table and find the matching values to insert into two javascript fields I added to the Expenses detail table.

image

var activeEarnings;
if(record.tables.earnings) {
    for(var i = 0; i <record.tables.earnings.length; i++){
        activeEarnings = record.tables.earnings[i].fields.REPORTING_NAME;
        if(record.tables.earnings_amts) {
            for(var j = 0; j <record.tables.earnings_amts.length; j++){
                if(record.tables.earnings_amts[j].fields.BASE_BALANCE_NAME == activeEarnings ){
                record.tables.earnings[i].fields.CURRENT_AMT = record.tables.earnings_amts[j].fields.PAYMENT;
                record.tables.earnings[i].fields.YTD_AMT = record.tables.earnings_amts[j].fields.YEAR;
                }
            }
        }
    }
}