add refund logic and fix books inventory logic
Deploy to Shared Hosting / Shared hosting deploy (push) Failing after 47s
Tests / PHPUnit (push) Failing after 1m20s

This commit is contained in:
root
2026-08-22 13:44:25 -04:00
parent 23d1cbb64c
commit d906a915d6
45 changed files with 4711 additions and 442 deletions
+16
View File
@@ -0,0 +1,16 @@
<?= $this->extend('layout/management_layout') ?>
<?= $this->section('content') ?>
<?php $c = $calculation ?? []; $money = static fn ($v): string => '$' . number_format(((int) $v) / 100, 2); ?>
<div class="container py-4">
<div class="d-flex justify-content-between align-items-center mb-3"><div><h2 class="mb-1">Withdrawal Calculation #<?= (int) ($c['id'] ?? 0) ?></h2><div class="text-muted">Version <?= (int) ($c['version'] ?? 0) ?> · <?= esc($c['status'] ?? '') ?> · <?= esc($c['student_firstname'] ?? '') ?> <?= esc($c['student_lastname'] ?? '') ?></div></div><a class="btn btn-outline-secondary" href="<?= site_url('administrator/withdrawals/' . (int) ($c['enrollment_id'] ?? 0) . '/review') ?>">Review</a></div>
<?php if (session()->getFlashdata('success')): ?><div class="alert alert-success"><?= esc(session()->getFlashdata('success')) ?></div><?php endif; ?>
<?php if (!empty($c['blockers'])): ?><div class="alert alert-danger"><ul class="mb-0"><?php foreach ($c['blockers'] as $b): ?><li><?= esc($b) ?></li><?php endforeach; ?></ul></div><?php endif; ?>
<div class="card mb-3"><div class="card-body"><div class="row g-3">
<?php foreach ([
'Annual allocation'=>'annual_fee_allocation_cents','Issued books'=>'issued_book_charge_cents','Instruction component'=>'annual_instruction_cents','Earned tuition'=>'earned_tuition_cents','Retained charge'=>'retained_charge_cents','Original invoice charge'=>'original_invoice_charge_cents','Invoice adjustment'=>'invoice_adjustment_cents','Adjusted invoice charge'=>'adjusted_invoice_charge_cents','Valid payments'=>'valid_payment_cents','Completed payouts'=>'completed_payout_cents','Open reservations'=>'open_reservation_cents','Refundable credit'=>'refundable_credit_cents','New refund request'=>'new_refund_request_cents','Balance due'=>'balance_due_cents'
] as $label=>$field): ?><div class="col-md-3"><div class="text-muted small"><?= esc($label) ?></div><div class="fw-semibold"><?= $money($c[$field] ?? 0) ?></div></div><?php endforeach; ?>
</div></div></div>
<div class="card mb-3"><div class="card-header fw-semibold">Policy snapshot</div><div class="card-body"><dl class="row mb-0"><dt class="col-sm-4">Dates</dt><dd class="col-sm-8"><?= esc($c['school_year_start_date'] ?? '') ?> / <?= esc($c['enrollment_date'] ?? '') ?> / <?= esc($c['withdrawal_request_date'] ?? '') ?></dd><dt class="col-sm-4">Studied</dt><dd class="col-sm-8"><?= (int) ($c['studied_calendar_days'] ?? 0) ?> calendar day(s), rounded to <?= (int) ($c['studied_weeks'] ?? 0) ?> week(s), of <?= (int) ($c['total_instructional_weeks'] ?? 0) ?></dd><dt class="col-sm-4">Policy</dt><dd class="col-sm-8"><?= esc($c['policy_version'] ?? '') ?>; annual tuition includes books; no no-school subtraction; no book returns</dd><dt class="col-sm-4">Override reason</dt><dd class="col-sm-8"><?= esc($c['override_reason'] ?? '—') ?></dd></dl></div></div>
<div class="card"><div class="card-header fw-semibold">Book price snapshots</div><div class="table-responsive"><table class="table table-sm mb-0"><thead><tr><th>Issue</th><th>Book</th><th>Date</th><th>Status</th><th>Price</th><th>Total</th></tr></thead><tbody><?php foreach ((array) ($c['books'] ?? []) as $book): ?><tr><td>#<?= (int) ($book['id'] ?? 0) ?></td><td><?= esc($book['book_name'] ?? '') ?></td><td><?= esc($book['issued_at'] ?? '') ?></td><td><?= esc($book['status'] ?? '') ?></td><td><?= $money($book['unit_charge_price_cents'] ?? 0) ?></td><td><?= $money($book['total_charge_cents'] ?? 0) ?></td></tr><?php endforeach; ?></tbody></table></div></div>
</div>
<?= $this->endSection() ?>
+11
View File
@@ -0,0 +1,11 @@
<?= $this->extend('layout/management_layout') ?>
<?= $this->section('content') ?>
<?php $money = static fn ($v): string => '$' . number_format(((int) $v) / 100, 2); ?>
<div class="container py-4">
<div class="d-flex justify-content-between align-items-center mb-3"><h2>Invoice #<?= (int) $invoiceId ?> Withdrawal Breakdown</h2><a class="btn btn-outline-secondary" href="<?= site_url('refunds/list') ?>">Back to refunds</a></div>
<?php foreach ($calculations as $c): ?>
<div class="card mb-3"><div class="card-header d-flex justify-content-between"><strong><?= esc(trim(($c['student_firstname'] ?? '') . ' ' . ($c['student_lastname'] ?? ''))) ?></strong><span class="badge bg-<?= ($c['status'] ?? '') === 'posted' ? 'success' : 'danger' ?>"><?= esc($c['status'] ?? '') ?></span></div><div class="card-body"><div class="row g-2"><div class="col-md-3">Studied: <strong><?= (int) ($c['studied_weeks'] ?? 0) ?> week(s)</strong></div><div class="col-md-3">Books: <strong><?= $money($c['issued_book_charge_cents'] ?? 0) ?></strong></div><div class="col-md-3">Earned tuition: <strong><?= $money($c['earned_tuition_cents'] ?? 0) ?></strong></div><div class="col-md-3">Retained: <strong><?= $money($c['retained_charge_cents'] ?? 0) ?></strong></div></div><div class="mt-2"><a href="<?= site_url('administrator/withdrawal-calculations/' . (int) $c['id']) ?>">Calculation #<?= (int) $c['id'] ?> version <?= (int) $c['version'] ?></a></div></div></div>
<?php endforeach; ?>
<?php if (empty($calculations)): ?><div class="alert alert-warning">No posted withdrawal calculations were found for this invoice.</div><?php endif; ?>
</div>
<?= $this->endSection() ?>
+82
View File
@@ -0,0 +1,82 @@
<?= $this->extend('layout/management_layout') ?>
<?= $this->section('content') ?>
<?php
$c = $calculation ?? [];
$money = static fn ($cents): string => '$' . number_format(((int) $cents) / 100, 2);
$blockers = (array) ($c['blockers'] ?? []);
$discountProjection = (array) (($c['explanation']['discount_projection'] ?? []));
?>
<div class="container py-4">
<div class="d-flex justify-content-between align-items-center mb-3">
<div>
<h2 class="mb-1">Withdrawal Refund Review</h2>
<div class="text-muted">
<?= esc(trim((string) ($c['student_firstname'] ?? '') . ' ' . (string) ($c['student_lastname'] ?? ''))) ?>
· <?= esc($c['school_year'] ?? '') ?> · calculation v<?= (int) ($c['version'] ?? 0) ?>
</div>
</div>
<a class="btn btn-outline-secondary" href="<?= site_url('refunds/list') ?>">Back</a>
</div>
<?php if (session()->getFlashdata('success')): ?><div class="alert alert-success"><?= esc(session()->getFlashdata('success')) ?></div><?php endif; ?>
<?php if (session()->getFlashdata('error')): ?><div class="alert alert-danger"><?= esc(session()->getFlashdata('error')) ?></div><?php endif; ?>
<?php if ($blockers !== []): ?>
<div class="alert alert-danger">
<strong>Cannot post this refund yet.</strong>
<ul class="mb-0 mt-2"><?php foreach ($blockers as $blocker): ?><li><?= esc($blocker) ?></li><?php endforeach; ?></ul>
</div>
<?php endif; ?>
<div class="row g-3 mb-4">
<div class="col-md-3"><div class="card h-100"><div class="card-body"><div class="text-muted small">Studied period</div><div class="fs-5 fw-semibold"><?= (int) ($c['studied_calendar_days'] ?? 0) ?> day(s) / <?= (int) ($c['studied_weeks'] ?? 0) ?> week(s)</div><div class="small">of <?= (int) ($c['total_instructional_weeks'] ?? 0) ?> instructional weeks</div></div></div></div>
<div class="col-md-3"><div class="card h-100"><div class="card-body"><div class="text-muted small">Annual allocation</div><div class="fs-5 fw-semibold"><?= $money($c['annual_fee_allocation_cents'] ?? 0) ?></div><div class="small">Includes books</div></div></div></div>
<div class="col-md-3"><div class="card h-100"><div class="card-body"><div class="text-muted small">Retained charge</div><div class="fs-5 fw-semibold"><?= $money($c['retained_charge_cents'] ?? 0) ?></div><div class="small">Books + earned tuition</div></div></div></div>
<div class="col-md-3"><div class="card h-100"><div class="card-body"><div class="text-muted small"><?= ((int) ($c['new_refund_request_cents'] ?? 0)) > 0 ? 'Expected refund' : 'Expected balance' ?></div><div class="fs-5 fw-semibold"><?= $money(((int) ($c['new_refund_request_cents'] ?? 0)) > 0 ? ($c['new_refund_request_cents'] ?? 0) : ($c['balance_due_cents'] ?? 0)) ?></div><div class="small">Payments: <?= $money($c['valid_payment_cents'] ?? 0) ?></div></div></div></div>
</div>
<div class="card mb-4">
<div class="card-header fw-semibold">Books Kept by Student</div>
<div class="table-responsive"><table class="table table-sm mb-0 no-mgmt-sticky no-dt-fixedheader" data-no-mgmt-sticky data-no-dt-fixedheader><thead><tr><th>Book</th><th>Issued</th><th>Status</th><th>Qty</th><th>Snapshot price</th><th>Total</th><th>Correction</th></tr></thead><tbody>
<?php foreach ((array) ($c['books'] ?? []) as $book): ?>
<tr><td><?= esc($book['book_name'] ?? ('Issue #' . ($book['id'] ?? ''))) ?></td><td><?= esc($book['issued_at'] ?? '') ?></td><td><?= esc($book['status'] ?? '') ?></td><td><?= (int) ($book['quantity'] ?? 0) ?></td><td><?= $money($book['unit_charge_price_cents'] ?? 0) ?></td><td><?= $money($book['total_charge_cents'] ?? 0) ?></td><td><?= esc($book['reversal_reason'] ?? '—') ?></td></tr>
<?php endforeach; ?>
<?php if (empty($c['books'])): ?><tr><td colspan="7" class="text-muted">No book issues were recorded by the withdrawal date.</td></tr><?php endif; ?>
</tbody></table></div>
</div>
<div class="card mb-4">
<div class="card-header fw-semibold">Refund Calculation</div>
<div class="card-body row g-2">
<div class="col-md-4">Issued books: <strong><?= $money($c['issued_book_charge_cents'] ?? 0) ?></strong></div>
<div class="col-md-4">Instruction component: <strong><?= $money($c['annual_instruction_cents'] ?? 0) ?></strong></div>
<div class="col-md-4">Earned tuition: <strong><?= $money($c['earned_tuition_cents'] ?? 0) ?></strong></div>
<div class="col-md-4">Invoice adjustment: <strong><?= $money($c['invoice_adjustment_cents'] ?? 0) ?></strong></div>
<div class="col-md-4">Adjusted invoice charge: <strong><?= $money($c['adjusted_invoice_charge_cents'] ?? 0) ?></strong></div>
<div class="col-md-4">Open reservations: <strong><?= $money($c['open_reservation_cents'] ?? 0) ?></strong></div>
<div class="col-md-4">Completed prior payouts: <strong><?= $money($c['completed_payout_cents'] ?? 0) ?></strong></div>
<div class="col-md-4">Projected applied discount: <strong><?= $money($discountProjection['adjusted_discount_cents'] ?? 0) ?></strong></div>
<div class="col-md-4">Refundable invoice credit: <strong><?= $money($c['refundable_credit_cents'] ?? 0) ?></strong></div>
</div>
</div>
<form method="post" action="<?= site_url('administrator/withdrawals/' . (int) $c['enrollment_id'] . '/recalculate') ?>" class="card mb-3">
<?= csrf_field() ?>
<div class="card-header fw-semibold">Review Inputs</div>
<div class="card-body row g-3">
<div class="col-md-3"><label class="form-label">Enrollment date</label><input class="form-control" type="date" name="enrollment_date" value="<?= esc($c['enrollment_date'] ?? '') ?>" required></div>
<div class="col-md-3"><label class="form-label">Withdrawal request date</label><input class="form-control" type="date" name="withdrawal_request_date" value="<?= esc($c['withdrawal_request_date'] ?? '') ?>" required></div>
<div class="col-md-3"><label class="form-label">Legacy invoice</label><div class="form-check mt-2"><input class="form-check-input" type="checkbox" value="1" name="legacy_invoice_confirmed" id="legacyConfirmed"><label class="form-check-label" for="legacyConfirmed">I reconciled the aggregate invoice</label></div></div>
<div class="col-md-12"><label class="form-label">Required audit reason when changing dates or confirming legacy data</label><textarea class="form-control" name="override_reason" rows="2"><?= esc($c['override_reason'] ?? '') ?></textarea></div>
</div>
<div class="card-footer"><button class="btn btn-outline-primary" type="submit">Update Preview</button></div>
</form>
<div class="d-flex gap-2">
<a class="btn btn-outline-secondary" href="<?= site_url('administrator/withdrawal-calculations/' . (int) $c['id']) ?>">Detailed Breakdown</a>
<form method="post" action="<?= site_url('administrator/withdrawal-calculations/' . (int) $c['id'] . '/confirm') ?>" onsubmit="return confirm('Post these invoice adjustments and finalize the withdrawal calculation?');">
<?= csrf_field() ?>
<button class="btn btn-success" type="submit" <?= $blockers !== [] || ($c['status'] ?? '') === 'posted' ? 'disabled' : '' ?>>Confirm and Post</button>
</form>
</div>
</div>
<?= $this->endSection() ?>