From 45d78951827c9820f9b805b198b6a82f1a8a9147 Mon Sep 17 00:00:00 2001 From: root Date: Sun, 19 Apr 2026 12:32:30 -0400 Subject: [PATCH] add waiver column to event table --- app/Config/Routes.php | 1 + app/Controllers/View/EventController.php | 95 +++++++++++++++++-- ...9-000001_AddWaiverSignedToEventCharges.php | 31 ++++++ app/Models/EventChargesModel.php | 1 + .../administrator/events/event_charges.php | 66 ++++++++++++- 5 files changed, 185 insertions(+), 9 deletions(-) create mode 100644 app/Database/Migrations/2026-04-19-000001_AddWaiverSignedToEventCharges.php diff --git a/app/Config/Routes.php b/app/Config/Routes.php index 2120aaa..c359189 100644 --- a/app/Config/Routes.php +++ b/app/Config/Routes.php @@ -537,6 +537,7 @@ $routes->post('payment/event_charges', 'View\EventController::eventUpdate'); $routes->get('administrator/event-charges', 'View\EventController::eventShow'); $routes->post('administrator/event-charges/remove/(:num)', 'View\EventController::removeCharge/$1'); $routes->post('administrator/event-charges/payment/(:num)', 'View\EventController::toggleEventPayment/$1'); +$routes->post('administrator/event-charges/waiver/(:num)', 'View\EventController::toggleWaiverStatus/$1'); $routes->get('administrator/get-students-with-charges', 'View\EventController::getStudentsWithCharges'); $routes->get('parent/events', 'View\ParentController::parentEventPage', ['filter' => 'auth:parent']); diff --git a/app/Controllers/View/EventController.php b/app/Controllers/View/EventController.php index 9bb09a1..5e3caf1 100644 --- a/app/Controllers/View/EventController.php +++ b/app/Controllers/View/EventController.php @@ -41,6 +41,7 @@ class EventController extends ResourceController protected $categories; protected $enrollmentModel; private ?bool $eventChargesHasCreatedBy = null; + private ?bool $eventChargesHasWaiverSigned = null; public function __construct() { @@ -85,6 +86,41 @@ class EventController extends ResourceController return $this->eventChargesHasCreatedBy; } + private function eventChargesSupportsWaiverSigned(): bool + { + if ($this->eventChargesHasWaiverSigned !== null) { + return $this->eventChargesHasWaiverSigned; + } + + try { + $db = Database::connect(); + $this->eventChargesHasWaiverSigned = $db->fieldExists('waiver_signed', 'event_charges'); + } catch (\Throwable $e) { + $this->eventChargesHasWaiverSigned = false; + } + + return $this->eventChargesHasWaiverSigned; + } + + private function getEventChargesReturnTo(): string + { + $fallback = site_url('administrator/event-charges'); + $returnTo = trim((string) ($this->request->getPost('return_to') ?? '')); + if ($returnTo === '') { + return $fallback; + } + + $base = rtrim((string) base_url(), '/'); + if (str_starts_with($returnTo, '/')) { + return $returnTo; + } + if ($base !== '' && str_starts_with($returnTo, $base)) { + return $returnTo; + } + + return $fallback; + } + public function index() { $eventModel = new EventModel(); @@ -570,6 +606,7 @@ class EventController extends ResourceController $parentsForInvoice = []; $supportsCreatedBy = $this->eventChargesSupportsCreatedBy(); + $supportsWaiverSigned = $this->eventChargesSupportsWaiverSigned(); foreach ($participations as $studentId => $value) { $existing = $this->eventChargesModel->where([ @@ -596,6 +633,9 @@ class EventController extends ResourceController 'updated_by' => $userId, 'class_section_id'=> $classSectionId, ]; + if ($supportsWaiverSigned) { + $updateData['waiver_signed'] = (int) ($existing['waiver_signed'] ?? 0); + } if (isset($event['amount']) && ((float)$event['amount'] !== (float)($existing['charged'] ?? 0))) { $updateData['charged'] = $event['amount']; } @@ -614,6 +654,9 @@ class EventController extends ResourceController 'semester' => $semester, 'updated_by' => $userId ]; + if ($supportsWaiverSigned) { + $insertData['waiver_signed'] = 0; + } if ($supportsCreatedBy) { $insertData['created_by'] = $userId; } @@ -630,6 +673,7 @@ class EventController extends ResourceController $parentPhone = trim($pieces['parent_phone'] ?? ''); $parentEmail = trim((string)($pieces['parent_email'] ?? '')); $markPaid = (string)($pieces['paid'] ?? '0') === '1'; + $waiverSigned = (string)($pieces['waiver_signed'] ?? '0') === '1'; if (!$firstname && !$lastname) { continue; @@ -664,7 +708,7 @@ class EventController extends ResourceController $existingExternal = $this->eventChargesModel->where($matchConditions)->first(); if ($existingExternal) { - $this->eventChargesModel->update($existingExternal['id'], [ + $updateData = [ 'participation' => 'yes', 'charged' => $event['amount'], 'updated_by' => $userId, @@ -674,7 +718,11 @@ class EventController extends ResourceController 'external_parent_lastname' => $parentLast !== '' ? $parentLast : ($existingExternal['external_parent_lastname'] ?? null), 'external_parent_phone' => $parentPhone !== '' ? $parentPhone : ($existingExternal['external_parent_phone'] ?? null), 'external_parent_email' => $parentEmail !== '' ? $parentEmail : ($existingExternal['external_parent_email'] ?? null), - ]); + ]; + if ($supportsWaiverSigned) { + $updateData['waiver_signed'] = $waiverSigned ? 1 : 0; + } + $this->eventChargesModel->update($existingExternal['id'], $updateData); continue; } @@ -696,6 +744,9 @@ class EventController extends ResourceController 'semester' => $semester, 'updated_by' => $userId, ]; + if ($supportsWaiverSigned) { + $insertData['waiver_signed'] = $waiverSigned ? 1 : 0; + } if ($supportsCreatedBy) { $insertData['created_by'] = $userId; } @@ -725,13 +776,14 @@ class EventController extends ResourceController public function removeCharge($chargeId = null) { + $returnTo = $this->getEventChargesReturnTo(); if (!$chargeId) { - return redirect()->back()->with('error', 'Invalid charge.'); + return redirect()->to($returnTo)->with('error', 'Invalid charge.'); } $charge = $this->eventChargesModel->find($chargeId); if (!$charge) { - return redirect()->back()->with('error', 'Charge not found.'); + return redirect()->to($returnTo)->with('error', 'Charge not found.'); } $paymentId = (int)($charge['event_payment_id'] ?? 0); @@ -744,19 +796,20 @@ class EventController extends ResourceController $this->invoiceController->generateInvoice((string)$charge['parent_id'], (string)($charge['school_year'] ?? $this->schoolYear), (string)($charge['semester'] ?? $this->semester), false); } - return redirect()->back()->with('success', 'Participation removed, invoices updated.'); + return redirect()->to($returnTo)->with('success', 'Participation removed, invoices updated.'); } public function toggleEventPayment($chargeId = null) { + $returnTo = $this->getEventChargesReturnTo(); if (!$chargeId) { - return redirect()->back()->with('error', 'Invalid charge.'); + return redirect()->to($returnTo)->with('error', 'Invalid charge.'); } $isPaid = $this->request->getPost('paid') === '1'; $meta = $this->applyEventPaymentStatus((int)$chargeId, $isPaid); if (!$meta) { - return redirect()->back()->with('error', 'Charge not found.'); + return redirect()->to($returnTo)->with('error', 'Charge not found.'); } if (!empty($meta['invoice_id'])) { @@ -764,7 +817,33 @@ class EventController extends ResourceController $this->fixInvoiceStatusAfterCharge((int)$meta['parent_id'], (string)$meta['school_year']); } - return redirect()->back()->with('success', 'Event payment status updated.'); + return redirect()->to($returnTo)->with('success', 'Event payment status updated.'); + } + + public function toggleWaiverStatus($chargeId = null) + { + $returnTo = $this->getEventChargesReturnTo(); + if (!$chargeId) { + return redirect()->to($returnTo)->with('error', 'Invalid charge.'); + } + + if (!$this->eventChargesSupportsWaiverSigned()) { + return redirect()->to($returnTo)->with('error', 'Waiver tracking is not available until the database migration is applied.'); + } + + $charge = $this->eventChargesModel->find($chargeId); + if (!$charge) { + return redirect()->to($returnTo)->with('error', 'Charge not found.'); + } + + $signed = $this->request->getPost('waiver_signed') === '1'; + + $this->eventChargesModel->update((int) $chargeId, [ + 'waiver_signed' => $signed ? 1 : 0, + 'updated_by' => session()->get('user_id'), + ]); + + return redirect()->to($returnTo)->with('success', $signed ? 'Waiver marked as signed.' : 'Waiver marked as unsigned.'); } private function parentHasEnrollment(int $parentId, string $schoolYear): bool diff --git a/app/Database/Migrations/2026-04-19-000001_AddWaiverSignedToEventCharges.php b/app/Database/Migrations/2026-04-19-000001_AddWaiverSignedToEventCharges.php new file mode 100644 index 0000000..26e5ac4 --- /dev/null +++ b/app/Database/Migrations/2026-04-19-000001_AddWaiverSignedToEventCharges.php @@ -0,0 +1,31 @@ + [ + 'type' => 'TINYINT', + 'constraint' => 1, + 'default' => 0, + 'after' => 'charged', + ], + ]; + + if (! $this->db->fieldExists('waiver_signed', 'event_charges')) { + $this->forge->addColumn('event_charges', $fields); + } + } + + public function down() + { + if ($this->db->fieldExists('waiver_signed', 'event_charges')) { + $this->forge->dropColumn('event_charges', 'waiver_signed'); + } + } +} diff --git a/app/Models/EventChargesModel.php b/app/Models/EventChargesModel.php index 426d641..e0a61fc 100644 --- a/app/Models/EventChargesModel.php +++ b/app/Models/EventChargesModel.php @@ -15,6 +15,7 @@ class EventChargesModel extends Model 'student_id', 'participation', 'charged', + 'waiver_signed', 'event_paid', 'event_payment_id', 'class_section_id', diff --git a/app/Views/administrator/events/event_charges.php b/app/Views/administrator/events/event_charges.php index a9f9c17..e1a762b 100644 --- a/app/Views/administrator/events/event_charges.php +++ b/app/Views/administrator/events/event_charges.php @@ -183,6 +183,7 @@ Class Section Charged Amount Created + Waiver Fees Paid Payment Actions @@ -193,6 +194,22 @@ @@ -237,8 +254,22 @@ $feeAmount = (float) ($charge['event_amount'] ?? 0); $hasBalanceRecord = array_key_exists((int)$charge['parent_id'], $parentBalances); $parentBalance = $hasBalanceRecord ? (float)$parentBalances[$charge['parent_id']] : null; + $waiverSigned = !empty($charge['waiver_signed']); $feeIsPaid = $isParticipating && ($isEventPaid || ($hasBalanceRecord && $parentBalance <= 0)); ?> + +
+ + + + + onchange="this.form.waiver_signed.value = this.checked ? 1 : 0; this.form.submit();"> + +
+ Paid @@ -249,6 +280,7 @@
+ @@ -263,6 +295,7 @@ +
@@ -280,7 +313,7 @@ Totals $ - + participating @@ -444,6 +477,7 @@ function loadStudentsWithCharges() { function buildExternalRow(entry) { const key = entry.key; const isPaid = !!entry.paid; + const waiverSigned = !!entry.waiverSigned; const firstEsc = escapeHtml(entry.firstname); const lastEsc = escapeHtml(entry.lastname); const noteEsc = escapeHtml(entry.note); @@ -472,6 +506,7 @@ function loadStudentsWithCharges() { + `); @@ -497,6 +532,12 @@ function loadStudentsWithCharges() { External $${amountDisplay} ${createdDisplay} + +
+ + ${waiverSigned ? 'Signed' : 'Unsigned'} +
+ ${isPaid ? 'Paid' : 'Unpaid'} @@ -526,6 +567,7 @@ function loadStudentsWithCharges() { function saveExternalDraft(entry) { entry.key = entry.key ?? `${Date.now()}_${Math.random().toString(36).slice(2)}`; entry.paid = !!entry.paid; + entry.waiverSigned = !!entry.waiverSigned; removeExternalEntryFromDom(entry.key); externalDrafts = externalDrafts.filter((existing) => existing.key !== entry.key); externalDrafts.push(entry); @@ -646,6 +688,7 @@ function loadStudentsWithCharges() { eventId: (draftEntry && draftEntry.eventId) ? draftEntry.eventId : eventId, eventAmount: (draftEntry && draftEntry.eventAmount) ? draftEntry.eventAmount : eventAmount, parentDisplayName: (draftEntry && draftEntry.parentDisplayName) ? draftEntry.parentDisplayName : parentName, + waiverSigned: (draftEntry && typeof draftEntry.waiverSigned !== 'undefined') ? !!draftEntry.waiverSigned : false, paid: (draftEntry && typeof draftEntry.paid !== 'undefined') ? !!draftEntry.paid : false, }; if (editingKey) { @@ -695,6 +738,27 @@ function loadStudentsWithCharges() { $badge.removeClass('bg-success').addClass('bg-danger').text('Unpaid'); } }); + $(document).on('change', '.external-waiver-toggle', function() { + const key = $(this).data('key'); + const signed = !!this.checked; + if (!key) { + return; + } + const entry = getDraftByKey(key); + if (entry) { + entry.waiverSigned = signed; + persistDrafts(); + } + $externalHidden + .find(`div[data-key="${key}"] input[name="external_participants[${key}][waiver_signed]"]`) + .val(signed ? '1' : '0'); + const $label = $(`tr.external-preview[data-key="${key}"] .external-waiver-label`); + if (signed) { + $label.text('Signed'); + } else { + $label.text('Unsigned'); + } + }); $('#event-participant-form').on('submit', function() { localStorage.removeItem(storageKey); });