add test batches

This commit is contained in:
root
2026-06-08 23:45:55 -04:00
parent c792b8be05
commit 8d4d610b82
1480 changed files with 22587 additions and 10762 deletions
@@ -3,7 +3,6 @@
namespace Tests\Unit\Services\Administrator;
use App\Services\Administrator\AdminNotificationSubjectService;
use App\Services\Administrator\AdminNotificationUserService;
use Illuminate\Foundation\Testing\RefreshDatabase;
use Illuminate\Support\Facades\DB;
use Tests\TestCase;
@@ -45,7 +44,7 @@ class AdminNotificationSubjectServiceTest extends TestCase
'role_id' => 1,
]);
$userService = new AdminNotificationUserService;
$userService = new \App\Services\Administrator\AdminNotificationUserService();
$service = new AdminNotificationSubjectService($userService);
$data = $service->alertsData();
@@ -66,7 +66,7 @@ class AdminNotificationUserServiceTest extends TestCase
['id' => 2, 'user_id' => 2, 'role_id' => 2],
]);
$service = new AdminNotificationUserService;
$service = new AdminNotificationUserService();
$admins = $service->fetchAdminNotificationUsers();
$this->assertCount(1, $admins);
@@ -2,7 +2,6 @@
namespace Tests\Unit\Services\Administrator;
use App\Services\Administrator\AdminNotificationUserService;
use App\Services\Administrator\AdminPrintRecipientService;
use Illuminate\Foundation\Testing\RefreshDatabase;
use Illuminate\Support\Facades\DB;
@@ -51,7 +50,7 @@ class AdminPrintRecipientServiceTest extends TestCase
'subject' => 'print_requests',
]);
$service = new AdminPrintRecipientService(new AdminNotificationUserService);
$service = new AdminPrintRecipientService(new \App\Services\Administrator\AdminNotificationUserService());
$data = $service->data();
$this->assertTrue($data['assigned'][1]);
@@ -2,13 +2,7 @@
namespace Tests\Unit\Services\Administrator;
use App\Models\StaffAttendance;
use App\Models\User;
use App\Services\Administrator\AdministratorAbsenceService;
use App\Services\Administrator\AdministratorSharedService;
use App\Services\SemesterRangeService;
use App\Services\Semesters\SemesterConfigService;
use App\Services\Staff\StaffTimeOffLinkService;
use Illuminate\Foundation\Testing\RefreshDatabase;
use Illuminate\Http\Request;
use Mockery;
@@ -20,17 +14,17 @@ class AdministratorAbsenceServiceTest extends TestCase
public function test_submit_requires_reason(): void
{
$shared = Mockery::mock(AdministratorSharedService::class);
$shared = Mockery::mock(\App\Services\Administrator\AdministratorSharedService::class);
$shared->shouldReceive('getSemester')->andReturn('Fall');
$shared->shouldReceive('getSchoolYear')->andReturn('2025-2026');
$shared->shouldReceive('allowedAbsenceDates')->andReturn(['2025-01-05']);
$service = new AdministratorAbsenceService(
$shared,
new User,
new StaffAttendance,
new SemesterRangeService(new SemesterConfigService),
Mockery::mock(StaffTimeOffLinkService::class)
new \App\Models\User(),
new \App\Models\StaffAttendance(),
new \App\Services\SemesterRangeService(new \App\Services\Semesters\SemesterConfigService()),
Mockery::mock(\App\Services\Staff\StaffTimeOffLinkService::class)
);
$request = Request::create('/absences', 'POST', ['dates' => ['2025-01-05']]);
@@ -3,8 +3,6 @@
namespace Tests\Unit\Services\Administrator;
use App\Services\Administrator\AdministratorDashboardService;
use App\Services\Administrator\AdministratorMetricsService;
use App\Services\Administrator\AdministratorUserSearchService;
use Illuminate\Foundation\Testing\RefreshDatabase;
use Mockery;
use Tests\TestCase;
@@ -15,10 +13,10 @@ class AdministratorDashboardServiceTest extends TestCase
public function test_delegates_to_metrics_and_search(): void
{
$metrics = Mockery::mock(AdministratorMetricsService::class);
$metrics = Mockery::mock(\App\Services\Administrator\AdministratorMetricsService::class);
$metrics->shouldReceive('metrics')->once()->andReturn(['counts' => []]);
$search = Mockery::mock(AdministratorUserSearchService::class);
$search = Mockery::mock(\App\Services\Administrator\AdministratorUserSearchService::class);
$search->shouldReceive('search')->once()->with('john')->andReturn(['results' => []]);
$service = new AdministratorDashboardService($metrics, $search);
@@ -15,7 +15,7 @@ class AdministratorEnrollmentEventServiceTest extends TestCase
{
Event::fake();
$service = new AdministratorEnrollmentEventService;
$service = new AdministratorEnrollmentEventService();
$service->dispatchGroupedEvents([], [], [], '2025-2026');
Event::assertNotDispatched('studentEnrolled');
@@ -2,12 +2,7 @@
namespace Tests\Unit\Services\Administrator;
use App\Models\ClassSection;
use App\Models\Enrollment;
use App\Models\Student;
use App\Models\StudentClass;
use App\Services\Administrator\AdministratorEnrollmentQueryService;
use App\Services\Administrator\AdministratorSharedService;
use Illuminate\Foundation\Testing\RefreshDatabase;
use Mockery;
use Tests\TestCase;
@@ -18,17 +13,17 @@ class AdministratorEnrollmentQueryServiceTest extends TestCase
public function test_enrollment_withdrawal_data_returns_structure(): void
{
$shared = Mockery::mock(AdministratorSharedService::class);
$shared = Mockery::mock(\App\Services\Administrator\AdministratorSharedService::class);
$shared->shouldReceive('getSchoolYear')->andReturn('2025-2026');
$shared->shouldReceive('getSemester')->andReturn('Fall');
$shared->shouldReceive('getSchoolYearStartYear')->andReturn(2025);
$service = new AdministratorEnrollmentQueryService(
$shared,
new Student,
new StudentClass,
new Enrollment,
new ClassSection
new \App\Models\Student(),
new \App\Models\StudentClass(),
new \App\Models\Enrollment(),
new \App\Models\ClassSection()
);
$data = $service->enrollmentWithdrawalData('2025-2026');
@@ -3,7 +3,6 @@
namespace Tests\Unit\Services\Administrator;
use App\Services\Administrator\AdministratorEnrollmentRefundService;
use App\Services\FeeCalculationService;
use Illuminate\Foundation\Testing\RefreshDatabase;
use Illuminate\Support\Facades\DB;
use Mockery;
@@ -25,7 +24,7 @@ class AdministratorEnrollmentRefundServiceTest extends TestCase
'school_year' => '2025-2026',
]);
$feeCalc = Mockery::mock(FeeCalculationService::class);
$feeCalc = Mockery::mock(\App\Services\FeeCalculationService::class);
$service = new AdministratorEnrollmentRefundService($feeCalc);
$result = $service->processRefunds([10], '2025-2026', 1);
@@ -2,9 +2,7 @@
namespace Tests\Unit\Services\Administrator;
use App\Services\Administrator\AdministratorEnrollmentQueryService;
use App\Services\Administrator\AdministratorEnrollmentService;
use App\Services\Administrator\AdministratorEnrollmentStatusService;
use Illuminate\Foundation\Testing\RefreshDatabase;
use Mockery;
use Tests\TestCase;
@@ -15,11 +13,11 @@ class AdministratorEnrollmentServiceTest extends TestCase
public function test_delegates_to_query_and_status_services(): void
{
$query = Mockery::mock(AdministratorEnrollmentQueryService::class);
$query = Mockery::mock(\App\Services\Administrator\AdministratorEnrollmentQueryService::class);
$query->shouldReceive('enrollmentWithdrawalData')->once()->andReturn(['rows' => []]);
$query->shouldReceive('newStudentsData')->once()->andReturn(['students' => []]);
$status = Mockery::mock(AdministratorEnrollmentStatusService::class);
$status = Mockery::mock(\App\Services\Administrator\AdministratorEnrollmentStatusService::class);
$status->shouldReceive('updateStatuses')->once()->andReturn(['success' => true]);
$service = new AdministratorEnrollmentService($query, $status);
@@ -2,12 +2,7 @@
namespace Tests\Unit\Services\Administrator;
use App\Models\Student;
use App\Models\User;
use App\Services\Administrator\AdministratorEnrollmentEventService;
use App\Services\Administrator\AdministratorEnrollmentRefundService;
use App\Services\Administrator\AdministratorEnrollmentStatusService;
use App\Services\Administrator\AdministratorSharedService;
use Illuminate\Foundation\Testing\RefreshDatabase;
use Mockery;
use Tests\TestCase;
@@ -18,17 +13,17 @@ class AdministratorEnrollmentStatusServiceTest extends TestCase
public function test_update_statuses_requires_payload(): void
{
$shared = Mockery::mock(AdministratorSharedService::class);
$shared = Mockery::mock(\App\Services\Administrator\AdministratorSharedService::class);
$shared->shouldReceive('getSchoolYear')->andReturn('2025-2026');
$shared->shouldReceive('getSemester')->andReturn('Fall');
$refundService = Mockery::mock(AdministratorEnrollmentRefundService::class);
$eventService = Mockery::mock(AdministratorEnrollmentEventService::class);
$refundService = Mockery::mock(\App\Services\Administrator\AdministratorEnrollmentRefundService::class);
$eventService = Mockery::mock(\App\Services\Administrator\AdministratorEnrollmentEventService::class);
$service = new AdministratorEnrollmentStatusService(
$shared,
new Student,
new User,
new \App\Models\Student(),
new \App\Models\User(),
$refundService,
$eventService
);
@@ -2,11 +2,7 @@
namespace Tests\Unit\Services\Administrator;
use App\Models\Configuration;
use App\Models\LoginActivity;
use App\Models\User;
use App\Services\Administrator\AdministratorMetricsService;
use App\Services\Administrator\AdministratorSharedService;
use Illuminate\Foundation\Testing\RefreshDatabase;
use Illuminate\Support\Facades\DB;
use Tests\TestCase;
@@ -23,9 +19,9 @@ class AdministratorMetricsServiceTest extends TestCase
]);
$service = new AdministratorMetricsService(
new AdministratorSharedService(new Configuration),
new User,
new LoginActivity
new \App\Services\Administrator\AdministratorSharedService(new \App\Models\Configuration()),
new \App\Models\User(),
new \App\Models\LoginActivity()
);
$metrics = $service->metrics();
@@ -3,8 +3,6 @@
namespace Tests\Unit\Services\Administrator;
use App\Services\Administrator\AdministratorNotificationService;
use App\Services\Administrator\AdminNotificationSubjectService;
use App\Services\Administrator\AdminPrintRecipientService;
use Illuminate\Foundation\Testing\RefreshDatabase;
use Mockery;
use Tests\TestCase;
@@ -15,11 +13,11 @@ class AdministratorNotificationServiceTest extends TestCase
public function test_delegates_to_subject_and_print_services(): void
{
$subject = Mockery::mock(AdminNotificationSubjectService::class);
$subject = Mockery::mock(\App\Services\Administrator\AdminNotificationSubjectService::class);
$subject->shouldReceive('alertsData')->once()->andReturn(['admins' => []]);
$subject->shouldReceive('save')->once()->andReturn(['success' => true]);
$print = Mockery::mock(AdminPrintRecipientService::class);
$print = Mockery::mock(\App\Services\Administrator\AdminPrintRecipientService::class);
$print->shouldReceive('data')->once()->andReturn(['admins' => []]);
$print->shouldReceive('save')->once()->andReturn(['success' => true]);
@@ -2,7 +2,6 @@
namespace Tests\Unit\Services\Administrator;
use App\Models\Configuration;
use App\Services\Administrator\AdministratorSharedService;
use Illuminate\Foundation\Testing\RefreshDatabase;
use Tests\TestCase;
@@ -13,7 +12,7 @@ class AdministratorSharedServiceTest extends TestCase
public function test_get_previous_school_year(): void
{
$service = new AdministratorSharedService(new Configuration);
$service = new AdministratorSharedService(new \App\Models\Configuration());
$this->assertSame('2024-2025', $service->getPreviousSchoolYear('2025-2026'));
$this->assertSame('2024-25', $service->getPreviousSchoolYear('2025-26'));
@@ -3,10 +3,7 @@
namespace Tests\Unit\Services\Administrator;
use App\Services\Administrator\AdministratorTeacherSubmissionService;
use App\Services\Administrator\TeacherSubmissionNotificationService;
use App\Services\Administrator\TeacherSubmissionReportService;
use Illuminate\Foundation\Testing\RefreshDatabase;
use Illuminate\Http\Request;
use Mockery;
use Tests\TestCase;
@@ -16,15 +13,15 @@ class AdministratorTeacherSubmissionServiceTest extends TestCase
public function test_delegates_to_report_and_notification(): void
{
$report = Mockery::mock(TeacherSubmissionReportService::class);
$report = Mockery::mock(\App\Services\Administrator\TeacherSubmissionReportService::class);
$report->shouldReceive('report')->once()->andReturn(['rows' => []]);
$notify = Mockery::mock(TeacherSubmissionNotificationService::class);
$notify = Mockery::mock(\App\Services\Administrator\TeacherSubmissionNotificationService::class);
$notify->shouldReceive('send')->once()->andReturn(['success' => true]);
$service = new AdministratorTeacherSubmissionService($report, $notify);
$this->assertSame(['rows' => []], $service->report());
$this->assertSame(['success' => true], $service->sendNotifications(new Request, 1));
$this->assertSame(['success' => true], $service->sendNotifications(new \Illuminate\Http\Request(), 1));
}
}
@@ -12,7 +12,7 @@ class AdministratorUserSearchServiceTest extends TestCase
public function test_empty_query_returns_no_results(): void
{
$service = new AdministratorUserSearchService;
$service = new AdministratorUserSearchService();
$result = $service->search('');
$this->assertSame('', $result['query']);
@@ -2,9 +2,7 @@
namespace Tests\Unit\Services\Administrator;
use App\Services\Administrator\AdministratorSharedService;
use App\Services\Administrator\TeacherSubmissionNotificationService;
use App\Services\Administrator\TeacherSubmissionSupportService;
use Illuminate\Foundation\Testing\RefreshDatabase;
use Illuminate\Http\Request;
use Mockery;
@@ -16,8 +14,8 @@ class TeacherSubmissionNotificationServiceTest extends TestCase
public function test_send_requires_targets(): void
{
$shared = Mockery::mock(AdministratorSharedService::class);
$support = Mockery::mock(TeacherSubmissionSupportService::class);
$shared = Mockery::mock(\App\Services\Administrator\AdministratorSharedService::class);
$support = Mockery::mock(\App\Services\Administrator\TeacherSubmissionSupportService::class);
$service = new TeacherSubmissionNotificationService($shared, $support);
$request = Request::create('/notify', 'POST', []);
@@ -2,9 +2,7 @@
namespace Tests\Unit\Services\Administrator;
use App\Services\Administrator\AdministratorSharedService;
use App\Services\Administrator\TeacherSubmissionReportService;
use App\Services\Administrator\TeacherSubmissionSupportService;
use Illuminate\Foundation\Testing\RefreshDatabase;
use Mockery;
use Tests\TestCase;
@@ -15,11 +13,11 @@ class TeacherSubmissionReportServiceTest extends TestCase
public function test_report_returns_summary(): void
{
$shared = Mockery::mock(AdministratorSharedService::class);
$shared = Mockery::mock(\App\Services\Administrator\AdministratorSharedService::class);
$shared->shouldReceive('getSemester')->andReturn('Fall');
$shared->shouldReceive('getSchoolYear')->andReturn('2025-2026');
$support = Mockery::mock(TeacherSubmissionSupportService::class);
$support = Mockery::mock(\App\Services\Administrator\TeacherSubmissionSupportService::class);
$support->shouldReceive('submissionStatus')->andReturn([
'label' => 'No students',
'badge' => 'bg-secondary',
@@ -12,7 +12,7 @@ class TeacherSubmissionSupportServiceTest extends TestCase
public function test_submission_status_labels(): void
{
$service = new TeacherSubmissionSupportService;
$service = new TeacherSubmissionSupportService();
$status = $service->submissionStatus(3, 5);
$this->assertSame('Missing', $status['label']);
@@ -21,7 +21,7 @@ class TeacherSubmissionSupportServiceTest extends TestCase
public function test_build_missing_items(): void
{
$service = new TeacherSubmissionSupportService;
$service = new TeacherSubmissionSupportService();
$items = $service->buildMissingItems([
'attendance_status' => ['completed' => false],
'midterm_score_status' => ['completed' => true],
@@ -19,7 +19,7 @@ class AssignmentContextServiceTest extends TestCase
['id' => 2, 'config_key' => 'semester', 'config_value' => 'Fall'],
]);
$service = new AssignmentContextService(new Configuration);
$service = new AssignmentContextService(new Configuration());
$this->assertSame('Fall', $service->getCurrentSemester());
$this->assertSame('2025-2026', $service->getCurrentSchoolYear());
@@ -17,7 +17,7 @@ class AssignmentDataLoaderServiceTest extends TestCase
{
$this->seedTeacherData();
$service = new AssignmentDataLoaderService(new TeacherClass, new StudentClass);
$service = new AssignmentDataLoaderService(new TeacherClass(), new StudentClass());
$rows = $service->loadTeacherClasses('2025-2026');
$this->assertCount(1, $rows);
@@ -28,7 +28,7 @@ class AssignmentDataLoaderServiceTest extends TestCase
{
$this->seedStudentData();
$service = new AssignmentDataLoaderService(new TeacherClass, new StudentClass);
$service = new AssignmentDataLoaderService(new TeacherClass(), new StudentClass());
$rows = $service->loadStudentClasses('2025-2026');
$this->assertCount(1, $rows);
@@ -13,7 +13,7 @@ class AssignmentMetaServiceTest extends TestCase
public function test_get_school_years_uses_fallback(): void
{
$service = new AssignmentMetaService;
$service = new AssignmentMetaService();
$years = $service->getSchoolYears('2025-2026');
$this->assertSame(['2025-2026'], $years);
@@ -27,7 +27,7 @@ class AssignmentMetaServiceTest extends TestCase
['class_section_id' => 103, 'teacher_id' => 3, 'position' => 'main', 'school_year' => '2025-2026', 'semester' => 'Fall'],
]);
$service = new AssignmentMetaService;
$service = new AssignmentMetaService();
$years = $service->getSchoolYears();
$this->assertSame(['2025-2026', '2024-2025'], $years);
@@ -35,7 +35,7 @@ class AssignmentMetaServiceTest extends TestCase
public function test_first_non_empty_returns_first_value(): void
{
$service = new AssignmentMetaService;
$service = new AssignmentMetaService();
$value = $service->firstNonEmpty([null, '', 'Fall', 'Spring']);
$this->assertSame('Fall', $value);
@@ -23,7 +23,7 @@ class AssignmentSectionServiceTest extends TestCase
'school_year' => '2025-2026',
]);
$service = new AssignmentSectionService(new ClassSection);
$service = new AssignmentSectionService(new ClassSection());
$map = $service->getSectionNamesMap([1]);
$this->assertSame('1-A', $map[1]);
@@ -17,19 +17,12 @@ class AssignmentServiceTest extends TestCase
use RefreshDatabase;
protected AssignmentService $service;
protected User $teacherMain;
protected User $teacherAssistant;
protected User $updatedByUser;
protected ClassSection $sectionA;
protected ClassSection $sectionB;
protected Student $studentOne;
protected Student $studentTwo;
protected function setUp(): void
@@ -16,7 +16,7 @@ class AttendanceAutoPublishJobServiceTest extends TestCase
public function test_run_publishes_due_records(): void
{
$autoPublish = new AttendanceAutoPublishService;
$autoPublish = new AttendanceAutoPublishService();
$service = new AttendanceAutoPublishJobService($autoPublish);
$now = new DateTimeImmutable('2025-03-01 10:00:00', new DateTimeZone('UTC'));
@@ -10,7 +10,7 @@ class AttendanceAutoPublishServiceTest extends TestCase
{
public function test_second_sunday_after_end_of_day(): void
{
$service = new AttendanceAutoPublishService;
$service = new AttendanceAutoPublishService();
$result = $service->secondSundayAfterEndOfDay('2025-02-03', 'UTC');
@@ -19,7 +19,7 @@ class AttendanceAutoPublishServiceTest extends TestCase
public function test_second_sunday_backward_date(): void
{
$service = new AttendanceAutoPublishService;
$service = new AttendanceAutoPublishService();
$now = new DateTimeImmutable('2025-02-16 10:00:00', new \DateTimeZone('UTC'));
$result = $service->secondSundayBackwardDate($now, 'UTC');
@@ -28,7 +28,7 @@ class AttendanceCommentServiceTest extends TestCase
],
]);
$service = new AttendanceCommentService;
$service = new AttendanceCommentService();
$comment = $service->commentFromScore(95, 'James');
$this->assertSame("James' attendance is excellent.", $comment);
@@ -43,7 +43,7 @@ class AttendanceCommentServiceTest extends TestCase
'is_active' => 1,
]);
$service = new AttendanceCommentService;
$service = new AttendanceCommentService();
$comment = $service->commentFromScore(70);
$this->assertSame("This student's needs improvement in attendance.", $comment);
@@ -7,7 +7,6 @@ use App\Services\Attendance\AttendanceConsequenceService;
use App\Services\EmailService;
use App\Services\Notifications\UserNotificationDispatchService;
use Illuminate\Foundation\Testing\RefreshDatabase;
use Illuminate\Support\Facades\DB;
use Mockery;
use Tests\TestCase;
@@ -31,7 +30,7 @@ class AttendanceConsequenceServiceTest extends TestCase
public function test_send_follow_up_marks_tracking_and_notifies(): void
{
DB::table('students')->insert([
\Illuminate\Support\Facades\DB::table('students')->insert([
'id' => 1,
'school_id' => 1,
'parent_id' => 1,
@@ -12,7 +12,7 @@ class AttendancePolicyServiceTest extends TestCase
public function test_teacher_can_edit_draft(): void
{
$service = new AttendancePolicyService;
$service = new AttendancePolicyService();
$allowed = $service->canEditDay(
['status' => 'draft'],
['roles' => ['Teacher'], 'permissions' => []]
@@ -23,7 +23,7 @@ class AttendancePolicyServiceTest extends TestCase
public function test_teacher_cannot_edit_finalized(): void
{
$service = new AttendancePolicyService;
$service = new AttendancePolicyService();
$allowed = $service->canEditDay(
['status' => 'finalized'],
['roles' => ['Teacher'], 'permissions' => []]
@@ -34,7 +34,7 @@ class AttendancePolicyServiceTest extends TestCase
public function test_admin_like_by_permission(): void
{
$service = new AttendancePolicyService;
$service = new AttendancePolicyService();
$isAdmin = $service->isAdminLike([], ['attendance.manage.daily']);
$this->assertTrue($isAdmin);
@@ -42,7 +42,7 @@ class AttendancePolicyServiceTest extends TestCase
public function test_can_manage_early_dismissals_for_teacher(): void
{
$service = new AttendancePolicyService;
$service = new AttendancePolicyService();
$allowed = $service->canManageEarlyDismissals(['Teacher']);
$this->assertTrue($allowed);
@@ -2,25 +2,7 @@
namespace Tests\Unit\Services\Attendance;
use App\Models\AttendanceData;
use App\Models\AttendanceDay;
use App\Models\AttendanceRecord;
use App\Models\Calendar;
use App\Models\ClassSection;
use App\Models\Configuration;
use App\Models\Student;
use App\Models\StudentClass;
use App\Models\TeacherClass;
use App\Models\User;
use App\Models\UserRole;
use App\Services\Attendance\AttendanceAutoPublishService;
use App\Services\Attendance\AttendancePolicyService;
use App\Services\Attendance\AttendanceQueryService;
use App\Services\Attendance\AttendanceRecordSyncService;
use App\Services\Attendance\AttendanceService;
use App\Services\Attendance\SemesterRangeService;
use App\Services\Attendance\StudentAttendanceWriterService;
use App\Services\Attendance\TeacherAttendanceSubmissionService;
use Illuminate\Foundation\Testing\RefreshDatabase;
use Illuminate\Support\Facades\DB;
use Tests\TestCase;
@@ -80,45 +62,45 @@ class AttendanceQueryServiceTest extends TestCase
]);
$service = new AttendanceQueryService(
new Configuration,
new AttendanceData,
new AttendanceDay,
new AttendanceRecord,
new Student,
new StudentClass,
new ClassSection,
new TeacherClass,
new Calendar,
new User,
new UserRole,
new AttendanceService(
new Configuration,
new AttendanceData,
new AttendanceDay,
new ClassSection,
new UserRole,
new AttendancePolicyService,
new StudentAttendanceWriterService(
new AttendanceData,
new Student,
new AttendanceRecordSyncService(new AttendanceRecord)
new \App\Models\Configuration(),
new \App\Models\AttendanceData(),
new \App\Models\AttendanceDay(),
new \App\Models\AttendanceRecord(),
new \App\Models\Student(),
new \App\Models\StudentClass(),
new \App\Models\ClassSection(),
new \App\Models\TeacherClass(),
new \App\Models\Calendar(),
new \App\Models\User(),
new \App\Models\UserRole(),
new \App\Services\Attendance\AttendanceService(
new \App\Models\Configuration(),
new \App\Models\AttendanceData(),
new \App\Models\AttendanceDay(),
new \App\Models\ClassSection(),
new \App\Models\UserRole(),
new \App\Services\Attendance\AttendancePolicyService(),
new \App\Services\Attendance\StudentAttendanceWriterService(
new \App\Models\AttendanceData(),
new \App\Models\Student(),
new \App\Services\Attendance\AttendanceRecordSyncService(new \App\Models\AttendanceRecord())
),
new TeacherAttendanceSubmissionService(
new AttendanceDay,
new AttendanceData,
new TeacherClass,
new Student,
new AttendancePolicyService,
new StudentAttendanceWriterService(
new AttendanceData,
new Student,
new AttendanceRecordSyncService(new AttendanceRecord)
new \App\Services\Attendance\TeacherAttendanceSubmissionService(
new \App\Models\AttendanceDay(),
new \App\Models\AttendanceData(),
new \App\Models\TeacherClass(),
new \App\Models\Student(),
new \App\Services\Attendance\AttendancePolicyService(),
new \App\Services\Attendance\StudentAttendanceWriterService(
new \App\Models\AttendanceData(),
new \App\Models\Student(),
new \App\Services\Attendance\AttendanceRecordSyncService(new \App\Models\AttendanceRecord())
),
new AttendanceAutoPublishService
new \App\Services\Attendance\AttendanceAutoPublishService()
),
new AttendanceRecordSyncService(new AttendanceRecord)
new \App\Services\Attendance\AttendanceRecordSyncService(new \App\Models\AttendanceRecord())
),
new SemesterRangeService
new \App\Services\Attendance\SemesterRangeService()
);
$grid = $service->teacherGrid('Fall', '2025-2026', '2025-01-05', 10);
@@ -2,7 +2,6 @@
namespace Tests\Unit\Services\Attendance;
use App\Models\AttendanceRecord;
use App\Services\Attendance\AttendanceRecordSyncService;
use Illuminate\Foundation\Testing\RefreshDatabase;
use Illuminate\Support\Facades\DB;
@@ -26,7 +25,7 @@ class AttendanceRecordSyncServiceTest extends TestCase
'school_year' => '2025-2026',
]);
$service = new AttendanceRecordSyncService(new AttendanceRecord);
$service = new AttendanceRecordSyncService(new \App\Models\AttendanceRecord());
$service->updateAttendanceRecord(10, 'S1', 'absent', 'Fall', '2025-2026', 'present', 1);
$row = DB::table('attendance_record')->where('student_id', 10)->first();
@@ -36,7 +35,7 @@ class AttendanceRecordSyncServiceTest extends TestCase
public function test_add_new_attendance_record_creates_row(): void
{
$service = new AttendanceRecordSyncService(new AttendanceRecord);
$service = new AttendanceRecordSyncService(new \App\Models\AttendanceRecord());
$service->addNewAttendanceRecord(1, 10, 'S1', 'present', 'Fall', '2025-2026', 1);
$this->assertDatabaseHas('attendance_record', [
@@ -12,7 +12,7 @@ class AttendanceSemesterRangeServiceTest extends TestCase
public function test_get_semester_range(): void
{
$service = new SemesterRangeService;
$service = new SemesterRangeService();
$range = $service->getSemesterRange('2025-2026', 'fall');
$this->assertSame(['2025-09-21', '2026-01-18'], $range);
@@ -20,7 +20,7 @@ class AttendanceSemesterRangeServiceTest extends TestCase
public function test_build_sunday_list_returns_sundays(): void
{
$service = new SemesterRangeService;
$service = new SemesterRangeService();
$dates = $service->buildSundayList('2025-09-01', '2025-09-30');
$this->assertContains('2025-09-07', $dates);
@@ -34,11 +34,11 @@ class AttendanceServiceTest extends TestCase
$recordSync = Mockery::mock(AttendanceRecordSyncService::class);
$service = new AttendanceService(
new Configuration,
new AttendanceData,
new AttendanceDay,
new ClassSection,
new UserRole,
new Configuration(),
new AttendanceData(),
new AttendanceDay(),
new ClassSection(),
new UserRole(),
$policy,
$writer,
$teacherSubmission,
@@ -70,11 +70,11 @@ class AttendanceServiceTest extends TestCase
$recordSync = Mockery::mock(AttendanceRecordSyncService::class);
$service = new AttendanceService(
new Configuration,
new AttendanceData,
new AttendanceDay,
new ClassSection,
new UserRole,
new Configuration(),
new AttendanceData(),
new AttendanceDay(),
new ClassSection(),
new UserRole(),
$policy,
$writer,
$teacherSubmission,
@@ -46,7 +46,7 @@ class AttendanceSummaryRebuildServiceTest extends TestCase
],
]);
$service = new AttendanceSummaryRebuildService;
$service = new AttendanceSummaryRebuildService();
$result = $service->rebuild();
$this->assertSame(2, $result['inserted']);
@@ -2,13 +2,13 @@
namespace App\Services;
use App\Models\AttendanceData;
use App\Models\AttendanceEmailTemplate;
use App\Models\AttendanceTracking;
use App\Models\Configuration;
use App\Models\ParentNotification;
use App\Models\Student;
use App\Models\StudentClass;
use App\Models\Student;
use App\Models\Configuration;
use App\Models\AttendanceData;
use App\Models\ParentNotification;
use App\Models\AttendanceEmailTemplate;
use Carbon\Carbon;
use Illuminate\Database\Eloquent\Builder;
use Illuminate\Support\Facades\DB;
@@ -19,11 +19,8 @@ use Throwable;
class AttendanceTrackingService
{
protected string $semester;
protected string $schoolYear;
protected array $debugCompute = [];
protected ?array $attendanceReportedColumns = null;
public function __construct(
@@ -43,19 +40,19 @@ class AttendanceTrackingService
public function getPendingViolations(?string $schoolYearParam = null, ?string $semesterParam = null): array
{
$schoolYear = filled($schoolYearParam) ? (string) $schoolYearParam : (string) $this->schoolYear;
$semester = filled($semesterParam) ? (string) $semesterParam : null; // disabled like legacy
$semester = filled($semesterParam) ? (string) $semesterParam : null; // disabled like legacy
$debugInfo = [
'school_year_param' => $schoolYear,
'semester_param' => $semester,
'class_students' => 0,
'student_ids' => 0,
'attendance_rows' => 0,
'filtered_rows' => 0,
'violations' => 0,
'active_weeks' => 0,
'abs_last5' => 0,
'late_last5' => 0,
'semester_param' => $semester,
'class_students' => 0,
'student_ids' => 0,
'attendance_rows' => 0,
'filtered_rows' => 0,
'violations' => 0,
'active_weeks' => 0,
'abs_last5' => 0,
'late_last5' => 0,
];
$classStudentsQuery = $this->studentClassModel->query();
@@ -76,9 +73,9 @@ class AttendanceTrackingService
->orderByDesc('id')
->first();
if ($latestTerm && ! empty($latestTerm->school_year)) {
if ($latestTerm && !empty($latestTerm->school_year)) {
$schoolYear = (string) $latestTerm->school_year;
if (! empty($latestTerm->semester)) {
if (!empty($latestTerm->semester)) {
$semester = (string) $latestTerm->semester;
}
@@ -109,7 +106,7 @@ class AttendanceTrackingService
if (empty($studentIds) && empty($studentCodes)) {
$attendanceStudents = DB::table('attendance_data')
->selectRaw('DISTINCT student_id')
->when(! empty($schoolYear), fn ($q) => $q->where('school_year', $schoolYear))
->when(!empty($schoolYear), fn ($q) => $q->where('school_year', $schoolYear))
->orderBy('student_id')
->get();
@@ -126,7 +123,7 @@ class AttendanceTrackingService
$studentIds = array_values(array_unique(array_filter($studentIds, fn ($v) => $v > 0)));
$studentCodes = array_values(array_unique(array_filter($studentCodes, fn ($v) => $v !== '')));
if (! empty($studentIds)) {
if (!empty($studentIds)) {
$inactiveRows = $this->studentModel->query()
->select('id')
->whereIn('id', $studentIds)
@@ -141,11 +138,11 @@ class AttendanceTrackingService
$studentIds = array_values(array_filter(
$studentIds,
static fn ($id) => ! isset($inactiveIdSet[$id])
static fn ($id) => !isset($inactiveIdSet[$id])
));
}
if (! empty($studentCodes)) {
if (!empty($studentCodes)) {
$inactiveCodeRows = $this->studentModel->query()
->select('school_id')
->whereIn('school_id', $studentCodes)
@@ -160,7 +157,7 @@ class AttendanceTrackingService
$studentCodes = array_values(array_filter(
$studentCodes,
static fn ($code) => $code !== '' && ! isset($inactiveCodeSet[$code])
static fn ($code) => $code !== '' && !isset($inactiveCodeSet[$code])
));
}
@@ -177,7 +174,7 @@ class AttendanceTrackingService
}
$students = [];
if (! empty($studentIds)) {
if (!empty($studentIds)) {
$students = $this->studentModel->query()
->whereIn('id', $studentIds)
->where('is_active', 1)
@@ -185,7 +182,7 @@ class AttendanceTrackingService
->toArray();
}
if (! empty($studentCodes)) {
if (!empty($studentCodes)) {
$byCode = $this->studentModel->query()
->whereIn('school_id', $studentCodes)
->where('is_active', 1)
@@ -195,7 +192,7 @@ class AttendanceTrackingService
$seen = array_flip(array_map(fn ($s) => (int) ($s['id'] ?? 0), $students));
foreach ($byCode as $s) {
$sid = (int) ($s['id'] ?? 0);
if ($sid > 0 && ! isset($seen[$sid])) {
if ($sid > 0 && !isset($seen[$sid])) {
$students[] = $s;
$seen[$sid] = true;
}
@@ -211,7 +208,7 @@ class AttendanceTrackingService
}
foreach ($studentCodes as $code) {
if (! isset($knownCodes[$code])) {
if (!isset($knownCodes[$code])) {
$virtualId = abs(crc32($code)) ?: random_int(1000000, 1999999);
$students[] = [
'id' => $virtualId,
@@ -247,7 +244,7 @@ class AttendanceTrackingService
foreach (array_merge($studentIds, $studentCodes) as $sidOrCode) {
if (is_numeric($sidOrCode)) {
$sid = (int) $sidOrCode;
if ($sid > 0 && ! isset($existingIds[$sid])) {
if ($sid > 0 && !isset($existingIds[$sid])) {
$students[] = [
'id' => $sid,
'school_id' => (string) $sid,
@@ -260,7 +257,7 @@ class AttendanceTrackingService
}
} else {
$code = (string) $sidOrCode;
if ($code !== '' && ! isset($studentCodeToId[$code])) {
if ($code !== '' && !isset($studentCodeToId[$code])) {
$virtualId = abs(crc32($code)) ?: random_int(2000000, 2999999);
$students[] = [
'id' => $virtualId,
@@ -298,14 +295,14 @@ class AttendanceTrackingService
'is_reported',
'is_notified',
])
->when(! empty($schoolYear), fn ($q) => $q->where('school_year', $schoolYear))
->when(! empty($semester), fn ($q) => $q->where('semester', $semester))
->when(!empty($schoolYear), fn ($q) => $q->where('school_year', $schoolYear))
->when(!empty($semester), fn ($q) => $q->where('semester', $semester))
->where(function ($q) use ($studentIds, $studentCodes) {
if (! empty($studentIds)) {
if (!empty($studentIds)) {
$q->whereIn('student_id', $studentIds);
}
if (! empty($studentCodes)) {
if (! empty($studentIds)) {
if (!empty($studentCodes)) {
if (!empty($studentIds)) {
$q->orWhereIn('student_id', $studentCodes);
} else {
$q->whereIn('student_id', $studentCodes);
@@ -325,16 +322,16 @@ class AttendanceTrackingService
if (empty($termRows)) {
$latestAttendanceTerm = DB::table('attendance_data')
->select('school_year', 'semester', 'date')
->when(! empty($studentIds), fn ($q) => $q->whereIn('student_id', $studentIds))
->when(!empty($studentIds), fn ($q) => $q->whereIn('student_id', $studentIds))
->orderByDesc('date')
->first();
if ($latestAttendanceTerm) {
$schoolYear = ! empty($latestAttendanceTerm->school_year)
$schoolYear = !empty($latestAttendanceTerm->school_year)
? (string) $latestAttendanceTerm->school_year
: $schoolYear;
$semester = ! empty($latestAttendanceTerm->semester)
$semester = !empty($latestAttendanceTerm->semester)
? (string) $latestAttendanceTerm->semester
: $semester;
@@ -357,14 +354,14 @@ class AttendanceTrackingService
'is_reported',
'is_notified',
])
->when(! empty($schoolYear), fn ($q) => $q->where('school_year', $schoolYear))
->when(! empty($semester), fn ($q) => $q->where('semester', $semester))
->when(!empty($schoolYear), fn ($q) => $q->where('school_year', $schoolYear))
->when(!empty($semester), fn ($q) => $q->where('semester', $semester))
->where(function ($q) use ($studentIds, $studentCodes) {
if (! empty($studentIds)) {
if (!empty($studentIds)) {
$q->whereIn('student_id', $studentIds);
}
if (! empty($studentCodes)) {
if (! empty($studentIds)) {
if (!empty($studentCodes)) {
if (!empty($studentIds)) {
$q->orWhereIn('student_id', $studentCodes);
} else {
$q->whereIn('student_id', $studentCodes);
@@ -395,7 +392,7 @@ class AttendanceTrackingService
$attendanceRows = [];
foreach ($termRows as $row) {
$dStr = $row['date'] ?? null;
if (! $dStr) {
if (!$dStr) {
continue;
}
@@ -405,7 +402,7 @@ class AttendanceTrackingService
continue;
}
if ($d->lt($start) || ! $d->lt($endEx)) {
if ($d->lt($start) || !$d->lt($endEx)) {
continue;
}
@@ -421,7 +418,7 @@ class AttendanceTrackingService
if ($sid === null) {
$sid = abs(crc32($code)) ?: random_int(3000000, 3999999);
$studentCodeToId[$code] = $sid;
if (! isset($existingIds[$sid])) {
if (!isset($existingIds[$sid])) {
$students[] = [
'id' => $sid,
'school_id' => $code,
@@ -446,7 +443,7 @@ class AttendanceTrackingService
foreach ($attendanceRows as $arow) {
$sid = (int) ($arow['student_id'] ?? 0);
if ($sid > 0 && ! isset($existingIds[$sid])) {
if ($sid > 0 && !isset($existingIds[$sid])) {
$students[] = [
'id' => $sid,
'school_id' => (string) ($arow['student_code'] ?? $arow['student_id']),
@@ -498,13 +495,13 @@ class AttendanceTrackingService
$parentEmail = $data['parent_email'] ?? '';
$parentName = $data['parent_name'] ?? '';
if (! $parentEmail) {
if (!$parentEmail) {
$parent = $this->getPrimaryParentForStudent($sid) ?? [];
$parentEmail = $parent['email'] ?? '';
$parentName = $parent['parent_name'] ?? '';
}
if (! $parentEmail) {
if (!$parentEmail) {
return [
'success' => false,
'message' => 'No parent email found for this student.',
@@ -555,7 +552,7 @@ class AttendanceTrackingService
try {
$this->attendanceMailerService->queueAttendanceEvent($payload);
if ($row && ! empty($row['id']) && method_exists($this->attendanceTrackingModel, 'markAsNotified')) {
if ($row && !empty($row['id']) && method_exists($this->attendanceTrackingModel, 'markAsNotified')) {
$this->attendanceTrackingModel->markAsNotified((int) $row['id']);
}
@@ -565,7 +562,7 @@ class AttendanceTrackingService
'data' => $payload,
];
} catch (Throwable $e) {
Log::error('Attendance record queue failed: '.$e->getMessage());
Log::error('Attendance record queue failed: ' . $e->getMessage());
return [
'success' => false,
@@ -585,7 +582,7 @@ class AttendanceTrackingService
?string $semesterFilter = null
): array {
$student = $this->studentModel->query()->find($studentId);
if (! $student) {
if (!$student) {
return [
'success' => false,
'message' => 'Student not found',
@@ -594,7 +591,7 @@ class AttendanceTrackingService
}
$student = $student->toArray();
$student['name'] = trim(($student['firstname'] ?? '').' '.($student['lastname'] ?? ''));
$student['name'] = trim(($student['firstname'] ?? '') . ' ' . ($student['lastname'] ?? ''));
$codeParam = strtoupper((string) ($codeParam ?? ''));
$incidentDate = preg_match('/^\d{4}-\d{2}-\d{2}$/', (string) $incidentDate) ? (string) $incidentDate : '';
@@ -659,7 +656,7 @@ class AttendanceTrackingService
])
->where(function ($q) use ($studentIdValues, $studentCodeValues) {
$q->whereIn('student_id', $studentIdValues);
if (! empty($studentCodeValues)) {
if (!empty($studentCodeValues)) {
$q->orWhereIn('student_id', $studentCodeValues);
}
})
@@ -677,7 +674,7 @@ class AttendanceTrackingService
if ($pendingOnly) {
$this->applyUnreportedAttendanceFilter($qb);
if (! $includeNotified) {
if (!$includeNotified) {
$qb->whereRaw("(LOWER(TRIM(COALESCE(is_notified, ''))) NOT IN ('yes','1'))");
}
}
@@ -703,11 +700,11 @@ class AttendanceTrackingService
'semester' => $semester,
];
if ($data['violation_date'] === '' && ! empty($data['attendance'][0]['date'])) {
if ($data['violation_date'] === '' && !empty($data['attendance'][0]['date'])) {
$data['violation_date'] = substr((string) $data['attendance'][0]['date'], 0, 10);
}
if (! empty($data['attendance'][0]['is_notified'])) {
if (!empty($data['attendance'][0]['is_notified'])) {
$data['violation_notified'] = $data['attendance'][0]['is_notified'];
}
@@ -785,17 +782,15 @@ class AttendanceTrackingService
$to = trim((string) ($v['parent_email'] ?? ''));
$pname = (string) ($v['parent_name'] ?? '');
if ($sid <= 0 || ! $code || ! $date || ! $to) {
if ($sid <= 0 || !$code || !$date || !$to) {
$errors++;
$details[] = ['student_id' => $sid, 'code' => $code, 'date' => $date, 'result' => 'missing data'];
continue;
}
if ($this->notificationAlreadySent($sid, $code, $date, 'email', $to)) {
$skipped++;
$details[] = ['student_id' => $sid, 'code' => $code, 'date' => $date, 'result' => 'duplicate'];
continue;
}
@@ -816,7 +811,7 @@ class AttendanceTrackingService
$tpl = $this->renderTemplate($code, $variant, $context);
if (! $tpl) {
if (!$tpl) {
$subject = match ($code) {
'ABS_1' => 'Attendance Notice: Unreported Absence',
'LATE_2' => 'Attendance Notice: Repeated Lateness',
@@ -824,14 +819,14 @@ class AttendanceTrackingService
};
$studentName = (string) ($v['name'] ?? '');
$body = '<p>Dear '.($pname ?: 'Parent/Guardian').'</p>'
."<p>We'd like to inform you about your student's recent attendance:</p>"
.'<ul>'
."<li><strong>Student:</strong> {$studentName}</li>"
.'<li><strong>Issue:</strong> '.(string) ($v['violation'] ?? '').'</li>'
."<li><strong>As of:</strong> {$date}</li>"
.'</ul>'
.'<p>If you have any questions, please contact the school office.</p>';
$body = "<p>Dear " . ($pname ?: 'Parent/Guardian') . "</p>"
. "<p>We'd like to inform you about your student's recent attendance:</p>"
. "<ul>"
. "<li><strong>Student:</strong> {$studentName}</li>"
. "<li><strong>Issue:</strong> " . (string) ($v['violation'] ?? '') . "</li>"
. "<li><strong>As of:</strong> {$date}</li>"
. "</ul>"
. "<p>If you have any questions, please contact the school office.</p>";
} else {
[$subject, $body] = $tpl;
}
@@ -854,7 +849,7 @@ class AttendanceTrackingService
} catch (Throwable $e) {
$errors++;
$this->logNotification($sid, $code, $date, 'email', $to, $subject, 'failed', $e->getMessage());
$details[] = ['student_id' => $sid, 'code' => $code, 'date' => $date, 'result' => 'error: '.$e->getMessage()];
$details[] = ['student_id' => $sid, 'code' => $code, 'date' => $date, 'result' => 'error: ' . $e->getMessage()];
}
try {
@@ -880,7 +875,7 @@ class AttendanceTrackingService
}
} catch (Throwable $e) {
$errors++;
$details[] = ['student_id' => $sid, 'code' => $code, 'date' => $date, 'result' => 'cc_error: '.$e->getMessage()];
$details[] = ['student_id' => $sid, 'code' => $code, 'date' => $date, 'result' => 'cc_error: ' . $e->getMessage()];
}
if ($anyOk) {
@@ -900,10 +895,10 @@ class AttendanceTrackingService
$absDates = array_map(fn ($d) => substr((string) $d, 0, 10), (array) ($v['absences'] ?? []));
$lateDates = array_map(fn ($d) => substr((string) $d, 0, 10), (array) ($v['lates'] ?? []));
if (! empty($absDates)) {
if (!empty($absDates)) {
$allDates = array_merge($allDates, $absDates);
}
if (! empty($lateDates)) {
if (!empty($lateDates)) {
$allDates = array_merge($allDates, $lateDates);
}
if (empty($allDates)) {
@@ -1039,7 +1034,7 @@ class AttendanceTrackingService
foreach ($rows as $row) {
$sid = (int) ($row->student_id ?? 0);
if ($sid > 0 && ! isset($classByStudent[$sid])) {
if ($sid > 0 && !isset($classByStudent[$sid])) {
$classByStudent[$sid] = [
'class_section_id' => $row->class_section_id ?? null,
'class_section_name' => (string) ($row->class_section_name ?? ''),
@@ -1054,7 +1049,7 @@ class AttendanceTrackingService
$mergedData = [];
foreach ($trackingData as $row) {
$sid = (int) ($row['student_id'] ?? 0);
if (! isset($studentMap[$sid])) {
if (!isset($studentMap[$sid])) {
continue;
}
@@ -1062,7 +1057,7 @@ class AttendanceTrackingService
if (isset($classByStudent[$sid])) {
$merged['class_section_name'] = (string) ($classByStudent[$sid]['class_section_name'] ?? '');
$merged['class_name'] = (string) ($classByStudent[$sid]['class_name'] ?? '');
} elseif (! empty($merged['registration_grade'])) {
} elseif (!empty($merged['registration_grade'])) {
$merged['grade'] = (string) $merged['registration_grade'];
}
@@ -1106,7 +1101,7 @@ class AttendanceTrackingService
$violation = [
'id' => $studentId,
'name' => trim(($student['firstname'] ?? '').' '.($student['lastname'] ?? '')),
'name' => trim(($student['firstname'] ?? '') . ' ' . ($student['lastname'] ?? '')),
'last_date' => $lastDate,
'violation' => $code,
'violation_code' => $code,
@@ -1115,7 +1110,7 @@ class AttendanceTrackingService
$ctx = $this->buildTemplateContext($violation, $parent);
$rendered = $this->renderTemplate($code, $variant, $ctx);
if (! $rendered) {
if (!$rendered) {
return [
'success' => false,
'message' => "Template not found for {$code} ({$variant}).",
@@ -1193,7 +1188,7 @@ class AttendanceTrackingService
$sec = $this->getSecondaryParentForStudent($sid);
$to2 = trim((string) ($sec['email'] ?? ''));
if ($to2 !== '' && strcasecmp($to2, $to) !== 0) {
if (! $this->notificationAlreadySent($sid, $code, $ymd, 'email', $to2)) {
if (!$this->notificationAlreadySent($sid, $code, $ymd, 'email', $to2)) {
$ok2 = $this->attendanceMailerService->send($to2, $subject, $wrapped);
$this->logNotification(
$sid,
@@ -1211,7 +1206,7 @@ class AttendanceTrackingService
}
}
if (! $anyOk) {
if (!$anyOk) {
return [
'success' => false,
'message' => 'Failed to send email.',
@@ -1256,7 +1251,7 @@ class AttendanceTrackingService
return [
'success' => false,
'message' => 'Error: '.$e->getMessage(),
'message' => 'Error: ' . $e->getMessage(),
'status' => 500,
];
}
@@ -1278,7 +1273,7 @@ class AttendanceTrackingService
->where('id', $studentId)
->first();
if (! $stu) {
if (!$stu) {
return [
'success' => false,
'message' => 'Student not found',
@@ -1303,7 +1298,7 @@ class AttendanceTrackingService
] : null;
$pb = DB::table('parents')->where('firstparent_id', $primaryId);
if (! empty($schoolYear)) {
if (!empty($schoolYear)) {
$pb->where('school_year', $schoolYear);
}
@@ -1330,7 +1325,7 @@ class AttendanceTrackingService
}
}
if (! $secondary) {
if (!$secondary) {
$fallbackEmail = (string) ($pRow->secondparent_email ?? '');
$fallbackPhone = (string) ($pRow->secondparent_phone ?? '');
$fallbackFirst = (string) ($pRow->secondparent_firstname ?? '');
@@ -1356,7 +1351,7 @@ class AttendanceTrackingService
],
];
} catch (Throwable $e) {
Log::error('parentsInfo() failed: '.$e->getMessage());
Log::error('parentsInfo() failed: ' . $e->getMessage());
return [
'success' => false,
@@ -1384,11 +1379,11 @@ class AttendanceTrackingService
->where('school_year', $this->schoolYear)
->where('date', '>=', $start)
->where('date', '<', $end)
->where('reason', 'like', '%'.$code.'%')
->where('reason', 'like', '%' . $code . '%')
->orderByDesc('date')
->first();
if (! $row) {
if (!$row) {
$row = $this->attendanceTrackingModel->query()
->where('student_id', $sid)
->where('semester', $this->semester)
@@ -1399,7 +1394,7 @@ class AttendanceTrackingService
->first();
}
if ($row && ! empty($row->id)) {
if ($row && !empty($row->id)) {
DB::table($this->attendanceTrackingModel->getTable())
->where('id', (int) $row->id)
->update([
@@ -1431,7 +1426,7 @@ class AttendanceTrackingService
} catch (Throwable $e) {
return [
'success' => false,
'message' => 'Failed to save note: '.$e->getMessage(),
'message' => 'Failed to save note: ' . $e->getMessage(),
'status' => 500,
];
}
@@ -1455,14 +1450,13 @@ class AttendanceTrackingService
->where('incident_date', $ymd)
->where('channel', $channel);
if (! empty($to)) {
if (!empty($to)) {
$query->where('to_address', $to);
}
return $query->exists();
} catch (Throwable $e) {
Log::debug('notificationAlreadySent(): '.$e->getMessage());
Log::debug('notificationAlreadySent(): ' . $e->getMessage());
return false;
}
}
@@ -1485,7 +1479,7 @@ class AttendanceTrackingService
'channel' => $channel,
];
if (! empty($to)) {
if (!empty($to)) {
$where['to_address'] = $to;
}
@@ -1494,7 +1488,7 @@ class AttendanceTrackingService
->orderByDesc('id')
->first();
if ($existing && ! empty($existing->id)) {
if ($existing && !empty($existing->id)) {
$existing->update([
'subject' => $subject,
'status' => $status,
@@ -1518,7 +1512,7 @@ class AttendanceTrackingService
]);
}
} catch (Throwable $e) {
Log::debug('logNotification(): '.$e->getMessage());
Log::debug('logNotification(): ' . $e->getMessage());
}
}
@@ -1530,7 +1524,7 @@ class AttendanceTrackingService
->where('s.id', $studentId)
->first();
if (! $row || empty($row->user_id)) {
if (!$row || empty($row->user_id)) {
return null;
}
@@ -1550,7 +1544,7 @@ class AttendanceTrackingService
->where('id', $studentId)
->first();
if (! $stu) {
if (!$stu) {
return null;
}
@@ -1563,7 +1557,7 @@ class AttendanceTrackingService
}
$pRow = $pb->orderByDesc('updated_at')->first();
if (! $pRow) {
if (!$pRow) {
return null;
}
@@ -1574,11 +1568,11 @@ class AttendanceTrackingService
->where('id', $secondId)
->first();
if ($u2 && ! empty($u2->email)) {
if ($u2 && !empty($u2->email)) {
return [
'user_id' => (int) $u2->id,
'email' => (string) $u2->email,
'parent_name' => trim(($u2->firstname ?? '').' '.($u2->lastname ?? '')),
'parent_name' => trim(($u2->firstname ?? '') . ' ' . ($u2->lastname ?? '')),
'phone' => (string) ($u2->cellphone ?? ''),
];
}
@@ -1593,12 +1587,12 @@ class AttendanceTrackingService
return [
'user_id' => $secondId ?: null,
'email' => $fallbackEmail,
'parent_name' => trim($fallbackFirst.' '.$fallbackLast) ?: null,
'parent_name' => trim($fallbackFirst . ' ' . $fallbackLast) ?: null,
'phone' => $fallbackPhone,
];
}
} catch (Throwable $e) {
Log::error('getSecondaryParentForStudent() failed: '.$e->getMessage());
Log::error('getSecondaryParentForStudent() failed: ' . $e->getMessage());
}
return null;
@@ -1637,7 +1631,7 @@ class AttendanceTrackingService
$ymd = substr((string) ($r['date'] ?? ''), 0, 10);
$stat = strtolower(trim((string) ($r['status'] ?? '')));
if (! in_array($stat, ['absent', 'late'], true)) {
if (!in_array($stat, ['absent', 'late'], true)) {
continue;
}
@@ -1649,10 +1643,10 @@ class AttendanceTrackingService
[$syStart, $syEnd] = $this->deriveSchoolYearBounds($schoolYear);
$weekRowsSource = null;
if (! empty($weekRowsFallback)) {
if (!empty($weekRowsFallback)) {
foreach ($weekRowsFallback as $r) {
$st = strtolower(trim((string) ($r['status'] ?? '')));
if ($st !== '' && ! in_array($st, ['absent', 'late'], true)) {
if ($st !== '' && !in_array($st, ['absent', 'late'], true)) {
$weekRowsSource = $weekRowsFallback;
break;
}
@@ -1685,7 +1679,6 @@ class AttendanceTrackingService
}
$out = array_values(array_unique($out));
rsort($out);
return $out;
};
@@ -1696,7 +1689,7 @@ class AttendanceTrackingService
$students
)));
if (! empty($studentIdsForClass)) {
if (!empty($studentIdsForClass)) {
$rows = DB::table('student_class as sc')
->select([
'sc.student_id',
@@ -1714,7 +1707,7 @@ class AttendanceTrackingService
foreach ($rows as $row) {
$sid = (int) ($row->student_id ?? 0);
if ($sid > 0 && ! isset($classByStudent[$sid])) {
if ($sid > 0 && !isset($classByStudent[$sid])) {
$classByStudent[$sid] = [
'class_section_id' => $row->class_section_id ?? null,
'class_section_name' => (string) ($row->class_section_name ?? ''),
@@ -1725,7 +1718,7 @@ class AttendanceTrackingService
}
}
} catch (Throwable $e) {
Log::debug('computeViolations(): class prefetch failed: '.$e->getMessage());
Log::debug('computeViolations(): class prefetch failed: ' . $e->getMessage());
}
$out = [];
@@ -1734,7 +1727,7 @@ class AttendanceTrackingService
foreach ($students as $stu) {
$sid = (int) ($stu['id'] ?? 0);
$name = trim(($stu['firstname'] ?? '').' '.($stu['lastname'] ?? ''));
$name = trim(($stu['firstname'] ?? '') . ' ' . ($stu['lastname'] ?? ''));
$absDatesAll = array_values(array_unique($byStudent[$sid]['absent'] ?? []));
$lateDatesAll = array_values(array_unique($byStudent[$sid]['late'] ?? []));
@@ -1752,7 +1745,7 @@ class AttendanceTrackingService
$lateCountLast5 += count($lateDates);
$absenceViolation = null;
if (! empty($absDates)) {
if (!empty($absDates)) {
$lastAbsDate = $absDates[0];
$A2row = $this->hasNConsecutiveItems($absDates, 2, 7);
$A3row = $this->hasNConsecutiveItems($absDates, 3, 7);
@@ -1799,7 +1792,7 @@ class AttendanceTrackingService
}
$lateViolation = null;
if (! empty($lateDates)) {
if (!empty($lateDates)) {
$lastLateDate = $lateDates[0];
$L2row = $this->hasNConsecutiveItems($lateDates, 2, 7);
@@ -1814,7 +1807,7 @@ class AttendanceTrackingService
$lateWeeksSet = array_flip($lateWeekIdx);
$lateCoversAllWindowWeeks = true;
for ($i = $windowStartIdx; $i <= $currentWeekIdx; $i++) {
if (! isset($lateWeeksSet[$i])) {
if (!isset($lateWeeksSet[$i])) {
$lateCoversAllWindowWeeks = false;
break;
}
@@ -1865,7 +1858,7 @@ class AttendanceTrackingService
$chosen = $this->chooseHigherSeverity($absenceViolation, $lateViolation);
if (! $chosen && count($absDates) === 1) {
if (!$chosen && count($absDates) === 1) {
$chosen = [
'type' => 'absence',
'code' => 'ABS_1',
@@ -1878,7 +1871,7 @@ class AttendanceTrackingService
];
}
if (! $chosen) {
if (!$chosen) {
continue;
}
@@ -1890,7 +1883,7 @@ class AttendanceTrackingService
->where('school_year', $schoolYear)
->where('date', '>=', $start)
->where('date', '<', $end)
->where('reason', 'like', '%'.$chosen['code'].'%');
->where('reason', 'like', '%' . $chosen['code'] . '%');
if ($semester !== null) {
$trackingQ->where('semester', $semester);
@@ -1904,7 +1897,7 @@ class AttendanceTrackingService
$cls = $classByStudent[$sid] ?? [];
$classLabel = (string) ($cls['class_section_name'] ?? '');
if ($classLabel === '' && ! empty($cls['class_name'])) {
if ($classLabel === '' && !empty($cls['class_name'])) {
$classLabel = (string) $cls['class_name'];
}
@@ -1954,7 +1947,6 @@ class AttendanceTrackingService
if ($a && $b) {
if ($a['severity'] === $b['severity']) {
$prio = ['expel_notify' => 3, 'team_notify' => 2, 'auto_email' => 1];
return ($prio[$a['action']] ?? 0) >= ($prio[$b['action']] ?? 0) ? $a : $b;
}
@@ -1984,11 +1976,11 @@ class AttendanceTrackingService
{
$cols = $this->attendanceReportedColumns();
if (! empty($cols['is_reported'])) {
if (!empty($cols['is_reported'])) {
$qb->whereRaw("(LOWER(TRIM(COALESCE(is_reported, ''))) NOT IN ('yes','1','true','y','on'))");
}
if (! empty($cols['reported'])) {
if (!empty($cols['reported'])) {
$qb->whereRaw("(LOWER(TRIM(COALESCE(reported, ''))) NOT IN ('yes','1','true','y','on'))");
}
@@ -2010,10 +2002,9 @@ class AttendanceTrackingService
protected function dayBounds(string $ymd): array
{
$d = substr($ymd, 0, 10);
return [
$d.' 00:00:00',
date('Y-m-d', strtotime($d.' +1 day')).' 00:00:00',
$d . ' 00:00:00',
date('Y-m-d', strtotime($d . ' +1 day')) . ' 00:00:00',
];
}
@@ -2033,10 +2024,9 @@ class AttendanceTrackingService
protected function deriveSchoolYearBounds(string $schoolYear): array
{
if (! preg_match('/^(\d{4})-(\d{4})$/', $schoolYear, $m)) {
if (!preg_match('/^(\d{4})-(\d{4})$/', $schoolYear, $m)) {
$y = (int) date('Y');
$startY = (int) (date('n') >= 8 ? $y : $y - 1);
return [sprintf('%d-08-01', $startY), sprintf('%d-07-31', $startY + 1)];
}
@@ -2076,7 +2066,7 @@ class AttendanceTrackingService
->where('is_active', 1)
->first();
if (! $row) {
if (!$row) {
$row = $this->attendanceEmailTemplateModel->query()
->where('code', $code)
->where('variant', 'default')
@@ -2087,7 +2077,7 @@ class AttendanceTrackingService
$row = $row?->toArray();
}
if (! $row) {
if (!$row) {
return null;
}
@@ -2145,10 +2135,10 @@ class AttendanceTrackingService
$semester = ($semester !== null && $semester !== '') ? (string) $semester : null;
$weeks = [];
if (! empty($weekRowsFallback)) {
if (!empty($weekRowsFallback)) {
foreach ($weekRowsFallback as $r) {
$d = $r['date'] ?? null;
if (! $d) {
if (!$d) {
continue;
}
$ts = strtotime($d);
@@ -2205,38 +2195,25 @@ class AttendanceTrackingService
}
$idx = array_keys($set);
sort($idx);
return $idx;
}
protected function windowWeeksForViolationCode(string $code): int
{
$code = strtoupper(trim($code));
if ($code === '') {
return 5;
}
if (Str::startsWith($code, 'ABS_2') || Str::startsWith($code, 'LATE_2')) {
return 3;
}
if (Str::startsWith($code, 'ABS_3') || Str::startsWith($code, 'LATE_3') || Str::startsWith($code, 'MIX')) {
return 4;
}
if (Str::startsWith($code, 'LATE_4')) {
return 4;
}
if (Str::startsWith($code, 'ABS_4')) {
return 5;
}
if ($code === '') return 5;
if (Str::startsWith($code, 'ABS_2') || Str::startsWith($code, 'LATE_2')) return 3;
if (Str::startsWith($code, 'ABS_3') || Str::startsWith($code, 'LATE_3') || Str::startsWith($code, 'MIX')) return 4;
if (Str::startsWith($code, 'LATE_4')) return 4;
if (Str::startsWith($code, 'ABS_4')) return 5;
return 5;
}
protected function isoWeekStartYmd(string $weekKey): string
{
if (preg_match('/^(\d{4})-W(\d{2})$/', $weekKey, $m)) {
return date('Y-m-d', strtotime($m[1].'-W'.$m[2].'-1'));
return date('Y-m-d', strtotime($m[1] . '-W' . $m[2] . '-1'));
}
return date('Y-m-d');
}
@@ -2257,15 +2234,14 @@ class AttendanceTrackingService
if (empty($activeWeekKeys)) {
$days = ($windowWeeks * 7) - 1;
return date('Y-m-d', strtotime($endBound.' -'.$days.' days'));
return date('Y-m-d', strtotime($endBound . ' -' . $days . ' days'));
}
$activeWeekKeys = array_values($activeWeekKeys);
$weekIndex = array_flip($activeWeekKeys);
$endWeekKey = date('o-\WW', strtotime($endBound));
if (! isset($weekIndex[$endWeekKey])) {
if (!isset($weekIndex[$endWeekKey])) {
$endWeekKey = null;
for ($i = count($activeWeekKeys) - 1; $i >= 0; $i--) {
$wkStart = $this->isoWeekStartYmd($activeWeekKeys[$i]);
@@ -2289,9 +2265,7 @@ class AttendanceTrackingService
protected function hasNConsecutiveItems(array $datesDesc, int $n, int $daysApart): bool
{
$N = count($datesDesc);
if ($N < $n) {
return false;
}
if ($N < $n) return false;
$dates = $datesDesc;
sort($dates);
@@ -2304,9 +2278,7 @@ class AttendanceTrackingService
if ($diff === $daysApart) {
$run++;
if ($run >= $n) {
return true;
}
if ($run >= $n) return true;
} else {
$run = 1;
}
@@ -2318,9 +2290,7 @@ class AttendanceTrackingService
protected function hasNInWActiveWeeks(array $weekIdx, int $n, int $w): bool
{
$N = count($weekIdx);
if ($N < $n) {
return false;
}
if ($N < $n) return false;
$l = 0;
for ($r = 0; $r < $N; $r++) {
@@ -2337,9 +2307,7 @@ class AttendanceTrackingService
protected function twoLatesOneAbsInWWeeks(array $lateIdx, array $absIdx, int $w): bool
{
if (count($lateIdx) < 2 || count($absIdx) < 1) {
return false;
}
if (count($lateIdx) < 2 || count($absIdx) < 1) return false;
$L = $lateIdx;
$A = $absIdx;
@@ -2354,7 +2322,6 @@ class AttendanceTrackingService
if ($L[$iL2] > $windowEnd) {
$iL1++;
$iL2 = $iL1 + 1;
continue;
}
@@ -2375,4 +2342,4 @@ class AttendanceTrackingService
return false;
}
}
}
@@ -9,7 +9,7 @@ class SemesterRangeServiceTest extends TestCase
{
public function test_normalize_semester(): void
{
$service = new SemesterRangeService;
$service = new SemesterRangeService();
$this->assertSame('Fall', $service->normalizeSemester('fall'));
$this->assertSame('Spring', $service->normalizeSemester('SPRING'));
@@ -18,7 +18,7 @@ class SemesterRangeServiceTest extends TestCase
public function test_get_school_year_range(): void
{
$service = new SemesterRangeService;
$service = new SemesterRangeService();
[$start, $end] = $service->getSchoolYearRange('2025-2026');
@@ -28,7 +28,7 @@ class SemesterRangeServiceTest extends TestCase
public function test_build_sunday_list(): void
{
$service = new SemesterRangeService;
$service = new SemesterRangeService();
$sundays = $service->buildSundayList('2025-09-01', '2025-09-30');
@@ -37,4 +37,4 @@ class SemesterRangeServiceTest extends TestCase
$this->assertContains('2025-09-21', $sundays);
$this->assertContains('2025-09-28', $sundays);
}
}
}
@@ -2,11 +2,6 @@
namespace Tests\Unit\Services\Attendance;
use App\Models\ClassSection;
use App\Models\Configuration;
use App\Models\StaffAttendance;
use App\Models\TeacherClass;
use App\Services\Attendance\SemesterRangeService;
use App\Services\Attendance\StaffAttendanceService;
use Illuminate\Foundation\Testing\RefreshDatabase;
use Illuminate\Support\Facades\DB;
@@ -80,11 +75,11 @@ class StaffAttendanceServiceTest extends TestCase
]);
$service = new StaffAttendanceService(
new Configuration,
new StaffAttendance,
new TeacherClass,
new ClassSection,
new SemesterRangeService
new \App\Models\Configuration(),
new \App\Models\StaffAttendance(),
new \App\Models\TeacherClass(),
new \App\Models\ClassSection(),
new \App\Services\Attendance\SemesterRangeService()
);
$payload = $service->monthData('Fall', '2025-2026');
@@ -2,8 +2,6 @@
namespace Tests\Unit\Services\Attendance;
use App\Models\AttendanceData;
use App\Models\Student;
use App\Services\Attendance\AttendanceRecordSyncService;
use App\Services\Attendance\StudentAttendanceWriterService;
use Illuminate\Foundation\Testing\RefreshDatabase;
@@ -31,7 +29,7 @@ class StudentAttendanceWriterServiceTest extends TestCase
]);
$sync = Mockery::mock(AttendanceRecordSyncService::class);
$service = new StudentAttendanceWriterService(new AttendanceData, new Student, $sync);
$service = new StudentAttendanceWriterService(new \App\Models\AttendanceData(), new \App\Models\Student(), $sync);
$this->assertSame('S1', $service->resolveStudentSchoolId(1, null));
}
@@ -67,7 +65,7 @@ class StudentAttendanceWriterServiceTest extends TestCase
$sync = Mockery::mock(AttendanceRecordSyncService::class);
$sync->shouldReceive('updateAttendanceRecord')->once();
$service = new StudentAttendanceWriterService(new AttendanceData, new Student, $sync);
$service = new StudentAttendanceWriterService(new \App\Models\AttendanceData(), new \App\Models\Student(), $sync);
$service->saveOrUpdateStudentAttendance([
'class_section_id' => 1,
@@ -2,16 +2,7 @@
namespace Tests\Unit\Services\Attendance;
use App\Models\AttendanceData;
use App\Models\AttendanceDay;
use App\Models\AttendanceRecord;
use App\Models\Student;
use App\Models\TeacherClass;
use App\Models\User;
use App\Services\Attendance\AttendanceAutoPublishService;
use App\Services\Attendance\AttendancePolicyService;
use App\Services\Attendance\AttendanceRecordSyncService;
use App\Services\Attendance\StudentAttendanceWriterService;
use App\Services\Attendance\TeacherAttendanceSubmissionService;
use Illuminate\Foundation\Testing\RefreshDatabase;
use Illuminate\Support\Facades\DB;
@@ -35,21 +26,21 @@ class TeacherAttendanceSubmissionServiceTest extends TestCase
$user = $this->seedUser();
$policy = Mockery::mock(AttendancePolicyService::class);
$policy = Mockery::mock(\App\Services\Attendance\AttendancePolicyService::class);
$policy->shouldReceive('canEditDay')->andReturn(true);
$service = new TeacherAttendanceSubmissionService(
new AttendanceDay,
new AttendanceData,
new TeacherClass,
new Student,
new \App\Models\AttendanceDay(),
new \App\Models\AttendanceData(),
new \App\Models\TeacherClass(),
new \App\Models\Student(),
$policy,
new StudentAttendanceWriterService(
new AttendanceData,
new Student,
new AttendanceRecordSyncService(new AttendanceRecord)
new \App\Services\Attendance\StudentAttendanceWriterService(
new \App\Models\AttendanceData(),
new \App\Models\Student(),
new \App\Services\Attendance\AttendanceRecordSyncService(new \App\Models\AttendanceRecord())
),
new AttendanceAutoPublishService
new \App\Services\Attendance\AttendanceAutoPublishService()
);
$this->expectException(\RuntimeException::class);
@@ -16,7 +16,7 @@ class AttendanceManagementServiceTest extends TestCase
$this->seedConfiguration();
$this->seedUserWithBadge('RFID-100');
$service = new AttendanceManagementService;
$service = new AttendanceManagementService();
$row = $service->badgeScan([
'badge_scan' => 'RFID-100',
@@ -46,7 +46,7 @@ class AttendanceManagementServiceTest extends TestCase
'updated_at' => now(),
]);
$service = new AttendanceManagementService;
$service = new AttendanceManagementService();
$data = $service->dashboard([
'date' => '2026-02-10',
@@ -2,12 +2,7 @@
namespace Tests\Unit\Services\AttendanceTracking;
use App\Models\AttendanceData;
use App\Models\AttendanceTracking;
use App\Models\Student;
use App\Services\AttendanceTracking\AttendanceCaseQueryService;
use App\Services\AttendanceTracking\AttendanceParentLookupService;
use App\Services\AttendanceTracking\ViolationRuleEngineService;
use Illuminate\Foundation\Testing\RefreshDatabase;
use Mockery;
use Tests\TestCase;
@@ -19,11 +14,11 @@ class AttendanceCaseQueryServiceTest extends TestCase
public function test_returns_not_found_when_student_missing(): void
{
$service = new AttendanceCaseQueryService(
new Student,
new AttendanceData,
new AttendanceTracking,
Mockery::mock(ViolationRuleEngineService::class),
Mockery::mock(AttendanceParentLookupService::class)
new \App\Models\Student(),
new \App\Models\AttendanceData(),
new \App\Models\AttendanceTracking(),
Mockery::mock(\App\Services\AttendanceTracking\ViolationRuleEngineService::class),
Mockery::mock(\App\Services\AttendanceTracking\AttendanceParentLookupService::class)
);
$result = $service->getStudentCase(999);
@@ -2,12 +2,7 @@
namespace Tests\Unit\Services\AttendanceTracking;
use App\Models\AttendanceData;
use App\Models\Configuration;
use App\Models\Student;
use App\Services\AttendanceTracking\AttendanceCommunicationSupportService;
use App\Services\AttendanceTracking\AttendanceEmailComposerService;
use App\Services\AttendanceTracking\AttendanceParentLookupService;
use Illuminate\Foundation\Testing\RefreshDatabase;
use Illuminate\Support\Facades\DB;
use Mockery;
@@ -54,13 +49,13 @@ class AttendanceCommunicationSupportServiceTest extends TestCase
private function makeService(): AttendanceCommunicationSupportService
{
$parentLookup = Mockery::mock(AttendanceParentLookupService::class);
$emailComposer = Mockery::mock(AttendanceEmailComposerService::class);
$parentLookup = Mockery::mock(\App\Services\AttendanceTracking\AttendanceParentLookupService::class);
$emailComposer = Mockery::mock(\App\Services\AttendanceTracking\AttendanceEmailComposerService::class);
return new AttendanceCommunicationSupportService(
new Student,
new AttendanceData,
new Configuration,
new \App\Models\Student(),
new \App\Models\AttendanceData(),
new \App\Models\Configuration(),
$parentLookup,
$emailComposer
);
@@ -2,7 +2,6 @@
namespace Tests\Unit\Services\AttendanceTracking;
use App\Models\AttendanceEmailTemplate;
use App\Services\AttendanceTracking\AttendanceEmailComposerService;
use Illuminate\Foundation\Testing\RefreshDatabase;
use Illuminate\Support\Facades\DB;
@@ -22,7 +21,7 @@ class AttendanceEmailComposerServiceTest extends TestCase
'is_active' => 1,
]);
$service = new AttendanceEmailComposerService(new AttendanceEmailTemplate);
$service = new AttendanceEmailComposerService(new \App\Models\AttendanceEmailTemplate());
$context = ['{{student_name}}' => 'Student A', '{{parent_name}}' => 'Parent A'];
$rendered = $service->renderTemplate('ABS_1', 'default', $context);
@@ -32,7 +31,7 @@ class AttendanceEmailComposerServiceTest extends TestCase
public function test_pick_variant_fallback(): void
{
$service = new AttendanceEmailComposerService(new AttendanceEmailTemplate);
$service = new AttendanceEmailComposerService(new \App\Models\AttendanceEmailTemplate());
$this->assertSame('no_answer', $service->pickVariant('ABS_2'));
$this->assertSame('default', $service->pickVariant('ABS_1'));
}
@@ -2,7 +2,6 @@
namespace Tests\Unit\Services\AttendanceTracking;
use App\Models\ParentNotification;
use App\Services\AttendanceTracking\AttendanceNotificationLogService;
use Illuminate\Foundation\Testing\RefreshDatabase;
use Illuminate\Support\Facades\DB;
@@ -14,7 +13,7 @@ class AttendanceNotificationLogServiceTest extends TestCase
public function test_log_notification_creates_and_updates(): void
{
$service = new AttendanceNotificationLogService(new ParentNotification);
$service = new AttendanceNotificationLogService(new \App\Models\ParentNotification());
$service->logNotification(1, 'ABS_1', '2025-01-01', 'email', 'parent@example.com', 'Subject', 'sent');
$this->assertDatabaseHas('parent_notifications', [
@@ -2,17 +2,7 @@
namespace Tests\Unit\Services\AttendanceTracking;
use App\Models\AttendanceData;
use App\Models\AttendanceTracking;
use App\Models\Configuration;
use App\Models\Student;
use App\Models\StudentClass;
use App\Services\AttendanceTracking\AttendanceEmailComposerService;
use App\Services\AttendanceTracking\AttendanceMailerService;
use App\Services\AttendanceTracking\AttendanceNotificationLogService;
use App\Services\AttendanceTracking\AttendanceNotificationWorkflowService;
use App\Services\AttendanceTracking\AttendanceParentLookupService;
use App\Services\AttendanceTracking\ViolationRuleEngineService;
use Illuminate\Foundation\Testing\RefreshDatabase;
use Illuminate\Support\Facades\DB;
use Mockery;
@@ -37,20 +27,20 @@ class AttendanceNotificationWorkflowServiceTest extends TestCase
'school_year' => '2025-2026',
]);
$parentLookup = Mockery::mock(AttendanceParentLookupService::class);
$parentLookup = Mockery::mock(\App\Services\AttendanceTracking\AttendanceParentLookupService::class);
$parentLookup->shouldReceive('getPrimaryParentForStudent')->once()->andReturn([]);
$service = new AttendanceNotificationWorkflowService(
new Student,
new StudentClass,
new AttendanceData,
new AttendanceTracking,
new Configuration,
Mockery::mock(AttendanceMailerService::class),
Mockery::mock(ViolationRuleEngineService::class),
new \App\Models\Student(),
new \App\Models\StudentClass(),
new \App\Models\AttendanceData(),
new \App\Models\AttendanceTracking(),
new \App\Models\Configuration(),
Mockery::mock(\App\Services\AttendanceTracking\AttendanceMailerService::class),
Mockery::mock(\App\Services\AttendanceTracking\ViolationRuleEngineService::class),
$parentLookup,
Mockery::mock(AttendanceEmailComposerService::class),
Mockery::mock(AttendanceNotificationLogService::class)
Mockery::mock(\App\Services\AttendanceTracking\AttendanceEmailComposerService::class),
Mockery::mock(\App\Services\AttendanceTracking\AttendanceNotificationLogService::class)
);
$result = $service->record([
@@ -47,7 +47,7 @@ class AttendanceParentLookupServiceTest extends TestCase
'school_year' => '2025-2026',
]);
$service = new AttendanceParentLookupService;
$service = new AttendanceParentLookupService();
$parent = $service->getPrimaryParentForStudent(1);
$this->assertSame('parent@example.com', $parent['email']);
@@ -80,7 +80,7 @@ class AttendanceParentLookupServiceTest extends TestCase
'school_year' => '2025-2026',
]);
$service = new AttendanceParentLookupService;
$service = new AttendanceParentLookupService();
$parent = $service->getSecondaryParentForStudent(1, '2025-2026');
$this->assertSame('second@example.com', $parent['email']);
@@ -2,10 +2,7 @@
namespace Tests\Unit\Services\AttendanceTracking;
use App\Models\AttendanceData;
use App\Services\AttendanceTracking\AttendancePendingViolationService;
use App\Services\AttendanceTracking\AttendanceViolationStudentResolverService;
use App\Services\AttendanceTracking\ViolationRuleEngineService;
use Illuminate\Foundation\Testing\RefreshDatabase;
use Mockery;
use Tests\TestCase;
@@ -16,7 +13,7 @@ class AttendancePendingViolationServiceTest extends TestCase
public function test_returns_error_when_no_students(): void
{
$resolver = Mockery::mock(AttendanceViolationStudentResolverService::class);
$resolver = Mockery::mock(\App\Services\AttendanceTracking\AttendanceViolationStudentResolverService::class);
$resolver->shouldReceive('resolveForSchoolYear')->andReturn([
'school_year' => '2025-2026',
'semester' => 'Fall',
@@ -28,10 +25,10 @@ class AttendancePendingViolationServiceTest extends TestCase
'debug' => [],
]);
$engine = Mockery::mock(ViolationRuleEngineService::class);
$engine = Mockery::mock(\App\Services\AttendanceTracking\ViolationRuleEngineService::class);
$service = new AttendancePendingViolationService(
new AttendanceData,
new \App\Models\AttendanceData(),
$engine,
$resolver
);
@@ -12,7 +12,7 @@ class AttendanceTrackingServiceTest extends TestCase
public function test_service_instantiates(): void
{
$service = new AttendanceTrackingService;
$service = new AttendanceTrackingService();
$this->assertInstanceOf(AttendanceTrackingService::class, $service);
}
}
@@ -2,9 +2,6 @@
namespace Tests\Unit\Services\AttendanceTracking;
use App\Models\AttendanceData;
use App\Models\Student;
use App\Models\StudentClass;
use App\Services\AttendanceTracking\AttendanceViolationStudentResolverService;
use Illuminate\Foundation\Testing\RefreshDatabase;
use Illuminate\Support\Facades\DB;
@@ -37,9 +34,9 @@ class AttendanceViolationStudentResolverServiceTest extends TestCase
]);
$service = new AttendanceViolationStudentResolverService(
new Student,
new StudentClass,
new AttendanceData
new \App\Models\Student(),
new \App\Models\StudentClass(),
new \App\Models\AttendanceData()
);
$result = $service->resolveForSchoolYear('2025-2026', 'Fall');
@@ -2,8 +2,6 @@
namespace Tests\Unit\Services\AttendanceTracking;
use App\Models\AttendanceTracking;
use App\Services\AttendanceTracking\AttendanceParentLookupService;
use App\Services\AttendanceTracking\ViolationRuleEngineService;
use Illuminate\Foundation\Testing\RefreshDatabase;
use Mockery;
@@ -32,8 +30,8 @@ class ViolationRuleEngineServiceTest extends TestCase
private function makeService(): ViolationRuleEngineService
{
$tracking = new AttendanceTracking;
$parentLookup = Mockery::mock(AttendanceParentLookupService::class);
$tracking = new \App\Models\AttendanceTracking();
$parentLookup = Mockery::mock(\App\Services\AttendanceTracking\AttendanceParentLookupService::class);
return new ViolationRuleEngineService($tracking, $parentLookup);
}
@@ -18,7 +18,7 @@ class PasswordResetCleanupServiceTest extends TestCase
['ip_address' => '127.0.0.1', 'requested_at' => now()->subDays(5)],
]);
$service = new PasswordResetCleanupService;
$service = new PasswordResetCleanupService();
$deleted = $service->cleanup(30);
$this->assertSame(1, $deleted);
@@ -34,7 +34,7 @@ class PermissionCheckServiceTest extends TestCase
'can_read' => 1,
]);
$service = new PermissionCheckService;
$service = new PermissionCheckService();
$this->assertTrue($service->hasPermission(10, 'edit_students'));
$this->assertFalse($service->hasPermission(10, 'missing_perm'));
@@ -12,7 +12,7 @@ class RegistrationCaptchaServiceTest extends TestCase
public function test_generate_sets_cache_value(): void
{
$service = new RegistrationCaptchaService;
$service = new RegistrationCaptchaService();
$value = $service->generate(6);
$this->assertNotEmpty($value);
@@ -21,7 +21,7 @@ class RegistrationCaptchaServiceTest extends TestCase
public function test_verify_and_clear(): void
{
$service = new RegistrationCaptchaService;
$service = new RegistrationCaptchaService();
$value = $service->generate(6);
$this->assertTrue($service->verify($value));
@@ -13,7 +13,7 @@ class RegistrationFormatterServiceTest extends TestCase
public function test_format_normalizes_parent_fields(): void
{
$formatter = new RegistrationFormatterService(new PhoneFormatterService);
$formatter = new RegistrationFormatterService(new PhoneFormatterService());
$result = $formatter->format([
'firstname' => 'john',
@@ -41,7 +41,7 @@ class RegistrationFormatterServiceTest extends TestCase
public function test_format_includes_second_parent_when_present(): void
{
$formatter = new RegistrationFormatterService(new PhoneFormatterService);
$formatter = new RegistrationFormatterService(new PhoneFormatterService());
$result = $formatter->format([
'firstname' => 'john',
@@ -35,7 +35,7 @@ class RegistrationServiceTest extends TestCase
'is_active' => 1,
]);
$captchaService = new RegistrationCaptchaService;
$captchaService = new RegistrationCaptchaService();
$captcha = $captchaService->generate(4);
$emailService = Mockery::mock(EmailService::class);
@@ -43,8 +43,8 @@ class RegistrationServiceTest extends TestCase
$service = new RegistrationService(
$emailService,
new SchoolIdService,
new RegistrationFormatterService(new PhoneFormatterService),
new SchoolIdService(),
new RegistrationFormatterService(new PhoneFormatterService()),
$captchaService
);
@@ -115,7 +115,7 @@ class RegistrationServiceTest extends TestCase
'token' => 'token',
]);
$captchaService = new RegistrationCaptchaService;
$captchaService = new RegistrationCaptchaService();
$captcha = $captchaService->generate(4);
$emailService = Mockery::mock(EmailService::class);
@@ -123,8 +123,8 @@ class RegistrationServiceTest extends TestCase
$service = new RegistrationService(
$emailService,
new SchoolIdService,
new RegistrationFormatterService(new PhoneFormatterService),
new SchoolIdService(),
new RegistrationFormatterService(new PhoneFormatterService()),
$captchaService
);
@@ -35,7 +35,7 @@ class UserRoleServiceTest extends TestCase
'can_manage' => 0,
]);
$service = new UserRoleService;
$service = new UserRoleService();
$roleIds = $service->getRoleIds(5);
$this->assertTrue($service->hasPermissionForCrud($roleIds, 'manage_students', 'read'));
@@ -19,7 +19,7 @@ class BadgeFormDataServiceTest extends TestCase
public function test_build_formats_users_and_assigns_latest_class_for_teacherish_roles(): void
{
$lookup = Mockery::mock(BadgeUserLookupService::class);
$formatter = new BadgeTextFormatter;
$formatter = new BadgeTextFormatter();
$lookup->shouldReceive('fetchStaffList')
->once()
@@ -75,7 +75,7 @@ class BadgeFormDataServiceTest extends TestCase
public function test_build_defaults_invalid_active_role_to_teacher(): void
{
$lookup = Mockery::mock(BadgeUserLookupService::class);
$formatter = new BadgeTextFormatter;
$formatter = new BadgeTextFormatter();
$lookup->shouldReceive('fetchStaffList')
->once()
@@ -91,4 +91,4 @@ class BadgeFormDataServiceTest extends TestCase
$this->assertSame('teacher', $result['active_role']);
}
}
}
@@ -22,7 +22,7 @@ class BadgePdfServiceTest extends TestCase
{
$lookup = Mockery::mock(BadgeUserLookupService::class);
$printLog = Mockery::mock(BadgePrintLogService::class);
$formatter = new BadgeTextFormatter;
$formatter = new BadgeTextFormatter();
$lookup->shouldReceive('getUserInfoById')
->once()
@@ -65,7 +65,7 @@ class BadgePdfServiceTest extends TestCase
{
$lookup = Mockery::mock(BadgeUserLookupService::class);
$printLog = Mockery::mock(BadgePrintLogService::class);
$formatter = new BadgeTextFormatter;
$formatter = new BadgeTextFormatter();
$lookup->shouldReceive('getUserInfoById')
->once()
@@ -94,7 +94,7 @@ class BadgePdfServiceTest extends TestCase
{
$lookup = Mockery::mock(BadgeUserLookupService::class);
$printLog = Mockery::mock(BadgePrintLogService::class);
$formatter = new BadgeTextFormatter;
$formatter = new BadgeTextFormatter();
$userInfo = [
'user_id' => 10,
@@ -20,7 +20,7 @@ class BadgePrintLogServiceTest extends TestCase
public function test_get_status_normalizes_ids_before_calling_model(): void
{
$model = Mockery::mock(BadgePrintLog::class);
$formatter = new BadgeTextFormatter;
$formatter = new BadgeTextFormatter();
$expected = [
5 => ['count' => 2, 'last_printed_at' => '2026-03-06 10:00:00', 'last_printed_by' => 1],
@@ -41,7 +41,7 @@ class BadgePrintLogServiceTest extends TestCase
public function test_log_normalizes_ids_and_returns_inserted_count(): void
{
$model = Mockery::mock(BadgePrintLog::class);
$formatter = new BadgeTextFormatter;
$formatter = new BadgeTextFormatter();
$model->shouldReceive('logPrints')
->once()
@@ -74,7 +74,7 @@ class BadgePrintLogServiceTest extends TestCase
Log::spy();
$model = Mockery::mock(BadgePrintLog::class);
$formatter = new BadgeTextFormatter;
$formatter = new BadgeTextFormatter();
$model->shouldReceive('logPrints')
->once()
@@ -91,4 +91,4 @@ class BadgePrintLogServiceTest extends TestCase
Log::shouldHaveReceived('error')->once();
$this->assertTrue(true);
}
}
}
@@ -13,7 +13,7 @@ class BadgeTextFormatterTest extends TestCase
protected function setUp(): void
{
parent::setUp();
$this->formatter = new BadgeTextFormatter;
$this->formatter = new BadgeTextFormatter();
}
public function test_normalize_ids_filters_and_deduplicates(): void
@@ -86,7 +86,7 @@ class BadgeTextFormatterTest extends TestCase
public function test_fit_text_returns_size_within_bounds(): void
{
$pdf = new FPDF;
$pdf = new FPDF();
$pdf->AddPage();
$pdf->SetFont('Arial', '', 12);
@@ -95,4 +95,4 @@ class BadgeTextFormatterTest extends TestCase
$this->assertGreaterThanOrEqual(7, $size);
$this->assertLessThanOrEqual(11, $size);
}
}
}
@@ -192,13 +192,13 @@ class BalanceCalculationServiceTest extends TestCase
private function makeService(): BalanceCalculationService
{
$config = new FeeConfigService;
$config = new FeeConfigService();
$tuition = new TuitionCalculationService(
$config,
new FeeStudentFeeService(new FeeGradeService, $config)
new FeeStudentFeeService(new FeeGradeService(), $config)
);
return new BalanceCalculationService($config, $tuition, new BillingTotalsService);
return new BalanceCalculationService($config, $tuition, new BillingTotalsService());
}
private function seedFeeConfig(): void
@@ -226,7 +226,7 @@ class BalanceCalculationServiceTest extends TestCase
{
return (int) DB::table('invoices')->insertGetId([
'parent_id' => $parentId,
'invoice_number' => 'INV-'.$parentId,
'invoice_number' => 'INV-' . $parentId,
'total_amount' => $total,
'balance' => 0.00,
'paid_amount' => 0.00,
@@ -51,7 +51,7 @@ class BillingTotalsServiceTest extends TestCase
],
]);
$service = new BillingTotalsService;
$service = new BillingTotalsService();
$eventSubtotal = $service->eventSubtotal(10, '2025-2026');
$additionalSubtotal = $service->additionalSubtotal(99, '2025-2026');
@@ -95,7 +95,7 @@ class BillingTotalsServiceTest extends TestCase
],
]);
$service = new BillingTotalsService;
$service = new BillingTotalsService();
$this->assertSame(30.0, $service->additionalSubtotalByParent(10, '2025-2026'));
}
@@ -126,7 +126,7 @@ class BillingTotalsServiceTest extends TestCase
],
]);
$service = new BillingTotalsService;
$service = new BillingTotalsService();
$byStudent = $service->eventSubtotalsByStudent(5, '2025-2026');
$this->assertSame(20.0, $byStudent[1]);
@@ -9,7 +9,7 @@ class BroadcastEmailComposerServiceTest extends TestCase
{
public function test_sanitize_removes_script_and_events(): void
{
$service = new BroadcastEmailComposerService;
$service = new BroadcastEmailComposerService();
$html = '<p onclick="alert(1)">Hi</p><script>alert(2)</script>';
$clean = $service->sanitizeHtml($html);
@@ -20,7 +20,7 @@ class BroadcastEmailComposerServiceTest extends TestCase
public function test_compose_replaces_name_when_personalized(): void
{
$service = new BroadcastEmailComposerService;
$service = new BroadcastEmailComposerService();
$html = '<p>Hello {{name}}</p>';
$rendered = $service->compose(false, 'Subject', $html, 'Parent', '', '', '', true);
@@ -15,7 +15,7 @@ class BroadcastEmailRecipientServiceTest extends TestCase
{
$this->seedParentData();
$service = new BroadcastEmailRecipientService;
$service = new BroadcastEmailRecipientService();
$parents = $service->parentsWithEmails();
$this->assertCount(1, $parents);
@@ -26,7 +26,7 @@ class BroadcastEmailRecipientServiceTest extends TestCase
{
$this->seedParentData();
$service = new BroadcastEmailRecipientService;
$service = new BroadcastEmailRecipientService();
$recipients = $service->recipientsByIds([10]);
$this->assertCount(1, $recipients);
@@ -12,12 +12,12 @@ class BroadcastEmailSenderOptionsServiceTest extends TestCase
$old = getenv('MAIL_SENDERS') ?: '';
putenv('MAIL_SENDERS={"general":{"name":"Office","email":"office@example.com"}}');
$service = new BroadcastEmailSenderOptionsService;
$service = new BroadcastEmailSenderOptionsService();
$options = $service->listOptions();
$this->assertSame('general', $options[0]['key']);
$this->assertSame('Office <office@example.com>', $options[0]['label']);
putenv('MAIL_SENDERS='.$old);
putenv('MAIL_SENDERS=' . $old);
}
}
@@ -15,7 +15,7 @@ class ClassRosterServiceTest extends TestCase
{
$this->seedRosterData();
$service = new ClassRosterService;
$service = new ClassRosterService();
$students = $service->listStudentsByClass(101, '2025-2026');
$this->assertCount(1, $students);
@@ -15,7 +15,7 @@ class StickerCountServiceTest extends TestCase
{
$this->seedStickerData();
$service = new StickerCountService;
$service = new StickerCountService();
$payload = $service->listAll('2025-2026', 'Fall');
$this->assertSame(2, $payload['totals']['students']);
@@ -27,7 +27,7 @@ class StickerCountServiceTest extends TestCase
{
$this->seedStickerData();
$service = new StickerCountService;
$service = new StickerCountService();
$payload = $service->listForClass('2025-2026', 'Fall', 101);
$this->assertSame(1, $payload['totals']['students']);
@@ -21,7 +21,7 @@ class ClassPreparationAdjustmentServiceTest extends TestCase
'adjustable' => 1,
]);
$service = new ClassPreparationAdjustmentService;
$service = new ClassPreparationAdjustmentService();
[$items, $adjMap] = $service->applyAdjustments(['Small Table' => 1], '101', '2025-2026');
$this->assertSame(3, $items['Small Table']);
@@ -22,7 +22,7 @@ class ClassPreparationCalculatorServiceTest extends TestCase
'school_year' => '2025-2026',
]);
$service = new ClassPreparationCalculatorService;
$service = new ClassPreparationCalculatorService();
$this->assertSame(2, $service->getClassLevelBySection('101'));
$this->assertSame(1, $service->getClassLevelBySection('KG'));
@@ -49,7 +49,7 @@ class ClassPreparationCalculatorServiceTest extends TestCase
'school_year' => '2025-2026',
]);
$service = new ClassPreparationCalculatorService;
$service = new ClassPreparationCalculatorService();
$items = $service->calculatePrepItems(6, 1, '101');
$this->assertSame(2, $items['Small Table']);
@@ -20,7 +20,7 @@ class ClassPreparationContextServiceTest extends TestCase
'school_year' => '2025-2026',
]);
$service = new ClassPreparationContextService;
$service = new ClassPreparationContextService();
$this->assertTrue($service->hasRosterForSemester('2025-2026', 'Fall'));
$this->assertFalse($service->hasRosterForSemester('2025-2026', 'Spring'));
@@ -40,7 +40,7 @@ class ClassPreparationInventoryServiceTest extends TestCase
],
]);
$service = new ClassPreparationInventoryService;
$service = new ClassPreparationInventoryService();
$map = $service->buildAvailability('2025-2026', 'Fall', true, ['Small Table']);
$this->assertSame(8, $map['Small Table']);
@@ -4,6 +4,7 @@ namespace Tests\Unit\Services\ClassPreparation;
use App\Services\ClassPreparation\ClassPreparationLogService;
use Illuminate\Foundation\Testing\RefreshDatabase;
use Illuminate\Support\Facades\DB;
use Tests\TestCase;
class ClassPreparationLogServiceTest extends TestCase
@@ -12,7 +13,7 @@ class ClassPreparationLogServiceTest extends TestCase
public function test_has_prep_changed_detects_diff(): void
{
$service = new ClassPreparationLogService;
$service = new ClassPreparationLogService();
$this->assertTrue($service->hasPrepChanged(['a' => 1], ['a' => 2]));
$this->assertFalse($service->hasPrepChanged(['a' => 1], ['a' => 1]));
@@ -20,7 +21,7 @@ class ClassPreparationLogServiceTest extends TestCase
public function test_create_log_and_get_latest(): void
{
$service = new ClassPreparationLogService;
$service = new ClassPreparationLogService();
$created = $service->createLog('101', '1-A', '2025-2026', ['Small Table' => 2], '2025-09-01 00:00:00');
$this->assertTrue($created);
@@ -15,7 +15,7 @@ class ClassPreparationRosterServiceTest extends TestCase
{
$this->seedRoster();
$service = new ClassPreparationRosterService;
$service = new ClassPreparationRosterService();
$rows = $service->getClassSectionStudentCounts('2025-2026', 'Fall', true);
$this->assertCount(1, $rows);
@@ -27,7 +27,7 @@ class ClassPreparationRosterServiceTest extends TestCase
{
$this->seedRoster();
$service = new ClassPreparationRosterService;
$service = new ClassPreparationRosterService();
$count = $service->getStudentCountForSection('2025-2026', 'Fall', true, '101');
$this->assertSame(2, $count);
@@ -18,7 +18,7 @@ class ClassProgressAttachmentServiceTest extends TestCase
Storage::fake('public');
$report = ClassProgressReport::factory()->create();
$service = new ClassProgressAttachmentService;
$service = new ClassProgressAttachmentService();
$files = [UploadedFile::fake()->create('sample.pdf', 12)];
$stored = $service->storeAttachments($report->id, $files);
@@ -9,7 +9,7 @@ class ClassProgressMetaServiceTest extends TestCase
{
public function test_subject_sections_return_configured_values(): void
{
$service = new ClassProgressMetaService;
$service = new ClassProgressMetaService();
$sections = $service->subjectSections();
@@ -22,8 +22,8 @@ class ClassProgressMutationServiceTest extends TestCase
$this->assignTeacher($user->id, 101);
$service = new ClassProgressMutationService(
new ClassProgressRuleService,
new ClassProgressAttachmentService
new ClassProgressRuleService(),
new ClassProgressAttachmentService()
);
$payload = [
@@ -47,8 +47,8 @@ class ClassProgressMutationServiceTest extends TestCase
]);
$service = new ClassProgressMutationService(
new ClassProgressRuleService,
new ClassProgressAttachmentService
new ClassProgressRuleService(),
new ClassProgressAttachmentService()
);
$updated = $service->updateReport($report, ['status' => 'behind'], []);
@@ -60,8 +60,8 @@ class ClassProgressMutationServiceTest extends TestCase
{
$report = ClassProgressReport::factory()->create();
$service = new ClassProgressMutationService(
new ClassProgressRuleService,
new ClassProgressAttachmentService
new ClassProgressRuleService(),
new ClassProgressAttachmentService()
);
$service->deleteReport($report);
@@ -26,7 +26,7 @@ class ClassProgressQueryServiceTest extends TestCase
'class_section_id' => 202,
]);
$service = new ClassProgressQueryService(new ClassProgressRuleService);
$service = new ClassProgressQueryService(new ClassProgressRuleService());
$results = $service->listReports($user, ['class_section_id' => 101]);
$this->assertSame(1, $results->total());
@@ -9,7 +9,7 @@ class ClassProgressRuleServiceTest extends TestCase
{
public function test_build_unit_chapter_summary_limits_length(): void
{
$service = new ClassProgressRuleService;
$service = new ClassProgressRuleService();
$summary = $service->buildUnitChapterSummary(['Unit 1'], ['Chapter A']);
@@ -18,7 +18,7 @@ class ClassProgressRuleServiceTest extends TestCase
public function test_ensure_week_end_defaults_to_six_days(): void
{
$service = new ClassProgressRuleService;
$service = new ClassProgressRuleService();
$weekEnd = $service->ensureWeekEnd('2025-09-07', null);
@@ -22,7 +22,7 @@ class CommunicationTemplateServiceTest extends TestCase
'is_active' => 1,
]);
$service = new CommunicationTemplateService;
$service = new CommunicationTemplateService();
$templates = $service->listActiveTemplates();
$this->assertSame('welcome', $templates[0]['template_key']);
@@ -2,6 +2,7 @@
namespace Tests\Unit\Services\CompetitionScores;
use App\Models\CompetitionScore;
use App\Services\CompetitionScores\CompetitionScoresSaveService;
use Illuminate\Foundation\Testing\RefreshDatabase;
use Illuminate\Support\Facades\DB;
@@ -13,7 +14,7 @@ class CompetitionScoresSaveServiceTest extends TestCase
public function test_filter_scores_rejects_non_integers(): void
{
$service = new CompetitionScoresSaveService;
$service = new CompetitionScoresSaveService();
[$clean, $invalid] = $service->filterScores([
1 => '10',
2 => '4.5',
@@ -32,7 +33,7 @@ class CompetitionScoresSaveServiceTest extends TestCase
'score' => 2,
]);
$service = new CompetitionScoresSaveService;
$service = new CompetitionScoresSaveService();
$service->saveScores(1, 101, [1 => 7]);
$this->assertDatabaseHas('competition_scores', [
@@ -30,7 +30,7 @@ class DiscountApplyServiceTest extends TestCase
'is_active' => 1,
]);
$service = new DiscountApplyService(new DiscountContextService, new DiscountInvoiceService);
$service = new DiscountApplyService(new DiscountContextService(), new DiscountInvoiceService());
$result = $service->applyVoucher(1, [10], true, 1);
$this->assertFalse($result['ok']);
@@ -44,7 +44,7 @@ class EmailExtractorServiceTest extends TestCase
'school_year' => '2025-2026',
]);
$service = new EmailExtractorService;
$service = new EmailExtractorService();
$emails = $service->listEmails();
$this->assertContains('user1@example.com', $emails['users']);
@@ -9,7 +9,7 @@ class EmailProfileServiceTest extends TestCase
{
public function test_resolve_profile_maps_aliases(): void
{
$service = new EmailProfileService;
$service = new EmailProfileService();
$this->assertSame('communication', $service->resolveProfile('comm'));
$this->assertSame('default', $service->resolveProfile('system'));
@@ -18,14 +18,14 @@ class EmailProfileServiceTest extends TestCase
public function test_env_key_from_profile(): void
{
$service = new EmailProfileService;
$service = new EmailProfileService();
$this->assertSame('PAYMENT_ALERTS', $service->envKeyFromProfile('payment alerts'));
}
public function test_sanitize_reply_to_name(): void
{
$service = new EmailProfileService;
$service = new EmailProfileService();
$this->assertSame('Fallback', $service->sanitizeReplyToName('no-reply', 'Fallback'));
$this->assertSame('Support', $service->sanitizeReplyToName('Support', 'Fallback'));
@@ -26,7 +26,7 @@ class EventChargeQueryServiceTest extends TestCase
'updated_at' => now(),
]);
$service = new EventChargeQueryService;
$service = new EventChargeQueryService();
$result = $service->list(['per_page' => 10, 'page' => 1]);
$this->assertSame(1, $result['pagination']['total']);
@@ -38,7 +38,7 @@ class EventListServiceTest extends TestCase
],
]);
$service = new EventListService;
$service = new EventListService();
$result = $service->list(['active' => true, 'per_page' => 10]);
$this->assertSame(1, $result['pagination']['total']);
@@ -31,7 +31,7 @@ class EventStudentChargeServiceTest extends TestCase
'semester' => 'Fall',
]);
$service = new EventStudentChargeService;
$service = new EventStudentChargeService();
$rows = $service->listStudentsWithCharges(10, '2025-2026', 'Fall');
$this->assertTrue($rows[0]['charged']);
@@ -21,7 +21,7 @@ class ExamDraftTeacherServiceTest extends TestCase
$classSectionId = $this->seedClassSection();
$this->seedAssignment($teacherId, $classSectionId);
$service = new ExamDraftTeacherService(new ExamDraftConfigService, new ExamDraftFileService);
$service = new ExamDraftTeacherService(new ExamDraftConfigService(), new ExamDraftFileService());
$file = UploadedFile::fake()->create('draft.docx', 10, 'application/vnd.openxmlformats-officedocument.wordprocessingml.document');
$result = $service->store($teacherId, [
@@ -16,11 +16,11 @@ class ExpenseReceiptServiceTest extends TestCase
{
Storage::fake();
$service = new ExpenseReceiptService;
$service = new ExpenseReceiptService();
$file = UploadedFile::fake()->create('receipt.pdf', 10, 'application/pdf');
$filename = $service->storeReceipt($file);
$this->assertNotEmpty($filename);
$this->assertSame(url('receipts/'.$filename), $service->receiptUrl($filename));
$this->assertSame(url('receipts/' . $filename), $service->receiptUrl($filename));
}
}
@@ -66,7 +66,7 @@ class ExpenseStaffServiceTest extends TestCase
['user_id' => 11, 'role_id' => 2],
]);
$service = new ExpenseStaffService;
$service = new ExpenseStaffService();
$staff = $service->listStaffUsers();
$this->assertCount(1, $staff);
@@ -13,7 +13,7 @@ class ExtraChargesMetaServiceTest extends TestCase
public function test_get_school_years_uses_fallback(): void
{
$service = new ExtraChargesMetaService;
$service = new ExtraChargesMetaService();
$years = $service->getSchoolYears('2025-2026');
$this->assertSame(['2025-2026', '2024-2025', '2023-2024', '2022-2023'], $years);
@@ -53,7 +53,7 @@ class ExtraChargesMetaServiceTest extends TestCase
],
]);
$service = new ExtraChargesMetaService;
$service = new ExtraChargesMetaService();
$years = $service->getSchoolYears();
$this->assertSame(['2025-2026', '2024-2025', '2023-2024'], $years);
@@ -40,7 +40,7 @@ class FamilyFinanceServiceTest extends TestCase
'updated_at' => now(),
]);
$service = new FamilyFinanceService;
$service = new FamilyFinanceService();
$result = $service->loadFinancialsForParents([1]);
$this->assertSame(1, $result['summary']['invoices_count']);
@@ -16,7 +16,7 @@ class FamilyMutationServiceTest extends TestCase
$this->seedUser(1);
$this->seedStudent(10, 1);
$service = new FamilyMutationService;
$service = new FamilyMutationService();
$result = $service->bootstrapFamilies();
$this->assertSame(1, $result['families_created']);
@@ -36,7 +36,7 @@ class FamilyMutationServiceTest extends TestCase
'updated_at' => now(),
]);
$service = new FamilyMutationService;
$service = new FamilyMutationService();
$result = $service->attachSecondByEmail(10, 'secondary@example.com', 'Second', 'Parent');
$this->assertTrue($result['ok']);
@@ -63,7 +63,7 @@ class FamilyMutationServiceTest extends TestCase
'updated_at' => now(),
]);
$service = new FamilyMutationService;
$service = new FamilyMutationService();
$service->setPrimaryHome($familyId, 10, true);
$this->assertDatabaseHas('family_students', [
@@ -80,7 +80,7 @@ class FamilyMutationServiceTest extends TestCase
'firstname' => 'Parent',
'lastname' => 'User',
'cellphone' => '9999999999',
'email' => 'parent'.$id.'@example.com',
'email' => 'parent' . $id . '@example.com',
'address_street' => '123 Street',
'city' => 'City',
'state' => 'ST',
@@ -20,7 +20,7 @@ class FamilyQueryServiceTest extends TestCase
$this->seedFamilyStudent($familyId, $studentId);
$this->seedFamilyGuardian($familyId, 1);
$service = new FamilyQueryService(new FamilyFinanceService);
$service = new FamilyQueryService(new FamilyFinanceService());
$result = $service->adminIndexData($studentId, null, '2025-2026');
$this->assertNotEmpty($result['families']);
@@ -31,7 +31,7 @@ class FamilyQueryServiceTest extends TestCase
{
$this->seedStudent(10, 1);
$service = new FamilyQueryService(new FamilyFinanceService);
$service = new FamilyQueryService(new FamilyFinanceService());
$items = $service->searchSuggestions('Student', 5);
$this->assertNotEmpty($items);
@@ -46,7 +46,7 @@ class FamilyQueryServiceTest extends TestCase
$this->seedFamilyStudent($familyId, $studentId);
$this->seedFamilyGuardian($familyId, 1);
$service = new FamilyQueryService(new FamilyFinanceService);
$service = new FamilyQueryService(new FamilyFinanceService());
$family = $service->familyCard(null, null, $familyId, '2025-2026');
$this->assertNotNull($family);
@@ -68,7 +68,7 @@ class FamilyQueryServiceTest extends TestCase
'firstname' => 'Parent',
'lastname' => 'User',
'cellphone' => '9999999999',
'email' => 'parent'.$parentId.'@example.com',
'email' => 'parent' . $parentId . '@example.com',
'address_street' => '123 Street',
'city' => 'City',
'state' => 'ST',
@@ -103,7 +103,7 @@ class FamilyQueryServiceTest extends TestCase
{
return DB::table('families')->insertGetId([
'family_code' => $code,
'household_name' => 'Family '.$code,
'household_name' => 'Family ' . $code,
'is_active' => 1,
'created_at' => now(),
'updated_at' => now(),
@@ -9,7 +9,7 @@ class FeeGradeServiceTest extends TestCase
{
public function test_grade_level_parsing(): void
{
$service = new FeeGradeService;
$service = new FeeGradeService();
$this->assertSame(0, $service->getGradeLevel('K'));
$this->assertSame(99, $service->getGradeLevel('Y'));
@@ -19,7 +19,7 @@ class FeeGradeServiceTest extends TestCase
public function test_compare_grades_orders_numeric_then_suffix(): void
{
$service = new FeeGradeService;
$service = new FeeGradeService();
$this->assertSame(-1, $service->compareGrades('2', '3'));
$this->assertSame(-1, $service->compareGrades('3-A', '3-B'));
@@ -225,8 +225,8 @@ class FeeRefundCalculatorServiceTest extends TestCase
private function makeService(): FeeRefundCalculatorService
{
return new FeeRefundCalculatorService(
new FeeConfigService,
new FeeStudentFeeService(new FeeGradeService, new FeeConfigService)
new FeeConfigService(),
new FeeStudentFeeService(new FeeGradeService(), new FeeConfigService())
);
}
@@ -258,7 +258,7 @@ class FeeRefundCalculatorServiceTest extends TestCase
{
$invoiceId = DB::table('invoices')->insertGetId([
'parent_id' => $parentId,
'invoice_number' => 'INV-'.$parentId,
'invoice_number' => 'INV-' . $parentId,
'total_amount' => $amount,
'balance' => 0.00,
'paid_amount' => $amount,
@@ -55,7 +55,7 @@ class FeeRefundDetailServiceTest extends TestCase
'status' => 'Paid',
]);
$service = new FeeRefundDetailService;
$service = new FeeRefundDetailService();
$result = $service->calculateRefundDetails([
[
'student_id' => 1,
@@ -138,7 +138,7 @@ class FeeStudentFeeServiceTest extends TestCase
private function makeService(): FeeStudentFeeService
{
return new FeeStudentFeeService(new FeeGradeService, new FeeConfigService);
return new FeeStudentFeeService(new FeeGradeService(), new FeeConfigService());
}
private function seedFeeConfig(): void
@@ -148,11 +148,10 @@ class TuitionCalculationServiceTest extends TestCase
private function makeService(): TuitionCalculationService
{
$config = new FeeConfigService;
$config = new FeeConfigService();
return new TuitionCalculationService(
$config,
new FeeStudentFeeService(new FeeGradeService, $config)
new FeeStudentFeeService(new FeeGradeService(), $config)
);
}
@@ -35,7 +35,7 @@ class ExamDraftDownloadNameServiceTest extends TestCase
'status' => 'draft',
]);
$service = new ExamDraftDownloadNameService;
$service = new ExamDraftDownloadNameService();
$name = $service->build('draft2.pdf', 'drafts');
$this->assertSame('2b_final_exam_v3', $name);
@@ -15,13 +15,13 @@ class FileServeServiceTest extends TestCase
public function test_meta_returns_file_metadata(): void
{
$dir = storage_path('testing/files');
if (! is_dir($dir)) {
if (!is_dir($dir)) {
mkdir($dir, 0777, true);
}
$path = $dir.DIRECTORY_SEPARATOR.'sample.pdf';
$path = $dir . DIRECTORY_SEPARATOR . 'sample.pdf';
file_put_contents($path, 'PDFDATA');
$service = new FileServeService;
$service = new FileServeService();
$meta = $service->meta($dir, 'sample.pdf', ['pdf'], 'download');
$this->assertSame('sample.pdf', $meta['name']);
@@ -33,13 +33,13 @@ class FileServeServiceTest extends TestCase
public function test_serve_inline_returns_304_when_etag_matches(): void
{
$dir = storage_path('testing/files');
if (! is_dir($dir)) {
if (!is_dir($dir)) {
mkdir($dir, 0777, true);
}
$path = $dir.DIRECTORY_SEPARATOR.'cached.pdf';
$path = $dir . DIRECTORY_SEPARATOR . 'cached.pdf';
file_put_contents($path, 'PDFDATA');
$service = new FileServeService;
$service = new FileServeService();
$meta = $service->meta($dir, 'cached.pdf', ['pdf']);
$request = Request::create('/files/cached.pdf', 'GET', [], [], [], [
@@ -57,13 +57,13 @@ class FileServeServiceTest extends TestCase
$this->expectException(HttpException::class);
$dir = storage_path('testing/files');
if (! is_dir($dir)) {
if (!is_dir($dir)) {
mkdir($dir, 0777, true);
}
$path = $dir.DIRECTORY_SEPARATOR.'sample.exe';
$path = $dir . DIRECTORY_SEPARATOR . 'sample.exe';
file_put_contents($path, 'DATA');
$service = new FileServeService;
$service = new FileServeService();
$service->meta($dir, 'sample.exe', ['pdf']);
}
}
@@ -12,7 +12,7 @@ class FinancialChartServiceTest extends TestCase
public function test_generate_charts_returns_null_in_tests(): void
{
$service = new FinancialChartService;
$service = new FinancialChartService();
$summary = [
'totalCharges' => 100,

Some files were not shown because too many files have changed in this diff Show More