Skip to content

18.6 Daily batch — order of operations

The daily batch is the LMS’s heartbeat. Mis-ordered or missed batch operations cause material accounting / compliance problems. This page sets the standard order.

00:00 Day rolls over
01:00 Tier-1 batch jobs start
├── Daily interest accrual (target: complete by 02:00)
├── Daily classification (target: complete by 02:30)
├── Daily provisioning (target: complete by 03:00)
03:00 Reconciliation prep
├── Reconciliation with prior-day sponsor-bank statement (target: 04:00)
06:00 Business-day ops batch starts
├── Co-lending settlement (per period; daily / weekly)
├── Borrower statement generation (on schedule)
├── Bureau monthly submission file (on monthly schedule)
Throughout day: real-time ops continue
├── Repayment receipts processed (real-time)
├── Charges applied (real-time)
├── Drawdowns processed (real-time)
├── Webhook handlers active (NACH ack incoming)
15:00 – 17:00 NACH file generation cut-off
├── Generate next-day's NACH presentation file
├── Submit to sponsor bank by 18:00 cut-off
18:00 – 19:00 End-of-business-day administrative batches
├── Daily MIS to partner lenders
├── EWS engine refresh
22:00 – 23:00 Pre-rollover housekeeping
├── Audit log integrity verification
├── Backup verification
23:30 Pre-rollover snapshot for next day

Inputs: All loans in ACTIVE or any SMA state (not NPA, not closed).

Operation:

for each loan:
if loan.classification in {NPA_SUBSTANDARD, NPA_DOUBTFUL_*, NPA_LOSS}:
continue # accrual frozen
outstanding = loan.current_outstanding (after all yesterday's events)
daily_interest = outstanding × loan.rate / 365
emit event: accrual(loan_id, date=yesterday, amount=daily_interest)
loan.accrued_interest += daily_interest (projection update)
post GL: Dr Accrued Interest Receivable, Cr Interest Income

Idempotency: Each accrual event has key accrual:{loan_id}:{date}. Re-running the job doesn’t double-post.

Performance: For ~10,000 active loans, completes in < 5 minutes with parallel batch.

Audit: Per-loan accrual event in audit chain.

Operation:

for each loan in {ACTIVE, SMA_*, NPA_*}:
days_overdue = compute_DPD(loan)
new_class = classify(days_overdue, current_class, time_in_NPA)
if new_class != loan.current_classification:
emit event: classification_change(loan_id, old_class, new_class)
loan.current_classification = new_class (projection)
post classification_snapshot(loan_id, date=yesterday, class=new_class, dpd=days_overdue)

DPD computation:

For each schedule_line in loan.schedule:
if schedule_line.total_due > schedule_line.total_paid:
overdue_amount += (schedule_line.total_due - schedule_line.total_paid)
days_overdue = max(days_overdue, days_from(schedule_line.due_date, today))

Classification logic:

if days_overdue == 0:
return STANDARD
elif days_overdue <= 30:
return SMA_0
elif days_overdue <= 60:
return SMA_1
elif days_overdue <= 90:
return SMA_2
elif days_overdue > 90:
if time_since_NPA <= 12 months:
return NPA_SUBSTANDARD
elif time_since_NPA <= 24 months:
return NPA_DOUBTFUL_D1
elif time_since_NPA <= 48 months:
return NPA_DOUBTFUL_D2
else:
return NPA_DOUBTFUL_D3
# Loss assignment per separate workflow

Upgrade gate (per Nov 2021 IRACP):

if loan.current_classification in NPA states:
if days_overdue == 0 AND total_arrears_cleared == True:
emit upgrade event (requires manual approval per board policy)
new_class = STANDARD

Upgrade is not automatic on DPD = 0 from partial payment; requires explicit clearance of all arrears.

Idempotency: snapshot per (loan_id, date) is unique; re-run is no-op.

Performance: ~20 minutes for ~10,000 loans with parallel batch.

Operation:

for each loan:
class = loan.current_classification
outstanding = loan.current_outstanding
provision_amount = compute_provision(class, outstanding, secured)
emit event: provision(loan_id, date=yesterday, class, amount=provision_amount)
post GL: Dr Provision Expense, Cr Provision for NPA (Liability)

Provisioning rates (illustrative; verify per SBR-MD):

ClassProvisioning
Standard0.25 – 0.40%
SMA-0/1/2Standard-level (no separate prov)
Sub-standard10% of outstanding
Doubtful D1100% of unsecured + 20% of secured
Doubtful D2100% of unsecured + 30% of secured
Doubtful D3100% of unsecured + 50% of secured
Loss100%

Daily snapshot: per (loan_id, date).

Operation:

  • Pull yesterday’s sponsor-bank statement (SFTP / API).
  • For each statement entry:
    • Match against expected LMS entries (disbursements, NACH credits, payouts).
    • On match: confirm.
    • On mismatch: route to exception queue.

Output: reconciled entries + exception list.

Operation:

  • Identify dues for tomorrow.
  • Generate NACH file per format (5.14).
  • Submit to sponsor bank by cut-off.

Order matters: must run after morning ops have processed any pre-day repayments that change tomorrow’s dues.

Job 6: Co-lending settlement (daily / per period)

Section titled “Job 6: Co-lending settlement (daily / per period)”

Operation:

  • Sum yesterday’s repayments by partner.
  • Compute partner share.
  • Generate settlement instruction.
  • Submit to sponsor bank for settlement.

Operation:

  • For each active borrower, refresh EWS signals (drawing on latest data).
  • Raise alerts on threshold breaches.
  • Route to risk-team queue.

Job 8: Borrower statement generation (scheduled)

Section titled “Job 8: Borrower statement generation (scheduled)”
  • Generate monthly statements for borrowers per cycle.
  • Email / WhatsApp / borrower-portal access.

Job 9: Bureau submission (monthly, on schedule)

Section titled “Job 9: Bureau submission (monthly, on schedule)”
  • On the 15th of each month (or per policy), generate file for all 4 CICs.
  • Submit; track acks.
  • Recompute hash chain.
  • Alert on any mismatch.
  • Run at low-volume time (e.g., 22:00).

Tier-1 jobs run before any other writes for the day to ensure:

  • Accrual is based on settled outstanding (no mid-day repayments confuse).
  • Classification reflects yesterday’s full picture.
  • Provisioning is based on yesterday’s classification.

NACH file generation must run late enough to include any same-day repayments that change tomorrow’s dues, but early enough to meet sponsor-bank cut-off.

If accrual / classification / provisioning fails for some loans:

  • Don’t proceed to dependent jobs without re-run.
  • Alert immediately.
  • Manual investigation; fix; re-run.

If NACH file generation fails:

  • Bypass for that day → borrowers’ dues continue but presentation delayed by a day.
  • Notify ops.
  • Fix and submit next day.

If reconciliation fails:

  • Continue with exception queue.
  • Don’t block other operations.

For 100,000 active loans:

  • Daily accrual: < 30 minutes with parallel batch.
  • Daily classification: < 45 minutes.
  • Daily provisioning: < 30 minutes.
  • NACH file generation: < 15 minutes for ~5,000 daily debits.

Beyond 1 million loans, batch jobs are partitioned by loan_id range and parallelised across workers.

By 06:00 of the next day, the platform’s dashboards show:

  • Yesterday’s classification distribution.
  • Yesterday’s roll-rate.
  • Yesterday’s NPA additions.
  • Yesterday’s repayments.
  • Yesterday’s reconciliation exceptions.

Ops, risk, finance all consume this in the morning.

  • All Tier-1 jobs must complete daily — no skip.
  • Idempotency so re-runs are safe.
  • Audit trail for each batch run (start time, end time, records processed, exceptions).
  • Monitoring of batch SLAs.
  • Alerting on failures.
  • Documented runbooks for batch failure scenarios.