fix financial and certificates

This commit is contained in:
root
2026-06-05 01:51:08 -04:00
parent d28d11e2e5
commit ad968eaff7
94 changed files with 9654 additions and 214 deletions
+4 -1
View File
@@ -63,7 +63,10 @@ class AttendanceCalculator implements ScoreCalculatorInterface
return ['attendance_score' => 100.0];
}
$attendanceRatio = ($totalDays + 1 - $absences) / $totalDays;
// School policy: one absence is excused by grace before attendance is reduced.
$graceAbsences = 1;
$effectiveAbsences = max(0, (int) $absences - $graceAbsences);
$attendanceRatio = ($totalDays - $effectiveAbsences) / $totalDays;
$score = min(100, max(0, $attendanceRatio * 100));
return ['attendance_score' => round($score, 2)];
+7 -2
View File
@@ -7,6 +7,7 @@ use App\Models\MissingScoreOverride;
use App\Models\Student;
use App\Models\TeacherClass;
use App\Models\ClassSection;
use App\Services\Grading\Validation\ScoreValueValidator;
use Illuminate\Support\Facades\DB;
class ExamScoreService
@@ -131,8 +132,10 @@ class ExamScoreService
if (!$student) {
continue;
}
$rawScore = $data['score'] ?? null;
$normalizedScore = is_numeric($rawScore) ? (float) $rawScore : null;
$rawScore = is_array($data) ? ($data['score'] ?? null) : $data;
$validator = new ScoreValueValidator();
$normalizedScore = $validator->normalizeNullable($rawScore, ScoreValueValidator::DEFAULT_MAX_POINTS, 'Exam score');
$missingAllowed = !empty($missingOk[$studentId]);
$existing = $builder
->where('student_id', $studentId)
@@ -147,6 +150,8 @@ class ExamScoreService
'class_section_id' => $classSectionId,
'updated_by' => $updatedBy,
'score' => $normalizedScore,
'max_points' => ScoreValueValidator::DEFAULT_MAX_POINTS,
'status' => $validator->inferStatus($normalizedScore, $missingAllowed),
'semester' => $semester,
'school_year' => $schoolYear,
'updated_at' => now(),
+6 -2
View File
@@ -9,6 +9,7 @@ use App\Models\Student;
use App\Models\StudentClass;
use App\Models\TeacherClass;
use App\Models\ClassSection;
use App\Services\Grading\Validation\ScoreValueValidator;
class HomeworkScoreService
{
@@ -248,8 +249,9 @@ class HomeworkScoreService
continue;
}
$rawScore = $score ?? null;
$isBlank = $rawScore === null || (is_string($rawScore) && trim($rawScore) === '');
$normalizedScore = (!$isBlank && is_numeric($rawScore)) ? (float) $rawScore : null;
$validator = new ScoreValueValidator();
$normalizedScore = $validator->normalizeNullable($rawScore, ScoreValueValidator::DEFAULT_MAX_POINTS, 'Homework score');
$missingAllowed = !empty($missingOk[$studentId][$index]);
$existing = Homework::query()
->where('student_id', $studentId)
@@ -266,6 +268,8 @@ class HomeworkScoreService
'updated_by' => $updatedBy,
'homework_index' => (int) $index,
'score' => $normalizedScore,
'max_points' => ScoreValueValidator::DEFAULT_MAX_POINTS,
'status' => $validator->inferStatus($normalizedScore, $missingAllowed),
'semester' => $semester,
'school_year' => $schoolYear,
'updated_at' => now(),
@@ -4,6 +4,7 @@ namespace App\Services\Scores;
use App\Models\GradingLock;
use App\Models\ClassSection;
use App\Services\Grading\Validation\ScoreValueValidator;
use App\Models\MissingScoreOverride;
use App\Models\Participation;
use App\Models\Student;
@@ -185,8 +186,9 @@ class ParticipationScoreService
}
$rawScore = $data['score'] ?? null;
$isBlank = $rawScore === null || (is_string($rawScore) && trim($rawScore) === '');
$normalizedScore = (!$isBlank && is_numeric($rawScore)) ? (float) $rawScore : null;
$validator = new ScoreValueValidator();
$normalizedScore = $validator->normalizeNullable($rawScore, ScoreValueValidator::DEFAULT_MAX_POINTS, 'Participation score');
$missingAllowed = !empty($missingOk[$studentId]);
$existing = Participation::query()
->where('student_id', $studentId)
@@ -201,6 +203,8 @@ class ParticipationScoreService
'class_section_id' => $classSectionId,
'updated_by' => $updatedBy,
'score' => $normalizedScore,
'max_points' => ScoreValueValidator::DEFAULT_MAX_POINTS,
'status' => $validator->inferStatus($normalizedScore, $missingAllowed),
'semester' => $semester,
'school_year' => $schoolYear,
'updated_at' => now(),
+6 -1
View File
@@ -9,6 +9,7 @@ use App\Models\Student;
use App\Models\StudentClass;
use App\Models\TeacherClass;
use App\Models\ClassSection;
use App\Services\Grading\Validation\ScoreValueValidator;
use Illuminate\Support\Facades\DB;
class ProjectScoreService
@@ -218,7 +219,9 @@ class ProjectScoreService
if (!is_numeric($index)) {
continue;
}
$normalizedScore = is_numeric($score) ? (float) $score : null;
$validator = new ScoreValueValidator();
$normalizedScore = $validator->normalizeNullable($score, ScoreValueValidator::DEFAULT_MAX_POINTS, 'Project score');
$missingAllowed = !empty($missingOk[$studentId][$index]);
$existing = Project::query()
->where('student_id', $studentId)
@@ -235,6 +238,8 @@ class ProjectScoreService
'updated_by' => $updatedBy,
'project_index' => (int) $index,
'score' => $normalizedScore,
'max_points' => ScoreValueValidator::DEFAULT_MAX_POINTS,
'status' => $validator->inferStatus($normalizedScore, $missingAllowed),
'semester' => $semester,
'school_year' => $schoolYear,
'updated_at' => now(),
+6 -2
View File
@@ -9,6 +9,7 @@ use App\Models\Student;
use App\Models\StudentClass;
use App\Models\TeacherClass;
use App\Models\ClassSection;
use App\Services\Grading\Validation\ScoreValueValidator;
class QuizScoreService
{
@@ -219,8 +220,9 @@ class QuizScoreService
if (!is_numeric($quizNumber)) {
continue;
}
$isBlank = $score === null || (is_string($score) && trim($score) === '');
$normalizedScore = (!$isBlank && is_numeric($score)) ? (float) $score : null;
$validator = new ScoreValueValidator();
$normalizedScore = $validator->normalizeNullable($score, ScoreValueValidator::DEFAULT_MAX_POINTS, 'Quiz score');
$missingAllowed = !empty($missingOk[$studentId][$quizNumber]);
$existing = Quiz::query()
->where('student_id', $studentId)
@@ -237,6 +239,8 @@ class QuizScoreService
'updated_by' => $updatedBy,
'quiz_index' => (int) $quizNumber,
'score' => $normalizedScore,
'max_points' => ScoreValueValidator::DEFAULT_MAX_POINTS,
'status' => $validator->inferStatus($normalizedScore, $missingAllowed),
'semester' => $semester,
'school_year' => $schoolYear,
'updated_at' => now(),
@@ -17,6 +17,7 @@ use App\Models\CalendarEvent;
use App\Models\Homework;
use App\Models\Project;
use RuntimeException;
use App\Services\Grading\Policy\GradingPolicyResolver;
class SemesterScoreService
{
@@ -26,6 +27,7 @@ class SemesterScoreService
private string $semester;
private string $schoolYear;
private ?int $actorId;
private GradingPolicyResolver $policyResolver;
public function __construct(
?AttendanceCalculator $attendanceCalculator = null,
@@ -36,6 +38,7 @@ class SemesterScoreService
$this->semester = (string) (Configuration::getConfig('semester') ?? '');
$this->schoolYear = (string) (Configuration::getConfig('school_year') ?? '');
$this->actorId = (int) (auth()->id() ?? 0) ?: null;
$this->policyResolver = new GradingPolicyResolver();
$attendanceCalculator = $attendanceCalculator ?? new AttendanceCalculator(
new AttendanceRecord(),
@@ -209,12 +212,16 @@ class SemesterScoreService
string $schoolYear,
array $scoreData
): void {
$calculationMode = $this->policyResolver->calculationMode($classSectionId, $semester, $schoolYear);
$payload = array_merge([
'student_id' => $studentId,
'school_id' => $schoolId,
'class_section_id' => $classSectionId,
'semester' => $semester,
'school_year' => $schoolYear,
'calculation_mode' => $calculationMode,
'calculation_policy_version' => $this->policyResolver->policyVersion($calculationMode),
'updated_at' => now(),
], $scoreData);