# Loan Journal and Payoff Dates ## Purpose The Loan Journal is the deduction history for an employee loan. It lets an authorized user review every posted repayment or manual adjustment, identify the current balance, and see when a fixed-term loan was paid off. ## Data model No database migration is required. The existing fields on `loans` are used: | Table | Field | Meaning | | --- | --- | --- | | `loans` | `balance` | Remaining balance for a fixed-term loan. | | `loans` | `status` | Lifecycle status. A paid fixed-term loan is `closed`. | | `loans` | `date_end` | Effective payoff/end date. This is the date shown to users. | | `loans` | `closed_at` | System audit timestamp for when the record was closed. | | `loans` | `is_open_ended` | Marks an ongoing deduction with no fixed payoff total. | | `loan_journal_entry` | `entry_type` | `credit` is a repayment; `debit` is a loan release or adjustment. | | `loan_journal_entry` | `entry_date` | Effective date of the repayment or adjustment. | | `loan_journal_entry` | `origin` / `entry_status` | Identifies manual, payroll, and reversal/audit-locked records. | Relationship: one `loans` record has many `loan_journal_entry` records through `loan_journal_entry.loan_id`. ## Lifecycle rules 1. A posted fixed-term `credit` reduces the loan balance. 2. When that balance reaches zero, the loan is set to `closed` and `date_end` is saved from the journal entry's `entry_date`. 3. `closed_at` remains the system closure timestamp; it is not used as the primary displayed payoff date unless `date_end` is unavailable on older data. 4. A `debit`, edited repayment, or deleted repayment that restores a positive balance reopens a previously closed loan and clears `date_end` and `closed_at`. 5. Ongoing/open-ended deductions do not get a payoff date from this process. 6. Cancelled loans are not automatically reopened or closed by journal lifecycle reconciliation. ## API behavior | Method | Endpoint | Behavior | | --- | --- | --- | | `POST` | `loan_journal_entry_api/create_journal_entry` | Creates a manual journal entry, updates the loan balance, and records/clears payoff lifecycle data as needed. | | `POST` | `loan_journal_entry_api/update_journal_entry` | Reverses the original entry effect, applies the new effect, then reconciles the old and target loan lifecycle. Payroll-origin entries remain protected. | | `POST` | `loan_journal_entry_api/delete_journal_entry` | Deletes an allowed manual entry, reverses its balance effect, and reopens a closed loan if a positive balance remains. | | `GET` | `loan_journal_entry_api/read_journal_entries` | Returns journal history used by the journal modal. | All write endpoints use company and brand context and retain the existing payroll/reversal audit protections. ## Frontend behavior ### Loan management cards - Fixed-term paid/closed cards display an **Ended** date using `date_end`, with `closed_at` as a fallback for existing records. - Ongoing deductions continue to display their ongoing status rather than an end date. ### Journal modal - The modal leads with deduction history rather than the manual-entry form. - Each loan section shows original amount, collected repayments, current balance, and either the payoff date or last deduction date. - Transaction rows show the type, amount, date, origin, remarks, and audit-lock/delete action. - Manual adjustments are available through **+ Manual entry** and are kept separate from the history view. - The Loan Management table remains mounted while the modal is open. This preserves its filters, loaded rows, and scroll position instead of rerendering the page. ## Files changed - `backend/loan_journal_entry_api/create_journal_entry.php` - `backend/loan_journal_entry_api/update_journal_entry.php` - `backend/loan_journal_entry_api/delete_journal_entry.php` - `frontend/src/components/loan/LoanJournalEntries/LoanJournalEntriesTable.jsx` - `frontend/src/components/loan/loan_components/LoanTable.jsx` - `frontend/src/components/loan/LoanPage.jsx` ## Verification checklist 1. Create a final manual repayment for a fixed-term loan; verify its balance becomes zero, status becomes `closed`, and the card/modal show the repayment date as **Ended**. 2. Edit or delete that repayment; verify the balance becomes positive, status becomes `active`, and the end date disappears. 3. Open the journal modal and close it; verify the Loan Management table retains its current view and scroll position. 4. Verify payroll and reversal entries remain audit locked.