Architecture¶
Overview¶
indian-bank-statement-parser/
├── bank_parser/
│ ├── __init__.py # Public API exports
│ ├── cli.py # Typer CLI entry point
│ ├── core.py # parse_file(), parse_statements()
│ ├── models.py # Pydantic models (Transaction, Statement, GSTR2AEntry)
│ ├── gstr2a.py # GST reconciliation logic
│ └── banks/
│ ├── __init__.py # BANK_PARSERS registry
│ ├── base.py # BaseBankParser (abstract)
│ ├── hdfc.py # HDFC Bank parser
│ ├── icici.py # ICICI Bank parser
│ ├── sbi.py # SBI parser
│ └── axis.py # Axis Bank parser
├── tests/
│ ├── test_models.py
│ └── test_banks.py
└── docs/
Core Components¶
1. Models (models.py)¶
Pydantic v2 models with validation:
- Transaction — Single transaction with
date,description,debit,credit,balance,ref_no,category - Statement — Container with account info + list of transactions
- GSTR2AEntry — GST reconciliation output model
Key features:
- Decimal for monetary precision
- Custom validators for amounts, dates, categories
- to_dict(), to_dataframe(), to_csv(), to_json() methods
2. Base Parser (banks/base.py)¶
Abstract BaseBankParser with shared functionality:
class BaseBankParser(ABC):
bank_code: str = ""
bank_name: str = ""
def __init__(self, pdf_path: str):
self.pdf_path = pdf_path
self.statement = Statement(bank=self.bank_code)
@abstractmethod
def parse(self) -> Statement:
pass
# Shared helpers:
def extract_text() -> list[str] # pdfplumber + pymupdf fallback
def parse_date(str) -> datetime # 12+ formats
def parse_amount(str) -> Decimal # Handles commas, spaces
def clean_description(str) -> str # Normalize whitespace
def detect_statement_period(str) -> tuple # Regex patterns
3. Bank Parsers (banks/*.py)¶
Each bank extends BaseBankParser:
class HDFCParser(BaseBankParser):
bank_code = "hdfc"
bank_name = "HDFC Bank"
def parse(self) -> Statement:
# 1. Extract text from PDF
# 2. Detect account info
# 3. Parse transactions line by line
# 4. Return Statement
Common pattern:
1. extract_text() → list of page texts
2. Join pages → full text for account detection
3. _parse_transactions(pages_text) → iterate lines
4. _parse_transaction_line(line) → regex matching
5. Return Transaction objects
4. Core API (core.py)¶
High-level functions:
list_banks()→list[str]parse_file(pdf_path, bank, ...)→ DataFrame/Statement/JSONparse_statements(input_dir, bank, ...)→ Combined DataFramegenerate_gstr2a()/generate_gstr2a_from_transactions()→ GSTR-2A DataFrame
5. CLI (cli.py)¶
Typer-based CLI with:
- Single command parse with all options
- Rich output (tables, colors)
- Docker-compatible
Data Flow¶
PDF File
│
▼
BaseBankParser.extract_text() (pdfplumber → pymupdf fallback)
│
▼
BankParser.parse() (bank-specific logic)
│
▼
List[Transaction] (validated Pydantic models)
│
▼
Statement (container + metadata)
│
▼
core.parse_file() / parse_statements()
│
▼
Output: DataFrame / CSV / JSON / GSTR-2A DataFrame
Plugin Architecture¶
New banks added via:
- Create
bank_parser/banks/yourbank.pywithYourBankParser(BaseBankParser) - Register in
banks/__init__.py:BANK_PARSERS["yourbank"] = YourBankParser - No core changes needed
PDF Extraction¶
Dual-engine approach: 1. pdfplumber (primary) — better table/text extraction 2. pymupdf (fallback) — handles scanned/complex PDFs
def extract_text(self) -> list[str]:
try:
return self.extract_text_pdfplumber()
except Exception:
return self.extract_text_pymupdf()
Type Safety¶
- Full type annotations
mypystrict mode passes- Pydantic v2 for runtime validation
TYPE_CHECKINGfor pandas imports
Extensibility Points¶
| Extension | Location |
|---|---|
| New bank | banks/yourbank.py + banks/__init__.py |
| New output format | core.py + models.py |
| New GST fields | models.py + gstr2a.py |
| New CLI flag | cli.py + core.py |
| New PDF engine | base.py |