reconstruction of the project
This commit is contained in:
+356
-399
@@ -2,15 +2,25 @@
|
||||
|
||||
namespace App\Models;
|
||||
|
||||
use Illuminate\Database\Eloquent\Builder;
|
||||
use App\Models\BaseModel;
|
||||
use Config\Services;
|
||||
use Illuminate\Database\Eloquent\Relations\BelongsTo;
|
||||
use Illuminate\Support\Facades\DB;
|
||||
|
||||
class StaffAttendance extends BaseModel
|
||||
{
|
||||
protected $table = 'staff_attendance';
|
||||
protected $primaryKey = 'id';
|
||||
protected $useAutoIncrement = true;
|
||||
|
||||
// ✅ Only columns that really exist in staff_attendance
|
||||
public $timestamps = true;
|
||||
|
||||
/**
|
||||
* IMPORTANT:
|
||||
* Your earlier comment said only these columns exist.
|
||||
* But later methods use class_section_id + position.
|
||||
*
|
||||
* ✅ If class_section_id/position DO exist in your DB, add them to fillable.
|
||||
* ❌ If they do NOT exist, remove them and also remove related methods/scopes.
|
||||
*/
|
||||
protected $fillable = [
|
||||
'user_id',
|
||||
'role_name',
|
||||
@@ -24,20 +34,107 @@ class StaffAttendance extends BaseModel
|
||||
'created_by',
|
||||
'updated_by',
|
||||
];
|
||||
public $timestamps = true;
|
||||
const CREATED_AT = 'created_at';
|
||||
const UPDATED_AT = 'updated_at';
|
||||
|
||||
// (Optional helper, keep if you like)
|
||||
public function currentEditorId(): int
|
||||
protected $casts = [
|
||||
'user_id' => 'integer',
|
||||
'class_section_id' => 'integer',
|
||||
'created_by' => 'integer',
|
||||
'updated_by' => 'integer',
|
||||
'date' => 'date:Y-m-d',
|
||||
'created_at' => 'datetime',
|
||||
'updated_at' => 'datetime',
|
||||
];
|
||||
|
||||
/* ============================================================
|
||||
* Constants
|
||||
* ============================================================
|
||||
*/
|
||||
public const STATUS_PRESENT = 'present';
|
||||
public const STATUS_ABSENT = 'absent';
|
||||
public const STATUS_LATE = 'late';
|
||||
|
||||
public const POS_MAIN = 'main';
|
||||
public const POS_TA = 'ta';
|
||||
|
||||
public static function allowedStatuses(): array
|
||||
{
|
||||
return (int) (session()->get('user_id') ?? 0);
|
||||
return [self::STATUS_PRESENT, self::STATUS_ABSENT, self::STATUS_LATE];
|
||||
}
|
||||
|
||||
/**
|
||||
* Upsert by (user_id, date, semester, school_year).
|
||||
/* ============================================================
|
||||
* Relationships (optional)
|
||||
* ============================================================
|
||||
*/
|
||||
public function upsertOne(
|
||||
public function user(): BelongsTo
|
||||
{
|
||||
return $this->belongsTo(User::class, 'user_id');
|
||||
}
|
||||
|
||||
public function creator(): BelongsTo
|
||||
{
|
||||
return $this->belongsTo(User::class, 'created_by'); // change to Admin if needed
|
||||
}
|
||||
|
||||
public function updater(): BelongsTo
|
||||
{
|
||||
return $this->belongsTo(User::class, 'updated_by'); // change to Admin if needed
|
||||
}
|
||||
|
||||
public function classSection(): BelongsTo
|
||||
{
|
||||
return $this->belongsTo(ClassSection::class, 'class_section_id');
|
||||
}
|
||||
|
||||
/* ============================================================
|
||||
* Scopes
|
||||
* ============================================================
|
||||
*/
|
||||
public function scopeForDay(Builder $q, string $date): Builder
|
||||
{
|
||||
return $q->whereDate('date', $date);
|
||||
}
|
||||
|
||||
public function scopeForTerm(Builder $q, string $semester, string $schoolYear): Builder
|
||||
{
|
||||
return $q->where('semester', $semester)->where('school_year', $schoolYear);
|
||||
}
|
||||
|
||||
public function scopeForUser(Builder $q, int $userId): Builder
|
||||
{
|
||||
return $q->where('user_id', $userId);
|
||||
}
|
||||
|
||||
public function scopeForSection(Builder $q, int $classSectionId): Builder
|
||||
{
|
||||
return $q->where('class_section_id', $classSectionId);
|
||||
}
|
||||
|
||||
/* ============================================================
|
||||
* Helpers
|
||||
* ============================================================
|
||||
*/
|
||||
protected static function normStatus(?string $status): ?string
|
||||
{
|
||||
$s = strtolower(trim((string) $status));
|
||||
return in_array($s, self::allowedStatuses(), true) ? $s : null;
|
||||
}
|
||||
|
||||
protected static function isValidDate(string $d): bool
|
||||
{
|
||||
$dt = date_create_from_format('Y-m-d', $d);
|
||||
return $dt && $dt->format('Y-m-d') === $d;
|
||||
}
|
||||
|
||||
/* ============================================================
|
||||
* Upserts (Laravel style)
|
||||
* ============================================================
|
||||
*/
|
||||
|
||||
/**
|
||||
* Upsert by (user_id, date, semester, school_year)
|
||||
* Returns saved model or null if invalid.
|
||||
*/
|
||||
public static function upsertOne(
|
||||
int $userId,
|
||||
?string $roleName,
|
||||
string $date,
|
||||
@@ -46,72 +143,44 @@ class StaffAttendance extends BaseModel
|
||||
string $status,
|
||||
?string $reason,
|
||||
?int $editorId = null
|
||||
): bool {
|
||||
$existing = $this->where([
|
||||
'user_id' => $userId,
|
||||
'date' => $date,
|
||||
'semester' => $semester,
|
||||
'school_year' => $schoolYear,
|
||||
])->first();
|
||||
|
||||
$row = [
|
||||
'user_id' => $userId,
|
||||
'role_name' => $roleName,
|
||||
'date' => $date,
|
||||
'semester' => $semester,
|
||||
'school_year' => $schoolYear,
|
||||
'status' => $status,
|
||||
'reason' => $reason ?: null,
|
||||
'updated_by' => $editorId ?? $this->currentEditorId(),
|
||||
];
|
||||
|
||||
if ($existing) {
|
||||
return $this->update((int)$existing['id'], $row);
|
||||
): ?self {
|
||||
if ($userId <= 0 || !self::isValidDate($date) || $semester === '' || $schoolYear === '') {
|
||||
return null;
|
||||
}
|
||||
|
||||
$row['created_by'] = $editorId ?? $this->currentEditorId();
|
||||
return $this->insert($row) !== false;
|
||||
$norm = self::normStatus($status);
|
||||
if ($norm === null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
$editorId = $editorId ?: (auth()->id() ?? null);
|
||||
|
||||
$keys = [
|
||||
'user_id' => $userId,
|
||||
'date' => $date,
|
||||
'semester' => $semester,
|
||||
'school_year' => $schoolYear,
|
||||
];
|
||||
|
||||
$payload = [
|
||||
'role_name' => $roleName,
|
||||
'status' => $norm,
|
||||
'reason' => $reason ?: null,
|
||||
'updated_by' => $editorId,
|
||||
];
|
||||
|
||||
// Ensure created_by is set on create
|
||||
return static::query()->updateOrCreate($keys, $payload + [
|
||||
'created_by' => $editorId,
|
||||
]);
|
||||
}
|
||||
|
||||
// Optional: helpers/constants
|
||||
public const STATUS_PRESENT = 'present';
|
||||
public const STATUS_ABSENT = 'absent';
|
||||
public const STATUS_LATE = 'late';
|
||||
|
||||
public const POS_MAIN = 'main';
|
||||
public const POS_TA = 'ta';
|
||||
|
||||
/** Normalize/validate a status string; return null for empty/invalid (means delete). */
|
||||
protected function normStatus(?string $status): ?string
|
||||
{
|
||||
$s = strtolower((string) $status);
|
||||
return in_array($s, [self::STATUS_PRESENT, self::STATUS_ABSENT, self::STATUS_LATE], true) ? $s : null;
|
||||
}
|
||||
|
||||
/** Quick session user id (safe). */
|
||||
protected function currentUserId(): ?int
|
||||
{
|
||||
$session = session();
|
||||
return $session ? ($session->get('user_id') ?: null) : null;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Upsert a single cell by (user_id, date).
|
||||
* - If $status is empty/invalid => delete the record for that day (if exists).
|
||||
* - Otherwise upsert with provided fields.
|
||||
*
|
||||
* @param int $userId
|
||||
* @param string $date Y-m-d
|
||||
* @param string|null $status present|absent|late or ''/null to delete
|
||||
* @param string $semester
|
||||
* @param string $schoolYear
|
||||
* @param string|null $roleName
|
||||
* @param int|null $classSectionId
|
||||
* @param string|null $position main|ta (nullable for non-teachers)
|
||||
* @param string|null $reason
|
||||
* - If status empty/invalid => delete record for that day (if exists) and return true.
|
||||
* - Otherwise upsert.
|
||||
*/
|
||||
public function upsertCell(
|
||||
public static function upsertCell(
|
||||
int $userId,
|
||||
string $date,
|
||||
?string $status,
|
||||
@@ -120,393 +189,281 @@ class StaffAttendance extends BaseModel
|
||||
?string $roleName = null,
|
||||
?int $classSectionId = null,
|
||||
?string $position = null,
|
||||
?string $reason = null
|
||||
?string $reason = null,
|
||||
?int $editorId = null
|
||||
): bool {
|
||||
if ($userId <= 0 || !$this->isValidDate($date) || $semester === '' || $schoolYear === '') {
|
||||
if ($userId <= 0 || !self::isValidDate($date) || $semester === '' || $schoolYear === '') {
|
||||
return false;
|
||||
}
|
||||
|
||||
$existing = $this->where(['user_id' => $userId, 'date' => $date])->first();
|
||||
$norm = $this->normStatus($status);
|
||||
$editorId = $editorId ?: (auth()->id() ?? null);
|
||||
|
||||
$existing = static::query()
|
||||
->where('user_id', $userId)
|
||||
->whereDate('date', $date)
|
||||
->first();
|
||||
|
||||
$norm = self::normStatus($status);
|
||||
|
||||
// DELETE if status cleared
|
||||
if ($norm === null) {
|
||||
if ($existing) {
|
||||
return (bool) $this->delete((int)$existing['id']);
|
||||
return (bool) $existing->delete();
|
||||
}
|
||||
return true; // nothing to delete
|
||||
return true;
|
||||
}
|
||||
|
||||
// UPSERT
|
||||
$row = [
|
||||
'user_id' => $userId,
|
||||
'role_name' => $roleName ?? ($existing['role_name'] ?? null),
|
||||
'class_section_id' => $classSectionId,
|
||||
'position' => $position,
|
||||
'date' => $date,
|
||||
'semester' => $semester,
|
||||
'school_year' => $schoolYear,
|
||||
'status' => $norm,
|
||||
'reason' => $reason,
|
||||
'updated_by' => $this->currentUserId(),
|
||||
$keys = [
|
||||
'user_id' => $userId,
|
||||
'date' => $date,
|
||||
'semester' => $semester,
|
||||
'school_year' => $schoolYear,
|
||||
];
|
||||
|
||||
if ($existing) {
|
||||
$row['id'] = (int)$existing['id'];
|
||||
return (bool) $this->save($row);
|
||||
}
|
||||
$payload = [
|
||||
'role_name' => $roleName ?? ($existing->role_name ?? null),
|
||||
'class_section_id' => $classSectionId,
|
||||
'position' => $position,
|
||||
'status' => $norm,
|
||||
'reason' => $reason,
|
||||
'updated_by' => $editorId,
|
||||
];
|
||||
|
||||
$row['created_by'] = $this->currentUserId();
|
||||
return $this->insert($row) !== false;
|
||||
static::query()->updateOrCreate($keys, $payload + ['created_by' => $editorId]);
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return all staff (non Parent/Guest) with their status for a given day/term.
|
||||
* If a user has multiple roles, this picks a stable one via MIN(r.name).
|
||||
*/
|
||||
public function staffWithStatusForDay(string $date, string $semester, string $schoolYear, array $excludeUserIds = []): array
|
||||
public static function getOne(int $userId, string $date): ?self
|
||||
{
|
||||
$db = $this->db;
|
||||
return static::query()->where('user_id', $userId)->whereDate('date', $date)->first();
|
||||
}
|
||||
|
||||
// Subquery to pick one role per user (MIN by name for stability)
|
||||
$roleSub = $db->table('user_roles ur')
|
||||
->select('ur.user_id, MIN(r.name) AS role_name')
|
||||
->join('roles r', 'r.id = ur.role_id', 'left')
|
||||
public static function deleteOne(int $userId, string $date): bool
|
||||
{
|
||||
$row = static::getOne($userId, $date);
|
||||
return $row ? (bool) $row->delete() : true;
|
||||
}
|
||||
|
||||
/* ============================================================
|
||||
* Staff list with status for a day (users + roles + staff_attendance)
|
||||
* Mirrors your CI staffWithStatusForDay()
|
||||
* ============================================================
|
||||
*/
|
||||
public static function staffWithStatusForDay(
|
||||
string $date,
|
||||
string $semester,
|
||||
string $schoolYear,
|
||||
array $excludeUserIds = []
|
||||
): array {
|
||||
// Subquery: pick one stable role name per user (MIN(name))
|
||||
$roleSub = DB::table('user_roles as ur')
|
||||
->selectRaw('ur.user_id, MIN(r.name) as role_name')
|
||||
->leftJoin('roles as r', 'r.id', '=', 'ur.role_id')
|
||||
->groupBy('ur.user_id');
|
||||
|
||||
$builder = $db->table('users u')
|
||||
->select("
|
||||
u.id AS user_id,
|
||||
$q = DB::table('users as u')
|
||||
->selectRaw("
|
||||
u.id as user_id,
|
||||
u.firstname,
|
||||
u.lastname,
|
||||
COALESCE(rs.role_name, '') AS role_name,
|
||||
sa.id AS satt_id,
|
||||
COALESCE(rs.role_name, '') as role_name,
|
||||
sa.id as satt_id,
|
||||
sa.status,
|
||||
sa.reason
|
||||
")
|
||||
->joinSubquery($roleSub, 'rs', 'rs.user_id = u.id', 'left')
|
||||
->join('staff_attendance sa', "sa.user_id = u.id AND sa.date = {$db->escape($date)} AND sa.semester = {$db->escape($semester)} AND sa.school_year = {$db->escape($schoolYear)}", 'left')
|
||||
->whereNotIn('LOWER(rs.role_name)', ['parent', 'guest'])
|
||||
->orderBy('u.firstname', 'ASC')
|
||||
->orderBy('u.lastname', 'ASC');
|
||||
->leftJoinSub($roleSub, 'rs', fn ($j) => $j->on('rs.user_id', '=', 'u.id'))
|
||||
->leftJoin('staff_attendance as sa', function ($j) use ($date, $semester, $schoolYear) {
|
||||
$j->on('sa.user_id', '=', 'u.id')
|
||||
->whereDate('sa.date', '=', $date)
|
||||
->where('sa.semester', '=', $semester)
|
||||
->where('sa.school_year', '=', $schoolYear);
|
||||
})
|
||||
->whereNotIn(DB::raw('LOWER(COALESCE(rs.role_name,""))'), ['parent', 'guest'])
|
||||
->orderBy('u.firstname')
|
||||
->orderBy('u.lastname');
|
||||
|
||||
if (!empty($excludeUserIds)) {
|
||||
$builder->whereNotIn('u.id', $excludeUserIds);
|
||||
$q->whereNotIn('u.id', array_values(array_map('intval', $excludeUserIds)));
|
||||
}
|
||||
|
||||
return $builder->get()->getResultArray();
|
||||
return $q->get()->map(function ($r) {
|
||||
return [
|
||||
'user_id' => (int) $r->user_id,
|
||||
'firstname' => (string) ($r->firstname ?? ''),
|
||||
'lastname' => (string) ($r->lastname ?? ''),
|
||||
'role_name' => (string) ($r->role_name ?? ''),
|
||||
'satt_id' => $r->satt_id ? (int) $r->satt_id : null,
|
||||
'status' => $r->status ? (string) $r->status : null,
|
||||
'reason' => $r->reason,
|
||||
];
|
||||
})->all();
|
||||
}
|
||||
|
||||
// ---------- helpers ----------
|
||||
/* ============================================================
|
||||
* Teacher/TA helpers (teacher_class-backed)
|
||||
* ============================================================
|
||||
*/
|
||||
|
||||
protected function isValidDate(string $d): bool
|
||||
public static function getAssignedTeachersForSection(int $classSectionId, string $schoolYear): array
|
||||
{
|
||||
$dt = date_create_from_format('Y-m-d', $d);
|
||||
return $dt && $dt->format('Y-m-d') === $d;
|
||||
}
|
||||
|
||||
public function getOne(int $userId, string $date): ?array
|
||||
{
|
||||
return $this->where(['user_id' => $userId, 'date' => $date])->first() ?: null;
|
||||
}
|
||||
|
||||
public function deleteOne(int $userId, string $date): bool
|
||||
{
|
||||
$row = $this->getOne($userId, $date);
|
||||
return $row ? (bool)$this->delete((int)$row['id']) : true;
|
||||
}
|
||||
/* =========================
|
||||
* TEACHER/TA HELPERS (staff_attendance-backed)
|
||||
* ========================= */
|
||||
|
||||
/** Return assigned teachers (main + TAs) for a section/term, with names. */
|
||||
public function getAssignedTeachersForSection(int $classSectionId, string $semester, string $schoolYear): array
|
||||
{
|
||||
return $this->db->table('teacher_class tc')
|
||||
->select("
|
||||
tc.class_section_id,
|
||||
tc.teacher_id AS teacher_id,
|
||||
tc.position,
|
||||
u.firstname, u.lastname
|
||||
", false)
|
||||
->join('users u', 'u.id = tc.teacher_id', 'inner')
|
||||
return DB::table('teacher_class as tc')
|
||||
->selectRaw('tc.class_section_id, tc.teacher_id as teacher_id, tc.position, u.firstname, u.lastname')
|
||||
->join('users as u', 'u.id', '=', 'tc.teacher_id')
|
||||
->where('tc.class_section_id', $classSectionId)
|
||||
->where('tc.semester', $semester)
|
||||
->where('tc.school_year', $schoolYear)
|
||||
->orderBy("FIELD(tc.position,'main','ta')", '', false)
|
||||
->orderBy('u.firstname', 'ASC')
|
||||
->get()->getResultArray();
|
||||
->orderByRaw("FIELD(tc.position,'main','ta')")
|
||||
->orderBy('u.firstname')
|
||||
->get()
|
||||
->map(fn ($r) => [
|
||||
'class_section_id' => (int) $r->class_section_id,
|
||||
'teacher_id' => (int) $r->teacher_id,
|
||||
'position' => (string) $r->position,
|
||||
'firstname' => (string) ($r->firstname ?? ''),
|
||||
'lastname' => (string) ($r->lastname ?? ''),
|
||||
])->all();
|
||||
}
|
||||
|
||||
|
||||
/** Get teacher/TA status for a given day/term in staff_attendance. */
|
||||
public function getStatusForDay(int $teacherId, int $classSectionId, string $date, string $semester, string $schoolYear): ?array
|
||||
{
|
||||
return $this->asArray()
|
||||
public static function getStatusForDay(
|
||||
int $teacherId,
|
||||
int $classSectionId,
|
||||
string $date,
|
||||
string $semester,
|
||||
string $schoolYear
|
||||
): ?array {
|
||||
$row = static::query()
|
||||
->where('user_id', $teacherId)
|
||||
->where('class_section_id', $classSectionId) // SECTION CODE
|
||||
->where('date', $date)
|
||||
->where('semester', $semester)
|
||||
->where('school_year', $schoolYear)
|
||||
->first() ?: null;
|
||||
}
|
||||
|
||||
public function assignedWithStatusForDay(int $classSectionId, string $date, string $semester, string $schoolYear): array
|
||||
{
|
||||
$db = $this->db;
|
||||
$join = "t.user_id = tc.teacher_id
|
||||
AND t.class_section_id = tc.class_section_id
|
||||
AND t.date = " . $db->escape($date) . "
|
||||
AND t.semester = " . $db->escape($semester) . "
|
||||
AND t.school_year = " . $db->escape($schoolYear);
|
||||
|
||||
return $db->table('teacher_class tc')
|
||||
->select("
|
||||
tc.class_section_id,
|
||||
tc.teacher_id AS teacher_id,
|
||||
tc.position,
|
||||
u.firstname, u.lastname,
|
||||
t.id AS satt_id, t.status, t.reason
|
||||
", false)
|
||||
->join('users u', 'u.id = tc.teacher_id', 'inner')
|
||||
->join($this->table . ' t', $join, 'left') // staff_attendance
|
||||
->where('tc.class_section_id', $classSectionId)
|
||||
->where('tc.semester', $semester)
|
||||
->where('tc.school_year', $schoolYear)
|
||||
->orderBy("FIELD(tc.position,'main','ta')", '', false)
|
||||
->orderBy('u.firstname', 'ASC')
|
||||
->get()->getResultArray();
|
||||
}
|
||||
|
||||
/** True if every assigned teacher has a status row for this day/term (in staff_attendance). */
|
||||
public function allAssignedStatusesPresent(int $classSectionId, string $date, string $semester, string $schoolYear): bool
|
||||
{
|
||||
// Count assigned teachers (from teacher_class)
|
||||
$assigned = $this->db->table('teacher_class')
|
||||
->where('class_section_id', $classSectionId) // SECTION CODE
|
||||
->where('semester', $semester)
|
||||
->where('school_year', $schoolYear)
|
||||
->countAllResults();
|
||||
|
||||
if ($assigned <= 0) return true;
|
||||
|
||||
// Count distinct users saved in staff_attendance for that day/term/section
|
||||
$present = (int)($this->db->table($this->table)
|
||||
->select('COUNT(DISTINCT user_id) AS c', false)
|
||||
->where('class_section_id', $classSectionId)
|
||||
->where('date', $date)
|
||||
->whereDate('date', $date)
|
||||
->where('semester', $semester)
|
||||
->where('school_year', $schoolYear)
|
||||
->get()->getRowArray()['c'] ?? 0);
|
||||
->first();
|
||||
|
||||
return $present === (int)$assigned;
|
||||
return $row ? $row->toArray() : null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Assigned teachers for a section/term with attendance for a specific date.
|
||||
* Returns: class_section_id, teacher_id, position, firstname, lastname, satt_id, status, reason
|
||||
*/
|
||||
|
||||
/**
|
||||
* Group assignments by section (SECTION CODE) for a term.
|
||||
* Returns: [class_section_id => [ ['teacher_id'=>..,'position'=>'main|ta','firstname'=>..,'lastname'=>..], ... ]]
|
||||
*/
|
||||
public function assignedByTerm(string $semester, string $schoolYear): array
|
||||
{
|
||||
$rows = $this->db->table('teacher_class tc')
|
||||
->select('tc.class_section_id, tc.teacher_id AS teacher_id, tc.position, u.firstname, u.lastname')
|
||||
->join('users u', 'u.id = tc.teacher_id', 'inner')
|
||||
->where('tc.semester', $semester)
|
||||
->where('tc.school_year', $schoolYear)
|
||||
->orderBy("FIELD(tc.position,'main','ta')", '', false)
|
||||
->orderBy('u.firstname', 'ASC')
|
||||
->get()->getResultArray();
|
||||
|
||||
$out = [];
|
||||
foreach ($rows as $r) {
|
||||
$sid = (int)$r['class_section_id'];
|
||||
$out[$sid][] = $r;
|
||||
}
|
||||
return $out;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Resolve assignment bundles with a resolved section PK and label.
|
||||
* Shape: [$requestedCode] = ['resolved_id'=>int,'label'=>string,'teachers'=>[...]]
|
||||
*/
|
||||
public function assignedByTermResolved(string $semester, string $schoolYear): array
|
||||
{
|
||||
$db = $this->db;
|
||||
|
||||
// 1) PK matches (tc.class_section_id == cs.id)
|
||||
$pkRows = $db->table('teacher_class tc')
|
||||
// ...inside assignedByTermResolved
|
||||
->select("
|
||||
tc.class_section_id AS requested_section_id,
|
||||
cs.id AS resolved_section_id,
|
||||
cs.class_section_name AS section_label,
|
||||
tc.teacher_id AS teacher_id,
|
||||
tc.position,
|
||||
u.firstname, u.lastname
|
||||
", false)
|
||||
->join('users u', 'u.id = tc.teacher_id', 'inner')
|
||||
->join('classSection cs', 'cs.id = tc.class_section_id', 'inner')
|
||||
->where('tc.semester', $semester)
|
||||
->where('tc.school_year', $schoolYear)
|
||||
->get()->getResultArray();
|
||||
|
||||
$out = [];
|
||||
$pkTaken = [];
|
||||
foreach ($pkRows as $r) {
|
||||
$req = (int)($r['requested_section_id'] ?? 0);
|
||||
if (!isset($out[$req])) {
|
||||
$out[$req] = [
|
||||
'resolved_id' => (int)$r['resolved_section_id'],
|
||||
'label' => trim((string)$r['section_label']),
|
||||
'teachers' => [],
|
||||
];
|
||||
$pkTaken[$req] = true;
|
||||
}
|
||||
$out[$req]['teachers'][] = [
|
||||
'teacher_id' => (int)$r['teacher_id'],
|
||||
'position' => (string)$r['position'],
|
||||
'firstname' => (string)($r['firstname'] ?? ''),
|
||||
'lastname' => (string)($r['lastname'] ?? ''),
|
||||
];
|
||||
}
|
||||
|
||||
// 2) CODE matches (tc.class_section_id == cs.class_section_id) if not matched by PK
|
||||
$codeRows = $db->table('teacher_class tc')
|
||||
->select("
|
||||
tc.class_section_id AS requested_section_id,
|
||||
cs.id AS resolved_section_id,
|
||||
cs.class_section_name AS section_label,
|
||||
tc.user_id AS teacher_id,
|
||||
tc.position,
|
||||
u.firstname, u.lastname
|
||||
", false)
|
||||
->join('users u', 'u.id = tc.user_id', 'inner')
|
||||
->join('classSection cs', 'cs.class_section_id = tc.class_section_id', 'inner')
|
||||
->where('tc.semester', $semester)
|
||||
->where('tc.school_year', $schoolYear)
|
||||
->get()->getResultArray();
|
||||
|
||||
foreach ($codeRows as $r) {
|
||||
$req = (int)($r['requested_section_id'] ?? 0);
|
||||
if (isset($pkTaken[$req])) continue;
|
||||
|
||||
if (!isset($out[$req])) {
|
||||
$out[$req] = [
|
||||
'resolved_id' => (int)$r['resolved_section_id'],
|
||||
'label' => trim((string)$r['section_label']),
|
||||
'teachers' => [],
|
||||
];
|
||||
}
|
||||
$out[$req]['teachers'][] = [
|
||||
'teacher_id' => (int)$r['teacher_id'],
|
||||
'position' => (string)$r['position'],
|
||||
'firstname' => (string)($r['firstname'] ?? ''),
|
||||
'lastname' => (string)($r['lastname'] ?? ''),
|
||||
];
|
||||
}
|
||||
|
||||
foreach ($out as $req => &$bundle) {
|
||||
if (empty($bundle['resolved_id'])) $bundle['resolved_id'] = (int)$req;
|
||||
if ($bundle['label'] === '') $bundle['label'] = 'Section #' . (int)$req;
|
||||
}
|
||||
unset($bundle);
|
||||
|
||||
return $out;
|
||||
}
|
||||
|
||||
/** Group assignments by SECTION CODE (uses tc.class_section_id). */
|
||||
public function assignedByTermByCode(string $semester, string $schoolYear): array
|
||||
{
|
||||
$rows = $this->db->table('teacher_class tc')
|
||||
->select('tc.class_section_id, tc.teacher_id AS teacher_id, tc.position, u.firstname, u.lastname')
|
||||
->join('users u', 'u.id = tc.teacher_id', 'inner')
|
||||
->where('tc.semester', $semester)
|
||||
->where('tc.school_year', $schoolYear)
|
||||
->orderBy('tc.class_section_id', 'ASC')
|
||||
->orderBy("FIELD(tc.position,'main','ta')", '', false)
|
||||
->orderBy('u.firstname', 'ASC')
|
||||
->get()->getResultArray();
|
||||
|
||||
$out = [];
|
||||
foreach ($rows as $r) {
|
||||
$code = (int)$r['class_section_id'];
|
||||
$out[$code][] = [
|
||||
'teacher_id' => (int)$r['teacher_id'],
|
||||
'position' => (string)$r['position'],
|
||||
'firstname' => (string)($r['firstname'] ?? ''),
|
||||
'lastname' => (string)($r['lastname'] ?? ''),
|
||||
];
|
||||
}
|
||||
return $out;
|
||||
}
|
||||
|
||||
|
||||
/** Assigned + status for a single day by SECTION CODE (joins staff_attendance). */
|
||||
public function assignedWithStatusForDayByCode(
|
||||
int $sectionCode,
|
||||
public static function assignedWithStatusForDay(
|
||||
int $classSectionId,
|
||||
string $date,
|
||||
string $semester,
|
||||
string $schoolYear
|
||||
): array {
|
||||
$db = $this->db;
|
||||
$join = "t.user_id = tc.teacher_id
|
||||
AND t.class_section_id = tc.class_section_id
|
||||
AND t.date = " . $db->escape($date) . "
|
||||
AND t.semester = " . $db->escape($semester) . "
|
||||
AND t.school_year = " . $db->escape($schoolYear);
|
||||
|
||||
return $db->table('teacher_class tc')
|
||||
->select("
|
||||
tc.class_section_id,
|
||||
tc.teacher_id AS teacher_id,
|
||||
tc.position,
|
||||
u.firstname, u.lastname,
|
||||
t.id AS satt_id, t.status, t.reason
|
||||
", false)
|
||||
->join('users u', 'u.id = tc.teacher_id', 'inner')
|
||||
->join($this->table . ' t', $join, 'left') // staff_attendance
|
||||
->where('tc.class_section_id', $sectionCode)
|
||||
->where('tc.semester', $semester)
|
||||
return DB::table('teacher_class as tc')
|
||||
->selectRaw("
|
||||
tc.class_section_id,
|
||||
tc.teacher_id as teacher_id,
|
||||
tc.position,
|
||||
u.firstname, u.lastname,
|
||||
t.id as satt_id, t.status, t.reason
|
||||
")
|
||||
->join('users as u', 'u.id', '=', 'tc.teacher_id')
|
||||
->leftJoin('staff_attendance as t', function ($j) use ($date, $semester, $schoolYear) {
|
||||
$j->on('t.user_id', '=', 'tc.teacher_id')
|
||||
->on('t.class_section_id', '=', 'tc.class_section_id')
|
||||
->whereDate('t.date', '=', $date)
|
||||
->where('t.semester', '=', $semester)
|
||||
->where('t.school_year', '=', $schoolYear);
|
||||
})
|
||||
->where('tc.class_section_id', $classSectionId)
|
||||
->where('tc.school_year', $schoolYear)
|
||||
->orderBy("FIELD(tc.position,'main','ta')", '', false)
|
||||
->orderBy('u.firstname', 'ASC')
|
||||
->get()->getResultArray();
|
||||
->orderByRaw("FIELD(tc.position,'main','ta')")
|
||||
->orderBy('u.firstname')
|
||||
->get()
|
||||
->map(fn ($r) => [
|
||||
'class_section_id' => (int) $r->class_section_id,
|
||||
'teacher_id' => (int) $r->teacher_id,
|
||||
'position' => (string) $r->position,
|
||||
'firstname' => (string) ($r->firstname ?? ''),
|
||||
'lastname' => (string) ($r->lastname ?? ''),
|
||||
'satt_id' => $r->satt_id ? (int) $r->satt_id : null,
|
||||
'status' => $r->status ? strtolower((string) $r->status) : null,
|
||||
'reason' => $r->reason,
|
||||
])->all();
|
||||
}
|
||||
|
||||
public static function allAssignedStatusesPresent(
|
||||
int $classSectionId,
|
||||
string $date,
|
||||
string $semester,
|
||||
string $schoolYear
|
||||
): bool {
|
||||
$assigned = (int) DB::table('teacher_class')
|
||||
->where('class_section_id', $classSectionId)
|
||||
->where('school_year', $schoolYear)
|
||||
->count();
|
||||
|
||||
/**
|
||||
* Statuses for a range (by SECTION CODE) from staff_attendance.
|
||||
* Returns: [sectionCode][teacherId][Y-m-d] = ['status'=>..,'reason'=>..]
|
||||
*/
|
||||
public function statusesByRange(array $sectionCodes, string $startDate, string $endDate, string $semester, string $schoolYear): array
|
||||
{
|
||||
if (empty($sectionCodes)) return [];
|
||||
if ($assigned <= 0) return true;
|
||||
|
||||
$rows = $this->db->table($this->table) // staff_attendance
|
||||
->select('class_section_id, user_id, DATE_FORMAT(date,"%Y-%m-%d") AS d, status, reason', false)
|
||||
->whereIn('class_section_id', $sectionCodes) // SECTION CODE
|
||||
->where('date >=', $startDate)
|
||||
->where('date <=', $endDate)
|
||||
$present = (int) DB::table('staff_attendance')
|
||||
->where('class_section_id', $classSectionId)
|
||||
->whereDate('date', $date)
|
||||
->where('semester', $semester)
|
||||
->where('school_year', $schoolYear)
|
||||
->get()->getResultArray();
|
||||
->distinct('user_id')
|
||||
->count('user_id');
|
||||
|
||||
return $present === $assigned;
|
||||
}
|
||||
|
||||
/**
|
||||
* Statuses for a range by section codes.
|
||||
* Returns: [sectionCode][teacherId][Y-m-d] = ['status'=>..,'reason'=>..]
|
||||
*/
|
||||
public static function statusesByRange(
|
||||
array $sectionCodes,
|
||||
string $startDate,
|
||||
string $endDate,
|
||||
string $semester,
|
||||
string $schoolYear
|
||||
): array {
|
||||
$codes = array_values(array_unique(array_filter(array_map('intval', $sectionCodes))));
|
||||
if (empty($codes)) return [];
|
||||
|
||||
$rows = DB::table('staff_attendance')
|
||||
->selectRaw('class_section_id, user_id, DATE_FORMAT(date,"%Y-%m-%d") as d, status, reason')
|
||||
->whereIn('class_section_id', $codes)
|
||||
->whereDate('date', '>=', $startDate)
|
||||
->whereDate('date', '<=', $endDate)
|
||||
->where('semester', $semester)
|
||||
->where('school_year', $schoolYear)
|
||||
->get();
|
||||
|
||||
$map = [];
|
||||
foreach ($rows as $r) {
|
||||
$sid = (int)$r['class_section_id'];
|
||||
$tid = (int)$r['user_id'];
|
||||
$day = (string)$r['d'];
|
||||
$sid = (int) $r->class_section_id;
|
||||
$tid = (int) $r->user_id;
|
||||
$day = (string) $r->d;
|
||||
|
||||
$map[$sid][$tid][$day] = [
|
||||
'status' => strtolower((string)$r['status']),
|
||||
'reason' => $r['reason'],
|
||||
'status' => strtolower((string) $r->status),
|
||||
'reason' => $r->reason,
|
||||
];
|
||||
}
|
||||
return $map;
|
||||
}
|
||||
}
|
||||
|
||||
/* ============================================================
|
||||
* Optional validation helper
|
||||
* ============================================================
|
||||
*/
|
||||
public static function rules(bool $updating = false): array
|
||||
{
|
||||
$req = $updating ? 'sometimes' : 'required';
|
||||
|
||||
return [
|
||||
'user_id' => [$req, 'integer', 'min:1', 'exists:users,id'],
|
||||
'date' => [$req, 'date_format:Y-m-d'],
|
||||
'semester' => [$req, 'string', 'max:20'],
|
||||
'school_year' => [$req, 'string', 'max:20'],
|
||||
|
||||
'role_name' => ['nullable', 'string', 'max:80'],
|
||||
'status' => ['nullable', 'string', 'in:' . implode(',', self::allowedStatuses())],
|
||||
'reason' => ['nullable', 'string', 'max:2000'],
|
||||
|
||||
'class_section_id' => ['nullable', 'integer'],
|
||||
'position' => ['nullable', 'string', 'in:' . implode(',', [self::POS_MAIN, self::POS_TA])],
|
||||
|
||||
'created_by' => ['nullable', 'integer'],
|
||||
'updated_by' => ['nullable', 'integer'],
|
||||
];
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user