# Co-Lending Regular Allocation Engine ## Overview The `co_lending_regular_allocation.py` script is the core financial engine responsible for dynamically allocating incoming loan payments across the various Co-Lending Repayment Schedules (RPS). It ensures that Master LMS configurations (like priority sequences and rounding rules) are strictly followed while preventing negative pendings and mathematical desynchronization. --- ## Part 1: Business Logic ### The Independent Allocation Strategy Schedules for `partner_a` (e.g., 80%) and `partner_b` (e.g., 20%) are generated as independent amortizing loans during the RPS generation phase. Because these schedules are rounded individually row-by-row, their mathematical sums do not always perfectly equal exactly 80% or 20% of the combined `orig` schedule over the entire tenure. To prevent over-allocating funds to a specific row (which would result in negative pending amounts), this engine splits the incoming payment *upfront* and allocates the cash entirely independently across four separate tracks: 1. **Orig Allocation**: The full `amount_paid` is processed strictly against the `orig` schedule. 2. **Partner A Allocation**: `amount_paid` is multiplied by `partner_a_share` (and rounded according to config). This `partner_a_cash` is allocated independently against the `partner_a` schedule. 3. **Partner B Allocation**: The remaining cash (`amount_paid - partner_a_cash`) is allocated independently against the `partner_b` schedule. 4. **Hurdle Allocation**: The primary lender's negotiated shadow schedule (`hurdle`) is paid off using the cash allocated to Partner A (`partner_a_cash`). ### Dynamic Priority Sequences & BPI Handling Rather than hardcoding the order in which funds are distributed (e.g., Bounce -> Penal -> Interest -> Principal), the engine dynamically fetches the `priority_sequence` from the Master LMS API configured specifically for that `product_id`. The engine smartly intercepts "interest" in the sequence and dynamically injects "bpi" (Broken Period Interest) right before it, ensuring BPI is always settled as a `pre_emi` component before regular interest. ### Advance Collections & Parked Transactions For `ADVANCE_COLLECTION` events, the engine consults the `park_advance_collection` flag from Master LMS. If enabled, surplus cash (after clearing current dues) is saved as a `parked_transaction` instead of an `over_payment`. A separate cron job (`co_lending_parked_cron.py`) executes daily to intelligently un-park these funds when future installments or BPI become due, reusing the original payment log ID to maintain an unblemished audit trail. ### Over-Payment Handling If the provided cash entirely clears a schedule with funds still remaining (and `park_advance_collection` is False), the script cleanly sweeps the leftover amount into an `over_payment` component assigned to `installment_number = 0` and `co_lending_emi_payments_id = 0`. This guarantees 100% of the input cash is mathematically accounted for and inserted into the `co_lending_payment_transactions` database table. --- ## Part 2: Technical Logic ### Optimized Database Fetching - The script pulls all four RPS types (`orig`, `partner_a`, `partner_b`, `hurdle`) in a **single**, highly-performant SQL query using `ORDER BY due_date ASC, installment_number ASC`. - Historical payment logs are aggregated efficiently in a single `GROUP BY` query, summing `paid_amount` per component per row, drastically reducing database overhead. ### Safe Transaction Architecture - **No Connections Leaked**: Instead of checking out database connections manually, the script leverages SQLAlchemy's `engine.begin()`. This context manager cleanly pulls a connection from the pool, executes all `INSERT` statements in a single transaction block, and safely returns the connection to the pool. - **Aggressive Pool Disposal**: Upon script completion, `engine.dispose()` and `engine_read.dispose()` are aggressively invoked, completely destroying the connection pools and closing TCP sockets to prevent `1040 Too many connections` errors on the MySQL server during repetitive executions. ### Real-time Configurations - Re-uses `utils.apply_rounding` to emulate complex Excel-style rounding behaviors driven strictly by the `interest_rounding` objects fetched from Master LMS. - Config lookups are heavily optimized using `@lru_cache` to prevent API exhaustion during bulk processing. - Fails safely with comprehensive `try/except` blocks, logging stack traces to AWS CloudWatch via `log_exception(logger, ...)` while returning clear JSON error messages instead of crashing. ### High-Volume Asynchronous Execution - Bulk allocations (via CSV/Excel or JSON payloads) are fully decoupled from the HTTP request-response cycle using FastAPI's `BackgroundTasks`. - The system returns an immediate 200 OK acceptance response, while processing tens of thousands of loan transactions securely in the background, streaming real-time operational metrics and errors to AWS CloudWatch.