diff --git a/app/Controllers/View/EventController.php b/app/Controllers/View/EventController.php index 442c46e..f61110d 100644 --- a/app/Controllers/View/EventController.php +++ b/app/Controllers/View/EventController.php @@ -93,58 +93,6 @@ class EventController extends ResourceController 'created_by' => session()->get('user_id'), ]); - if ($eventId) { - $amount = (float) $this->request->getPost('amount'); - $semester = (string) $this->request->getPost('semester'); - $schoolYear = (string) $this->request->getPost('school_year'); - $userId = (int) (session()->get('user_id') ?? 0); - - $enrollments = $this->enrollmentModel - ->select('enrollments.student_id, students.parent_id') - ->join('students', 'students.id = enrollments.student_id', 'left') - ->where('enrollments.school_year', $schoolYear) - ->whereIn('enrollments.enrollment_status', ['enrolled', 'payment pending']) - ->findAll(); - - $parentIds = []; - foreach ($enrollments as $row) { - $studentId = (int) ($row['student_id'] ?? 0); - $parentId = (int) ($row['parent_id'] ?? 0); - if ($studentId <= 0 || $parentId <= 0) { - continue; - } - - $exists = $this->eventChargesModel - ->where('event_id', $eventId) - ->where('student_id', $studentId) - ->where('school_year', $schoolYear) - ->where('semester', $semester) - ->first(); - - if ($exists) { - continue; - } - - $this->eventChargesModel->insert([ - 'event_id' => $eventId, - 'parent_id' => $parentId, - 'student_id' => $studentId, - 'participation' => 'yes', - 'charged' => $amount, - 'school_year' => $schoolYear, - 'semester' => $semester, - 'updated_by' => $userId ?: null, - ]); - - $parentIds[] = $parentId; - } - - $parentIds = array_unique($parentIds); - foreach ($parentIds as $pid) { - $this->invoiceController->generateInvoice((string) $pid); - } - } - return redirect()->to('/administrator/events')->with('success', 'Event created successfully'); } @@ -240,17 +188,24 @@ class EventController extends ResourceController $parents = $this->userModel->getParents(); $events = $this->eventModel->getActiveEvents($this->schoolYear); + $filterEventId = (int) ($this->request->getGet('event_id') ?? 0); - $charges = $this->eventChargesModel + $chargesBuilder = $this->eventChargesModel ->select('event_charges.*, users.firstname AS parent_firstname, users.lastname AS parent_lastname, students.firstname AS student_firstname, students.lastname AS student_lastname, - events.event_name') + events.event_name, events.description AS event_description, events.amount AS event_amount') ->join('users', 'users.id = event_charges.parent_id', 'left') ->join('students', 'students.id = event_charges.student_id', 'left') ->join('events', 'events.id = event_charges.event_id', 'left') ->where('event_charges.school_year', $schoolYear) - ->where('event_charges.semester', $semester) + ->where('event_charges.semester', $semester); + + if ($filterEventId > 0) { + $chargesBuilder->where('event_charges.event_id', $filterEventId); + } + + $charges = $chargesBuilder ->orderBy('event_charges.created_at', 'DESC') ->findAll(); @@ -260,6 +215,7 @@ class EventController extends ResourceController 'events' => $events, 'school_year' => $schoolYear, 'semester' => $semester, + 'filterEventId' => $filterEventId, ]); } diff --git a/app/Controllers/View/FinancialController.php b/app/Controllers/View/FinancialController.php index 8b2323c..d537820 100644 --- a/app/Controllers/View/FinancialController.php +++ b/app/Controllers/View/FinancialController.php @@ -216,25 +216,28 @@ public function financialReport() $schoolYears[] = (string)$schoolYear; } + $eventFeesTotal = $this->getEventFeesTotal($schoolYear, $dateFrom, $dateTo); + // JSON API support if ($this->wantsJson() || strtolower((string)($this->request->getGet('format') ?? '')) === 'json') { - return $this->response->setJSON([ - 'ok' => true, - 'selectedYear' => $schoolYear, - 'dateFrom' => $dateFrom, - 'dateTo' => $dateTo, + return $this->response->setJSON([ + 'ok' => true, + 'selectedYear' => $schoolYear, + 'dateFrom' => $dateFrom, + 'dateTo' => $dateTo, 'schoolYears' => $schoolYears, 'invoices' => $invoices, 'payments' => $payments, 'paymentBreakdown' => $paymentBreakdown, 'paymentTotals' => $paymentTotals, - 'refunds' => $refunds, - 'expenses' => $expenses, - 'reimbursements' => $reimbursements, - 'discounts' => $discounts, - 'csrf_token' => csrf_token(), - 'csrf_hash' => csrf_hash(), - ]); + 'refunds' => $refunds, + 'expenses' => $expenses, + 'reimbursements' => $reimbursements, + 'discounts' => $discounts, + 'eventFeesTotal' => $eventFeesTotal, + 'csrf_token' => csrf_token(), + 'csrf_hash' => csrf_hash(), + ]); } return view('payment/financial_report', [ @@ -246,6 +249,7 @@ public function financialReport() 'expenses' => $expenses, 'reimbursements' => $reimbursements, 'discounts' => $discounts, + 'eventFeesTotal' => $eventFeesTotal, 'selectedYear' => $schoolYear, 'schoolYears' => $schoolYears, 'dateFrom' => $dateFrom, @@ -1058,6 +1062,7 @@ public function financialReport() $amountCollected = $totalPaid; $netAmount = ($totalCharges - $totalDiscounts - $totalRefunds); + $totalEventFees = $this->getEventFeesTotal($schoolYear, $invoiceDateFrom, $invoiceDateTo); return [ 'schoolYear' => $schoolYear, 'dateFrom' => $dateFrom, @@ -1073,9 +1078,28 @@ public function financialReport() 'amountCollected' => $amountCollected, 'totalUnpaid' => $totalUnpaid, 'netAmount' => $netAmount, + 'totalEventFees' => $totalEventFees, ]; } + private function getEventFeesTotal(?string $schoolYear, ?string $dateFrom, ?string $dateTo): float + { + $db = \Config\Database::connect(); + $builder = $db->table('event_charges ec') + ->select('COALESCE(SUM(ec.charged),0) AS amount', false); + if (!empty($schoolYear)) { + $builder->where('ec.school_year', $schoolYear); + } + if (!empty($dateFrom)) { + $builder->where('DATE(ec.created_at) >=', $dateFrom); + } + if (!empty($dateTo)) { + $builder->where('DATE(ec.created_at) <=', $dateTo); + } + $row = $builder->get()->getRowArray(); + return $row ? (float)($row['amount'] ?? 0) : 0.0; + } + /** * Management page: list parents with outstanding balances (> 0) for a school year. @@ -1226,6 +1250,23 @@ public function financialReport() $byParent[$pid]['total_balance'] += $extra; } + $eventFeesPerParent = []; + try { + $eventFeesRows = $db->table('event_charges ec') + ->select('ec.parent_id, COALESCE(SUM(ec.charged),0) AS event_fees', false) + ->where('ec.school_year', $schoolYear) + ->groupBy('ec.parent_id') + ->get() + ->getResultArray(); + foreach ($eventFeesRows as $row) { + $pid = (int)($row['parent_id'] ?? 0); + if ($pid <= 0) continue; + $eventFeesPerParent[$pid] = (float)($row['event_fees'] ?? 0); + } + } catch (\Throwable $e) { + // ignore, fallback to no event fees data + } + // Reduce into rows list; only parents with positive balance // Also compute remaining installments and suggested monthly amount $rows = []; @@ -1305,7 +1346,7 @@ public function financialReport() } } - $dataRows = array_map(function(array $r) use ($hasPayments, $paidTotals, $paymentCounts, $nextInstallmentYmd) { + $dataRows = array_map(function(array $r) use ($hasPayments, $paidTotals, $paymentCounts, $nextInstallmentYmd, $eventFeesPerParent) { $pid = (int)($r['parent_id'] ?? 0); $name = trim((string)($r['firstname'] ?? '') . ' ' . (string)($r['lastname'] ?? '')); return [ @@ -1322,6 +1363,7 @@ public function financialReport() 'payment_count' => isset($paymentCounts[$pid]) ? (int)$paymentCounts[$pid] : 0, 'has_installment'=> isset($hasPayments[$pid]) ? 1 : 0, 'next_installment' => $nextInstallmentYmd, + 'event_fees' => (float)($eventFeesPerParent[$pid] ?? 0), ]; }, $rows); diff --git a/app/Models/EventChargesModel.php b/app/Models/EventChargesModel.php index 096c903..3c5dde5 100644 --- a/app/Models/EventChargesModel.php +++ b/app/Models/EventChargesModel.php @@ -30,7 +30,7 @@ class EventChargesModel extends Model public function getChargesWithEventInfo($parentId = null, $schoolYear = null, $semester = null) { - $builder = $this->select('event_charges.*, events.event_name, events.amount') + $builder = $this->select('event_charges.*, events.event_name, events.amount AS event_amount, events.description AS event_description') ->join('events', 'events.id = event_charges.event_id', 'left'); if ($parentId) { diff --git a/app/Views/administrator/events/create_event.php b/app/Views/administrator/events/create_event.php index e26f73b..355b85b 100644 --- a/app/Views/administrator/events/create_event.php +++ b/app/Views/administrator/events/create_event.php @@ -4,6 +4,11 @@

Create Event

+
@@ -15,7 +20,7 @@ diff --git a/app/Views/administrator/events/event_charges.php b/app/Views/administrator/events/event_charges.php index d1dc45f..fca7e83 100644 --- a/app/Views/administrator/events/event_charges.php +++ b/app/Views/administrator/events/event_charges.php @@ -11,44 +11,85 @@
getFlashdata('error') ?>
+ - - -
-
- - -
+
+
+ + +
+
+ + +
-
- - -
+ +
+
+
+
+ + $ fee + +
+

+ +

+ + + Expires: + + +
+
+ - +
+ +
+
+ + +
+
+ + + +
+ + Event List +
+
+
- - Event List - -
- - - - - - - - - - + + + + + + + + + + + + @@ -100,6 +143,8 @@ + + @@ -156,7 +201,19 @@ function loadStudentsWithCharges() { } } -$('#parent_id').on('change', loadStudentsWithCharges); -$('#event_id').on('change', loadStudentsWithCharges); +$(function() { + $('#parent_id').on('change', loadStudentsWithCharges); + + $('#event_id').on('change', function() { + let eventId = $(this).val(); + let url = new URL(window.location.href); + if (eventId) { + url.searchParams.set('event_id', eventId); + } else { + url.searchParams.delete('event_id'); + } + window.location.href = url.toString(); + }); +}); endSection() ?> diff --git a/app/Views/administrator/events/event_list.php b/app/Views/administrator/events/event_list.php index e31419b..6cc4af1 100644 --- a/app/Views/administrator/events/event_list.php +++ b/app/Views/administrator/events/event_list.php @@ -21,7 +21,8 @@ - + + @@ -33,7 +34,8 @@ - + + diff --git a/app/Views/administrator/teacher_submissions.php b/app/Views/administrator/teacher_submissions.php index 801c15e..6f463e7 100644 --- a/app/Views/administrator/teacher_submissions.php +++ b/app/Views/administrator/teacher_submissions.php @@ -23,6 +23,41 @@
0) { + $pageNotifications[] = [ + 'level' => 'danger', + 'message' => "{$missingItemsCount} missing item" . ($missingItemsCount === 1 ? '' : 's') . " awaiting teacher uploads.", + ]; + } + if ($completionPercent < 70) { + $pageNotifications[] = [ + 'level' => 'warning', + 'message' => "Submission completion is below 70% — follow up with remaining teachers.", + ]; + } + if ($flaggedClasses > 0) { + $pageNotifications[] = [ + 'level' => 'warning', + 'message' => "Progress is under 50% for {$flaggedClasses} class section" . ($flaggedClasses === 1 ? '' : 's') . ".", + ]; + } + if (empty($rows)) { + $pageNotifications[] = [ + 'level' => 'info', + 'message' => 'No class-section assignments submitted yet; encourage teachers to upload drafts.', + ]; + } + if (empty($pageNotifications)) { + $pageNotifications[] = [ + 'level' => 'info', + 'message' => 'All tracked classes are current. Use the controls below to send reminders.', + ]; + } + $examDraftDeadlineConfig = $examDraftDeadlineConfig ?? ''; + $examDraftDeadlineFormatted = $examDraftDeadlineFormatted ?? ''; ?> getFlashdata('success')): ?>
@@ -37,218 +72,278 @@ getFlashdata('info')) ?>
- - -
- Showing teachers for class sections with progress submissions below 50%. -
- -
-
-
-
Submission completion
-
%
-
-
+ $row) { + $classKey = (string) ($row['class_section_id'] ?? $row['class_section'] ?? 'class_' . $idx); + $groupedRows[$classKey][] = $row; + } + ?> +
+
+
+
+
+
+
Completion
+
%
+
+
+
+
+
+
+
+
+
+
Missing items
+
+
submitted / total
+
+
+
+
+
+
+
Submissions
+
+
teachers tracked
+
+
+
+
+
+
+
Flagged
+
+
Sections under 50% complete
+
+
-
-
Missing items
-
-
-
- submitted / total items +
+
+
+
+ Page notifications + alerts +
+
+
    + +
  • + + +
  • + +
+
-
-
IDParent NameStudent NameCharged AmountIs ParticipatingSemesterYearCreated
IDParent NameStudent NameCharged AmountIs ParticipatingSemesterYearCreatedDescriptionEvent Fees
$
Event Name CategoryAmountDescriptionEvent Fees Expiration Date Semester School Year
$
- - - - - - - - - - - - - - - - - - - - - - - 'N/A', 'badge' => 'bg-secondary']; ?> - - - 'N/A', 'badge' => 'bg-secondary']; ?> - - - - + +

No teacher-class assignments found for this term.

+ +
+
+ + + + + + + +
+
+ Exam draft deadline
+ + + + + -
- - + Not set - -
Class-SectionTeachers Name - Score -
- - -
-
- Comment -
- - -
-
- Participation -
- - -
-
- PTAP Comment -
- - -
-
- Class Progress -
- - -
-
- Exam Draft -
- - -
- -
- exam_draft_deadline
- - - - - - - Not set - -
-
- Homework -
- - -
-
Notifications
- - -
- - - Unassigned - -
- - - - -
- -
- - - - -
- -
- - - - - -
- - -
- - - Last on - by - - 1): ?> -
History: entries
- - - No notifications sent yet - -
-
- - - No teacher assigned - - -
- Outstanding: -
- -
No teacher-class assignments found for this term.
-
-
- -
+
+
+
+ $sectionRows): ?> + +
+

+ +

+
+
+
+ + + + + + + + + + + + + + + + + + + + + + 'N/A', 'badge' => 'bg-secondary']; ?> + + + 'N/A', 'badge' => 'bg-secondary']; ?> + + + + + +
Class-SectionTeachers Name Score CommentParticipationPTAP CommentClass ProgressExam DraftHomeworkNotifications
+ + +
+ + + Unassigned + +
+ + + + +
+ +
+ + + + +
+ +
+ + + + + +
+ + +
+ + + Last on + by + + 1): ?> +
History: entries
+ + + No notifications sent yet + +
+
+ + + No teacher assigned + + +
+ Outstanding: +
+ +
+
+
+
+
+ +
+
+ +
+ @@ -280,5 +375,77 @@ .card-body .table-responsive .teacher-submissions-table td { white-space: normal; } + + .summary-card { + background: var(--bs-white); + border-radius: 0.75rem; + } + .summary-card-body { + padding: 1.25rem; + } + .summary-label { + font-size: 0.7rem; + letter-spacing: 0.08em; + color: #6c757d; + } + .summary-value { + font-size: 1.75rem; + font-weight: 600; + margin-top: 0.35rem; + } + .summary-note { + font-size: 0.85rem; + color: #6c757d; + } + .summary-progress { + height: 6px; + margin-top: 0.65rem; + } + .summary-progress .progress-bar { + background: var(--bs-primary); + } + .page-notifications-card { + border-radius: 0.75rem; + } + .page-notifications-list { + list-style: none; + padding: 0; + margin: 0; + display: flex; + flex-direction: column; + gap: 0.5rem; + } + .page-notification-item { + display: flex; + align-items: center; + gap: 0.5rem; + font-size: 0.85rem; + } + .page-notification-info .notification-indicator { + background: #0dcaf0; + } + .page-notification-warning .notification-indicator { + background: #ffc107; + } + .page-notification-danger .notification-indicator { + background: #dc3545; + } + .notification-indicator { + width: 0.75rem; + height: 0.75rem; + border-radius: 50%; + display: inline-block; + } + .table-control-bar { + border: 1px solid rgba(0,0,0,0.08); + border-radius: 0.75rem; + padding: 0.75rem 1rem; + background: #f8f9fa; + } + .table-control-bar .form-check-label { + font-size: 0.8rem; + margin-left: 0.15rem; + text-transform: none; + } endSection() ?> diff --git a/app/Views/parent/event_participation.php b/app/Views/parent/event_participation.php index 0eb8963..8781ce0 100644 --- a/app/Views/parent/event_participation.php +++ b/app/Views/parent/event_participation.php @@ -42,9 +42,6 @@ - -

-
@@ -53,11 +50,13 @@
- - - - - + + + + + + + @@ -84,6 +83,8 @@ + + diff --git a/app/Views/payment/financial_report.php b/app/Views/payment/financial_report.php index d9fc296..82f998b 100644 --- a/app/Views/payment/financial_report.php +++ b/app/Views/payment/financial_report.php @@ -54,6 +54,11 @@ Display Summary Report + +
+ Event fees total: $ +
+
Student First NameStudent Last NameParticipate
Student First NameStudent Last NameParticipateDescriptionEvent Fees
$
diff --git a/app/Views/payment/financial_report_summary.php b/app/Views/payment/financial_report_summary.php index bd0b67a..405315f 100644 --- a/app/Views/payment/financial_report_summary.php +++ b/app/Views/payment/financial_report_summary.php @@ -32,6 +32,7 @@ + @@ -98,6 +99,9 @@ function loadSummary(){ if (!d || d.ok !== true) return; document.getElementById('summaryPeriod').textContent = 'Report for School Year: ' + (d.schoolYear||''); document.getElementById('sumCharges').textContent = fmt(d.totalCharges); + if (document.getElementById('sumEventFees')) { + document.getElementById('sumEventFees').textContent = fmt(d.totalEventFees || 0); + } if (document.getElementById('sumExtraCharges')) { document.getElementById('sumExtraCharges').textContent = fmt(d.totalExtraCharges || 0); } @@ -127,10 +131,10 @@ function renderCharts(d){ const summaryCtx = document.getElementById('summaryChart').getContext('2d'); window._summaryChart = new Chart(summaryCtx, { type: 'bar', - data: { labels: ['Charges','Paid','Unpaid','Discounts','Refunds','Expenses','Reimbursements','Net'], + data: { labels: ['Charges','Event Fees','Paid','Unpaid','Discounts','Refunds','Expenses','Reimbursements','Net'], datasets: [{ label:'Amount (USD)', data:[ - d.totalCharges||0, d.totalPaid||0, d.totalUnpaid||0, d.totalDiscounts||0, d.totalRefunds||0, d.totalExpenses||0, d.totalReimbursements||0, d.netAmount||0 - ], backgroundColor: ['#007bff','#28a745','#ffc107','#17a2b8','#ffc107','#dc3545','#6f42c1','#20c997']}] }, + d.totalCharges||0, d.totalEventFees||0, d.totalPaid||0, d.totalUnpaid||0, d.totalDiscounts||0, d.totalRefunds||0, d.totalExpenses||0, d.totalReimbursements||0, d.netAmount||0 + ], backgroundColor: ['#007bff','#6610f2','#28a745','#ffc107','#17a2b8','#ffc107','#dc3545','#6f42c1','#20c997']}] }, options: { responsive:true, scales:{ y:{ beginAtZero:true }}} }); @@ -177,11 +181,12 @@ document.addEventListener("DOMContentLoaded", function() { window._summaryChart = new Chart(summaryCtx, { type: 'bar', data: { - labels: ['Charges', 'Paid', 'Unpaid', 'Discounts', 'Refunds', 'Expenses', 'Reimbursements', 'Net'], + labels: ['Charges', 'Event Fees', 'Paid', 'Unpaid', 'Discounts', 'Refunds', 'Expenses', 'Reimbursements', 'Net'], datasets: [{ label: 'Amount (USD)', data: [ , + , , , , @@ -191,7 +196,7 @@ document.addEventListener("DOMContentLoaded", function() { ], backgroundColor: [ - '#007bff', '#28a745', '#ffc107', '#17a2b8', '#ffc107', '#dc3545', '#6f42c1', '#20c997' + '#007bff', '#6610f2', '#28a745', '#ffc107', '#17a2b8', '#ffc107', '#dc3545', '#6f42c1', '#20c997' ] }] }, diff --git a/app/Views/payment/unpaid_parents.php b/app/Views/payment/unpaid_parents.php index f492ba1..297b796 100644 --- a/app/Views/payment/unpaid_parents.php +++ b/app/Views/payment/unpaid_parents.php @@ -47,6 +47,7 @@ +
Total Charges$0.00
Event Fees$0.00
Total Extra Charges$0.00
Total Discounts$0.00
Total Refunds$0.00
@@ -54,6 +55,7 @@ + @@ -67,7 +69,7 @@ - + - - + + no payment + + installment + + + + + - + + @@ -129,6 +133,7 @@ + diff --git a/public/uploads/event_flyers/1775937190_efc22f21fd6ba28ae0d5.png b/public/uploads/event_flyers/1775937190_efc22f21fd6ba28ae0d5.png new file mode 100644 index 0000000..e97a976 Binary files /dev/null and b/public/uploads/event_flyers/1775937190_efc22f21fd6ba28ae0d5.png differ diff --git a/public/uploads/event_flyers/1775937871_fd3b88d6d7389ac5cc46.png b/public/uploads/event_flyers/1775937871_fd3b88d6d7389ac5cc46.png new file mode 100644 index 0000000..e97a976 Binary files /dev/null and b/public/uploads/event_flyers/1775937871_fd3b88d6d7389ac5cc46.png differ diff --git a/public/uploads/event_flyers/1775938017_701a1c5032604353ca78.png b/public/uploads/event_flyers/1775938017_701a1c5032604353ca78.png new file mode 100644 index 0000000..be36e21 Binary files /dev/null and b/public/uploads/event_flyers/1775938017_701a1c5032604353ca78.png differ
Parent Nbr of Installements TypeEvent Fees Invoice Amount Applied Discount Paid Amount
No parents with outstanding balance.
No parents with outstanding balance.
- - no payment - - installment - - $$$ -$ $
Totals:$ $ -$ $