227 lines
8.2 KiB
PHP
227 lines
8.2 KiB
PHP
<?php
|
|
|
|
namespace App\Http\Controllers\Api\Attendance;
|
|
|
|
use App\Http\Controllers\Api\Core\BaseApiController;
|
|
use App\Models\Configuration;
|
|
use App\Models\EarlyDismissalSignature;
|
|
use Illuminate\Http\JsonResponse;
|
|
use Illuminate\Http\Request;
|
|
use Illuminate\Support\Facades\Auth;
|
|
use Illuminate\Support\Facades\DB;
|
|
use Illuminate\Support\Facades\Storage;
|
|
use Illuminate\Support\Facades\Validator;
|
|
use Symfony\Component\HttpFoundation\Response;
|
|
|
|
class EarlyDismissalsController extends BaseApiController
|
|
{
|
|
public function __construct()
|
|
{
|
|
parent::__construct();
|
|
}
|
|
|
|
/**
|
|
* GET /api/v1/attendance/early-dismissals
|
|
* Returns early dismissal records grouped by report_date, plus a signature map.
|
|
*/
|
|
public function index(Request $request): JsonResponse
|
|
{
|
|
$schoolYear = $request->query('school_year');
|
|
$semester = $request->query('semester');
|
|
|
|
$query = DB::table('parent_attendance_reports as par')
|
|
->select([
|
|
'par.id',
|
|
'par.report_date',
|
|
'par.dismiss_time',
|
|
'par.reason',
|
|
'par.status',
|
|
'par.school_year',
|
|
'par.semester',
|
|
'par.class_section_id',
|
|
's.firstname',
|
|
's.lastname',
|
|
'cs.class_section_name',
|
|
])
|
|
->join('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')
|
|
->orderBy('par.report_date', 'desc')
|
|
->orderByRaw("CONCAT(s.firstname, ' ', s.lastname) ASC");
|
|
|
|
if ($schoolYear) {
|
|
$query->where('par.school_year', $schoolYear);
|
|
}
|
|
if ($semester) {
|
|
$query->where('par.semester', $semester);
|
|
}
|
|
|
|
$rows = $query->get();
|
|
|
|
$groups = [];
|
|
foreach ($rows as $row) {
|
|
$date = $row->report_date ?? 'Unknown';
|
|
$groups[$date][] = [
|
|
'id' => $row->id,
|
|
'firstname' => $row->firstname,
|
|
'lastname' => $row->lastname,
|
|
'class_section_name' => $row->class_section_name,
|
|
'dismiss_time' => $row->dismiss_time,
|
|
'reason' => $row->reason,
|
|
'status' => $row->status,
|
|
'school_year' => $row->school_year,
|
|
'semester' => $row->semester,
|
|
];
|
|
}
|
|
|
|
$dates = array_keys($groups);
|
|
$signatures = EarlyDismissalSignature::query()
|
|
->whereIn('report_date', $dates)
|
|
->get(['report_date', 'filename', 'original_name']);
|
|
|
|
$signatureByDate = [];
|
|
foreach ($signatures as $sig) {
|
|
$signatureByDate[(string) $sig->report_date] = [
|
|
'filename' => $sig->filename,
|
|
'original_name' => $sig->original_name,
|
|
];
|
|
}
|
|
|
|
return $this->success([
|
|
'groups' => $groups,
|
|
'signatureByDate' => $signatureByDate,
|
|
]);
|
|
}
|
|
|
|
/**
|
|
* GET /api/v1/attendance/early-dismissals/student-options
|
|
* Returns active students with their current class section.
|
|
*/
|
|
public function studentOptions(): JsonResponse
|
|
{
|
|
$students = DB::table('students as s')
|
|
->select([
|
|
's.id',
|
|
's.firstname',
|
|
's.lastname',
|
|
'cs.class_section_name',
|
|
])
|
|
->leftJoin('student_class as sc', function ($j) {
|
|
$j->on('sc.student_id', '=', 's.id')
|
|
->where('sc.school_year', '=', Configuration::getConfigValueByKey('school_year'));
|
|
})
|
|
->leftJoin('classSection as cs', 'cs.class_section_id', '=', 'sc.class_section_id')
|
|
->where('s.is_active', 1)
|
|
->orderByRaw("CONCAT(s.firstname, ' ', s.lastname) ASC")
|
|
->get();
|
|
|
|
return $this->success(['students' => $students]);
|
|
}
|
|
|
|
/**
|
|
* POST /api/v1/attendance/early-dismissals
|
|
* Creates a new early dismissal record.
|
|
*/
|
|
public function store(Request $request): JsonResponse
|
|
{
|
|
$validator = Validator::make($request->all(), [
|
|
'student_id' => ['required', 'integer', 'min:1', 'exists:students,id'],
|
|
'date' => ['required', 'date_format:Y-m-d'],
|
|
'dismiss_time' => ['required', 'date_format:H:i'],
|
|
'reason' => ['nullable', 'string', 'max:2000'],
|
|
]);
|
|
|
|
if ($validator->fails()) {
|
|
return $this->respondValidationError($validator->errors()->toArray());
|
|
}
|
|
|
|
$data = $validator->validated();
|
|
|
|
$schoolYear = (string) Configuration::getConfigValueByKey('school_year');
|
|
$semester = (string) Configuration::getConfigValueByKey('semester');
|
|
|
|
$sc = DB::table('student_class')
|
|
->where('student_id', $data['student_id'])
|
|
->where('school_year', $schoolYear)
|
|
->orderBy('id')
|
|
->first();
|
|
|
|
$classSectionId = $sc?->class_section_id ?? null;
|
|
|
|
$parentId = DB::table('students')
|
|
->where('id', $data['student_id'])
|
|
->value('parent_id') ?? Auth::id();
|
|
|
|
DB::table('parent_attendance_reports')->insert([
|
|
'parent_id' => $parentId ?? Auth::id(),
|
|
'student_id' => $data['student_id'],
|
|
'class_section_id' => $classSectionId,
|
|
'report_date' => $data['date'],
|
|
'type' => 'early_dismissal',
|
|
'dismiss_time' => $data['dismiss_time'],
|
|
'reason' => $data['reason'] ?? null,
|
|
'semester' => $semester,
|
|
'school_year' => $schoolYear,
|
|
'status' => 'new',
|
|
'created_at' => now(),
|
|
'updated_at' => now(),
|
|
]);
|
|
|
|
return $this->success([], 'Early dismissal saved.', Response::HTTP_CREATED);
|
|
}
|
|
|
|
/**
|
|
* POST /api/v1/attendance/early-dismissals/signature
|
|
* Uploads (or replaces) the signature file for a report date.
|
|
*/
|
|
public function uploadSignature(Request $request): JsonResponse
|
|
{
|
|
$validator = Validator::make($request->all(), [
|
|
'report_date' => ['required', 'date_format:Y-m-d'],
|
|
'school_year' => ['nullable', 'string', 'max:16'],
|
|
'semester' => ['nullable', 'string', 'max:32'],
|
|
'signature_file' => ['required', 'file', 'mimes:jpg,jpeg,png,webp,gif,pdf', 'max:5120'],
|
|
]);
|
|
|
|
if ($validator->fails()) {
|
|
return $this->respondValidationError($validator->errors()->toArray());
|
|
}
|
|
|
|
$file = $request->file('signature_file');
|
|
$reportDate = $request->input('report_date');
|
|
$schoolYear = $request->input('school_year') ?: (string) Configuration::getConfigValueByKey('school_year');
|
|
$semester = $request->input('semester') ?: (string) Configuration::getConfigValueByKey('semester');
|
|
|
|
$dir = storage_path('uploads/early_dismissal_signatures');
|
|
if (!is_dir($dir)) {
|
|
mkdir($dir, 0755, true);
|
|
}
|
|
|
|
$existing = EarlyDismissalSignature::query()
|
|
->where('report_date', $reportDate)
|
|
->where('school_year', $schoolYear)
|
|
->where('semester', $semester)
|
|
->first();
|
|
|
|
if ($existing?->filename && file_exists($dir . '/' . $existing->filename)) {
|
|
unlink($dir . '/' . $existing->filename);
|
|
}
|
|
|
|
$filename = uniqid('sig_', true) . '.' . $file->getClientOriginalExtension();
|
|
$file->move($dir, $filename);
|
|
|
|
EarlyDismissalSignature::updateOrCreate(
|
|
['report_date' => $reportDate, 'school_year' => $schoolYear, 'semester' => $semester],
|
|
[
|
|
'filename' => $filename,
|
|
'original_name' => $file->getClientOriginalName(),
|
|
'mime_type' => $file->getClientMimeType(),
|
|
'file_size' => $file->getSize(),
|
|
'uploaded_by' => Auth::id(),
|
|
]
|
|
);
|
|
|
|
return $this->success(['filename' => $filename], 'Signature uploaded.');
|
|
}
|
|
}
|