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
- Fineract must be running and reachable
- Database must be accessible (PostgreSQL)
config.pymust have valid Fineract and database credentials- Create the tracking table (one-time):
./venv/bin/python scripts/init_portfolio_migration_table.pyCSV 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:
# 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-001Step 2: Process Repayments
Place the filled repayments CSV at the project root as migration_repayments.csv, or specify a path:
# 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.csvWARNING
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:
./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):
# 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/YYYYDate Ordering Rule
Dates must be in chronological order:
activationDate ≤ submittedOnDate ≤ approvedOnDate ≤ actualDisbursementDateThe script validates this and exits with an error if violated.
What Happens Per Row
Loans Mode
Each row triggers 4 sequential Fineract API calls:
CLIENT_CREATE- create the clientLOAN_CREATE- create the loan under that clientLOAN_APPROVE- approve the loanLOAN_DISBURSE- disburse the loan
Repayments Mode
Each row triggers 1 Fineract API call:
REPAYMENT- post repayment to the loan
Supported Lenders
| lenderConfig | Lender |
|---|---|
LIQUILOANS | Liquiloans |
UGRO | Ugro Capital |
DMI | DMI Finance |
ARTHMATE | Arthmate |
SHIVALIK | Shivalik Small Finance Bank |
VIVRITI | Vivriti Capital |
GB | Gromor / Growth Bharat |
IKF | IKF Finance |
New products added in Fineract are picked up automatically at runtime.