Adding a New Lender
This guide walks through adding support for a new lender's payment/NACH file.
Step 1: Create YAML Mapping
Create config/lender_mappings/{lender_key}.yml:
yaml
lender_key: newlender
version: 1
columns:
"Loan Number": # CSV column header (exact, case-insensitive)
target: loan_id
type: text
"Amount":
target: amount
type: numeric
"Payment Date":
target: date
type: date
"Status":
target: status
type: text
"UTR":
target: payment_id
type: text
"Remarks":
target: note
type: text
date_formats:
date: "%d-%m-%Y" # Match the actual CSV date format
filters:
- column: amount
op: ">"
value: 0
- column: status
op: "in"
value: ["success", "cleared", "paid"]
defaults:
lender: newlenderStep 2: Register S3 Config
Add to tasks/flows/s3_processing.py in S3_LENDER_CONFIGS:
python
"newlender": {
"flow_name": "S3 NewLender Processing Flow",
"s3_prefix": "newlender/",
"s3_filename": "NEWLENDER_REPORT.csv",
"table_name": "RAW_NEWLENDER_REPORT",
},Step 3: Add dbt Source
Add to dbt/models/raw/sources.yml:
yaml
- name: RAW_NEWLENDER_REPORTStep 4: Create BASE Model
Create dbt/models/base/BASE_NEWLENDER_REPORT.sql:
sql
{{ config(materialized='incremental') }}
select
source_file,
lender,
trim(loan_id::text) as loan_id,
amount,
date as transaction_date,
trim(status::text) as status,
charge,
trim(note::text) as note,
trim(payment_id::text) as payment_id,
trim(sub_reference_id::text) as sub_reference_id
from {{ source('raw', 'RAW_NEWLENDER_REPORT') }}
{% if is_incremental() %}
where source_file not in (select distinct source_file from {{ this }})
{% endif %}Step 5: Add CTE to CORE_TRANSACTION_DATA
Add a new CTE in dbt/models/core/CORE_TRANSACTION_DATA.sql:
sql
, newlender as (
select
null::varchar(36) as uuid,
md5(
'newlender' || '|'
|| coalesce(trim(loan_id), '') || '|'
|| coalesce(to_char(transaction_date, 'YYYY-MM-DD'), '') || '|'
|| coalesce(to_char(round(amount::numeric, 2), 'FM999999999999990.00'), '')
) as source_key,
'newlender' as source_lender,
'nach_csv' as source_type,
loan_id as loan_external_id,
amount as transaction_amount,
transaction_date,
status as raw_status,
true as is_success,
null::text as payment_gateway,
null::text as external_id,
'INR' as currency,
source_file,
payment_id as utr_payment_id,
note,
null::text as recon_source -- set to 'newlender_nach' if recon needed
from {{ ref('BASE_NEWLENDER_REPORT') }}
)Add to the combined CTE:
sql
union all
select * from newlenderStep 6: Create Loan Product in Fineract
If loans from this lender will be created in Fineract, add a loan product and update lender_loan_products.py:
python
LENDER_LOAN_PRODUCT_MAP = {
...
"NEWLENDER": 10, # Fineract product ID
}Step 7: Test
- Upload a sample CSV via the Ops UI
- Check the File Processing Log for success
- Run
dbt run --select BASE_NEWLENDER_REPORT CORE_TRANSACTION_DATA - Verify records appear in CORE_TRANSACTION_DATA
Checklist
- [ ] YAML mapping file created
- [ ] S3 config registered
- [ ] dbt source added
- [ ] BASE model created
- [ ] CORE_TRANSACTION_DATA CTE added
- [ ] Loan product created (if applicable)
- [ ] Test file uploaded and verified