Here's a way to do it with Javascript, this might not be the most efficient, but it worked for me in a little test I threw together.
I'm doing something similar to create a posting file that we can import into our accounting system. In my case, it's not going into a table, but instead is creating a CSV file, but the theory is kind of similar.
For this example, I have it loading the journal table from a button on the form. You could make it run the function when the itemTable changes instead of when the button is clicked, but be aware that it's doing a lot of processing each time. It won't be a problem as long as the item table doesn't get too long, but if it starts getting much longer, the delay may become noticible, and if it is dragging each time the user changes anything on the item table, that could be a problem for your users, which is the reason that I did it from a button.
You could also call the function when the form loads if you wanted to ensure the journal table is populated from a prior task/submission (just add a line like this: populateJournalTable(); before line 3 of the code below). However, if you run it upon form load, be aware that I haven't tested it on an archive/read-only version of the form, and I suspect it would have some issues.
Give your item table the class name of: itemTable
Give your journal table the class name of: journalTable
In both tables, the six fields, should have these six class names respectively: vatCode, glDescription, glCode, netAmount, vatAmount, grossAmount
For the button, use a custom HTML element with this code:
<p style="text-align: center;" id="populateJournal"><button type="button">Populate the Accounts Journal</button></p>
Then this is the Javascript:
$(document).ready(function() {
//When the button is clicked to populate the journal table, call the function.
$('#populateJournal').click(populateJournalTable);
//Function to populate the journal table.
//Any existing rows are deleted, and then rows are created as needed.
//All fields in the table are set as readonly - this is done via Javascript
//instead of via the Layout page to ensure values populated by Javascript
//are saved in the fields.
function populateJournalTable () {
//Loop through each row of the journalTable, if the fields are not undefined,
//then delete that row of the table, so we are starting with an empty table.
$('.journalTable tr').each(function() {
var vatCode = $(this).find('.vatCode select').val();
if(vatCode != undefined) {
$(this).find('.cf-table-delete').click();
}
});
//Loop through each row of the itemsTable, and gather its values.
//Then loop through each row of the journalTable to find the match,
//and add the amounts. If no match is found, add a row for the match.
$('.itemTable tr').each(function() {
var vatCode = $(this).find('.vatCode select').val();
var glDescription = $(this).find('.glDescription select').val();
var glCode = $(this).find('.glCode select').val();
var netAmount = parseFloat($(this).find('.netAmount input').val()) || 0; // the || 0 will return zero for "falsey" values (NaN from blank fields)
var vatAmount = parseFloat($(this).find('.vatAmount input').val()) || 0; // the || 0 will return zero for "falsey" values (NaN from blank fields)
var grossAmount = parseFloat($(this).find('.grossAmount input').val()) || 0; // the || 0 will return zero for "falsey" values (NaN from blank fields)
if(vatCode != undefined) {
var rowFound = false;
//Find matching values in journalTable.
$('.journalTable tr').each(function() {
var vatCode2 = $(this).find('.vatCode select').val();
var glDescription2 = $(this).find('.glDescription select').val();
var glCode2 = $(this).find('.glCode select').val();
var netAmount2 = parseFloat($(this).find('.netAmount input').val()) || 0; // the || 0 will return zero for "falsey" values (NaN from blank fields)
var vatAmount2 = parseFloat($(this).find('.vatAmount input').val()) || 0; // the || 0 will return zero for "falsey" values (NaN from blank fields)
var grossAmount2 = parseFloat($(this).find('.grossAmount input').val()) || 0; // the || 0 will return zero for "falsey" values (NaN from blank fields)
//Match was found, add amounts to journalTable.
if(vatCode == vatCode2 && glDescription == glDescription2 && glCode) {
rowFound = true;
var netAmountNew = netAmount + netAmount2;
var vatAmountNew = vatAmount + vatAmount2;
var grossAmountNew = grossAmount + grossAmount2;
$(this).find('.netAmount input').val(netAmountNew.toFixed(2));
$(this).find('.vatAmount input').val(vatAmountNew.toFixed(2));
$(this).find('.grossAmount input').val(grossAmountNew.toFixed(2));
}
});
//No match was found in journalTable. Add a new row and populate fields.
if(!rowFound) {
$('.journalTable .cf-table-add-row').click();
$('.journalTable tr:last').find('.vatCode option').filter(function () { return $(this).html() == vatCode; }).prop('selected', true);
$('.journalTable tr:last').find('.glDescription option').filter(function () { return $(this).html().replace(/&/g, "&") == glDescription; }).prop('selected', true); //this: replace(/&/g, "&") is necessary to deal with & in the value, since we're dealing with the html not a string variable.
$('.journalTable tr:last').find('.glCode option').filter(function () { return $(this).html() == glCode; }).prop('selected', true);
$('.journalTable tr:last').find('.netAmount input').val(netAmount.toFixed(2));
$('.journalTable tr:last').find('.vatAmount input').val(vatAmount.toFixed(2));
$('.journalTable tr:last').find('.grossAmount input').val(grossAmount.toFixed(2));
}
}
});
//Loop through each row of the journalTable and make the field readonly,
//hide the buttons to add rows and delete rows.
$('.journalTable tr').each(function() {
$(this).find('input').each(function() {
$(this).attr('readonly', 'true');
});
$(this).find('select').each(function() {
$(this).attr('readonly', 'true');
});
$(this).find('.cf-table-delete').each(function() {
$(this).hide();
});
});
$('.journalTable').find('.cf-table-add-row').hide();
}
});
Here's how it looks before I click the button:

And then after clicking the button:
