Bank statement
Generate a simple bank statement with Carbone
- Template type document docx expert
- Carbone min. v5.0.0+
- Features loop set count aggregator subtotal
- Target bank insurance quote custom
Objectives
You want to generate a bank statement with the following features:
- Display an intermediate balance on each page.
- The number of lines that fit on each page is fixed.
Solution
Here’s how you can achieve this using Carbone:
Structuring data by page
Based on your layout settings, estimate:
- the number of block-lines that fit on a page (e.g.,
15
)
- the number of block-lines that fit on a page (e.g.,
Compute the objects to be printed on a same page using
{d.operations[]:cumCount():div(15):ceil:set(.numPage)}
where:- the cumulative count of entries
cumCount
is divideddiv
by the number of block-lines (15
), and rounded upceil
to the nearest whole number - the result is stored in a new variable using
:set(.numPage)
.
At this stage, each object has a new property named
numPage
, which indicates the page number on which it will be printed.- the cumulative count of entries
Restructure the datas into a new array
d.group
by grouping objects that share the samenumPage
, using the following formula:{d.operations[]:set(d.group[id=.numPage].data[])}
Use this new structure in the template to print the data:
{d.group[i].data[i].transaction}
.
JSON Before
{
"operations": [
{
"transaction": "School fees payment",
"amount": -300
},
{...},
{
"transaction": "Supermarket shopping",
"amount": -130.2
},
{...}
]
}
JSON After
{
"operations": [
...
],
"group":[
{
"data": [
{
"transaction": "School fees payment",
"amount": -300,
"numPage": 1
},
{...}
]
},
{
"data": [
{
"transaction": "Supermarket shopping",
"amount": -130.2,
"numPage": 2
},
{...}
]
}
]
}
Subtotal computation
To print the subtotal per page, use the aggSum
formatter.
In our example, since there is a column for debits and a column for credits, the subtotals are computed as follows:
For debits (negative amounts), set positive amounts to
0
using:ifGTE(0):show(0)
beforeaggSum
:{d.group[i].data[].amount:ifGTE(0):show(0):aggSum:formatC}
For credits (positive amounts), set negative amounts to
0
using:ifLT(0):show(0)
beforeaggSum
:{d.group[i].data[].amount:ifLT(0):show(0):aggSum:formatC}
Intermediate balance computation
To print the intermediate balance, combine the aggSum
formatter with the cumSum
formatter, and add it to the open_bal stored in the JSON:
{d.group[i].data[].amount:aggSum:cumSum:add(...open_bal):formatC}`
Extra tip / Pagination
To ensure consistent rendering, insert a page break between each page. Define it after the different elements to be displayed on the same page and before the iteration [i+1]
.
As a result, the report ends with a final blank page. To avoid this, delete the page break conditionally if the last element of the list is printed. Use the following formula on the paragraph of the page break: {d.group[i]..group:len():sub(1):ifEQ(.i):drop(p)}
, where
- {d.list[i] : Access the list element using the iterator [i], the index i will be used for the condition.
- ..list:len() : Retrieve the length of the list by backtracking to the parent object.
- :sub(1): Subtract one from the list length, accounting for zero-based indexing. (The list length is counted from one, however the index from [i] is starting from zero.)
- :ifEQ(.i):drop(p)}: Delete the paragraph, including the page break, if the current index .i is equal to the list size, indicating the last element.
And there you go!