205 lines
6.1 KiB
PHP
Executable File
205 lines
6.1 KiB
PHP
Executable File
<?php
|
||
|
||
namespace App\Models;
|
||
|
||
use App\Models\BaseModel;
|
||
use Illuminate\Support\Carbon;
|
||
use Illuminate\Database\QueryException;
|
||
|
||
class AttendanceDay extends BaseModel
|
||
{
|
||
protected $table = 'attendance_day';
|
||
|
||
// ✅ You are storing created_at / updated_at manually in CI (timestamps off there)
|
||
// User didn't explicitly ask to auto-manage here, so we keep it OFF to match CI.
|
||
// If you want auto-manage, tell me and I’ll flip it.
|
||
public $timestamps = true;
|
||
|
||
protected $fillable = [
|
||
'class_section_id',
|
||
'date',
|
||
'status',
|
||
'submitted_by',
|
||
'submitted_at',
|
||
'published_by',
|
||
'published_at',
|
||
'auto_publish_at',
|
||
'reopened_by',
|
||
'reopened_at',
|
||
'reopen_reason',
|
||
'semester',
|
||
'school_year',
|
||
'created_at',
|
||
'updated_at',
|
||
];
|
||
|
||
protected $casts = [
|
||
'class_section_id' => 'integer',
|
||
'submitted_by' => 'integer',
|
||
'published_by' => 'integer',
|
||
'reopened_by' => 'integer',
|
||
|
||
// If these are DATETIME columns:
|
||
'submitted_at' => 'datetime',
|
||
'published_at' => 'datetime',
|
||
'auto_publish_at' => 'datetime',
|
||
'reopened_at' => 'datetime',
|
||
'created_at' => 'datetime',
|
||
'updated_at' => 'datetime',
|
||
|
||
// If date is stored as DATE string; if it's DATETIME, change to 'datetime'
|
||
'date' => 'date',
|
||
];
|
||
|
||
/* =========================
|
||
* CI method equivalents
|
||
* ========================= */
|
||
|
||
/**
|
||
* Legacy helper: True iff (section, date, term) is hard-locked for everyone.
|
||
* Treats 'published' and legacy 'finalized' as finalized.
|
||
*/
|
||
public static function isFinalized(
|
||
int $classSectionId,
|
||
string $date,
|
||
?string $semester = null,
|
||
?string $schoolYear = null
|
||
): bool {
|
||
$q = static::query()
|
||
->select('id')
|
||
->where('class_section_id', $classSectionId)
|
||
->where('date', $date)
|
||
->where(function ($w) {
|
||
$w->where('status', 'published')
|
||
->orWhere('status', 'finalized'); // legacy
|
||
});
|
||
|
||
if ($semester !== null) $q->where('semester', $semester);
|
||
if ($schoolYear !== null) $q->where('school_year', $schoolYear);
|
||
|
||
return $q->exists();
|
||
}
|
||
|
||
/**
|
||
* Find day row by (section, date, term).
|
||
*/
|
||
public static function findBySectionDate(
|
||
int $classSectionId,
|
||
string $date,
|
||
?string $semester,
|
||
?string $schoolYear
|
||
): ?self {
|
||
return static::query()
|
||
->where('class_section_id', $classSectionId)
|
||
->where('date', $date)
|
||
->where('semester', $semester)
|
||
->where('school_year', $schoolYear)
|
||
->first();
|
||
}
|
||
|
||
/**
|
||
* Get draft row for (section,date,term), create it if missing.
|
||
* Requires unique index: (class_section_id, date, semester, school_year)
|
||
*/
|
||
public static function getOrCreateDraft(
|
||
int $classSectionId,
|
||
string $date,
|
||
?string $semester,
|
||
?string $schoolYear,
|
||
int $userId = 0
|
||
): self {
|
||
$row = static::findBySectionDate($classSectionId, $date, $semester, $schoolYear);
|
||
if ($row) return $row;
|
||
|
||
$now = now();
|
||
|
||
try {
|
||
return static::create([
|
||
'class_section_id' => $classSectionId,
|
||
'date' => $date,
|
||
'status' => 'draft',
|
||
'submitted_by' => null,
|
||
'submitted_at' => null,
|
||
'published_by' => null,
|
||
'published_at' => null,
|
||
'auto_publish_at' => null,
|
||
'reopened_by' => null,
|
||
'reopened_at' => null,
|
||
'reopen_reason' => null,
|
||
'semester' => $semester,
|
||
'school_year' => $schoolYear,
|
||
'created_at' => $now,
|
||
'updated_at' => $now,
|
||
]);
|
||
} catch (QueryException $e) {
|
||
// Race: someone inserted same unique key.
|
||
$row = static::findBySectionDate($classSectionId, $date, $semester, $schoolYear);
|
||
if ($row) return $row;
|
||
throw $e;
|
||
}
|
||
}
|
||
|
||
/**
|
||
* DEPRECATED legacy finalize => submit (teacher submit).
|
||
*/
|
||
public static function finalize(int $id, int $userId): bool
|
||
{
|
||
return static::submit($id, $userId, null);
|
||
}
|
||
|
||
/**
|
||
* Teacher submit: status => submitted (teacher locked, admin editable).
|
||
*/
|
||
public static function submit(int $id, int $userId, ?string $autoPublishAt = null): bool
|
||
{
|
||
$now = now();
|
||
|
||
$payload = [
|
||
'status' => 'submitted',
|
||
'submitted_by' => $userId,
|
||
'submitted_at' => $now,
|
||
'updated_at' => $now,
|
||
];
|
||
|
||
if ($autoPublishAt !== null) {
|
||
$payload['auto_publish_at'] = $autoPublishAt;
|
||
}
|
||
|
||
return static::query()->whereKey($id)->update($payload) >= 0;
|
||
}
|
||
|
||
/**
|
||
* Admin publish (hard lock): status => published.
|
||
*/
|
||
public static function publish(int $id, int $userId): bool
|
||
{
|
||
$now = now();
|
||
|
||
return static::query()->whereKey($id)->update([
|
||
'status' => 'published',
|
||
'published_by' => $userId,
|
||
'published_at' => $now,
|
||
'updated_at' => $now,
|
||
]) >= 0;
|
||
}
|
||
|
||
/**
|
||
* Admin reopen a day back to 'draft' (default) or 'submitted'.
|
||
*/
|
||
public static function reopen(int $id, int $userId, string $toStatus = 'draft', ?string $reason = null): bool
|
||
{
|
||
if (!in_array($toStatus, ['draft', 'submitted'], true)) {
|
||
throw new \InvalidArgumentException('toStatus must be "draft" or "submitted".');
|
||
}
|
||
|
||
$now = now();
|
||
|
||
return static::query()->whereKey($id)->update([
|
||
'status' => $toStatus,
|
||
'reopened_by' => $userId,
|
||
'reopened_at' => $now,
|
||
'reopen_reason' => $reason,
|
||
'updated_at' => $now,
|
||
]) >= 0;
|
||
}
|
||
} |