Files
alrahma_sunday_school_api/app/Services/Parents/ParentEnrollmentService.php
T
2026-06-09 02:32:58 -04:00

237 lines
8.5 KiB
PHP

<?php
namespace App\Services\Parents;
use App\Models\Enrollment;
use App\Models\Refund;
use App\Models\Student;
use App\Models\StudentClass;
use Illuminate\Support\Facades\DB;
class ParentEnrollmentService
{
public function __construct(private ParentConfigService $configService)
{
}
public function overview(int $parentId, ?string $schoolYear = null): array
{
$context = $this->configService->context();
$selectedYear = $schoolYear ?: $context['school_year'];
$students = Student::query()
->where('parent_id', $parentId)
->orderBy('lastname')
->orderBy('firstname')
->get()
->map(function ($student) use ($selectedYear) {
$classSections = StudentClass::getClassSectionsByStudentId($student->id, $selectedYear, true);
$student->class_section = !empty($classSections)
? implode(', ', $classSections)
: 'Class not Assigned';
$isArabicClass = !empty($classSections) && array_reduce(
$classSections,
static fn ($carry, $name) => $carry || (is_string($name) && stripos($name, 'arabic') === 0),
false
);
$enrollment = Enrollment::query()
->select('enrollment_status', 'admission_status')
->where('student_id', $student->id)
->where('school_year', $selectedYear)
->first();
$statusMap = [
'admission under review' => 'admission under review',
'payment pending' => 'payment pending',
'enrolled' => 'enrolled',
'withdraw under review' => 'withdraw under review',
'refund pending' => 'refund pending',
'withdrawn' => 'withdrawn',
'denied' => 'denied',
'waitlist' => 'waitlist',
];
if ($enrollment && isset($enrollment->admission_status)) {
$student->admission_status = $enrollment->admission_status;
if ($enrollment->admission_status === 'denied') {
$student->enrollment_status = 'denied';
} else {
$student->enrollment_status = $statusMap[$enrollment->enrollment_status] ?? 'not enrolled';
}
} else {
$student->admission_status = null;
$student->enrollment_status = 'not enrolled';
}
if ($student->enrollment_status === 'not enrolled' && $isArabicClass) {
$student->enrollment_status = 'enrolled';
}
$student->disable_enroll = in_array(
$student->enrollment_status,
['admission under review', 'payment pending', 'enrolled', 'withdraw under review', 'denied'],
true
);
return $student->toArray();
})
->all();
$schoolYears = DB::table('enrollments')
->select('school_year')
->distinct()
->orderBy('school_year', 'desc')
->get()
->map(fn ($row) => (array) $row)
->all();
if (empty($schoolYears)) {
$schoolYears[] = ['school_year' => $selectedYear];
}
return [
'students' => $students,
'schoolYears' => $schoolYears,
'selectedYear' => $selectedYear,
'withdrawalDeadline' => $context['refund_deadline'],
'lastDayOfRegistration' => $context['enrollment_deadline'],
'schoolStartDate' => $context['fall_semester_start'],
];
}
public function updateEnrollment(int $parentId, array $enrollIds, array $withdrawIds): array
{
$context = $this->configService->context();
$schoolYear = $context['school_year'];
$semester = $context['semester'];
// Authorize every submitted student id against this parent up-front to
// prevent IDOR — a parent must not be able to enroll or withdraw
// another family's students by passing arbitrary ids.
$requestedIds = array_values(array_unique(array_map(
'intval',
array_merge($enrollIds, $withdrawIds)
)));
$requestedIds = array_values(array_filter($requestedIds, static fn ($id) => $id > 0));
$ownedIds = $requestedIds === []
? []
: Student::query()
->where('parent_id', $parentId)
->whereIn('id', $requestedIds)
->pluck('id')
->map(fn ($id) => (int) $id)
->all();
$ownedSet = array_flip($ownedIds);
$enrolled = [];
$withdrawn = [];
foreach ($enrollIds as $studentId) {
$studentId = (int) $studentId;
if (! isset($ownedSet[$studentId])) {
continue;
}
$existing = Enrollment::query()
->where('student_id', $studentId)
->where('school_year', $schoolYear)
->where('semester', $semester)
->first();
if ($existing) {
if ((int) $existing->is_withdrawn === 1) {
$existing->update([
'is_withdrawn' => 0,
'withdrawal_date' => null,
'enrollment_status' => 'payment pending',
'updated_at' => now(),
]);
}
} else {
Enrollment::query()->create([
'student_id' => $studentId,
'parent_id' => $parentId,
'school_year' => $schoolYear,
'semester' => $semester,
'enrollment_date' => now()->toDateString(),
'is_withdrawn' => 0,
'enrollment_status' => 'admission under review',
'admission_status' => 'pending',
'created_at' => now(),
]);
}
$enrolled[] = $studentId;
}
foreach ($withdrawIds as $studentId) {
$studentId = (int) $studentId;
if (! isset($ownedSet[$studentId])) {
continue;
}
$enrollment = Enrollment::query()
->where('student_id', $studentId)
->where('parent_id', $parentId)
->where('school_year', $schoolYear)
->where('semester', $semester)
->where('is_withdrawn', 0)
->first();
if (!$enrollment) {
continue;
}
$enrollment->update([
'withdrawal_date' => now()->toDateString(),
'enrollment_status' => 'withdraw under review',
'updated_at' => now(),
]);
$invoice = DB::table('invoices')
->where('parent_id', $parentId)
->where('school_year', $schoolYear)
->orderByDesc('created_at')
->first();
if ($invoice) {
$refund = Refund::query()
->where('parent_id', $parentId)
->where('invoice_id', $invoice->id)
->where('school_year', $schoolYear)
->first();
if ($refund) {
$refund->update([
'reason' => 'Withdrawal under review for student ID ' . $studentId,
'note' => null,
'updated_by' => $parentId,
]);
} else {
Refund::query()->create([
'parent_id' => $parentId,
'invoice_id' => $invoice->id,
'requested_at' => now(),
'school_year' => $schoolYear,
'status' => Refund::STATUS_PENDING,
'reason' => 'Withdrawal under review for student ID ' . $studentId,
'request' => 'new',
'semester' => $semester,
'refund_paid_amount' => 0.0,
]);
}
}
$withdrawn[] = $studentId;
}
return [
'enrolled' => $enrolled,
'withdrawn' => $withdrawn,
];
}
}