# Bounce & Penal Calculation Engine The `co_lending_bounce_penal.py` module (often triggered via `/api/co_lending_bounce_penal_cron`) acts as the automated enforcer for delinquent loans. It runs on a nightly schedule (or immediately following a backdated payment) to dynamically assess how many days past due (DPD) a loan is, and applies financial penalties accordingly. ## Business Logic ### Days Past Due (DPD) Calculation DPD is calculated independently for each `rps_type`. The core formula is: `Delay Days = Current Date - Oldest Unpaid EMI Due Date` If `Delay Days > 0`, the system begins compounding penalties. ### Penal Charges & Partner Splits Because a co-lended loan involves two financial entities, penalty revenue must be split according to contractual obligations (fetched live via `fetch_product_config`). There are generally two major Split Modes for penalties: 1. **50-50 Split**: Both Partner A and Partner B simply split the generated penalty equally, regardless of their principal share ratio. 2. **Option A vs Option B**: More complex structures where Partner A might take the entirety of the penalty, or penalties are applied based strictly on the delay of the individual partner's amortizing schedule. ### The Hurdle Conundrum The **Hurdle Schedule (RPS 4)** is a shadow schedule designed to reconcile Partner A's guaranteed returns. * **Rule**: Partner A Hurdle *must* mirror Partner A's penalty accruals exactly. * If Partner A receives ₹100 in penal charges on their schedule, the Hurdle schedule must also receive ₹100 in penal charges. This ensures the backend accounting perfectly matches the cash allocation expectations. ## Technical Logic ### The Execution Flow 1. **Batch Ingestion**: The system fetches all `loan_ids` that currently have an unpaid EMI prior to today's date. 2. **Iterative Evaluation**: For each loan, it retrieves the current state of `co_lending_emi_payments`. 3. **Calculation**: It computes the true delay days. It cross-references the live `product_config` to get the penal rate (e.g., 36% annualized) and calculates the new penalty value. 4. **Differential Update**: To ensure idempotent operations (safe to run multiple times a day), it calculates `New Penalty - Old Penalty`. It only executes an `UPDATE` if there is a positive delta. 5. **Database Persistence**: Updates are pushed directly to `co_lending_emi_payments` modifying the `delay_penalty` and `bounce_charges` columns. ### Cron Integration & Race Conditions The penal engine is heavily integrated into the ecosystem: * **Nightly Cron**: It runs automatically every night at 12:01 AM to age all delinquent loans by 1 day. * **Backdated Payments**: If a payment is logged for a *past* date (e.g., paid 5 days ago but recorded today), the penal engine is immediately invoked synchronously *before* the historical snapshot engine. This immediately zeroes out the `delay_penalty` for that EMI, ensuring the historical snapshot doesn't record fake penalties for the last 5 days. ### Table References * **Reads from**: `co_lending_loans`, `co_lending_emi_payments` * **Updates**: `co_lending_emi_payments` (specifically `delay_penalty` and `bounce_charges` columns)