configService->context(); $schoolYear = $context['school_year']; $semester = $context['semester']; $activeEvents = Event::getActiveEvents($schoolYear, $semester) ?? []; $chargesList = EventCharges::getChargesWithEventInfo($parentId, $schoolYear) ?? []; $charges = []; foreach ($chargesList as $charge) { $key = $charge['student_id'] . ':' . $charge['event_id']; $charges[$key] = [ 'participation' => $charge['participation'], 'date' => $charge['updated_at'] ?? $charge['created_at'], ]; } $students = Enrollment::getEnrolledStudents($parentId, $schoolYear); return [ 'activeEvents' => $activeEvents, 'charges' => $charges, 'yourStudents' => $students, ]; } public function updateParticipation(int $parentId, array $participations): void { $context = $this->configService->context(); $schoolYear = $context['school_year']; $semester = $context['semester']; // First pass: parse + validate every `student_id:event_id` key and // collect the student ids that this parent actually owns. This stops // a parent from creating or deleting charges on another family's // students by manipulating the participation keys. $parsed = []; $candidateStudentIds = []; foreach ($participations as $key => $value) { $parts = explode(':', (string) $key, 2); if (count($parts) !== 2 || ! ctype_digit($parts[0]) || ! ctype_digit($parts[1])) { continue; } $studentId = (int) $parts[0]; $eventId = (int) $parts[1]; if ($studentId <= 0 || $eventId <= 0) { continue; } $normalized = strtolower(trim((string) $value)); if (! in_array($normalized, ['yes', 'no'], true)) { continue; } $parsed[] = [ 'student_id' => $studentId, 'event_id' => $eventId, 'value' => $normalized, ]; $candidateStudentIds[$studentId] = true; } if ($parsed === []) { return; } $ownedIds = Student::query() ->where('parent_id', $parentId) ->whereIn('id', array_keys($candidateStudentIds)) ->pluck('id') ->map(fn ($id) => (int) $id) ->all(); $ownedSet = array_flip($ownedIds); foreach ($parsed as $row) { if (! isset($ownedSet[$row['student_id']])) { continue; } $existing = EventCharges::query() ->where('parent_id', $parentId) ->where('student_id', $row['student_id']) ->where('event_id', $row['event_id']) ->first(); if ($row['value'] === 'no') { if ($existing) { $existing->delete(); } continue; } if ($existing) { $existing->update(['participation' => $row['value']]); } else { $event = Event::getEvent($row['event_id'], $schoolYear); EventCharges::query()->create([ 'parent_id' => $parentId, 'student_id' => $row['student_id'], 'event_id' => $row['event_id'], 'participation' => $row['value'], 'charged' => $event['amount'] ?? 0, 'school_year' => $schoolYear, 'semester' => $semester, 'updated_by' => $parentId, ]); } } $this->invoiceGeneration->generateInvoice($parentId, $schoolYear); } }