---
source: https://carbone.io/examples/bank-statement-simple/
title: "Bank statement | Carbone Sample"
description: "Generate professional bank statements. Merge JSON data with templates in formats like DOCX, PPTX, XLSX, and more using our simple document generator tool."
generated_at: "2026-07-13"
---

# Bank statement (simple)

Generate a simple bank statement with Carbone

[![bank-statement-simple thumbnail](/examples/bank-statement-simple/thumb-result.webp)](/examples/bank-statement-simple/result.pdf)

[Download Template](/examples/bank-statement-simple/template.docx)

[Download data sample](/examples/bank-statement-simple/data.json)

-   Template type document docx expert
-   Carbone min. v5.0.0+
-   Features loop set count aggregator subtotal
-   Target bank insurance quote

## Overview

Learn how to generate a professional bank statement with these key features:

-   Display a fixed number of transactions per page
-   Show running balance on each page

## Step-by-Step Guide

This example demonstrates how to create a bank statement template when your transaction data is stored in a JSON array. We'll show you how to organize the data by page and calculate balances using Carbone's features.

### 1\. Organizing Data by Page

First, we need to structure the data to display a specific number of transactions per page:

-   Define how many transactions can fit on one page (for example, 15 lines)
-   Calculate which page each transaction belongs to by:
    -   Using the [:cumCount](/documentation/design/computation/aggregation.md#cumcount-partitionby) formatter to count entries
    -   Dividing by the number of lines per page (15) using [:div](/documentation/design/formatters/number.md#div-value)
    -   Rounding up to the nearest whole number with [:ceil](/documentation/design/formatters/number.md#ceil)
    -   Storing the result in a new variable with [:set](/documentation/design/computation/store-and-transform.md#modify-json-set-relativepath). Example:
        
        ```text
        {d.operations[]:cumCount():div(15):ceil:set(.numPage)}
        ```
        
-   Group transactions by page number into a new array called `d.group` using [:set](/documentation/design/computation/store-and-transform.md#store-values-set-absolutepath):
    
    ```text
    {d.operations[]:set(d.group[id=.numPage].data[])}
    ```
    
-   Use the new structure to display data in your template:
    
    ```text
    {d.group[i].data[i].transaction}
    ```
    

Here's how the data structure changes:

**Original JSON:**

```json
{
  "operations": [
    { "transaction": "School fees payment",
      "amount": -300 },
    {...},
    { "transaction": "Supermarket shopping",
      "amount": -130.2 },
    {...}
  ]
}
```

**Transformed JSON:**

```json
{
  "operations": [...],
  "group":[
    { "data": [
      { "transaction": "School fees payment",
        "amount": -300,
        "numPage": 1 },
      {...}
      ]
    },
    { "data": [
      { "transaction": "Supermarket shopping",
        "amount": -130.2,
        "numPage": 2 },
      {...}
      ]
    }
  ]
}
```

### 2\. Calculating Page Subtotals

To show subtotals for debits and credits on each page, use the [:aggSum](/documentation/design/computation/aggregation.md#learn-with-aggsum-partitionby) formatter:

-   For debits (negative amounts):
    
    ```text
    {d.group[i].data[].amount:ifGTE(0):show(0):aggSum:formatC}
    ```
    
-   For credits (positive amounts):
    
    ```text
    {d.group[i].data[].amount:ifLT(0):show(0):aggSum:formatC}
    ```
    

### 3\. Calculating Running Balance

To display the running balance on each page:

-   Combine [:aggSum](/documentation/design/computation/aggregation.md#learn-with-aggsum-partitionby) and [:cumSum](/documentation/design/computation/aggregation.md#cumsum-partitionby) formatters
-   Add the opening balance from your JSON:
    
    ```text
     {d.group[i].data[].amount:aggSum:cumSum:add(...open_bal):formatC}
    ```
    

### 4\. Page Layout Tips

To ensure proper page breaks:

-   Insert a page break between pages after displaying all elements
-   Remove the final page break to avoid a blank page at the end using this condition:
    
    ```text
    {d.group[i]..group:len():sub(1):ifEQ(.i):drop(p)}
    ```
    

This formula:

-   Gets the current list element with `{d.list[i]`
-   Finds list length with `..group:len()`
-   Adjusts for zero-based indexing with `:sub(1)`
-   Removes the page break if it's the last element with `:ifEQ(.i):drop(p)}`

## More Examples

[![bank-statement-expert thumbnail example](/examples/bank-statement-expert/thumb-result.webp)](/examples/bank-statement-expert/index.md)

## Trusted by 800+ paid customers in 40+ countries

## Related topics

- [Transport ticket](/examples/ticket/index.md)
- [Summary](/examples/summary/index.md)
- [Stock inventory spreadsheet](/examples/stock-inventory-spreadsheet/index.md)
- [Store Inventory](/examples/shoes/index.md)
- [Sensor readings](/examples/sensor-readings/index.md)
- [Real estate property document](/examples/real-estate-property-description/index.md)
