event = model(Event::class); $this->config = model(Configuration::class); $this->eventCharges = model(EventCharges::class); $this->student = model(Student::class); $this->user = model(User::class); $this->invoiceController = app(InvoiceController::class); $this->schoolYear = $this->config->getConfig('school_year'); $this->semester = $this->config->getConfig('semester'); } public function index() { $schoolYear = $this->request->getGet('school_year') ?? $this->schoolYear; $upcomingOnly = $this->request->getGet('upcoming') === 'true'; $query = $this->event->newQuery(); if ($schoolYear) { $query->where('school_year', $schoolYear); } if ($upcomingOnly) { $today = Carbon::now()->toDateString(); $query->where('expiration_date', '>=', $today); } $events = $query->orderBy('created_at', 'DESC')->get()->toArray(); // Count active events $today = Carbon::now()->toDateString(); $activeEventCount = $this->event->newQuery() ->where('expiration_date', '>=', $today) ->count(); return $this->success([ 'events' => $events, 'active_event_count' => $activeEventCount, ], 'Events retrieved successfully'); } public function show($id = null) { $event = $this->event->find($id); if (!$event) { return $this->respondError('Event not found', Response::HTTP_NOT_FOUND); } return $this->success($event, 'Event retrieved successfully'); } public function create() { $data = $this->payloadData(); if (empty($data)) { return $this->respondError('Invalid request data', Response::HTTP_BAD_REQUEST); } $rules = [ 'event_name' => 'required|max_length[255]', 'description' => 'nullable', 'amount' => 'required|numeric', 'expiration_date' => 'required|date', 'semester' => 'nullable', 'school_year' => 'nullable', ]; $errors = $this->validateRequest($data, $rules); if (!empty($errors)) { return $this->respondValidationError($errors); } $flyerPath = null; // Handle file upload if present if ($this->laravelRequest->hasFile('flyer')) { $file = $this->laravelRequest->file('flyer'); if ($file->isValid()) { $path = $file->store('event_flyers', 'public'); $flyerPath = $path; } } $payload = [ 'event_name' => $data['event_name'], 'description' => $data['description'] ?? null, 'amount' => $data['amount'], 'flyer' => $flyerPath, 'expiration_date' => $data['expiration_date'], 'semester' => $data['semester'] ?? $this->semester, 'school_year' => $data['school_year'] ?? $this->schoolYear, 'created_by' => $this->getCurrentUserId(), ]; try { $event = $this->event->create($payload); return $this->respondCreated($event->toArray(), 'Event created successfully'); } catch (\Throwable $e) { Log::error('Event creation error: ' . $e->getMessage()); return $this->respondError('Failed to create event', Response::HTTP_INTERNAL_SERVER_ERROR); } } public function update($id = null) { $event = $this->event->find($id); if (!$event) { return $this->respondError('Event not found', Response::HTTP_NOT_FOUND); } $data = $this->payloadData(); if (empty($data)) { return $this->respondError('Invalid request data', Response::HTTP_BAD_REQUEST); } $flyerPath = $event['flyer']; // Keep existing flyer by default // Handle file upload if present if ($this->laravelRequest->hasFile('flyer')) { $file = $this->laravelRequest->file('flyer'); if ($file->isValid()) { // Delete old flyer if exists if ($flyerPath && Storage::disk('public')->exists($flyerPath)) { Storage::disk('public')->delete($flyerPath); } $path = $file->store('event_flyers', 'public'); $flyerPath = $path; } } $updateData = []; $allowed = ['event_name', 'description', 'amount', 'expiration_date', 'semester', 'school_year']; foreach ($allowed as $field) { if (array_key_exists($field, $data)) { $updateData[$field] = $data[$field]; } } $updateData['flyer'] = $flyerPath; try { $this->event->update($id, $updateData); $updated = $this->event->find($id); return $this->success($updated->toArray(), 'Event updated successfully'); } catch (\Throwable $e) { Log::error('Event update error: ' . $e->getMessage()); return $this->respondError('Failed to update event', Response::HTTP_INTERNAL_SERVER_ERROR); } } public function delete($id = null) { $event = $this->event->find($id); if (!$event) { return $this->respondError('Event not found', Response::HTTP_NOT_FOUND); } try { // Delete related charges and collect parent IDs $charges = $this->eventCharges->newQuery() ->where('event_id', $id) ->get() ->toArray(); $parentIds = []; foreach ($charges as $charge) { $this->eventCharges->delete($charge['id']); $parentIds[] = $charge['parent_id']; } // Delete event $this->event->delete($id); $parentIds = array_unique($parentIds); // Regenerate invoices for affected parents // Note: generateInvoice method may need to be implemented in InvoiceController foreach ($parentIds as $parentId) { // This would need to be implemented or called differently // $this->invoiceController->generateInvoice($parentId); } return $this->success(null, 'Event, charges, and invoices updated successfully'); } catch (\Throwable $e) { Log::error('Event deletion error: ' . $e->getMessage()); return $this->respondError('Failed to delete event', Response::HTTP_INTERNAL_SERVER_ERROR); } } /** * GET /api/v1/events/charges * Returns event charges with parent and student information */ public function charges() { $schoolYear = $this->request->getGet('school_year') ?? $this->schoolYear; $semester = $this->request->getGet('semester') ?? $this->semester; try { $parents = $this->user->getParents(); $events = $this->event->getActiveEvents($this->schoolYear); $charges = $this->eventCharges->newQuery() ->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' ]) ->leftJoin('users', 'users.id', '=', 'event_charges.parent_id') ->leftJoin('students', 'students.id', '=', 'event_charges.student_id') ->leftJoin('events', 'events.id', '=', 'event_charges.event_id') ->where('event_charges.school_year', $schoolYear) ->where('event_charges.semester', $semester) ->orderBy('event_charges.created_at', 'DESC') ->get() ->toArray(); return $this->success([ 'charges' => $charges, 'parents' => $parents, 'events' => $events, 'school_year' => $schoolYear, 'semester' => $semester, ], 'Event charges retrieved successfully'); } catch (\Throwable $e) { Log::error('Event charges retrieval error: ' . $e->getMessage()); return $this->respondError('Failed to retrieve event charges', Response::HTTP_INTERNAL_SERVER_ERROR); } } /** * POST /api/v1/events/charges/update * Updates event charges for a parent and students */ public function updateCharges() { $data = $this->payloadData(); if (empty($data)) { return $this->respondError('Invalid request data', Response::HTTP_BAD_REQUEST); } $schoolYear = $data['school_year'] ?? $this->schoolYear; $semester = $data['semester'] ?? $this->semester; $parentId = $data['parent_id'] ?? null; $eventId = $data['event_id'] ?? null; $participations = $data['participation'] ?? []; if (!$parentId || !$eventId || empty($participations)) { return $this->respondError('Missing required information: parent_id, event_id, and participation are required', Response::HTTP_BAD_REQUEST); } try { $userId = $this->getCurrentUserId(); $event = $this->event->getEvent($eventId, $schoolYear); if (!$event) { return $this->respondError('Event not found', Response::HTTP_NOT_FOUND); } foreach ($participations as $studentId => $value) { $existing = $this->eventCharges->newQuery() ->where('parent_id', $parentId) ->where('student_id', $studentId) ->where('event_id', $eventId) ->where('school_year', $schoolYear) ->where('semester', $semester) ->first(); if ($value === 'no') { if ($existing) { $this->eventCharges->delete($existing['id']); } continue; } // value is 'yes' $chargeData = [ 'parent_id' => $parentId, 'student_id' => $studentId, 'event_id' => $eventId, 'participation' => 'yes', 'charged' => $event['amount'], 'school_year' => $schoolYear, 'semester' => $semester, ]; if ($existing) { $chargeData['updated_by'] = $userId; $this->eventCharges->update($existing['id'], $chargeData); } else { $chargeData['updated_by'] = $userId; $this->eventCharges->insert($chargeData); } } // Regenerate invoice for parent // Note: generateInvoice method may need to be implemented // $this->invoiceController->generateInvoice($parentId); return $this->success(null, 'Event charges updated successfully'); } catch (\Throwable $e) { Log::error('Event charges update error: ' . $e->getMessage()); return $this->respondError('Failed to update event charges', Response::HTTP_INTERNAL_SERVER_ERROR); } } /** * GET /api/v1/events/students-with-charges * Returns students for a parent with their charge status */ public function getStudentsWithCharges() { $parentId = $this->request->getGet('parent_id'); $semester = $this->request->getGet('semester') ?? $this->semester; $schoolYear = $this->request->getGet('school_year') ?? $this->schoolYear; if (!$parentId) { return $this->respondError('parent_id is required', Response::HTTP_BAD_REQUEST); } try { // Get students for parent $students = $this->student->newQuery() ->where('parent_id', $parentId) ->get() ->toArray(); // Get student_ids that already have charges $chargedStudentIds = $this->eventCharges->newQuery() ->where('parent_id', $parentId) ->where('semester', $semester) ->where('school_year', $schoolYear) ->groupBy('student_id') ->pluck('student_id') ->toArray(); $data = []; foreach ($students as $student) { $data[] = [ 'id' => $student['id'], 'name' => trim(($student['firstname'] ?? '') . ' ' . ($student['lastname'] ?? '')), 'charged' => in_array($student['id'], $chargedStudentIds), ]; } return $this->success($data, 'Students with charges retrieved successfully'); } catch (\Throwable $e) { Log::error('Get students with charges error: ' . $e->getMessage()); return $this->respondError('Failed to retrieve students with charges', Response::HTTP_INTERNAL_SERVER_ERROR); } } }