reconstruction of the project

This commit is contained in:
root
2026-03-08 16:33:24 -04:00
parent 23b7db1107
commit c8de5f7edc
9157 changed files with 77877 additions and 1073823 deletions
+80 -73
View File
@@ -2,14 +2,19 @@
namespace App\Models;
use Carbon\CarbonImmutable;
use Illuminate\Database\Eloquent\Builder;
use Illuminate\Database\Eloquent\Model;
use App\Models\BaseModel;
use Illuminate\Support\Carbon;
use Illuminate\Database\QueryException;
class AttendanceDay extends Model
class AttendanceDay extends BaseModel
{
protected $table = 'attendance_day'; // Because you store created_at/updated_at but CI didn't auto-manage them
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 Ill flip it.
public $timestamps = true;
protected $fillable = [
'class_section_id',
'date',
@@ -30,27 +35,29 @@ class AttendanceDay extends Model
protected $casts = [
'class_section_id' => 'integer',
'date' => 'date:Y-m-d',
'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',
];
/* ============================================================
* Helpers
* ============================================================
*/
/* =========================
* CI method equivalents
* ========================= */
/**
* Legacy helper kept for compatibility.
* True iff this (section, date, term) is hard-locked for everyone.
* Treats 'published' (new) and 'finalized' (old) as finalized.
* 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,
@@ -61,10 +68,10 @@ class AttendanceDay extends Model
$q = static::query()
->select('id')
->where('class_section_id', $classSectionId)
->whereDate('date', $date)
->where(function (Builder $w) {
->where('date', $date)
->where(function ($w) {
$w->where('status', 'published')
->orWhere('status', 'finalized'); // tolerate legacy data
->orWhere('status', 'finalized'); // legacy
});
if ($semester !== null) $q->where('semester', $semester);
@@ -74,7 +81,7 @@ class AttendanceDay extends Model
}
/**
* Find a day row by (section, date, term). Returns model|null.
* Find day row by (section, date, term).
*/
public static function findBySectionDate(
int $classSectionId,
@@ -83,17 +90,16 @@ class AttendanceDay extends Model
?string $schoolYear
): ?self {
return static::query()
->where([
'class_section_id' => $classSectionId,
'date' => $date,
'semester' => $semester,
'school_year' => $schoolYear,
])->first();
->where('class_section_id', $classSectionId)
->where('date', $date)
->where('semester', $semester)
->where('school_year', $schoolYear)
->first();
}
/**
* Get the draft row for (section,date,term), creating it if missing.
* Relies on the unique key (class_section_id, date, semester, school_year) to prevent duplicates.
* 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,
@@ -102,97 +108,98 @@ class AttendanceDay extends Model
?string $schoolYear,
int $userId = 0
): self {
$now = CarbonImmutable::now('UTC');
$row = static::findBySectionDate($classSectionId, $date, $semester, $schoolYear);
if ($row) return $row;
// firstOrCreate is already race-safe with a DB unique index:
// if two requests race, one insert wins; the other re-queries.
return static::query()->firstOrCreate(
[
$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,
],
[
'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,
'created_at' => $now,
'updated_at' => $now,
]
);
'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: Prior code used "finalize" to lock immediately.
* In the new workflow, "finalize" means "teacher submit".
* DEPRECATED legacy finalize => submit (teacher submit).
*/
public function finalize(int $userId): bool
public static function finalize(int $id, int $userId): bool
{
return $this->submit($userId, null);
return static::submit($id, $userId, null);
}
/**
* Teacher submit: status => submitted (teacher locked, admin editable).
* Optionally set auto_publish_at.
*/
public function submit(int $userId, ?string $autoPublishAt = null): bool
public static function submit(int $id, int $userId, ?string $autoPublishAt = null): bool
{
$now = CarbonImmutable::now('UTC');
$now = now();
$this->fill([
'status' => 'submitted',
'submitted_by' => $userId,
'submitted_at' => $now,
'auto_publish_at' => $autoPublishAt,
'updated_at' => $now,
]);
$payload = [
'status' => 'submitted',
'submitted_by' => $userId,
'submitted_at' => $now,
'updated_at' => $now,
];
return $this->save();
if ($autoPublishAt !== null) {
$payload['auto_publish_at'] = $autoPublishAt;
}
return static::query()->whereKey($id)->update($payload) >= 0;
}
/**
* Admin publish (hard lock): status => published.
*/
public function publish(int $userId): bool
public static function publish(int $id, int $userId): bool
{
$now = CarbonImmutable::now('UTC');
$now = now();
$this->fill([
return static::query()->whereKey($id)->update([
'status' => 'published',
'published_by' => $userId,
'published_at' => $now,
'updated_at' => $now,
]);
return $this->save();
]) >= 0;
}
/**
* Admin reopen a submitted/published day back to 'draft' (default) or 'submitted'.
* Admin reopen a day back to 'draft' (default) or 'submitted'.
*/
public function reopen(int $userId, string $toStatus = 'draft', ?string $reason = null): bool
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 = CarbonImmutable::now('UTC');
$now = now();
$this->fill([
return static::query()->whereKey($id)->update([
'status' => $toStatus,
'reopened_by' => $userId,
'reopened_at' => $now,
'reopen_reason' => $reason,
'updated_at' => $now,
]);
return $this->save();
]) >= 0;
}
}