reconstruction of the project
This commit is contained in:
@@ -0,0 +1,220 @@
|
||||
<?php
|
||||
|
||||
namespace App\Services\Attendance;
|
||||
|
||||
use App\Libraries\AttendanceAutoPublish;
|
||||
use App\Models\AttendanceData;
|
||||
use App\Models\AttendanceDay;
|
||||
use App\Models\Student;
|
||||
use App\Models\TeacherClass;
|
||||
use Carbon\Carbon;
|
||||
use Illuminate\Contracts\Auth\Authenticatable;
|
||||
use Illuminate\Support\Facades\DB;
|
||||
use RuntimeException;
|
||||
|
||||
class TeacherAttendanceSubmissionService
|
||||
{
|
||||
public function __construct(
|
||||
protected AttendanceDay $attendanceDay,
|
||||
protected AttendanceData $attendanceData,
|
||||
protected TeacherClass $teacherClass,
|
||||
protected Student $student,
|
||||
protected AttendancePolicyService $attendancePolicyService,
|
||||
protected StudentAttendanceWriterService $studentAttendanceWriterService,
|
||||
) {}
|
||||
|
||||
public function submit(
|
||||
Authenticatable $user,
|
||||
array $data,
|
||||
callable $detectExternalSubmission,
|
||||
callable $currentSemester,
|
||||
callable $currentSchoolYear
|
||||
): array {
|
||||
$rawSection = trim((string)$data['class_section_id']);
|
||||
|
||||
if ($rawSection === '') {
|
||||
throw new RuntimeException('Missing or invalid class section.');
|
||||
}
|
||||
|
||||
$sectionRow = DB::table('classSection')
|
||||
->select('id', 'class_id', 'class_section_id')
|
||||
->where('class_section_id', $rawSection)
|
||||
->first();
|
||||
|
||||
if (!$sectionRow && ctype_digit($rawSection)) {
|
||||
$sectionRow = DB::table('classSection')
|
||||
->select('id', 'class_id', 'class_section_id')
|
||||
->where('id', (int)$rawSection)
|
||||
->first();
|
||||
}
|
||||
|
||||
if (!$sectionRow) {
|
||||
throw new RuntimeException('Invalid class section.');
|
||||
}
|
||||
|
||||
$classSectionId = (int)$sectionRow->class_section_id;
|
||||
$classId = (int)$sectionRow->class_id;
|
||||
$attendanceData = $data['attendance'] ?? [];
|
||||
$teachersData = $data['teachers'] ?? [];
|
||||
|
||||
if (empty($attendanceData) || !is_array($attendanceData)) {
|
||||
throw new RuntimeException('No student attendance data provided.');
|
||||
}
|
||||
|
||||
if (empty($teachersData) || !is_array($teachersData)) {
|
||||
throw new RuntimeException('No teacher attendance data provided.');
|
||||
}
|
||||
|
||||
$rawDate = $data['date'] ?? now()->toDateString();
|
||||
$attendDate = Carbon::parse($rawDate)->toDateString();
|
||||
$semester = (string)$currentSemester();
|
||||
$schoolYear = (string)$currentSchoolYear();
|
||||
|
||||
$dayRow = AttendanceDay::query()
|
||||
->where('class_section_id', $classSectionId)
|
||||
->whereDate('date', $attendDate)
|
||||
->where('semester', $semester)
|
||||
->where('school_year', $schoolYear)
|
||||
->first();
|
||||
|
||||
$currentUser = [
|
||||
'id' => $user->id,
|
||||
'roles' => method_exists($user, 'roles') ? $user->roles->pluck('name')->all() : [],
|
||||
'permissions' => method_exists($user, 'permissions') ? $user->permissions->pluck('name')->all() : [],
|
||||
];
|
||||
|
||||
$dayPolicyRow = $dayRow?->toArray() ?? [
|
||||
'status' => 'draft',
|
||||
'class_section_id' => $classSectionId,
|
||||
'date' => $attendDate,
|
||||
'semester' => $semester,
|
||||
'school_year' => $schoolYear,
|
||||
];
|
||||
|
||||
if (!$this->attendancePolicyService->canEditDay($dayPolicyRow, $currentUser)) {
|
||||
throw new RuntimeException('Attendance is locked for your role.');
|
||||
}
|
||||
|
||||
$existingRows = AttendanceData::query()
|
||||
->where('class_section_id', $classSectionId)
|
||||
->whereDate('date', $attendDate)
|
||||
->where('semester', $semester)
|
||||
->where('school_year', $schoolYear)
|
||||
->get();
|
||||
|
||||
$existingByStudent = [];
|
||||
foreach ($existingRows as $row) {
|
||||
$existingByStudent[(int)$row->student_id] = $row->toArray();
|
||||
}
|
||||
|
||||
$allowedStatuses = ['present', 'absent', 'late'];
|
||||
$assignedTeachers = $this->teacherClass->assignedForSectionTerm($classSectionId, $semester, $schoolYear);
|
||||
|
||||
foreach ($assignedTeachers as $teacher) {
|
||||
$teacherId = (int)$teacher['teacher_id'];
|
||||
$status = strtolower((string)($teachersData[$teacherId]['status'] ?? ''));
|
||||
|
||||
if (!in_array($status, $allowedStatuses, true)) {
|
||||
throw new RuntimeException('Please fill teacher attendance for all assigned teachers.');
|
||||
}
|
||||
}
|
||||
|
||||
$isTeacher = $this->attendancePolicyService->isTeacher($currentUser['roles']);
|
||||
|
||||
DB::transaction(function () use (
|
||||
$user,
|
||||
$assignedTeachers,
|
||||
$teachersData,
|
||||
$attendanceData,
|
||||
$existingByStudent,
|
||||
$classSectionId,
|
||||
$classId,
|
||||
$attendDate,
|
||||
$semester,
|
||||
$schoolYear,
|
||||
$isTeacher,
|
||||
$detectExternalSubmission
|
||||
) {
|
||||
$dayRow = AttendanceDay::query()->firstOrCreate(
|
||||
[
|
||||
'class_section_id' => $classSectionId,
|
||||
'date' => $attendDate,
|
||||
'semester' => $semester,
|
||||
'school_year' => $schoolYear,
|
||||
],
|
||||
[
|
||||
'status' => 'draft',
|
||||
'created_at' => now(),
|
||||
'updated_at' => now(),
|
||||
]
|
||||
);
|
||||
|
||||
foreach ($assignedTeachers as $teacher) {
|
||||
$teacherId = (int)$teacher['teacher_id'];
|
||||
$position = strtolower((string)($teacher['position'] ?? 'main'));
|
||||
$position = $position === 'ta' ? 'ta' : 'main';
|
||||
|
||||
DB::table('staff_attendance')->updateOrInsert(
|
||||
[
|
||||
'user_id' => $teacherId,
|
||||
'date' => $attendDate,
|
||||
'semester' => $semester,
|
||||
'school_year' => $schoolYear,
|
||||
],
|
||||
[
|
||||
'role_name' => 'Teacher',
|
||||
'class_section_id' => $classSectionId,
|
||||
'position' => $position,
|
||||
'status' => strtolower((string)($teachersData[$teacherId]['status'] ?? 'present')),
|
||||
'reason' => ($teachersData[$teacherId]['reason'] ?? null) ?: null,
|
||||
'updated_at' => now(),
|
||||
'created_at' => now(),
|
||||
]
|
||||
);
|
||||
}
|
||||
|
||||
foreach ($attendanceData as $row) {
|
||||
$studentId = (int)($row['student_id'] ?? 0);
|
||||
$status = strtolower(trim((string)($row['status'] ?? '')));
|
||||
$reason = trim((string)($row['reason'] ?? ''));
|
||||
$existing = $existingByStudent[$studentId] ?? null;
|
||||
|
||||
if ($isTeacher && $existing) {
|
||||
$lockInfo = $detectExternalSubmission($existing);
|
||||
if (($lockInfo['locked'] ?? false) === true) {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
$this->studentAttendanceWriterService->saveOrUpdateStudentAttendance([
|
||||
'class_section_id' => $classSectionId,
|
||||
'student_id' => $studentId,
|
||||
'school_id' => $row['school_id'] ?? null,
|
||||
'status' => $status,
|
||||
'reason' => $reason,
|
||||
'is_reported' => 'no',
|
||||
'semester' => $semester,
|
||||
'school_year' => $schoolYear,
|
||||
'date' => $attendDate,
|
||||
'class_id' => $classId,
|
||||
'user_id' => $user->id,
|
||||
]);
|
||||
}
|
||||
|
||||
$autoPublishAt = AttendanceAutoPublish::secondSundayAfterEndOfDay($attendDate);
|
||||
|
||||
$dayRow->update([
|
||||
'status' => 'submitted',
|
||||
'submitted_by' => $user->id,
|
||||
'submitted_at' => now(),
|
||||
'auto_publish_at' => $autoPublishAt,
|
||||
'updated_at' => now(),
|
||||
]);
|
||||
});
|
||||
|
||||
return [
|
||||
'ok' => true,
|
||||
'message' => 'Attendance submitted successfully.',
|
||||
];
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user