Skip to content

Portfolio Migration

Standalone script to migrate existing loan portfolios into Fineract. Reads two CSVs (loans and repayments), calls Fineract APIs directly, and tracks every operation in a portfolio_migration database table.

No API endpoints, no Prefect, no dbt - just run the script.

Prerequisites

  1. Fineract must be running and reachable
  2. Database must be accessible (PostgreSQL)
  3. config.py must have valid Fineract and database credentials
  4. Create the tracking table (one-time):
bash
./venv/bin/python scripts/init_portfolio_migration_table.py

CSV Templates

Two template files are required. Download them here:

See Operate Template docs and Repayments Template docs for field-by-field details.

Running the Migration

Step 1: Process Loans

Place the filled loans CSV at the project root as migration_loans.csv, or specify a path:

bash
# Dry run - validates CSV, inserts pending rows, no Fineract calls
./venv/bin/python scripts/run_portfolio_migration.py --mode loans --dry-run

# Run for real
./venv/bin/python scripts/run_portfolio_migration.py --mode loans

# Custom CSV path
./venv/bin/python scripts/run_portfolio_migration.py --mode loans --csv path/to/loans.csv

# Process a single loan only
./venv/bin/python scripts/run_portfolio_migration.py --mode loans --loan-id DMI-LN-001

Step 2: Process Repayments

Place the filled repayments CSV at the project root as migration_repayments.csv, or specify a path:

bash
# Dry run
./venv/bin/python scripts/run_portfolio_migration.py --mode repayments --dry-run

# Run for real
./venv/bin/python scripts/run_portfolio_migration.py --mode repayments

# Custom CSV path
./venv/bin/python scripts/run_portfolio_migration.py --mode repayments --csv path/to/repayments.csv

WARNING

Always run loans first, then repayments. Repayment rows reference loanExternalId which must already exist in Fineract.

Retry Failed Operations

If some operations fail (network issues, Fineract downtime), retry using the batch ID printed at the end of every run:

bash
./venv/bin/python scripts/run_portfolio_migration.py --retry --batch-id <uuid>

This resets failed and skipped rows to pending and reprocesses them. Already-succeeded rows are not touched.

Date Formats

Recommended format: dd MMMM yyyy (e.g., 01 March 2026)

Other supported formats:

  • YYYY-MM-DD (e.g., 2026-03-01)
  • DD-MM-YYYY (e.g., 01-03-2026)
  • DD/MM/YYYY (e.g., 15/01/2026)

Ambiguous Dates

If your CSV has slash dates where both parts are ≤ 12 (e.g., 03/04/2026):

bash
# Interpret as DD/MM/YYYY (Indian standard)
./venv/bin/python scripts/run_portfolio_migration.py --mode loans --date-format DD/MM/YYYY

# Interpret as MM/DD/YYYY (US standard)
./venv/bin/python scripts/run_portfolio_migration.py --mode loans --date-format MM/DD/YYYY

Date Ordering Rule

Dates must be in chronological order:

activationDate ≤ submittedOnDate ≤ approvedOnDate ≤ actualDisbursementDate

The script validates this and exits with an error if violated.

What Happens Per Row

Loans Mode

Each row triggers 4 sequential Fineract API calls:

  1. CLIENT_CREATE - create the client
  2. LOAN_CREATE - create the loan under that client
  3. LOAN_APPROVE - approve the loan
  4. LOAN_DISBURSE - disburse the loan

Repayments Mode

Each row triggers 1 Fineract API call:

  1. REPAYMENT - post repayment to the loan

Supported Lenders

lenderConfigLender
LIQUILOANSLiquiloans
UGROUgro Capital
DMIDMI Finance
ARTHMATEArthmate
SHIVALIKShivalik Small Finance Bank
VIVRITIVivriti Capital
GBGromor / Growth Bharat
IKFIKF Finance

New products added in Fineract are picked up automatically at runtime.

Turno Fineract LMS Documentation