fix financial and certificates
This commit is contained in:
@@ -0,0 +1,99 @@
|
||||
<?php
|
||||
|
||||
namespace App\Services\Grading\Validation;
|
||||
|
||||
use Illuminate\Support\Facades\DB;
|
||||
|
||||
class GradebookFinalizationValidator
|
||||
{
|
||||
/**
|
||||
* Strong-mode validation only. Legacy mode deliberately remains displayable/lockable
|
||||
* so historical records are not reinterpreted by the new policy.
|
||||
*/
|
||||
public function validateClassSectionBeforeLock(int $classSectionId, string $semester, string $schoolYear): array
|
||||
{
|
||||
$errors = [];
|
||||
|
||||
foreach ($this->scoreTables() as $table => $meta) {
|
||||
if (!DB::getSchemaBuilder()->hasTable($table)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$query = DB::table($table)
|
||||
->where('class_section_id', $classSectionId)
|
||||
->where('semester', $semester)
|
||||
->where('school_year', $schoolYear);
|
||||
|
||||
if (DB::getSchemaBuilder()->hasColumn($table, 'status')) {
|
||||
$pending = (clone $query)->where(function ($q) {
|
||||
$q->whereNull('status')->orWhere('status', 'pending');
|
||||
})->get();
|
||||
|
||||
foreach ($pending as $row) {
|
||||
$errors[] = $this->error($table, $meta['category'], $row, 'pending_score', 'Score must be marked scored, missing, excused, or not_assigned before strong-mode lock.');
|
||||
}
|
||||
}
|
||||
|
||||
if (DB::getSchemaBuilder()->hasColumn($table, 'max_points')) {
|
||||
$invalidMax = (clone $query)->where(function ($q) {
|
||||
$q->whereNull('max_points')->orWhere('max_points', '<=', 0);
|
||||
})->get();
|
||||
|
||||
foreach ($invalidMax as $row) {
|
||||
$errors[] = $this->error($table, $meta['category'], $row, 'invalid_max_points', 'Max points must be greater than 0.');
|
||||
}
|
||||
|
||||
$invalidScores = (clone $query)
|
||||
->whereNotNull('score')
|
||||
->where(function ($q) {
|
||||
$q->where('score', '<', 0)->orWhereRaw('score > max_points');
|
||||
})
|
||||
->get();
|
||||
|
||||
foreach ($invalidScores as $row) {
|
||||
$errors[] = $this->error($table, $meta['category'], $row, 'score_out_of_range', 'Score must be between 0 and max_points.');
|
||||
}
|
||||
} else {
|
||||
$invalidScores = (clone $query)
|
||||
->whereNotNull('score')
|
||||
->where(function ($q) {
|
||||
$q->where('score', '<', 0)->orWhere('score', '>', 100);
|
||||
})
|
||||
->get();
|
||||
|
||||
foreach ($invalidScores as $row) {
|
||||
$errors[] = $this->error($table, $meta['category'], $row, 'score_out_of_range', 'Score must be between 0 and 100.');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return [
|
||||
'valid' => empty($errors),
|
||||
'errors' => $errors,
|
||||
];
|
||||
}
|
||||
|
||||
private function scoreTables(): array
|
||||
{
|
||||
return [
|
||||
'homework' => ['category' => 'homework'],
|
||||
'quiz' => ['category' => 'quiz'],
|
||||
'project' => ['category' => 'project'],
|
||||
'participation' => ['category' => 'participation'],
|
||||
'midterm_exam' => ['category' => 'midterm_exam'],
|
||||
'final_exam' => ['category' => 'final_exam'],
|
||||
];
|
||||
}
|
||||
|
||||
private function error(string $table, string $category, object $row, string $code, string $message): array
|
||||
{
|
||||
return [
|
||||
'table' => $table,
|
||||
'category' => $category,
|
||||
'student_id' => isset($row->student_id) ? (int) $row->student_id : null,
|
||||
'item_index' => $row->homework_index ?? $row->quiz_index ?? $row->project_index ?? null,
|
||||
'code' => $code,
|
||||
'message' => $message,
|
||||
];
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user