update controllers logic

This commit is contained in:
root
2026-04-23 00:04:35 -04:00
parent 1977a513df
commit ca4ba272fc
353 changed files with 13402 additions and 1301 deletions
@@ -4,10 +4,12 @@ namespace App\Services\Parents;
use App\Models\AttendanceData;
use App\Models\AttendanceRecord;
use App\Models\EarlyDismissalSignature;
use App\Models\ParentAttendanceReport;
use App\Models\Student;
use App\Models\ClassSection;
use App\Services\SemesterRangeService;
use Illuminate\Http\UploadedFile;
use Illuminate\Support\Facades\DB;
class ParentAttendanceReportService
@@ -254,12 +256,12 @@ class ParentAttendanceReportService
];
}
public function listReports(string $start, ?string $end, ?string $schoolYear, ?string $semester): array
public function listReports(string $start, ?string $end, ?string $schoolYear, ?string $semester, int $parentId): array
{
return ParentAttendanceReport::listForDateRange($start, $end, $schoolYear, $semester);
return ParentAttendanceReport::listForDateRange($start, $end, $schoolYear, $semester, $parentId);
}
public function checkExisting(array $payload): array
public function checkExisting(int $parentId, array $payload): array
{
$datesPost = $payload['dates'] ?? null;
$dateSingle = $payload['date'] ?? null;
@@ -286,6 +288,15 @@ class ParentAttendanceReportService
return [];
}
$uniqueIds = array_values(array_unique($studentIds));
$ownedCount = Student::query()
->where('parent_id', $parentId)
->whereIn('id', $uniqueIds)
->count();
if ($ownedCount !== count($uniqueIds)) {
throw new \RuntimeException('Invalid student selection.');
}
$rows = DB::table('parent_attendance_reports as par')
->select('par.student_id', 'par.type', 'par.report_date', 's.firstname', 's.lastname')
->leftJoin('students as s', 's.id', '=', 'par.student_id')
@@ -371,6 +382,275 @@ class ParentAttendanceReportService
$row->update($update);
}
/**
* Staff index: early dismissal rows grouped by date (+ signature metadata per date).
*
* Parity with CodeIgniter {@see \App\Controllers\View\ParentAttendanceReportController::earlyDismissals}.
*
* @return array{groups: array<string, array<int, array<string, mixed>>>, school_year: string, semester: string, signature_by_date: array<string, array<string, mixed>>}
*/
public function staffEarlyDismissalsIndex(?string $schoolYearParam, ?string $semesterParam): array
{
$context = $this->configService->context();
$schoolYear = filled($schoolYearParam) ? trim((string) $schoolYearParam) : (string) ($context['school_year'] ?? '');
$semester = filled($semesterParam) ? trim((string) $semesterParam) : '';
$b = DB::table('parent_attendance_reports as par')
->select('par.*', 's.firstname', 's.lastname', 'cs.class_section_name')
->leftJoin('students as s', 's.id', '=', 'par.student_id')
->leftJoin('classSection as cs', 'cs.class_section_id', '=', 'par.class_section_id')
->where('par.type', '=', 'early_dismissal');
if ($schoolYear !== '') {
$b->where('par.school_year', $schoolYear);
}
if ($semester !== '') {
$b->where('par.semester', $semester);
}
$rows = $b->orderByDesc('par.report_date')
->orderBy('par.dismiss_time')
->get()
->map(fn ($r) => (array) $r)
->all();
$groups = [];
foreach ($rows as $r) {
$d = substr((string) ($r['report_date'] ?? ''), 0, 10);
if ($d === '') {
$d = 'Unknown';
}
$groups[$d][] = $r;
}
uksort($groups, static function ($a, $b) {
if ($a === 'Unknown' && $b === 'Unknown') {
return 0;
}
if ($a === 'Unknown') {
return 1;
}
if ($b === 'Unknown') {
return -1;
}
return strcmp((string) $b, (string) $a);
});
$signatureByDate = [];
$dates = array_values(array_filter(array_keys($groups), static function ($d) {
return preg_match('/^\d{4}-\d{2}-\d{2}$/', (string) $d) === 1;
}));
if ($dates !== []) {
$sigQ = EarlyDismissalSignature::query()->whereIn('report_date', $dates);
if ($schoolYear !== '') {
$sigQ->where('school_year', $schoolYear);
}
if ($semester !== '') {
$sigQ->where('semester', $semester);
}
foreach ($sigQ->orderBy('report_date')->get() as $sig) {
$d = $sig->report_date instanceof \DateTimeInterface
? $sig->report_date->format('Y-m-d')
: substr((string) $sig->report_date, 0, 10);
if ($d !== '') {
$signatureByDate[$d] = $sig->toArray();
}
}
}
return [
'groups' => $groups,
'school_year' => $schoolYear,
'semester' => $semester,
'signature_by_date' => $signatureByDate,
];
}
/**
* Parity with CodeIgniter {@see \App\Controllers\View\ParentAttendanceReportController::addEarlyDismissalForm}.
*
* @return array{students: array<int, array<string, mixed>>, default_date: string, school_year: string, semester: string}
*/
public function staffAddEarlyDismissalFormData(): array
{
$context = $this->configService->context();
$schoolYear = (string) ($context['school_year'] ?? '');
$semester = (string) ($context['semester'] ?? '');
try {
$students = DB::table('students as s')
->select('s.id', 's.firstname', 's.lastname', 's.parent_id', 'cs.class_section_name', 'sc.class_section_id')
->leftJoin('student_class as sc', function ($join) use ($schoolYear) {
$join->on('sc.student_id', '=', 's.id')
->where('sc.school_year', '=', $schoolYear);
})
->leftJoin('classSection as cs', 'cs.class_section_id', '=', 'sc.class_section_id')
->orderBy('s.lastname')
->orderBy('s.firstname')
->get()
->map(fn ($r) => (array) $r)
->all();
} catch (\Throwable) {
$students = [];
}
[, $defaultDate] = $this->calendarService->computeSundays(0, 26);
return [
'students' => $students,
'default_date' => $defaultDate,
'school_year' => $schoolYear,
'semester' => $semester,
];
}
/**
* Parity with CodeIgniter {@see \App\Controllers\View\ParentAttendanceReportController::saveEarlyDismissal}.
*/
public function saveStaffEarlyDismissal(int $studentId, string $reportDate, string $dismissTime, string $reason): void
{
$context = $this->configService->context();
$schoolYear = (string) ($context['school_year'] ?? '');
$semester = (string) ($context['semester'] ?? '');
$dt = \DateTime::createFromFormat('Y-m-d', $reportDate);
if (! $dt || (int) $dt->format('w') !== 0) {
throw new \RuntimeException('Please select a Sunday date.');
}
$stu = Student::query()->select('id', 'parent_id', 'firstname', 'lastname')->find($studentId);
if (! $stu || empty($stu->parent_id)) {
throw new \RuntimeException('Selected student not found or has no primary parent assigned.');
}
$parentId = (int) $stu->parent_id;
$sectionRow = DB::table('student_class')
->select('class_section_id')
->where('student_id', $studentId)
->where('school_year', $schoolYear)
->orderByDesc('updated_at')
->first();
$classSectionId = $sectionRow ? (int) $sectionRow->class_section_id : null;
$qb = ParentAttendanceReport::query()
->where('student_id', $studentId)
->whereDate('report_date', $reportDate)
->where('school_year', $schoolYear);
$existingAl = (clone $qb)->where(function ($q) {
$q->where('type', 'absent')->orWhere('type', 'late');
})->first();
if ($existingAl) {
throw new \RuntimeException('Absent/Late already submitted for this student on the selected date.');
}
$existingEd = (clone $qb)->where('type', 'early_dismissal')->first();
if ($existingEd) {
$existingEd->update([
'dismiss_time' => $dismissTime !== '' ? $dismissTime : $existingEd->dismiss_time,
'reason' => $reason !== '' ? $reason : $existingEd->reason,
'status' => 'new',
]);
return;
}
ParentAttendanceReport::query()->create([
'parent_id' => $parentId,
'student_id' => $studentId,
'class_section_id' => $classSectionId,
'report_date' => $reportDate,
'type' => 'early_dismissal',
'dismiss_time' => $dismissTime,
'reason' => $reason !== '' ? $reason : null,
'semester' => $semester,
'school_year' => $schoolYear,
'status' => 'new',
]);
}
/**
* Parity with CodeIgniter {@see \App\Controllers\View\ParentAttendanceReportController::uploadEarlyDismissalSignature}.
* Files live under {@see storage_path('uploads/early_dismissal_signatures')} (same as {@see \App\Http\Controllers\Api\Reports\FilesController::earlyDismissalSignature}).
*/
public function storeEarlyDismissalSignature(
UploadedFile $file,
string $reportDate,
?string $schoolYearPost,
?string $semesterPost,
int $uploadedByUserId,
): void {
$dt = \DateTime::createFromFormat('Y-m-d', $reportDate);
if (! $dt || (int) $dt->format('w') !== 0) {
throw new \RuntimeException('Please select a Sunday date.');
}
$context = $this->configService->context();
$schoolYear = $schoolYearPost !== null && $schoolYearPost !== ''
? (string) $schoolYearPost
: (string) ($context['school_year'] ?? '');
$semesterRaw = (string) ($semesterPost ?? '');
$hasSemesterFilter = $semesterRaw !== '';
$semester = $hasSemesterFilter ? $semesterRaw : null;
$existsQ = ParentAttendanceReport::query()
->where('type', 'early_dismissal')
->whereDate('report_date', $reportDate);
if ($schoolYear !== '') {
$existsQ->where('school_year', $schoolYear);
}
if ($hasSemesterFilter) {
$existsQ->where('semester', $semester);
}
if (! $existsQ->exists()) {
throw new \RuntimeException('No early dismissal records found for that date.');
}
$targetDir = storage_path('uploads/early_dismissal_signatures');
if (! is_dir($targetDir) && ! @mkdir($targetDir, 0755, true) && ! is_dir($targetDir)) {
throw new \RuntimeException('Upload directory is not available.');
}
$originalName = $file->getClientOriginalName();
$mimeType = $file->getClientMimeType();
$fileSize = (int) $file->getSize();
$filename = $file->hashName();
$file->move($targetDir, $filename);
$payload = [
'report_date' => $reportDate,
'school_year' => $schoolYear !== '' ? $schoolYear : null,
'semester' => $semester,
'filename' => $filename,
'original_name' => $originalName,
'mime_type' => $mimeType,
'file_size' => $fileSize,
'uploaded_by' => $uploadedByUserId,
];
$existingQ = EarlyDismissalSignature::query()->whereDate('report_date', $reportDate);
if ($schoolYear !== '') {
$existingQ->where('school_year', $schoolYear);
}
if ($hasSemesterFilter) {
$existingQ->where('semester', $semester);
}
$existing = $existingQ->first();
if ($existing) {
$oldFile = (string) ($existing->filename ?? '');
$existing->update($payload);
if ($oldFile !== '' && $oldFile !== $filename) {
@unlink($targetDir . DIRECTORY_SEPARATOR . $oldFile);
}
} else {
EarlyDismissalSignature::query()->create($payload);
}
}
private function resolveClassSectionLabel(?int $classSectionId): string
{
$cid = (int) ($classSectionId ?? 0);