Source code for babilonia.tools.report
# SPDX-License-Identifier: GPL-3.0-or-later
#
# Copyright (C) 2025 The Project Authors
# See pyproject.toml for authors/maintainers.
# See LICENSE for license details.
"""
Generate cash flow reports from daily canonical cashflow files.
This script scans a target directory for daily cash flow CSV files
(previously generated from standardized bank statements), groups them
by year, and produces yearly cash flow reports using the ``CashFlow``
analysis engine.
For each year found, the script:
- Loads the daily cash flow file.
- Retrieves the initial balance for the year from a balances table.
- Computes a yearly cash flow report (summary and panel views).
- Displays formatted tables in the terminal.
Processing can be restricted to a single year or applied to all available
years. Terminal output is structured for quick inspection and logging.
Script Examples
---------------
The script is intended for command-line execution.
.. dropdown:: Minimal PowerShell example (Windows)
:icon: code-square
:open:
Save as ``run_report.ps1`` and execute from PowerShell.
.. code-block:: powershell
# ! Warning -- change paths and parameters
# Paths
$REPO = "C:\\path\\to\\repo"
$SCRIPT = "$REPO\\babilonia\\tools\\report.py"
$DATA = "C:\\data\\bank_statements"
# Parameters
$TYPE = "bb-cc"
$YEAR = 2024
# Run script
python $SCRIPT `
--folder $DATA `
--type $TYPE `
--year $YEAR
.. dropdown:: Minimal shell example (Linux)
:icon: code-square
:open:
Save as ``run_report.sh`` and execute from a terminal.
.. code-block:: bash
#!/usr/bin/env bash
# ! Warning -- change paths and parameters
# Paths
REPO="/path/to/repo"
SCRIPT="$REPO/babilonia/tools/report.py"
DATA="/data/bank"
# Parameters
TYPE="bb-cc"
YEAR=2024
# Run script
python "$SCRIPT" --folder "$DATA" --type "$TYPE" --year "$YEAR"
Expected Folder Structure
-------------------------
The input data is expected to follow a simple hierarchical layout:
::
bb/ # Bank
└── cc/ # Bank account
├── SALDOS_BB_CC.csv # Initial balances per year
├── 2023/
│ └── CAIXA_BB_CC_2023_DIARIO.csv
└── 2024/
└── CAIXA_BB_CC_2024_DIARIO.csv
Each ``CAIXA_*_DIARIO.csv`` file represents a daily canonical cashflow
dataset for a given year.
The balances file (``SALDOS_*.csv``) must contain, at minimum, the
columns:
- ``Year`` : reference year
- ``Value`` : initial balance at the start of the year
Data Levels
-----------
- **Daily**: Transaction-level canonical cashflow.
- **Report**: Derived yearly report with summary and panel tables.
The script does not modify input files and does not write new CSV files;
it only produces terminal reports.
"""
# IMPORTS
# ***********************************************************************
# import modules from other libs
# Native imports
# =======================================================================
import glob
import argparse
from pathlib import Path
# ... {develop}
# External imports
# =======================================================================
import pandas as pd
# ... {develop}
# Project-level imports
# =======================================================================
from babilonia.tools.core import *
from babilonia.accounting import CashFlow
# ... {develop}
# FUNCTIONS
# ***********************************************************************
[docs]
def main():
char_w = 120
args = get_arguments()
data_folder = Path(args.folder)
data_type = args.type.lower()
year_arg = args.year
bank = get_bank(data_type)
account = get_account(data_type)
file_initial = data_folder / f"SALDOS_{bank.upper()}_{account.upper()}.csv"
df_initial = pd.read_csv(file_initial, sep=";")
print("\n\n")
print("=" * char_w)
print(" Cashflow Report\n".upper())
print(f" Folder : {data_folder}")
print(f" Bank : {BANK_NAMES[data_type]}")
print(f" Account : {ACCOUNT_NAMES[data_type]}")
print(f" Year : {year_arg if year_arg is not None else 'ALL'}")
print("=" * char_w)
# Resolve file pattern (year wildcard handled inside helper)
pattern_files = get_file_pattern_cashflow_daily(data_type, data_folder, year_arg)
ls_files = glob.glob(pattern_files)
if not ls_files:
print(" No input files found. Nothing to process.")
print("=" * char_w)
return None
# Group files by year (assumes year is the parent directory name)
# ------------------------------------------------------------------
files_by_year = {}
for f in ls_files:
fpath = Path(f)
try:
year = fpath.parent.name
except IndexError:
continue
files_by_year.setdefault(year, []).append(fpath)
cf = PARSERS[data_type]()
total_processed = 0
for year in sorted(files_by_year):
print()
# print("-" * char_w)
print(f" Year {year}")
print("-" * char_w)
file_csv = files_by_year[year][0]
cf = CashFlow()
cf.load_data(file_csv)
print(f"[INFO] Processing year {year}")
# Retrieve initial balance for the year
initial_cash = df_initial.query(f"Year == {year}")["Value"].values[0]
# Generate cash flow report
dc_cfa = cf.get_cashflow_report(
df=cf.data,
year=year,
initial_cash=initial_cash,
)
# ------------------------------------------------------------------
# Terminal output
# ------------------------------------------------------------------
print("\n[Summary]")
df = dc_cfa["Summary"]
cols_to_format = df.columns[2:4]
print(CashFlow.format_currency_columns(df, cols_to_format))
print("\n[Panel]")
df = dc_cfa["Pannel"]
cols_to_format = df.columns[2:]
print(CashFlow.format_currency_columns(df, cols_to_format))
print()
print("=" * char_w)
print(f" Completed. Output files written: {total_processed}")
print("=" * char_w)
print("\n\n")
return None
# SCRIPT
# ***********************************************************************
# standalone behaviour as a script
if __name__ == "__main__":
main()