fix parent, teacher and admin pages
This commit is contained in:
@@ -15,6 +15,7 @@ use App\Models\User;
|
||||
use App\Models\UserRole;
|
||||
use Carbon\Carbon;
|
||||
use Illuminate\Support\Facades\DB;
|
||||
use Illuminate\Support\Facades\Schema;
|
||||
use RuntimeException;
|
||||
|
||||
class AttendanceQueryService
|
||||
@@ -181,6 +182,10 @@ class AttendanceQueryService
|
||||
$attendanceRows = [];
|
||||
$lockedByStudent = [];
|
||||
|
||||
$attendanceTable = $this->attendanceData->getTable();
|
||||
$dateInSundaysSql = 'DATE(`' . $attendanceTable . '`.`date`) IN (' .
|
||||
implode(',', array_fill(0, count($sundayDates), '?')) . ')';
|
||||
|
||||
foreach ($students as $student) {
|
||||
$studentId = (int)$student['id'];
|
||||
|
||||
@@ -189,11 +194,13 @@ class AttendanceQueryService
|
||||
->where('student_id', $studentId)
|
||||
->where('school_year', $schoolYear)
|
||||
->where('semester', $semester)
|
||||
->where(function ($q) use ($classSectionId) {
|
||||
$q->where('class_section_id', $classSectionId)
|
||||
->orWhere('class_section_id', (int)$classSectionId);
|
||||
->where(function ($q) use ($classSectionId, $classSectionPk) {
|
||||
$q->where('class_section_id', $classSectionId);
|
||||
if ($classSectionPk > 0 && $classSectionPk !== $classSectionId) {
|
||||
$q->orWhere('class_section_id', $classSectionPk);
|
||||
}
|
||||
})
|
||||
->whereIn('date', $sundayDates)
|
||||
->whereRaw($dateInSundaysSql, $sundayDates)
|
||||
->get()
|
||||
->map(fn($row) => (array)$row)
|
||||
->all();
|
||||
@@ -205,9 +212,11 @@ class AttendanceQueryService
|
||||
->where('student_id', $studentId)
|
||||
->where('school_year', $schoolYear)
|
||||
->where('semester', $semester)
|
||||
->where(function ($q) use ($classSectionId) {
|
||||
$q->where('class_section_id', $classSectionId)
|
||||
->orWhere('class_section_id', (int)$classSectionId);
|
||||
->where(function ($q) use ($classSectionId, $classSectionPk) {
|
||||
$q->where('class_section_id', $classSectionId);
|
||||
if ($classSectionPk > 0 && $classSectionPk !== $classSectionId) {
|
||||
$q->orWhere('class_section_id', $classSectionPk);
|
||||
}
|
||||
})
|
||||
->orderByDesc('date')
|
||||
->limit(3)
|
||||
@@ -225,7 +234,10 @@ class AttendanceQueryService
|
||||
$snapshot = [];
|
||||
$rowsByDate = [];
|
||||
foreach ($rows as $entry) {
|
||||
$d = (string)($entry['date'] ?? '');
|
||||
$d = $this->calendarDateKey($entry['date'] ?? null);
|
||||
if ($d === '') {
|
||||
continue;
|
||||
}
|
||||
$snapshot[$d] = $entry['status'] ?? null;
|
||||
$rowsByDate[$d] = $entry;
|
||||
}
|
||||
@@ -237,6 +249,23 @@ class AttendanceQueryService
|
||||
: ($date === $currentSunday ? null : 'N/A');
|
||||
}
|
||||
|
||||
// Recent list uses the same section filters but no Sunday window; if the windowed query
|
||||
// missed rows (SQL/driver quirks), still show statuses that match the visible columns.
|
||||
foreach ($recentRows as $row) {
|
||||
$d = $this->calendarDateKey($row['date'] ?? null);
|
||||
if ($d === '' || !in_array($d, $sundayDates, true)) {
|
||||
continue;
|
||||
}
|
||||
$rowStatus = $row['status'] ?? null;
|
||||
if ($rowStatus === null || $rowStatus === '') {
|
||||
continue;
|
||||
}
|
||||
$current = $statusHistory[$d] ?? null;
|
||||
if ($current === 'N/A' || $current === null) {
|
||||
$statusHistory[$d] = $rowStatus;
|
||||
}
|
||||
}
|
||||
|
||||
$todayRow = $rowsByDate[$currentSunday] ?? null;
|
||||
$lockInfo = $this->attendanceService->detectExternalSubmission($todayRow);
|
||||
$todayReason = $todayRow['reason'] ?? null;
|
||||
@@ -259,7 +288,56 @@ class AttendanceQueryService
|
||||
];
|
||||
}
|
||||
|
||||
$assigned = $this->teacherClass->assignedForSectionTerm($classSectionId, $semester, $schoolYear);
|
||||
$candidates = array_values(array_unique(array_filter(
|
||||
[$classSectionCode, $classSectionPk],
|
||||
static fn (int $v): bool => $v > 0,
|
||||
)));
|
||||
$alsoSectionId = null;
|
||||
foreach ($candidates as $cid) {
|
||||
if ($cid !== $classSectionId) {
|
||||
$alsoSectionId = $cid;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
$assigned = $this->teacherClass->assignedForSectionTerm(
|
||||
$classSectionId,
|
||||
$semester,
|
||||
$schoolYear,
|
||||
$alsoSectionId,
|
||||
true,
|
||||
);
|
||||
if ($assigned === []) {
|
||||
$assigned = $this->teacherClass->assignedForSectionTerm(
|
||||
$classSectionId,
|
||||
$semester,
|
||||
$schoolYear,
|
||||
$alsoSectionId,
|
||||
false,
|
||||
);
|
||||
}
|
||||
if ($assigned === []) {
|
||||
$assigned = $this->teacherRosterFromStaffAttendance(
|
||||
$sundayDates,
|
||||
$semester,
|
||||
$schoolYear,
|
||||
$classSectionId,
|
||||
$classSectionPk,
|
||||
);
|
||||
}
|
||||
if ($assigned === [] && $userId > 0) {
|
||||
$self = $this->user->query()->select(['id', 'firstname', 'lastname'])->find($userId);
|
||||
if ($self) {
|
||||
$assigned = [[
|
||||
'class_section_id' => $classSectionId,
|
||||
'teacher_id' => (int) $self->id,
|
||||
'position' => 'main',
|
||||
'firstname' => $self->firstname,
|
||||
'lastname' => $self->lastname,
|
||||
]];
|
||||
}
|
||||
}
|
||||
|
||||
$teacherRows = [];
|
||||
|
||||
if (!empty($assigned)) {
|
||||
@@ -267,16 +345,34 @@ class AttendanceQueryService
|
||||
|
||||
$statusMap = [];
|
||||
if (!empty($teacherIds)) {
|
||||
$rows = DB::table('staff_attendance as sa')
|
||||
$staffDateInSql = 'DATE(`sa`.`date`) IN (' .
|
||||
implode(',', array_fill(0, count($sundayDates), '?')) . ')';
|
||||
$staffQ = DB::table('staff_attendance as sa')
|
||||
->select('sa.user_id', 'sa.date', 'sa.status', 'sa.reason')
|
||||
->where('sa.semester', $semester)
|
||||
->where('sa.school_year', $schoolYear)
|
||||
->whereIn('sa.user_id', $teacherIds)
|
||||
->whereIn('sa.date', $sundayDates)
|
||||
->get();
|
||||
->whereRaw($staffDateInSql, $sundayDates);
|
||||
|
||||
if (Schema::hasColumn('staff_attendance', 'class_section_id')) {
|
||||
$staffQ->where(function ($w) use ($classSectionId, $classSectionPk) {
|
||||
$w->whereNull('sa.class_section_id')
|
||||
->orWhere('sa.class_section_id', 0)
|
||||
->orWhere('sa.class_section_id', $classSectionId);
|
||||
if ($classSectionPk > 0 && $classSectionPk !== $classSectionId) {
|
||||
$w->orWhere('sa.class_section_id', $classSectionPk);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
$rows = $staffQ->get();
|
||||
|
||||
foreach ($rows as $row) {
|
||||
$statusMap[(int)$row->user_id][(string)$row->date] = [
|
||||
$dk = $this->calendarDateKey($row->date ?? null);
|
||||
if ($dk === '') {
|
||||
continue;
|
||||
}
|
||||
$statusMap[(int)$row->user_id][$dk] = [
|
||||
'status' => $row->status,
|
||||
'reason' => $row->reason,
|
||||
];
|
||||
@@ -296,27 +392,39 @@ class AttendanceQueryService
|
||||
: ($date === $currentSunday ? null : 'N/A');
|
||||
}
|
||||
|
||||
$todayReason = null;
|
||||
if (isset($statusMap[$teacherId][$currentSunday]) && is_array($statusMap[$teacherId][$currentSunday])) {
|
||||
$todayReason = $statusMap[$teacherId][$currentSunday]['reason'] ?? null;
|
||||
}
|
||||
|
||||
$teacherRows[] = [
|
||||
'teacher_id' => $teacherId,
|
||||
'position' => $position,
|
||||
'name' => $name !== '' ? $name : 'User#' . $teacherId,
|
||||
'sunday_statuses' => $history,
|
||||
'today' => $history[$currentSunday] ?? null,
|
||||
'today_reason' => $todayReason,
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
$noSchoolDays = [];
|
||||
$calendarTable = $this->calendar->getTable();
|
||||
$calDateInSql = 'DATE(`' . $calendarTable . '`.`date`) IN (' .
|
||||
implode(',', array_fill(0, count($sundayDates), '?')) . ')';
|
||||
$calendarRows = $this->calendar->query()
|
||||
->where('no_school', 1)
|
||||
->whereIn('date', $sundayDates)
|
||||
->whereRaw($calDateInSql, $sundayDates)
|
||||
->where('semester', $semester)
|
||||
->where('school_year', $schoolYear)
|
||||
->get()
|
||||
->toArray();
|
||||
|
||||
foreach ($calendarRows as $row) {
|
||||
$d = (string)($row['date'] ?? '');
|
||||
$d = $this->calendarDateKey($row['date'] ?? null);
|
||||
if ($d === '') {
|
||||
continue;
|
||||
}
|
||||
$noSchoolDays[$d] = [
|
||||
'title' => $row['title'] ?? 'No School',
|
||||
'description' => $row['description'] ?? '',
|
||||
@@ -601,4 +709,171 @@ class AttendanceQueryService
|
||||
'currentAdminName' => $adminName,
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Distinct user_ids with staff rows on the given Sundays, scoped by teacher_class section assignment
|
||||
* (works when staff_attendance has no class_section_id column).
|
||||
*
|
||||
* @return list<int>
|
||||
*/
|
||||
private function distinctStaffAttendeeIdsViaTeacherClassJoin(
|
||||
array $sundayDates,
|
||||
string $semester,
|
||||
string $schoolYear,
|
||||
int $classSectionId,
|
||||
int $classSectionPk,
|
||||
): array {
|
||||
$sectionIds = array_values(array_unique(array_filter(
|
||||
[$classSectionId, $classSectionPk],
|
||||
static fn (int $v): bool => $v > 0,
|
||||
)));
|
||||
if ($sundayDates === [] || $sectionIds === []) {
|
||||
return [];
|
||||
}
|
||||
|
||||
$staffDateInSql = 'DATE(`sa`.`date`) IN (' .
|
||||
implode(',', array_fill(0, count($sundayDates), '?')) . ')';
|
||||
|
||||
$run = static function (
|
||||
bool $requireTcSemester,
|
||||
bool $requireSaSemester,
|
||||
) use ($sundayDates, $schoolYear, $semester, $staffDateInSql, $sectionIds): array {
|
||||
$q = DB::table('staff_attendance as sa')
|
||||
->join('teacher_class as tc', function ($j) use ($sectionIds, $schoolYear, $requireTcSemester, $semester) {
|
||||
$j->on('tc.teacher_id', '=', 'sa.user_id')
|
||||
->whereIn('tc.class_section_id', $sectionIds)
|
||||
->where('tc.school_year', '=', $schoolYear);
|
||||
if ($requireTcSemester && trim($semester) !== '') {
|
||||
$j->whereRaw('TRIM(tc.semester) = ?', [trim($semester)]);
|
||||
}
|
||||
})
|
||||
->where('sa.school_year', $schoolYear)
|
||||
->whereRaw($staffDateInSql, $sundayDates);
|
||||
if ($requireSaSemester && trim($semester) !== '') {
|
||||
$q->whereRaw('TRIM(sa.semester) = ?', [trim($semester)]);
|
||||
}
|
||||
|
||||
return $q->distinct()
|
||||
->pluck('sa.user_id')
|
||||
->map(static fn ($v): int => (int) $v)
|
||||
->filter(static fn (int $v): bool => $v > 0)
|
||||
->values()
|
||||
->all();
|
||||
};
|
||||
|
||||
$ids = $run(true, true);
|
||||
if ($ids === []) {
|
||||
$ids = $run(false, true);
|
||||
}
|
||||
if ($ids === []) {
|
||||
$ids = $run(false, false);
|
||||
}
|
||||
|
||||
return $ids;
|
||||
}
|
||||
|
||||
/**
|
||||
* When teacher_class has no rows, build a roster from staff_attendance for this section/window.
|
||||
*
|
||||
* @return array<int, array{class_section_id:int, teacher_id:int, position:string, firstname:?string, lastname:?string}>
|
||||
*/
|
||||
private function teacherRosterFromStaffAttendance(
|
||||
array $sundayDates,
|
||||
string $semester,
|
||||
string $schoolYear,
|
||||
int $classSectionId,
|
||||
int $classSectionPk,
|
||||
): array {
|
||||
if ($sundayDates === []) {
|
||||
return [];
|
||||
}
|
||||
|
||||
$staffDateInSql = 'DATE(`sa`.`date`) IN (' .
|
||||
implode(',', array_fill(0, count($sundayDates), '?')) . ')';
|
||||
|
||||
$ids = [];
|
||||
|
||||
if (Schema::hasColumn('staff_attendance', 'class_section_id')) {
|
||||
$applySectionScope = function ($query) use ($classSectionId, $classSectionPk): void {
|
||||
$query->where(function ($w) use ($classSectionId, $classSectionPk) {
|
||||
$w->whereNull('sa.class_section_id')
|
||||
->orWhere('sa.class_section_id', 0)
|
||||
->orWhere('sa.class_section_id', $classSectionId);
|
||||
if ($classSectionPk > 0 && $classSectionPk !== $classSectionId) {
|
||||
$w->orWhere('sa.class_section_id', $classSectionPk);
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
$base = function (bool $useSemester) use ($sundayDates, $schoolYear, $semester, $staffDateInSql, $applySectionScope) {
|
||||
$q = DB::table('staff_attendance as sa')
|
||||
->where('sa.school_year', $schoolYear)
|
||||
->whereRaw($staffDateInSql, $sundayDates);
|
||||
if ($useSemester && trim($semester) !== '') {
|
||||
$q->whereRaw('TRIM(sa.semester) = ?', [trim($semester)]);
|
||||
}
|
||||
$applySectionScope($q);
|
||||
|
||||
return $q->distinct()->pluck('sa.user_id')->map(static fn ($v) => (int) $v)->filter(static fn (int $v) => $v > 0)->values()->all();
|
||||
};
|
||||
|
||||
$ids = $base(true);
|
||||
if ($ids === []) {
|
||||
$ids = $base(false);
|
||||
}
|
||||
}
|
||||
|
||||
if ($ids === []) {
|
||||
$ids = $this->distinctStaffAttendeeIdsViaTeacherClassJoin(
|
||||
$sundayDates,
|
||||
$semester,
|
||||
$schoolYear,
|
||||
$classSectionId,
|
||||
$classSectionPk,
|
||||
);
|
||||
}
|
||||
|
||||
if ($ids === []) {
|
||||
return [];
|
||||
}
|
||||
|
||||
$users = $this->user->query()
|
||||
->select(['id', 'firstname', 'lastname'])
|
||||
->whereIn('id', $ids)
|
||||
->orderBy('firstname')
|
||||
->orderBy('lastname')
|
||||
->get();
|
||||
|
||||
$out = [];
|
||||
foreach ($users as $u) {
|
||||
$out[] = [
|
||||
'class_section_id' => $classSectionId,
|
||||
'teacher_id' => (int) $u->id,
|
||||
'position' => 'main',
|
||||
'firstname' => $u->firstname,
|
||||
'lastname' => $u->lastname,
|
||||
];
|
||||
}
|
||||
|
||||
return $out;
|
||||
}
|
||||
|
||||
/**
|
||||
* Normalize DB datetimes / date strings to Y-m-d so they match {@see $sundayDates} keys.
|
||||
*/
|
||||
private function calendarDateKey(mixed $value): string
|
||||
{
|
||||
$s = trim((string)($value ?? ''));
|
||||
if ($s === '') {
|
||||
return '';
|
||||
}
|
||||
if (preg_match('/^(\d{4}-\d{2}-\d{2})/', $s, $m)) {
|
||||
return $m[1];
|
||||
}
|
||||
try {
|
||||
return Carbon::parse($s)->toDateString();
|
||||
} catch (\Throwable) {
|
||||
return '';
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user