fix logic and tests, update docker CI file
This commit is contained in:
@@ -4,7 +4,7 @@ namespace App\Services\Administrator;
|
||||
|
||||
use App\Models\Enrollment;
|
||||
use App\Models\Invoice;
|
||||
use App\Models\RefundModel as Refund;
|
||||
use App\Models\Refund;
|
||||
use App\Services\FeeCalculationService;
|
||||
|
||||
class AdministratorEnrollmentRefundService
|
||||
@@ -73,4 +73,4 @@ class AdministratorEnrollmentRefundService
|
||||
'refundAmountByParent' => $refundAmountByParent,
|
||||
];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
|
||||
namespace App\Services\Administrator;
|
||||
|
||||
use App\Mail\TeacherSubmissionReminderMail;
|
||||
use App\Models\ClassSection;
|
||||
use App\Models\TeacherSubmissionNotificationHistory;
|
||||
use App\Models\User;
|
||||
@@ -88,9 +89,7 @@ class TeacherSubmissionNotificationService
|
||||
|
||||
if (!empty($email) && filter_var($email, FILTER_VALIDATE_EMAIL)) {
|
||||
try {
|
||||
Mail::html($body, function ($message) use ($email, $subject) {
|
||||
$message->to($email)->subject($subject);
|
||||
});
|
||||
Mail::to($email)->send(new TeacherSubmissionReminderMail($subject, $body));
|
||||
$status = 'sent';
|
||||
} catch (\Throwable $e) {
|
||||
Log::error('Teacher submission notification failed: ' . $e->getMessage());
|
||||
@@ -158,4 +157,4 @@ class TeacherSubmissionNotificationService
|
||||
|
||||
return array_values($targets);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -163,7 +163,7 @@ class TeacherSubmissionReportService
|
||||
->where('class_section_id', $classSectionId)
|
||||
->where('semester', $semester)
|
||||
->where('school_year', $schoolYear)
|
||||
->where('date', $today)
|
||||
->whereDate('date', $today)
|
||||
->first();
|
||||
|
||||
$attendanceSubmitted = $attendanceRow
|
||||
@@ -187,7 +187,7 @@ class TeacherSubmissionReportService
|
||||
$midtermCommentStatus = $this->support->submissionStatus(count($midtermCommentStudents), $expected);
|
||||
$participationStatus = $this->support->submissionStatus(count($participationStudents), $expected);
|
||||
$ptapCommentStatus = $this->support->submissionStatus(count($ptapCommentStudents), $expected);
|
||||
$attendanceStatus = $this->support->attendanceStatus($attendanceSubmitted);
|
||||
$attendanceStatus = $this->support->attendanceStatus($attendanceSubmitted, $expected);
|
||||
|
||||
$statusDetails = [
|
||||
'midterm_score_status' => $midtermScoreStatus,
|
||||
@@ -282,4 +282,4 @@ class TeacherSubmissionReportService
|
||||
|
||||
return $historyMap;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -25,8 +25,16 @@ class TeacherSubmissionSupportService
|
||||
];
|
||||
}
|
||||
|
||||
public function attendanceStatus(bool $submitted): array
|
||||
public function attendanceStatus(bool $submitted, int $expected): array
|
||||
{
|
||||
if ($expected <= 0) {
|
||||
return [
|
||||
'label' => 'No students',
|
||||
'badge' => 'bg-secondary',
|
||||
'completed' => true,
|
||||
];
|
||||
}
|
||||
|
||||
return [
|
||||
'label' => $submitted ? 'Submitted' : 'Missing',
|
||||
'badge' => $submitted ? 'bg-success' : 'bg-danger',
|
||||
@@ -115,4 +123,4 @@ class TeacherSubmissionSupportService
|
||||
$last = array_pop($items);
|
||||
return implode(', ', $items) . ' and ' . $last;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -20,7 +20,14 @@ class AssignmentDataLoaderService
|
||||
->with([
|
||||
'teacher:id,firstname,lastname',
|
||||
])
|
||||
->when(filled($schoolYear), fn ($q) => $q->where('school_year', $schoolYear))
|
||||
->when(
|
||||
filled($schoolYear),
|
||||
fn ($q) => $q->where(function ($query) use ($schoolYear) {
|
||||
$query->where('school_year', $schoolYear)
|
||||
->orWhereNull('school_year')
|
||||
->orWhere('school_year', '');
|
||||
})
|
||||
)
|
||||
->get()
|
||||
->map(function ($row) {
|
||||
$row->teacher_full_name = trim(
|
||||
@@ -40,7 +47,14 @@ class AssignmentDataLoaderService
|
||||
'student:id,firstname,lastname,age,gender,registration_grade,photo_consent,tuition_paid,school_id,is_active',
|
||||
])
|
||||
->whereHas('student', fn ($q) => $q->where('is_active', 1))
|
||||
->when(filled($schoolYear), fn ($q) => $q->where('school_year', $schoolYear))
|
||||
->when(
|
||||
filled($schoolYear),
|
||||
fn ($q) => $q->where(function ($query) use ($schoolYear) {
|
||||
$query->where('school_year', $schoolYear)
|
||||
->orWhereNull('school_year')
|
||||
->orWhere('school_year', '');
|
||||
})
|
||||
)
|
||||
->get();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -16,11 +16,11 @@ class AssignmentSectionService
|
||||
|
||||
return $this->classSection
|
||||
->newQuery()
|
||||
->whereIn('id', $sectionIds)
|
||||
->whereIn('class_section_id', $sectionIds)
|
||||
->get()
|
||||
->mapWithKeys(function ($section) {
|
||||
$name = $section->class_section_name ?? $section->section_name ?? $section->name ?? '';
|
||||
return [(int) $section->id => (string) $name];
|
||||
return [(int) $section->class_section_id => (string) $name];
|
||||
})
|
||||
->all();
|
||||
}
|
||||
|
||||
@@ -0,0 +1,9 @@
|
||||
<?php
|
||||
|
||||
namespace App\Services;
|
||||
|
||||
use App\Services\Assignment\AssignmentService as AssignmentDomainService;
|
||||
|
||||
class AssignmentService extends AssignmentDomainService
|
||||
{
|
||||
}
|
||||
@@ -55,21 +55,17 @@ class AttendanceNotificationLogService
|
||||
?string $schoolYear = null
|
||||
): void {
|
||||
try {
|
||||
$where = [
|
||||
'student_id' => $studentId,
|
||||
'code' => $code,
|
||||
'incident_date' => $ymd,
|
||||
'channel' => $channel,
|
||||
];
|
||||
$existingQuery = $this->notificationModel->query()
|
||||
->where('student_id', $studentId)
|
||||
->where('code', $code)
|
||||
->whereDate('incident_date', $ymd)
|
||||
->where('channel', $channel);
|
||||
|
||||
if (!empty($to)) {
|
||||
$where['to_address'] = $to;
|
||||
$existingQuery->where('to_address', $to);
|
||||
}
|
||||
|
||||
$existing = $this->notificationModel->query()
|
||||
->where($where)
|
||||
->orderByDesc('id')
|
||||
->first();
|
||||
$existing = $existingQuery->orderByDesc('id')->first();
|
||||
|
||||
if ($existing && !empty($existing->id)) {
|
||||
$existing->update([
|
||||
@@ -98,4 +94,4 @@ class AttendanceNotificationLogService
|
||||
Log::debug('logNotification(): ' . $e->getMessage());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -29,7 +29,7 @@ class AttendanceViolationStudentResolverService
|
||||
}
|
||||
|
||||
$classStudents = $classStudentsQuery
|
||||
->where('school_year', $schoolYear)
|
||||
->where('student_class.school_year', $schoolYear)
|
||||
->get()
|
||||
->toArray();
|
||||
|
||||
@@ -53,7 +53,7 @@ class AttendanceViolationStudentResolverService
|
||||
}
|
||||
|
||||
$classStudents = $classStudentsQuery
|
||||
->where('school_year', $schoolYear)
|
||||
->where('student_class.school_year', $schoolYear)
|
||||
->get()
|
||||
->toArray();
|
||||
|
||||
@@ -368,4 +368,4 @@ class AttendanceViolationStudentResolverService
|
||||
|
||||
return [$studentCodeToId, $existingIds];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -25,8 +25,8 @@ class BadgeTextFormatter
|
||||
{
|
||||
$s = (string) $s;
|
||||
$s = str_replace(["\xC2\xA0", "\xA0"], ' ', $s);
|
||||
$s = preg_replace('/\s+/u', ' ', $s) ?? $s;
|
||||
$s = preg_replace('/[\x00-\x08\x0B-\x0C\x0E-\x1F\x7F]/u', '', $s) ?? $s;
|
||||
$s = preg_replace('/\s+/u', ' ', $s) ?? $s;
|
||||
|
||||
return trim($s);
|
||||
}
|
||||
@@ -203,4 +203,4 @@ class BadgeTextFormatter
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -6,7 +6,7 @@ class BroadcastEmailSenderOptionsService
|
||||
{
|
||||
public function listOptions(): array
|
||||
{
|
||||
$json = env('MAIL_SENDERS', '{}');
|
||||
$json = getenv('MAIL_SENDERS') ?: env('MAIL_SENDERS', '{}');
|
||||
$arr = json_decode($json, true);
|
||||
|
||||
if (!is_array($arr) || empty($arr)) {
|
||||
|
||||
@@ -91,7 +91,7 @@ class ClassPreparationCalculatorService
|
||||
$qty = $isLowerGrades ? 0 : $students;
|
||||
break;
|
||||
case 'teacher chair':
|
||||
$qty = (int) $teacherCount;
|
||||
$qty = $teacherCount > 0 ? (int) $teacherCount : 1;
|
||||
break;
|
||||
case 'trash bin':
|
||||
case 'white board':
|
||||
@@ -114,13 +114,22 @@ class ClassPreparationCalculatorService
|
||||
{
|
||||
$schoolYear = $schoolYear ?: (string) Configuration::getConfig('school_year');
|
||||
|
||||
$row = DB::table('teacher_class')
|
||||
$baseQuery = DB::table('teacher_class')
|
||||
->select('COUNT(*) AS cnt')
|
||||
->where('class_section_id', $classSectionId)
|
||||
->whereIn('position', ['main', 'ta'])
|
||||
->where('school_year', $schoolYear)
|
||||
->first();
|
||||
->whereIn('position', ['main', 'ta']);
|
||||
|
||||
return (int) ($row->cnt ?? 0);
|
||||
$row = $schoolYear !== null && $schoolYear !== ''
|
||||
? (clone $baseQuery)->where('school_year', $schoolYear)->first()
|
||||
: $baseQuery->first();
|
||||
|
||||
$count = (int) ($row->cnt ?? 0);
|
||||
|
||||
if ($count === 0 && $schoolYear !== null && $schoolYear !== '') {
|
||||
$row = $baseQuery->first();
|
||||
$count = (int) ($row->cnt ?? 0);
|
||||
}
|
||||
|
||||
return $count;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -11,7 +11,7 @@ class ClassPreparationInventoryService
|
||||
$inventoryMap = array_fill_keys($allowed, 0);
|
||||
|
||||
$joinRows = DB::table('inventory_items as ii')
|
||||
->select('ic.name AS item_name, COALESCE(SUM(CASE WHEN ii.good_qty IS NOT NULL THEN ii.good_qty WHEN ii.`condition`=\"good\" THEN ii.quantity ELSE 0 END),0) AS available')
|
||||
->select('ic.name AS item_name, COALESCE(SUM(CASE WHEN ii.good_qty IS NOT NULL AND ii.good_qty > 0 THEN ii.good_qty WHEN ii.`condition`=\"good\" THEN ii.quantity ELSE ii.quantity END),0) AS available')
|
||||
->join('inventory_categories as ic', 'ic.id', '=', 'ii.category_id')
|
||||
->where('ii.type', 'classroom')
|
||||
->where('ii.school_year', $schoolYear)
|
||||
@@ -31,7 +31,7 @@ class ClassPreparationInventoryService
|
||||
}
|
||||
|
||||
$nameRows = DB::table('inventory_items')
|
||||
->select('name AS item_name, COALESCE(SUM(CASE WHEN good_qty IS NOT NULL THEN good_qty WHEN `condition`=\"good\" THEN quantity ELSE 0 END),0) AS available')
|
||||
->select('name AS item_name, COALESCE(SUM(CASE WHEN good_qty IS NOT NULL AND good_qty > 0 THEN good_qty WHEN `condition`=\"good\" THEN quantity ELSE quantity END),0) AS available')
|
||||
->where('type', 'classroom')
|
||||
->where('school_year', $schoolYear)
|
||||
->whereIn('name', $allowed);
|
||||
@@ -49,6 +49,49 @@ class ClassPreparationInventoryService
|
||||
}
|
||||
}
|
||||
|
||||
$needsFallback = array_filter($inventoryMap, static fn ($v) => (int) $v === 0);
|
||||
if (!empty($needsFallback)) {
|
||||
$fallbackRows = DB::table('inventory_items')
|
||||
->select('name AS item_name, COALESCE(SUM(CASE WHEN good_qty IS NOT NULL AND good_qty > 0 THEN good_qty WHEN `condition`=\"good\" THEN quantity ELSE quantity END),0) AS available')
|
||||
->where('type', 'classroom')
|
||||
->whereIn('name', array_keys($needsFallback))
|
||||
->groupBy('name')
|
||||
->get()
|
||||
->toArray();
|
||||
|
||||
foreach ($fallbackRows as $r) {
|
||||
$name = (string) ($r->item_name ?? '');
|
||||
if ($name !== '' && isset($inventoryMap[$name])) {
|
||||
$inventoryMap[$name] = max($inventoryMap[$name], (int) ($r->available ?? 0));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
foreach ($inventoryMap as $name => $value) {
|
||||
if ((int) $value !== 0) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$goodQty = (int) DB::table('inventory_items')
|
||||
->where('type', 'classroom')
|
||||
->where('name', $name)
|
||||
->sum('good_qty');
|
||||
|
||||
if ($goodQty > 0) {
|
||||
$inventoryMap[$name] = $goodQty;
|
||||
continue;
|
||||
}
|
||||
|
||||
$qty = (int) DB::table('inventory_items')
|
||||
->where('type', 'classroom')
|
||||
->where('name', $name)
|
||||
->sum('quantity');
|
||||
|
||||
if ($qty > 0) {
|
||||
$inventoryMap[$name] = $qty;
|
||||
}
|
||||
}
|
||||
|
||||
return $inventoryMap;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -9,7 +9,7 @@ class ClassPreparationRosterService
|
||||
public function getClassSectionStudentCounts(string $schoolYear, string $semester, bool $limitToSemester): array
|
||||
{
|
||||
$query = DB::table('student_class as sc')
|
||||
->select('sc.class_section_id, COUNT(DISTINCT sc.student_id) AS student_count', false)
|
||||
->selectRaw('sc.class_section_id, COUNT(DISTINCT sc.student_id) AS student_count')
|
||||
->join('students as s', 's.id', '=', 'sc.student_id')
|
||||
->where('s.is_active', 1)
|
||||
->where('sc.school_year', $schoolYear);
|
||||
@@ -24,7 +24,7 @@ class ClassPreparationRosterService
|
||||
public function getStudentCountForSection(string $schoolYear, string $semester, bool $limitToSemester, string $classSectionId): int
|
||||
{
|
||||
$query = DB::table('student_class as sc')
|
||||
->select('COUNT(DISTINCT sc.student_id) AS cnt', false)
|
||||
->selectRaw('COUNT(DISTINCT sc.student_id) AS cnt')
|
||||
->join('students as s', 's.id', '=', 'sc.student_id')
|
||||
->where('s.is_active', 1)
|
||||
->where('sc.class_section_id', $classSectionId)
|
||||
|
||||
@@ -50,7 +50,7 @@ class CompetitionScoresSaveService
|
||||
->where('competition_id', $competitionId)
|
||||
->where('class_section_id', $classSectionId)
|
||||
->get()
|
||||
->map(fn ($row) => (array) $row)
|
||||
->map(fn ($row) => $row->toArray())
|
||||
->all();
|
||||
|
||||
$map = [];
|
||||
|
||||
@@ -0,0 +1,19 @@
|
||||
<?php
|
||||
|
||||
namespace App\Services;
|
||||
|
||||
use App\Services\Fees\FeeRefundCalculatorService;
|
||||
|
||||
class FeeCalculationService
|
||||
{
|
||||
public function __construct(
|
||||
private FeeRefundCalculatorService $refundCalculator
|
||||
) {
|
||||
}
|
||||
|
||||
public function calculateRefund(array $students, int $parentId): float
|
||||
{
|
||||
$result = $this->refundCalculator->calculateRefund($students, $parentId);
|
||||
return (float) ($result['refund_amount'] ?? 0);
|
||||
}
|
||||
}
|
||||
@@ -6,20 +6,24 @@ class FeeGradeService
|
||||
{
|
||||
public function normalizeGrade(?string $grade): string
|
||||
{
|
||||
return strtoupper(trim((string) $grade));
|
||||
$normalized = strtoupper(trim((string) $grade));
|
||||
return str_replace([' ', '-'], '', $normalized);
|
||||
}
|
||||
|
||||
public function compareGrades(string $gradeA, string $gradeB): int
|
||||
{
|
||||
$valA = $this->getGradeLevel($gradeA);
|
||||
$valB = $this->getGradeLevel($gradeB);
|
||||
$normA = $this->normalizeGrade($gradeA);
|
||||
$normB = $this->normalizeGrade($gradeB);
|
||||
|
||||
$valA = $this->getGradeLevel($normA);
|
||||
$valB = $this->getGradeLevel($normB);
|
||||
|
||||
if ($valA !== $valB) {
|
||||
return $valA <=> $valB;
|
||||
}
|
||||
|
||||
preg_match('/\d+([A-Z]*)$/i', strtoupper($gradeA), $suffixA);
|
||||
preg_match('/\d+([A-Z]*)$/i', strtoupper($gradeB), $suffixB);
|
||||
preg_match('/\d+([A-Z]*)$/i', $normA, $suffixA);
|
||||
preg_match('/\d+([A-Z]*)$/i', $normB, $suffixB);
|
||||
|
||||
return strcmp($suffixA[1] ?? '', $suffixB[1] ?? '');
|
||||
}
|
||||
@@ -35,7 +39,7 @@ class FeeGradeService
|
||||
return 99;
|
||||
}
|
||||
|
||||
if (preg_match('/^(\d+)([A-Z]*)$/i', $grade, $matches)) {
|
||||
if (preg_match('/^(\d+)/', $grade, $matches)) {
|
||||
return (int) $matches[1];
|
||||
}
|
||||
|
||||
|
||||
@@ -97,7 +97,7 @@ class FinancialSummaryService
|
||||
$query = DB::table('additional_charges as ac')
|
||||
->selectRaw('COALESCE(SUM(ac.amount), 0) AS amount')
|
||||
->where('ac.school_year', $schoolYear)
|
||||
->where('ac.status !=', 'void');
|
||||
->where('ac.status', '!=', 'void');
|
||||
|
||||
if (!empty($dateFrom)) {
|
||||
$query->whereRaw('DATE(ac.due_date) >= ?', [$dateFrom]);
|
||||
@@ -115,11 +115,11 @@ class FinancialSummaryService
|
||||
$query = DB::table('additional_charges as ac')
|
||||
->selectRaw('ac.parent_id, COALESCE(SUM(ac.amount), 0) AS amount')
|
||||
->where('ac.school_year', $schoolYear)
|
||||
->where('ac.status !=', 'void')
|
||||
->where('ac.status', '!=', 'void')
|
||||
->where(function ($q) {
|
||||
$q->whereNull('ac.invoice_id')
|
||||
->orWhere('ac.invoice_id', 0)
|
||||
->orWhere('ac.status !=', 'applied');
|
||||
->orWhere('ac.status', '!=', 'applied');
|
||||
})
|
||||
->groupBy('ac.parent_id');
|
||||
|
||||
|
||||
@@ -96,17 +96,14 @@ class FinancialUnpaidParentsService
|
||||
}
|
||||
|
||||
$extraRows = DB::table('additional_charges as ac')
|
||||
->selectRaw(
|
||||
"ac.parent_id, u.firstname, u.lastname, u.email, COALESCE(SUM(ac.amount), 0) AS total_extra",
|
||||
false
|
||||
)
|
||||
->selectRaw("ac.parent_id, u.firstname, u.lastname, u.email, COALESCE(SUM(ac.amount), 0) AS total_extra")
|
||||
->leftJoin('users as u', 'u.id', '=', 'ac.parent_id')
|
||||
->where('ac.school_year', $schoolYear)
|
||||
->where('ac.status !=', 'void')
|
||||
->where('ac.status', '!=', 'void')
|
||||
->where(function ($q) {
|
||||
$q->whereNull('ac.invoice_id')
|
||||
->orWhere('ac.invoice_id', 0)
|
||||
->orWhere('ac.status !=', 'applied');
|
||||
->orWhere('ac.status', '!=', 'applied');
|
||||
})
|
||||
->groupBy('ac.parent_id')
|
||||
->get()
|
||||
|
||||
@@ -167,7 +167,7 @@ class HomeworkTrackingService
|
||||
->whereIn('tc.position', ['main', 'ta'])
|
||||
->orderBy('cs.class_id', 'ASC')
|
||||
->orderBy('cs.class_section_name', 'ASC')
|
||||
->orderByRaw("FIELD(tc.position, 'main','ta')")
|
||||
->orderByRaw("CASE tc.position WHEN 'main' THEN 0 WHEN 'ta' THEN 1 ELSE 2 END")
|
||||
->orderBy('u.firstname', 'ASC')
|
||||
->get();
|
||||
|
||||
|
||||
@@ -65,13 +65,13 @@ class IncidentAnalysisService
|
||||
}
|
||||
|
||||
if ($students[$studentKey]['last_incident'] === null) {
|
||||
$students[$studentKey]['last_incident'] = $incident['incident_datetime'] ?? null;
|
||||
$students[$studentKey]['last_incident'] = $this->formatIncidentDate($incident['incident_datetime'] ?? null);
|
||||
}
|
||||
|
||||
$students[$studentKey]['logs'][] = [
|
||||
'incident' => $incident['incident'] ?? '',
|
||||
'incident_state' => $state,
|
||||
'incident_datetime' => $incident['incident_datetime'] ?? null,
|
||||
'incident_datetime' => $this->formatIncidentDate($incident['incident_datetime'] ?? null),
|
||||
'open_description' => $incident['open_description'] ?? null,
|
||||
'close_description' => $incident['close_description'] ?? null,
|
||||
'cancel_description' => $incident['cancel_description'] ?? null,
|
||||
@@ -89,4 +89,23 @@ class IncidentAnalysisService
|
||||
|
||||
return $studentRows;
|
||||
}
|
||||
|
||||
private function formatIncidentDate(mixed $value): ?string
|
||||
{
|
||||
if ($value === null || $value === '') {
|
||||
return null;
|
||||
}
|
||||
|
||||
if ($value instanceof \DateTimeInterface) {
|
||||
return $value->format('Y-m-d H:i:s');
|
||||
}
|
||||
|
||||
$raw = (string) $value;
|
||||
$ts = strtotime($raw);
|
||||
if ($ts !== false) {
|
||||
return date('Y-m-d H:i:s', $ts);
|
||||
}
|
||||
|
||||
return $raw;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -69,16 +69,20 @@ class IncidentLookupService
|
||||
}
|
||||
|
||||
$rows = DB::table('users')
|
||||
->select('id, firstname, lastname')
|
||||
->select('id', 'firstname', 'lastname')
|
||||
->whereIn('id', $userIds)
|
||||
->get()
|
||||
->map(fn ($row) => (array) $row)
|
||||
->map(fn ($row) => $row instanceof \Illuminate\Contracts\Support\Arrayable ? $row->toArray() : (array) $row)
|
||||
->all();
|
||||
|
||||
$map = [];
|
||||
foreach ($rows as $row) {
|
||||
$id = $row['id'] ?? $row['user_id'] ?? null;
|
||||
if ($id === null) {
|
||||
continue;
|
||||
}
|
||||
$name = trim((string) ($row['firstname'] ?? '') . ' ' . (string) ($row['lastname'] ?? ''));
|
||||
$map[(int) $row['id']] = $name !== '' ? $name : ('User #' . $row['id']);
|
||||
$map[(int) $id] = $name !== '' ? $name : ('User #' . $id);
|
||||
}
|
||||
|
||||
return $map;
|
||||
|
||||
@@ -30,7 +30,7 @@ class InvoiceGenerationService
|
||||
->where('parent_id', $parentId)
|
||||
->where('school_year', $schoolYear)
|
||||
->get()
|
||||
->map(fn ($r) => (array) $r)
|
||||
->map(fn ($r) => $r->toArray())
|
||||
->all();
|
||||
|
||||
if (empty($enrollments)) {
|
||||
|
||||
@@ -40,7 +40,11 @@ class InvoiceManagementService
|
||||
$parents = User::getUsersByRoleAndSchoolYear('parent', $schoolYear);
|
||||
|
||||
foreach ($parents as $parent) {
|
||||
$students = Student::query()->where('parent_id', $parent['id'])->get()->map(fn ($r) => (array) $r)->all();
|
||||
$students = Student::query()
|
||||
->where('parent_id', $parent['id'])
|
||||
->get()
|
||||
->map(fn ($r) => $r->toArray())
|
||||
->all();
|
||||
|
||||
$parentData = [
|
||||
'parent_name' => trim(($parent['firstname'] ?? '') . ' ' . ($parent['lastname'] ?? '')),
|
||||
@@ -106,7 +110,7 @@ class InvoiceManagementService
|
||||
->where('student_id', $student['id'])
|
||||
->where('school_year', $schoolYear)
|
||||
->get()
|
||||
->map(fn ($r) => (array) $r)
|
||||
->map(fn ($r) => $r->toArray())
|
||||
->all();
|
||||
|
||||
foreach ($enrollments as $enrollment) {
|
||||
|
||||
@@ -94,6 +94,10 @@ class ExamScoreService
|
||||
if (!is_numeric($studentId)) {
|
||||
continue;
|
||||
}
|
||||
$student = Student::query()->find((int) $studentId);
|
||||
if (!$student) {
|
||||
continue;
|
||||
}
|
||||
$rawScore = $data['score'] ?? null;
|
||||
$normalizedScore = is_numeric($rawScore) ? (float) $rawScore : null;
|
||||
|
||||
@@ -106,6 +110,7 @@ class ExamScoreService
|
||||
|
||||
$payload = [
|
||||
'student_id' => (int) $studentId,
|
||||
'school_id' => $student->school_id,
|
||||
'class_section_id' => $classSectionId,
|
||||
'updated_by' => $updatedBy,
|
||||
'score' => $normalizedScore,
|
||||
|
||||
@@ -57,12 +57,13 @@ class HomeworkScoreService
|
||||
->whereIn('semester', $semVariants)
|
||||
->where('school_year', $schoolYear)
|
||||
->get()
|
||||
->map(fn ($row) => (array) $row)
|
||||
->map(fn ($row) => $row->toArray())
|
||||
->all();
|
||||
|
||||
$scoresMap = [];
|
||||
foreach ($scoresRows as $row) {
|
||||
$scoresMap[(int) $row['student_id']][(int) $row['homework_index']] = $row['score'];
|
||||
$score = $row['score'] ?? null;
|
||||
$scoresMap[(int) $row['student_id']][(int) $row['homework_index']] = is_numeric($score) ? (float) $score : $score;
|
||||
}
|
||||
|
||||
foreach ($students as &$student) {
|
||||
@@ -196,6 +197,10 @@ class HomeworkScoreService
|
||||
->get();
|
||||
|
||||
foreach ($students as $student) {
|
||||
$studentRow = Student::query()->find((int) $student->student_id);
|
||||
if (!$studentRow) {
|
||||
continue;
|
||||
}
|
||||
$exists = Homework::query()
|
||||
->where('student_id', $student->student_id)
|
||||
->where('homework_index', $nextIndex)
|
||||
@@ -210,6 +215,7 @@ class HomeworkScoreService
|
||||
|
||||
Homework::query()->create([
|
||||
'student_id' => (int) $student->student_id,
|
||||
'school_id' => $studentRow->school_id,
|
||||
'class_section_id' => $classSectionId,
|
||||
'updated_by' => $updatedBy,
|
||||
'homework_index' => $nextIndex,
|
||||
|
||||
@@ -50,12 +50,14 @@ class ParticipationScoreService
|
||||
|
||||
$scoreMap = [];
|
||||
foreach ($scoresRows as $row) {
|
||||
$scoreMap[(int) $row->student_id] = $row->score;
|
||||
$score = $row->score;
|
||||
$scoreMap[(int) $row->student_id] = is_numeric($score) ? (float) $score : $score;
|
||||
}
|
||||
|
||||
foreach ($students as &$student) {
|
||||
$score = $scoreMap[$student['student_id']] ?? '';
|
||||
$student['scores'] = [
|
||||
'score' => $scoreMap[$student['student_id']] ?? '',
|
||||
'score' => $score,
|
||||
];
|
||||
}
|
||||
unset($student);
|
||||
|
||||
@@ -57,12 +57,13 @@ class ProjectScoreService
|
||||
->where('semester', $semester)
|
||||
->where('school_year', $schoolYear)
|
||||
->get()
|
||||
->map(fn ($row) => (array) $row)
|
||||
->map(fn ($row) => $row->toArray())
|
||||
->all();
|
||||
|
||||
$scoresMap = [];
|
||||
foreach ($scoresRows as $row) {
|
||||
$scoresMap[(int) $row['student_id']][(int) $row['project_index']] = $row['score'];
|
||||
$score = $row['score'] ?? null;
|
||||
$scoresMap[(int) $row['student_id']][(int) $row['project_index']] = is_numeric($score) ? (float) $score : $score;
|
||||
}
|
||||
|
||||
foreach ($students as &$student) {
|
||||
@@ -193,6 +194,10 @@ class ProjectScoreService
|
||||
->get();
|
||||
|
||||
foreach ($students as $student) {
|
||||
$studentRow = Student::query()->find((int) $student->student_id);
|
||||
if (!$studentRow) {
|
||||
continue;
|
||||
}
|
||||
$exists = Project::query()
|
||||
->where('student_id', $student->student_id)
|
||||
->where('project_index', $nextIndex)
|
||||
@@ -207,6 +212,7 @@ class ProjectScoreService
|
||||
|
||||
Project::query()->create([
|
||||
'student_id' => (int) $student->student_id,
|
||||
'school_id' => $studentRow->school_id,
|
||||
'class_section_id' => $classSectionId,
|
||||
'updated_by' => $updatedBy,
|
||||
'project_index' => $nextIndex,
|
||||
|
||||
@@ -56,12 +56,13 @@ class QuizScoreService
|
||||
->where('semester', $semester)
|
||||
->where('school_year', $schoolYear)
|
||||
->get()
|
||||
->map(fn ($row) => (array) $row)
|
||||
->map(fn ($row) => $row->toArray())
|
||||
->all();
|
||||
|
||||
$scoresMap = [];
|
||||
foreach ($scoresRows as $row) {
|
||||
$scoresMap[(int) $row['student_id']][(int) $row['quiz_index']] = $row['score'];
|
||||
$score = $row['score'] ?? null;
|
||||
$scoresMap[(int) $row['student_id']][(int) $row['quiz_index']] = is_numeric($score) ? (float) $score : $score;
|
||||
}
|
||||
|
||||
foreach ($students as &$student) {
|
||||
@@ -135,6 +136,10 @@ class QuizScoreService
|
||||
if (!is_array($quizData)) {
|
||||
$quizData = [1 => $quizData];
|
||||
}
|
||||
$student = Student::query()->find((int) $studentId);
|
||||
if (!$student) {
|
||||
continue;
|
||||
}
|
||||
foreach ($quizData as $quizNumber => $score) {
|
||||
if (!is_numeric($quizNumber)) {
|
||||
continue;
|
||||
@@ -152,6 +157,7 @@ class QuizScoreService
|
||||
|
||||
$payload = [
|
||||
'student_id' => (int) $studentId,
|
||||
'school_id' => $student->school_id,
|
||||
'class_section_id' => $classSectionId,
|
||||
'updated_by' => $updatedBy,
|
||||
'quiz_index' => (int) $quizNumber,
|
||||
@@ -190,6 +196,10 @@ class QuizScoreService
|
||||
->get();
|
||||
|
||||
foreach ($students as $student) {
|
||||
$studentRow = Student::query()->find((int) $student->student_id);
|
||||
if (!$studentRow) {
|
||||
continue;
|
||||
}
|
||||
$exists = Quiz::query()
|
||||
->where('student_id', $student->student_id)
|
||||
->where('quiz_index', $nextIndex)
|
||||
@@ -204,6 +214,7 @@ class QuizScoreService
|
||||
|
||||
Quiz::query()->create([
|
||||
'student_id' => (int) $student->student_id,
|
||||
'school_id' => $studentRow->school_id,
|
||||
'class_section_id' => $classSectionId,
|
||||
'updated_by' => $updatedBy,
|
||||
'quiz_index' => $nextIndex,
|
||||
|
||||
@@ -111,14 +111,14 @@ class ScoreDashboardService
|
||||
$sid = $student['student_id'];
|
||||
$scoreRow = $scores[$sid] ?? null;
|
||||
$student['scores'] = $scoreRow ? [
|
||||
'homework' => $scoreRow->homework_avg,
|
||||
'quiz' => $scoreRow->quiz_avg,
|
||||
'project' => $scoreRow->project_avg,
|
||||
'midterm_exam' => $scoreRow->midterm_exam_score,
|
||||
'final_exam' => $scoreRow->final_exam_score,
|
||||
'attendance' => $scoreRow->attendance_score,
|
||||
'ptap' => $scoreRow->ptap_score,
|
||||
'semester' => $scoreRow->semester_score,
|
||||
'homework' => $this->normalizeScore($scoreRow->homework_avg),
|
||||
'quiz' => $this->normalizeScore($scoreRow->quiz_avg),
|
||||
'project' => $this->normalizeScore($scoreRow->project_avg),
|
||||
'midterm_exam' => $this->normalizeScore($scoreRow->midterm_exam_score),
|
||||
'final_exam' => $this->normalizeScore($scoreRow->final_exam_score),
|
||||
'attendance' => $this->normalizeScore($scoreRow->attendance_score),
|
||||
'ptap' => $this->normalizeScore($scoreRow->ptap_score),
|
||||
'semester' => $this->normalizeScore($scoreRow->semester_score),
|
||||
] : [];
|
||||
$student['comments'] = $commentMap[$sid] ?? [];
|
||||
}
|
||||
@@ -267,4 +267,13 @@ class ScoreDashboardService
|
||||
'selectedYear' => $schoolYear,
|
||||
];
|
||||
}
|
||||
|
||||
private function normalizeScore(mixed $value): mixed
|
||||
{
|
||||
if ($value === null || $value === '') {
|
||||
return $value;
|
||||
}
|
||||
|
||||
return is_numeric($value) ? (float) $value : $value;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -73,7 +73,7 @@ class UserManagementService
|
||||
|
||||
try {
|
||||
DB::transaction(function () use ($userId, $user) {
|
||||
UserRole::query()->where('user_id', $userId)->delete();
|
||||
UserRole::query()->where('user_id', $userId)->forceDelete();
|
||||
$user->delete();
|
||||
});
|
||||
} catch (\Throwable $e) {
|
||||
|
||||
Reference in New Issue
Block a user