Files
alrahma_sunday_school_api/app/Models/StudentPromotionRecord.php
T
2026-06-09 00:03:03 -04:00

209 lines
6.0 KiB
PHP
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Builder;
use Illuminate\Database\Eloquent\Relations\BelongsTo;
/**
* Per-student record tracking the promotion lifecycle for a single
* school year transition (see plan sections 3, 6, 9, 11).
*
* Promotion eligibility lives here separately from the actual class
* assignment the student is only "Promoted and Enrolled" once the
* parent finishes enrollment for the next school year.
*/
class StudentPromotionRecord extends BaseModel
{
protected $table = 'student_promotion_records';
protected $primaryKey = 'promotion_id';
public $timestamps = true;
public const STATUS_NOT_REVIEWED = 'not_reviewed';
public const STATUS_ELIGIBLE = 'eligible_for_promotion';
public const STATUS_AWAITING_PARENT = 'awaiting_parent_enrollment';
public const STATUS_ENROLLMENT_STARTED = 'enrollment_started';
public const STATUS_PROMOTED_AND_ENROLLED = 'promoted_and_enrolled';
public const STATUS_CONDITIONAL = 'conditional_promotion';
public const STATUS_REPEATED = 'repeated_level';
public const STATUS_ON_HOLD = 'on_hold';
public const STATUS_WITHDRAWN = 'withdrawn';
public const STATUS_GRADUATED = 'graduated';
public const STATUS_NOT_ENROLLED = 'not_enrolled_for_next_year';
public const ENROLLMENT_NOT_STARTED = 'not_started';
public const ENROLLMENT_IN_PROGRESS = 'in_progress';
public const ENROLLMENT_COMPLETED = 'completed';
public const ENROLLMENT_EXPIRED = 'expired';
public const ALL_STATUSES = [
self::STATUS_NOT_REVIEWED,
self::STATUS_ELIGIBLE,
self::STATUS_AWAITING_PARENT,
self::STATUS_ENROLLMENT_STARTED,
self::STATUS_PROMOTED_AND_ENROLLED,
self::STATUS_CONDITIONAL,
self::STATUS_REPEATED,
self::STATUS_ON_HOLD,
self::STATUS_WITHDRAWN,
self::STATUS_GRADUATED,
self::STATUS_NOT_ENROLLED,
];
protected $fillable = [
'student_id',
'parent_id',
'current_school_year',
'next_school_year',
'current_level_id',
'promoted_level_id',
'current_level_name',
'promoted_level_name',
'promotion_status',
'passed_current_level',
'final_average',
'attendance_ok',
'eligibility_notes',
'enrollment_required',
'enrollment_status',
'enrollment_id',
'parent_notified_at',
'enrollment_deadline',
'enrollment_started_at',
'enrollment_completed_at',
'promotion_finalized_at',
'info_confirmed',
'documents_uploaded',
'agreement_accepted',
'payment_completed',
'updated_by',
];
protected $casts = [
'student_id' => 'integer',
'parent_id' => 'integer',
'current_level_id' => 'integer',
'promoted_level_id' => 'integer',
'passed_current_level' => 'boolean',
'final_average' => 'float',
'attendance_ok' => 'boolean',
'enrollment_required' => 'boolean',
'enrollment_id' => 'integer',
'parent_notified_at' => 'datetime',
'enrollment_deadline' => 'date',
'enrollment_started_at' => 'datetime',
'enrollment_completed_at' => 'datetime',
'promotion_finalized_at' => 'datetime',
'info_confirmed' => 'boolean',
'documents_uploaded' => 'boolean',
'agreement_accepted' => 'boolean',
'payment_completed' => 'boolean',
'updated_by' => 'integer',
];
public function student(): BelongsTo
{
return $this->belongsTo(Student::class, 'student_id');
}
public function parent(): BelongsTo
{
return $this->belongsTo(User::class, 'parent_id');
}
public function enrollment(): BelongsTo
{
return $this->belongsTo(Enrollment::class, 'enrollment_id');
}
public function scopeForNextYear(Builder $q, string $year): Builder
{
return $q->where('next_school_year', $year);
}
public function scopeStatus(Builder $q, string $status): Builder
{
return $q->where('promotion_status', $status);
}
public function scopeForStudent(Builder $q, int $studentId): Builder
{
return $q->where('student_id', $studentId);
}
public function scopeForParent(Builder $q, int $parentId): Builder
{
return $q->where('parent_id', $parentId);
}
/**
* Statuses the parent portal should treat as "actionable" (parent
* can still complete enrollment).
*/
public static function parentActionableStatuses(): array
{
return [
self::STATUS_ELIGIBLE,
self::STATUS_AWAITING_PARENT,
self::STATUS_ENROLLMENT_STARTED,
];
}
/**
* Statuses considered "open" for the next school year (admin views
* may want to filter on these).
*/
public static function openStatuses(): array
{
return [
self::STATUS_NOT_REVIEWED,
self::STATUS_ELIGIBLE,
self::STATUS_AWAITING_PARENT,
self::STATUS_ENROLLMENT_STARTED,
self::STATUS_CONDITIONAL,
self::STATUS_ON_HOLD,
];
}
public function isFinalized(): bool
{
return in_array(
$this->promotion_status,
[
self::STATUS_PROMOTED_AND_ENROLLED,
self::STATUS_REPEATED,
self::STATUS_WITHDRAWN,
self::STATUS_GRADUATED,
self::STATUS_NOT_ENROLLED,
],
true
);
}
public function enrollmentChecklistComplete(): bool
{
// Mirrors plan section 8 parent-side completion criteria.
// Payment is only required when payment_completed has been
// explicitly set; treat null/false as still required by default.
return (bool) $this->info_confirmed
&& (bool) $this->documents_uploaded
&& (bool) $this->agreement_accepted
&& (bool) $this->payment_completed;
}
}