From 5ead80fdc72c1cb5ef200ad602caced45d9412b1 Mon Sep 17 00:00:00 2001 From: root Date: Thu, 11 Jun 2026 11:46:12 -0400 Subject: [PATCH] fix tests --- .../Commands/AttendanceAutoPublishCommand.php | 1 + .../Commands/CheckMissedPaymentsCommand.php | 2 + .../CleanupExpiredNotificationsCommand.php | 2 + .../Commands/CleanupPasswordResetsCommand.php | 1 + app/Console/Commands/ConfigUpdateCommand.php | 11 +- .../Commands/DeleteInactiveUsersCommand.php | 1 + .../Commands/DeleteUnverifiedUsersCommand.php | 1 + app/Console/Commands/InventoryReconcile.php | 3 +- .../Commands/RecalculateAttendanceCommand.php | 3 +- .../Commands/SendAbsenteesSummaryCommand.php | 1 + .../Commands/SendLatesSummaryCommand.php | 1 + ...SendMonthlyPaymentNotificationsCommand.php | 10 +- .../SendTestPaymentNotificationCommand.php | 11 +- .../Commands/SyncPaypalPaymentsCommand.php | 3 +- app/Events/DeleteUnverifiedUser.php | 4 +- app/Events/WhatsappInvitesSend.php | 4 +- app/Helpers/app_helpers.php | 18 +- .../AdministratorAbsenceController.php | 5 +- .../AdministratorDashboardController.php | 5 +- .../AdministratorEnrollmentController.php | 7 +- .../AdministratorNotificationController.php | 7 +- .../AdministratorPromotionController.php | 30 +- ...ministratorTeacherSubmissionController.php | 3 +- .../EmergencyContactController.php | 7 +- .../TeacherClassAssignmentController.php | 3 - .../AdminAttendanceApiController.php | 6 +- .../AttendanceCommentTemplateController.php | 5 +- .../Attendance/EarlyDismissalsController.php | 83 +- .../Api/Attendance/LateSlipLogsController.php | 9 +- .../StaffAttendanceApiController.php | 18 +- .../TeacherAttendanceApiController.php | 15 +- .../AttendanceManagementController.php | 24 +- .../AttendanceTrackingController.php | 3 +- .../Controllers/Api/Auth/AuthController.php | 8 +- .../Api/Auth/AuthSessionController.php | 2 +- .../Controllers/Api/Auth/IpBanController.php | 15 +- .../Api/Auth/RegisterController.php | 3 +- .../Api/Auth/RolePermissionController.php | 41 +- .../Api/Auth/RoleSwitcherController.php | 5 +- .../Api/Auth/SessionTimeoutController.php | 3 +- .../Api/Badges/BadgeController.php | 10 +- .../Controllers/Api/BaseApiController.php | 4 +- .../ClassPreparationController.php | 8 +- .../ClassProgress/ClassProgressController.php | 22 +- .../Api/Classes/ClassController.php | 7 +- .../Communication/CommunicationController.php | 19 +- .../CompetitionScoresController.php | 16 +- .../CompetitionWinnersController.php | 4 - .../Api/Core/BaseApiController.php | 82 +- .../Api/Discounts/DiscountController.php | 11 +- .../Api/Email/BroadcastEmailController.php | 9 +- .../Controllers/Api/Email/EmailController.php | 2 +- .../Api/Email/EmailExtractorController.php | 1 - .../Api/Exams/ExamDraftController.php | 9 +- .../Api/Expenses/ExpenseController.php | 12 +- .../ExtraCharges/ExtraChargesController.php | 9 +- .../Api/Family/FamilyAdminController.php | 5 +- .../Api/Family/FamilyController.php | 15 +- .../Finance/BalanceCarryforwardController.php | 9 +- .../Api/Finance/ChargeController.php | 3 - .../Api/Finance/EventChargeController.php | 69 +- .../Finance/FinanceNotificationController.php | 11 +- .../Api/Finance/FinancialController.php | 20 +- .../Api/Finance/InstallmentPlanController.php | 51 +- .../Api/Finance/InvoiceController.php | 7 +- .../Api/Finance/PaymentController.php | 2 +- .../Finance/PaymentEventChargesController.php | 3 - .../Api/Finance/PaymentManualController.php | 12 +- .../Finance/PaymentTransactionController.php | 2 +- .../Finance/PaypalTransactionsController.php | 2 +- .../Api/Finance/PurchaseOrderController.php | 10 +- .../Api/Finance/RefundController.php | 6 +- .../Api/Finance/ReimbursementController.php | 21 +- .../Api/Frontend/FrontendController.php | 2 +- .../Api/Frontend/InfoIconController.php | 3 - .../Api/Frontend/LandingPageController.php | 6 +- .../Api/Frontend/PageController.php | 2 +- .../Api/Grading/GradingController.php | 9 +- .../Api/Incidents/IncidentController.php | 4 - .../Inventory/InventoryCategoryController.php | 6 +- .../Api/Inventory/InventoryController.php | 26 +- .../Inventory/InventoryMovementController.php | 15 +- .../Api/Inventory/SupplierController.php | 6 +- .../Inventory/SupplyCategoryController.php | 6 +- .../Api/Messaging/MessagesController.php | 20 +- .../Api/Messaging/WhatsappController.php | 13 +- .../Notifications/NotificationController.php | 12 +- .../ParentAttendanceReportController.php | 7 +- .../Api/Parents/ParentController.php | 4 +- .../Api/Parents/ParentPromotionController.php | 8 +- .../PrintRequests/PrintRequestsController.php | 3 +- .../Api/Reports/ReportCardsController.php | 18 +- .../Api/Reports/SlipPrinterController.php | 11 +- .../Api/Reports/StickersController.php | 7 +- .../Api/RolePermissionController.php | 38 +- .../Api/RoleSwitcherController.php | 2 +- .../Controllers/Api/ScannerController.php | 82 +- .../Api/Scores/FinalController.php | 3 - .../Api/Scores/HomeworkController.php | 3 - .../Api/Scores/MidtermController.php | 3 - .../Api/Scores/ParticipationController.php | 3 - .../Api/Scores/ProjectController.php | 3 - .../Controllers/Api/Scores/QuizController.php | 3 - .../Api/Scores/ScoreCommentController.php | 7 +- .../Api/Scores/ScoreController.php | 3 - .../Settings/ConfigurationAdminController.php | 6 +- .../Api/Settings/EventController.php | 6 +- .../Api/Settings/PreferencesController.php | 18 +- .../Api/Settings/SchoolCalendarController.php | 24 +- .../Api/Settings/SettingsController.php | 6 +- .../Controllers/Api/Staff/StaffController.php | 20 +- .../Api/Staff/TeacherController.php | 7 +- app/Http/Controllers/Api/StatsController.php | 3 +- .../Api/Students/StudentController.php | 52 +- .../Subjects/SubjectCurriculumController.php | 6 +- .../Api/Support/SupportController.php | 8 +- .../Api/System/DashboardController.php | 2 +- .../System/DashboardRedirectController.php | 5 +- .../Api/System/DatabaseHealthController.php | 2 +- .../Api/System/NavBuilderController.php | 7 +- .../Api/System/StatsController.php | 2 + app/Http/Controllers/Api/Ui/UiController.php | 3 - .../Controllers/Api/Users/UserController.php | 3 - .../Web/SchoolYearAdminPageController.php | 3 +- .../Web/TeacherCalendarPageController.php | 3 +- app/Http/Middleware/ApiDocsAuth.php | 6 +- app/Http/Middleware/ApiJwtAuth.php | 2 +- app/Http/Middleware/CleanupScheduler.php | 4 +- .../EnsurePrintRequestsAdminAccess.php | 3 +- .../Middleware/EnsureSchoolYearEditable.php | 8 +- app/Http/Middleware/MultiAuth.php | 2 + app/Http/Middleware/PrimeTimezone.php | 4 +- app/Http/Middleware/RequirePermission.php | 7 +- .../Admin/SaveCompetitionScoresRequest.php | 3 +- .../Admin/UpdateCompetitionWinnerRequest.php | 4 +- .../SubmitAdministratorAbsenceRequest.php | 2 +- .../Api/Admin/IndexAdminProgressRequest.php | 3 +- .../SaveAdminNotificationSubjectsRequest.php | 4 +- ...SavePrintNotificationRecipientsRequest.php | 4 +- ...dTeacherSubmissionNotificationsRequest.php | 4 +- .../StoreStudentAssignmentRequest.php | 12 +- .../AdminAddAttendanceEntryRequest.php | 2 +- .../Attendance/SaveAdminAttendanceRequest.php | 2 +- .../SaveStaffAttendanceCellRequest.php | 2 +- .../TeacherSubmitAttendanceRequest.php | 2 +- .../UpdateAttendanceManagementRequest.php | 2 +- .../ComposeAttendanceEmailRequest.php | 6 +- .../RecordAttendanceTrackingRequest.php | 10 +- .../SaveAttendanceNotificationNoteRequest.php | 12 +- .../SendAttendanceManualEmailRequest.php | 12 +- app/Http/Requests/Auth/RegisterRequest.php | 2 +- .../Badges/BadgePrintStatusRequest.php | 4 +- .../Badges/GenerateBadgePdfRequest.php | 4 +- .../Requests/Badges/LogBadgePrintRequest.php | 4 +- .../ClassProgressIndexRequest.php | 4 +- .../ClassProgressMetaRequest.php | 2 - .../ClassProgressStoreRequest.php | 6 +- .../ClassProgressUpdateRequest.php | 8 +- .../Families/FamilyComposeEmailRequest.php | 2 +- .../Finance/CarryforwardAdjustmentRequest.php | 10 +- .../Finance/CarryforwardFinalizeRequest.php | 14 +- .../Finance/CarryforwardPreviewRequest.php | 14 +- .../Finance/EventChargeLifecycleRequest.php | 26 +- .../Finance/FinanceNotificationLogRequest.php | 16 +- .../Finance/FinancialSummaryRequest.php | 4 +- .../Requests/Finance/FollowUpNoteRequest.php | 24 +- .../Finance/InstallmentPlanRequest.php | 16 +- .../Finance/ParentPaymentFollowUpRequest.php | 32 +- .../Finance/PaymentAllocationRequest.php | 12 +- .../Incidents/IncidentStoreRequest.php | 2 +- .../Promotions/AdminListPromotionsRequest.php | 2 +- .../ParentEnrollmentStepRequest.php | 1 + .../PurchaseOrderStoreRequest.php | 2 +- .../Refunds/RefundDecisionRequest.php | 2 +- .../Requests/Refunds/RefundIndexRequest.php | 2 +- .../Stickers/StickerFormDataRequest.php | 2 +- .../Reports/Stickers/StickerPrintRequest.php | 4 +- .../StickerStudentsByClassRequest.php | 2 +- .../Requests/Roles/PermissionStoreRequest.php | 1 - app/Http/Requests/Security/IpBanRequest.php | 2 +- app/Http/Requests/Security/IpUnbanRequest.php | 2 +- .../Admin/AdminNotificationAlertsResource.php | 2 +- .../Admin/PrintRecipientsResource.php | 2 +- .../Admin/Progress/ProgressGroupResource.php | 2 +- .../Progress/ProgressReportDetailResource.php | 2 +- .../Admin/TeacherSubmissionReportResource.php | 2 +- .../Attendance/AdminAttendanceResource.php | 2 +- .../Attendance/DailyAttendanceResource.php | 2 +- .../TeacherAttendanceFormResource.php | 4 +- .../Attendance/TeacherGridResource.php | 4 +- .../ClassProgressGroupCollection.php | 4 +- .../Resources/Events/EventChargeResource.php | 4 +- .../Resources/Messaging/MessageCollection.php | 2 +- .../Resources/Messaging/MessageResource.php | 4 +- .../PaymentNotificationLogResource.php | 2 +- .../Promotions/StudentPromotionResource.php | 1 + .../ReimbursementRecipientResource.php | 2 +- app/Http/Resources/Staff/StaffCollection.php | 2 +- .../Support/SupportRequestCollection.php | 2 +- app/Http/Resources/System/NavItemResource.php | 2 +- .../TeacherSubmissionRowResource.php | 2 +- app/Libraries/StaffTimeOffLinkService.php | 4 +- .../AttendanceConsequenceListener.php | 4 +- app/Listeners/BelowSixtyEmailListener.php | 4 +- .../DeleteUnverifiedUserListener.php | 4 +- app/Listeners/SchoolEventListener.php | 3 +- app/Listeners/WhatsappInviteListener.php | 4 +- app/Mail/TeacherSubmissionReminderMail.php | 3 +- app/Models/AdditionalCharge.php | 21 +- app/Models/AdminNotificationSubject.php | 7 +- app/Models/AttendanceCommentTemplate.php | 4 +- app/Models/AttendanceData.php | 190 +- app/Models/AttendanceDay.php | 87 +- app/Models/AttendanceEmailTemplate.php | 4 +- app/Models/AttendanceRecord.php | 34 +- app/Models/AttendanceTracking.php | 158 +- app/Models/AuthorizedUser.php | 6 +- app/Models/BadgePrintLog.php | 70 +- app/Models/BaseModel.php | 20 +- app/Models/Calendar.php | 4 +- app/Models/CalendarEvent.php | 21 +- app/Models/ClassModel.php | 4 +- app/Models/ClassPrepAdjustment.php | 6 +- app/Models/ClassPreparationLog.php | 6 +- app/Models/ClassProgressAttachment.php | 6 +- app/Models/ClassProgressReport.php | 10 +- app/Models/ClassSection.php | 4 +- app/Models/Communication.php | 4 +- app/Models/CommunicationLog.php | 18 +- app/Models/Competition.php | 23 +- app/Models/CompetitionClassWinner.php | 12 +- app/Models/CompetitionScore.php | 10 +- app/Models/CompetitionWinner.php | 16 +- app/Models/Configuration.php | 10 +- app/Models/ContactUs.php | 6 +- app/Models/CurrentFlag.php | 14 +- app/Models/CurrentIncident.php | 3 +- app/Models/DiscountUsage.php | 15 +- app/Models/DiscountVoucher.php | 26 +- app/Models/EarlyDismissalSignature.php | 6 +- app/Models/EmailTemplate.php | 6 +- app/Models/EmergencyContact.php | 8 +- app/Models/Enrollment.php | 21 +- app/Models/Event.php | 29 +- app/Models/EventCharge.php | 4 +- app/Models/EventCharges.php | 19 +- app/Models/Exam.php | 9 +- app/Models/ExamDraft.php | 16 +- app/Models/Expense.php | 29 +- app/Models/Family.php | 4 +- app/Models/FamilyCommPref.php | 12 +- app/Models/FamilyGuardian.php | 14 +- app/Models/FamilyStudent.php | 9 +- app/Models/FinalExam.php | 22 +- app/Models/FinalScore.php | 12 +- app/Models/FinanceBalanceCarryforward.php | 2 + app/Models/FinanceFollowUpNote.php | 1 + app/Models/FinanceNotificationLog.php | 1 + app/Models/FinanceNotificationPreference.php | 1 + app/Models/FinanceReceiptSequence.php | 1 + app/Models/GradingLock.php | 11 +- app/Models/Homework.php | 39 +- app/Models/Incident.php | 10 +- app/Models/InventoryCategory.php | 4 +- app/Models/InventoryItem.php | 28 +- app/Models/InventoryMovement.php | 22 +- app/Models/Invoice.php | 52 +- app/Models/InvoiceEvent.php | 10 +- app/Models/InvoiceInstallment.php | 1 + app/Models/InvoiceInstallmentPlan.php | 1 + app/Models/InvoiceStudentList.php | 8 +- app/Models/IpAttempt.php | 9 +- app/Models/LateSlipLog.php | 36 +- app/Models/LoginActivity.php | 8 +- app/Models/ManualPayment.php | 6 +- app/Models/Message.php | 20 +- app/Models/MidtermExam.php | 22 +- app/Models/MissingScoreOverride.php | 39 +- app/Models/NavItem.php | 8 +- app/Models/Notification.php | 13 +- app/Models/ParentAttendanceReport.php | 25 +- app/Models/ParentMeetingSchedule.php | 12 +- app/Models/ParentModel.php | 4 +- app/Models/ParentNotification.php | 8 +- app/Models/Participation.php | 42 +- app/Models/PasswordReset.php | 5 +- app/Models/PasswordResetRequest.php | 4 +- app/Models/PayPalPayment.php | 15 +- app/Models/Payment.php | 47 +- app/Models/PaymentCarryforwardAllocation.php | 1 + app/Models/PaymentError.php | 16 +- app/Models/PaymentInstallmentAllocation.php | 1 + app/Models/PaymentNotificationLog.php | 26 +- app/Models/PaymentTransaction.php | 13 +- app/Models/PaypalTransaction.php | 12 +- app/Models/Permission.php | 9 +- app/Models/PlacementBatch.php | 4 +- app/Models/PlacementLevel.php | 4 +- app/Models/PlacementScore.php | 8 +- app/Models/Preferences.php | 16 +- app/Models/PrintRequest.php | 14 +- app/Models/Project.php | 27 +- app/Models/PromotionAuditLog.php | 11 + app/Models/PromotionQueue.php | 22 +- app/Models/PromotionReminderLog.php | 4 + app/Models/PurchaseOrder.php | 52 +- app/Models/PurchaseOrderItem.php | 30 +- app/Models/Quiz.php | 22 +- app/Models/Refund.php | 61 +- app/Models/Reimbursement.php | 53 +- app/Models/ReimbursementBatch.php | 35 +- app/Models/ReimbursementBatchAdminFile.php | 23 +- app/Models/ReimbursementBatchItem.php | 35 +- app/Models/ReportCardAcknowledgement.php | 34 +- app/Models/Role.php | 88 +- app/Models/RoleNavItem.php | 21 +- app/Models/RolePermission.php | 39 +- app/Models/SchoolClass.php | 10 +- app/Models/SchoolYear.php | 3 + app/Models/ScoreComment.php | 35 +- app/Models/Section.php | 24 +- app/Models/SectionPlacementBatch.php | 3 + app/Models/SemesterScore.php | 73 +- app/Models/SemesterScoreSnapshot.php | 1 - app/Models/Setting.php | 13 +- app/Models/Staff.php | 39 +- app/Models/StaffAttendance.php | 147 +- app/Models/Stats.php | 11 +- app/Models/Student.php | 74 +- app/Models/StudentAllergy.php | 12 +- app/Models/StudentClass.php | 57 +- app/Models/StudentMedicalCondition.php | 12 +- app/Models/StudentPromotionRecord.php | 14 + app/Models/SubjectCurriculum.php | 28 +- app/Models/Supplier.php | 3 +- app/Models/Supply.php | 2 - app/Models/SupplyCategory.php | 3 +- app/Models/SupplyTransaction.php | 2 - app/Models/SupportRequest.php | 3 +- app/Models/Teacher.php | 4 +- app/Models/TeacherClass.php | 74 +- .../TeacherSubmissionNotificationHistory.php | 29 +- app/Models/User.php | 112 +- app/Models/UserNotification.php | 19 +- app/Models/UserRole.php | 35 +- app/Models/WhatsappGroupLink.php | 49 +- app/Models/WhatsappGroupMembership.php | 71 +- app/Models/WhatsappInviteLog.php | 48 +- app/Policies/NavItemPolicy.php | 2 +- app/Providers/AppServiceProvider.php | 48 +- app/Providers/EventServiceProvider.php | 60 +- .../CompetitionWinnersAdminService.php | 7 +- .../CompetitionWinnersDomain.php | 2 + .../AdminNotificationSubjectService.php | 15 +- .../AdminNotificationUserService.php | 4 +- .../AdminPrintRecipientService.php | 15 +- .../AdministratorAbsenceService.php | 62 +- .../AdministratorDashboardService.php | 5 +- .../AdministratorEnrollmentEventService.php | 7 +- .../AdministratorEnrollmentQueryService.php | 21 +- .../AdministratorEnrollmentRefundService.php | 6 +- .../AdministratorEnrollmentService.php | 3 +- .../AdministratorEnrollmentStatusService.php | 22 +- .../AdministratorMetricsService.php | 5 +- .../AdministratorNotificationService.php | 5 +- .../AdministratorSharedService.php | 8 +- .../AdministratorTeacherSubmissionService.php | 3 +- .../AdministratorUserSearchService.php | 34 +- .../TeacherSubmissionNotificationService.php | 37 +- .../TeacherSubmissionReportService.php | 15 +- .../TeacherSubmissionSupportService.php | 11 +- .../Trophy/TrophyReportService.php | 4 +- app/Services/ApplicationUrlService.php | 9 +- .../AssignmentDataLoaderService.php | 2 +- .../Assignment/AssignmentSectionService.php | 1 + app/Services/Assignment/AssignmentService.php | 20 +- app/Services/AssignmentService.php | 4 +- .../AttendanceAutoPublishJobService.php | 5 +- .../AttendanceAutoPublishService.php | 5 +- .../Attendance/AttendanceCommentService.php | 4 +- .../AttendanceConsequenceService.php | 33 +- .../AttendanceDailySummaryService.php | 13 +- .../Attendance/AttendancePolicyService.php | 13 +- .../Attendance/AttendanceQueryService.php | 162 +- .../AttendanceRecordSyncService.php | 25 +- app/Services/Attendance/AttendanceService.php | 57 +- .../AttendanceSummaryRebuildService.php | 4 +- .../Attendance/LateSlipLogQueryService.php | 9 +- .../Attendance/SemesterRangeService.php | 6 +- .../Attendance/StaffAttendanceService.php | 134 +- .../StudentAttendanceWriterService.php | 28 +- .../TeacherAttendanceSubmissionService.php | 44 +- .../AttendanceManagementService.php | 122 +- .../AttendanceCaseQueryService.php | 79 +- .../AttendanceCommunicationSupportService.php | 13 +- .../AttendanceEmailComposerService.php | 98 +- .../AttendanceMailerService.php | 2 +- .../AttendanceNotificationLogService.php | 44 +- .../AttendanceNotificationWorkflowService.php | 162 +- .../AttendanceParentLookupService.php | 68 +- .../AttendancePendingViolationService.php | 115 +- .../AttendanceTrackingService.php | 9 +- ...endanceViolationStudentResolverService.php | 103 +- .../DefaultAttendanceMailerService.php | 25 +- .../ViolationRuleEngineService.php | 259 +- app/Services/Auth/ApiLoginSecurityService.php | 4 +- app/Services/Auth/AuthSessionService.php | 6 +- app/Services/Auth/PermissionCheckService.php | 2 +- .../Auth/RegistrationCaptchaService.php | 3 +- .../Auth/RegistrationFormatterService.php | 10 +- app/Services/Auth/RegistrationService.php | 25 +- app/Services/Auth/UserRoleService.php | 8 +- app/Services/BadgeScan/BadgeScanService.php | 3 +- app/Services/Badges/BadgeFormDataService.php | 35 +- app/Services/Badges/BadgePdfService.php | 123 +- app/Services/Badges/BadgePrintLogService.php | 7 +- app/Services/Badges/BadgeService.php | 62 +- .../Badges/BadgeStudentLookupService.php | 13 +- app/Services/Badges/BadgeTextFormatter.php | 13 +- .../Badges/BadgeUserLookupService.php | 51 +- .../Billing/BalanceCalculationService.php | 11 +- app/Services/Billing/BillingTotalsService.php | 2 +- app/Services/Billing/ChargeService.php | 25 +- .../BroadcastEmailComposerService.php | 3 +- .../BroadcastEmailDispatchService.php | 3 +- .../BroadcastEmailImageService.php | 8 +- .../BroadcastEmailRecipientService.php | 1 + .../BroadcastEmailSenderOptionsService.php | 7 +- .../Certificates/CertificateAdminService.php | 2 +- .../Certificates/CertificatePdfService.php | 27 +- ...lassPreparationAdjustmentWriterService.php | 3 +- .../ClassPreparationCalculatorService.php | 3 + .../ClassPreparationContextService.php | 1 + .../ClassPreparationInventoryService.php | 3 +- .../ClassPreparationLogService.php | 1 + .../ClassPreparationPrintService.php | 6 +- .../ClassPreparationRosterService.php | 1 + .../ClassPreparationService.php | 22 +- .../ClassProgressAttachmentService.php | 15 +- .../ClassProgressMetaService.php | 2 +- .../ClassProgressMutationService.php | 15 +- .../ClassProgressQueryService.php | 20 +- .../ClassProgressRuleService.php | 2 + .../ClassSections/ClassAttendanceService.php | 2 +- .../ClassSectionQueryService.php | 14 +- .../CommunicationFamilyService.php | 2 +- .../Communication/CommunicationLogService.php | 2 +- .../CommunicationPreviewService.php | 10 +- .../CommunicationSendService.php | 4 +- .../CommunicationStudentService.php | 4 +- .../CommunicationTemplateService.php | 4 +- .../CompetitionScoresQueryService.php | 4 +- .../CompetitionScoresRosterService.php | 3 +- .../CompetitionScoresSaveService.php | 3 +- .../Dashboard/DashboardRouteService.php | 5 +- .../Discounts/DiscountApplyService.php | 13 +- .../Discounts/DiscountInvoiceService.php | 22 +- .../Discounts/DiscountVoucherService.php | 1 + app/Services/Docs/ApiDocsService.php | 3 +- app/Services/Docs/DocsCatalogService.php | 6 +- app/Services/Docs/OpenApiRouteExporter.php | 18 +- app/Services/Email/EmailAttachmentService.php | 6 +- app/Services/Email/EmailDispatchService.php | 20 +- app/Services/Email/EmailExtractorService.php | 4 +- app/Services/Email/EmailProfileService.php | 2 + .../Email/EmailSenderOptionsService.php | 6 +- app/Services/EmailService.php | 12 +- .../EmergencyContactCrudService.php | 3 +- .../EmergencyContactDirectoryService.php | 11 +- .../Events/EventChargeQueryService.php | 9 +- app/Services/Events/EventChargeService.php | 7 +- app/Services/Events/EventListService.php | 12 +- .../Events/EventManagementService.php | 15 +- .../Events/EventStudentChargeService.php | 2 +- app/Services/Exams/ExamDraftAdminService.php | 27 +- app/Services/Exams/ExamDraftConfigService.php | 4 + app/Services/Exams/ExamDraftFileService.php | 39 +- .../Exams/ExamDraftTeacherService.php | 10 +- .../Expenses/ExpenseReceiptService.php | 5 +- app/Services/Expenses/ExpenseStaffService.php | 7 +- app/Services/Expenses/ExpenseWriteService.php | 1 + .../ExtraChargesChargeService.php | 29 +- .../ExtraChargesInvoiceService.php | 6 +- .../ExtraCharges/ExtraChargesMetaService.php | 6 +- .../ExtraChargesParentService.php | 9 +- .../Families/FamilyMutationService.php | 21 +- .../Families/FamilyNotificationService.php | 4 +- app/Services/Families/FamilyQueryService.php | 23 +- app/Services/FeeCalculationService.php | 4 +- app/Services/Fees/FeeGradeService.php | 1 + .../Fees/FeeRefundCalculatorService.php | 21 +- app/Services/Fees/FeeRefundDetailService.php | 23 +- app/Services/Fees/FeeStudentFeeService.php | 12 +- .../Fees/TuitionCalculationService.php | 10 +- .../Files/ExamDraftDownloadNameService.php | 7 +- app/Services/Files/FileServeService.php | 14 +- .../Finance/FinanceNotificationLogService.php | 21 +- .../Finance/FinancialChartService.php | 7 +- .../Finance/FinancialPaymentService.php | 8 +- .../Finance/FinancialPdfReportService.php | 14 +- .../Finance/FinancialReportService.php | 9 +- .../Finance/FinancialSchoolYearService.php | 2 +- .../Finance/FinancialSummaryService.php | 88 +- .../Finance/FinancialUnpaidParentsService.php | 20 +- .../Finance/InstallmentPlanService.php | 33 +- .../Finance/ParentPaymentFollowUpService.php | 140 +- .../PriorYearBalanceCarryforwardService.php | 76 +- .../StakeholderFinancialAnalysisService.php | 83 +- .../Frontend/ContactSubmissionService.php | 15 +- .../LandingPageParentDashboardService.php | 3 +- .../Frontend/LandingPageRoleService.php | 4 +- app/Services/Frontend/LandingPageService.php | 7 +- .../LandingPageTeacherSummaryService.php | 60 +- app/Services/Frontend/ProfileIconService.php | 4 +- app/Services/Frontend/StaticPageService.php | 4 +- .../Grading/BelowSixtyEmailService.php | 14 +- .../Grading/GradingBelowSixtyService.php | 118 +- app/Services/Grading/GradingLockService.php | 16 +- .../Grading/GradingOverviewService.php | 19 +- .../Grading/GradingPlacementService.php | 23 +- .../Grading/GradingRefreshService.php | 6 +- app/Services/Grading/GradingScoreService.php | 32 +- .../HomeworkTrackingCalendarService.php | 4 +- .../Grading/HomeworkTrackingService.php | 26 +- .../Grading/Policy/GradingPolicyResolver.php | 6 +- .../StrongCategoryAverageCalculator.php | 3 + .../GradebookFinalizationValidator.php | 2 +- .../Validation/ScoreValueValidator.php | 3 +- .../Incidents/CurrentIncidentService.php | 20 +- .../Incidents/IncidentAnalysisService.php | 9 +- .../Incidents/IncidentHistoryService.php | 8 +- .../Incidents/IncidentLookupService.php | 7 +- .../Inventory/InventoryCategoryService.php | 16 +- .../Inventory/InventoryItemService.php | 39 +- .../Inventory/InventoryMovementService.php | 55 +- .../Inventory/InventorySummaryService.php | 14 +- .../InventoryTeacherDistributionService.php | 61 +- app/Services/Inventory/SupplierService.php | 19 +- .../Inventory/SupplyCategoryService.php | 13 +- .../Invoices/InvoiceConfigService.php | 3 +- .../Invoices/InvoiceGenerationService.php | 21 +- app/Services/Invoices/InvoiceGradeService.php | 10 +- .../Invoices/InvoiceManagementService.php | 17 +- .../Invoices/InvoicePaymentService.php | 12 +- app/Services/Invoices/InvoicePdfService.php | 55 +- app/Services/Invoices/InvoiceService.php | 3 +- .../Invoices/InvoiceTuitionService.php | 6 +- .../Messaging/MessageCommandService.php | 17 +- .../Messaging/MessageQueryService.php | 11 +- .../Messaging/MessageRecipientService.php | 4 +- app/Services/Navigation/NavBuilderService.php | 16 +- app/Services/Navigation/NavbarService.php | 4 +- .../NotificationManagementService.php | 4 +- .../Notifications/NotificationReadService.php | 2 +- .../NotificationRecipientService.php | 1 + .../Notifications/NotificationSendService.php | 7 +- .../NotificationUserListService.php | 4 +- .../AuthorizedUsersManagementService.php | 3 +- .../ParentAttendanceReportCalendarService.php | 14 +- .../Parents/ParentAttendanceReportService.php | 65 +- .../Parents/ParentAttendanceService.php | 4 +- .../Parents/ParentEmergencyContactService.php | 3 +- .../Parents/ParentEnrollmentService.php | 14 +- .../ParentEventParticipationService.php | 8 +- app/Services/Parents/ParentProfileService.php | 1 + .../Parents/ParentProgressQueryService.php | 11 +- .../Parents/ParentRegistrationService.php | 10 +- .../Parents/ParentReportCardService.php | 9 +- .../Parents/PrimaryParentUserResolver.php | 8 +- .../PaymentEnrollmentEventService.php | 9 +- .../Payments/PaymentEventChargesService.php | 8 +- .../Payments/PaymentManualService.php | 67 +- .../Payments/PaymentMissedCheckService.php | 9 +- .../PaymentNotificationDispatchService.php | 2 +- .../Payments/PaymentNotificationService.php | 34 +- .../PaymentTestNotificationService.php | 24 +- .../Payments/PaymentTransactionService.php | 2 +- .../Payments/PaypalPaymentService.php | 44 +- .../Payments/PaypalPaymentSyncService.php | 27 +- .../Payments/PaypalTransactionsService.php | 20 +- app/Services/Policy/PolicyContentService.php | 7 +- .../Preferences/PreferencesCommandService.php | 5 +- .../Preferences/PreferencesOptionsService.php | 6 +- .../Preferences/PreferencesQueryService.php | 14 +- .../PrintRequestsPortalService.php | 7 +- .../Promotions/LevelProgressionService.php | 27 +- .../BalancedSectionPlacementService.php | 3 + .../Placement/PlacementPoolBuilder.php | 18 +- .../PromotionSectionCapacityService.php | 3 +- .../SectionPlacementPreviewService.php | 17 +- .../PromotionEligibilityService.php | 27 +- .../Promotions/PromotionEnrollmentService.php | 31 +- .../Promotions/PromotionQueryService.php | 34 +- .../Promotions/PromotionReminderService.php | 18 +- .../Promotions/PromotionStatusService.php | 13 +- .../PublicWinnersPortalService.php | 6 +- .../PurchaseOrderCreateService.php | 2 +- .../PurchaseOrderQueryService.php | 2 +- .../PurchaseOrderReceiveService.php | 12 +- .../PurchaseOrderStatusService.php | 2 +- .../Refunds/RefundDecisionService.php | 6 +- .../RefundInvoiceAdjustmentService.php | 6 +- .../Refunds/RefundOverpaymentService.php | 10 +- app/Services/Refunds/RefundPayoutService.php | 5 +- app/Services/Refunds/RefundPolicyService.php | 13 +- app/Services/Refunds/RefundQueryService.php | 18 +- app/Services/Refunds/RefundRequestService.php | 12 +- .../ReimbursementBatchAssignmentService.php | 16 +- .../ReimbursementBatchService.php | 16 +- .../ReimbursementDonationService.php | 6 +- .../ReimbursementEmailService.php | 90 +- .../ReimbursementExportService.php | 43 +- .../ReimbursementFileService.php | 27 +- .../ReimbursementQueryService.php | 61 +- .../ReimbursementRecipientService.php | 14 +- .../Reports/ReportCards/ReportCardService.php | 152 +- .../Reports/SlipPrinterConfigService.php | 3 +- .../Reports/SlipPrinterFormatterService.php | 22 +- .../Reports/SlipPrinterPdfService.php | 8 +- app/Services/Reports/SlipPrinterService.php | 8 +- .../Reports/Stickers/StickerPresetService.php | 3 +- .../Reports/Stickers/StickerPrintService.php | 19 +- .../Reports/Stickers/StickerQueryService.php | 7 +- app/Services/Roles/PermissionCrudService.php | 7 +- app/Services/Roles/RoleAssignmentService.php | 9 +- app/Services/Roles/RoleCrudService.php | 13 +- app/Services/Roles/RoleDashboardService.php | 2 +- app/Services/Roles/RolePermissionService.php | 14 +- app/Services/Roles/RoleQueryService.php | 24 +- app/Services/Roles/RoleStaffService.php | 16 +- app/Services/Roles/RoleSwitchService.php | 5 +- app/Services/School/AccountEventService.php | 44 +- .../School/EnrollmentEventService.php | 42 +- app/Services/School/PaymentEventService.php | 35 +- app/Services/SchoolIdService.php | 6 +- .../SchoolIds/SchoolIdAssignmentService.php | 4 +- .../SchoolIds/SchoolIdGenerationService.php | 4 +- .../SchoolYears/SchoolYearClosureService.php | 52 +- .../SchoolYears/SchoolYearContextService.php | 12 +- .../SchoolYears/SchoolYearResolver.php | 2 +- .../SchoolYears/SchoolYearWriteGuard.php | 2 +- app/Services/Scores/AttendanceCalculator.php | 24 +- app/Services/Scores/ExamScoreService.php | 18 +- app/Services/Scores/HomeworkCalculator.php | 5 +- app/Services/Scores/HomeworkScoreService.php | 28 +- .../Scores/ParticipationScoreService.php | 18 +- app/Services/Scores/ProjectCalculator.php | 5 +- app/Services/Scores/ProjectScoreService.php | 25 +- app/Services/Scores/QuizCalculator.php | 2 +- app/Services/Scores/QuizScoreService.php | 26 +- app/Services/Scores/ScoreCommentService.php | 37 +- app/Services/Scores/ScoreDashboardService.php | 17 +- .../Scores/ScorePredictorDataService.php | 4 +- .../Scores/ScorePredictorRiskService.php | 25 +- app/Services/Scores/ScorePredictorService.php | 3 +- app/Services/Scores/ScoreTermService.php | 1 + app/Services/Scores/SemesterScoreService.php | 48 +- app/Services/Security/IpBanCommandService.php | 8 +- app/Services/Security/IpBanQueryService.php | 5 +- app/Services/SemesterRangeService.php | 15 +- .../Settings/ConfigurationService.php | 4 +- .../SchoolCalendarFormatterService.php | 20 +- .../SchoolCalendarMeetingService.php | 6 +- .../SchoolCalendarMutationService.php | 19 +- .../SchoolCalendarNotificationService.php | 26 +- .../SchoolCalendarQueryService.php | 5 +- app/Services/Settings/SettingsService.php | 2 +- app/Services/Staff/StaffCommandService.php | 14 +- app/Services/Staff/StaffQueryService.php | 13 +- .../Staff/StaffTimeOffLinkService.php | 13 +- .../Students/StudentAssignmentService.php | 21 +- .../StudentAutoDistributionService.php | 6 +- .../Students/StudentDirectoryService.php | 13 +- .../Students/StudentProfileService.php | 14 +- .../Students/StudentScoreCardService.php | 9 +- .../Students/StudentStatusService.php | 10 +- .../Subjects/SubjectCurriculumService.php | 4 +- .../Support/ContactMessageService.php | 12 +- .../Support/SupportRequestService.php | 15 +- .../System/CleanupSchedulerService.php | 2 +- app/Services/System/ConfigUpdateService.php | 24 +- app/Services/System/HealthCheckService.php | 12 +- app/Services/System/TimeService.php | 6 +- .../Teachers/TeacherAbsenceService.php | 63 +- .../Teachers/TeacherAssignmentService.php | 16 +- .../Teachers/TeacherDashboardService.php | 20 +- app/Services/Ui/UiStyleService.php | 10 +- .../Users/DeleteUnverifiedUserService.php | 8 +- .../Users/InactiveUserCleanupService.php | 1 + app/Services/Users/UserEventService.php | 11 +- app/Services/Users/UserListService.php | 8 +- app/Services/Users/UserManagementService.php | 22 +- .../Whatsapp/WhatsappContactService.php | 40 +- .../Whatsapp/WhatsappContextService.php | 4 +- .../Whatsapp/WhatsappInviteBundleService.php | 47 +- .../Whatsapp/WhatsappInviteEmailService.php | 59 +- .../WhatsappInviteNotificationService.php | 23 +- .../Whatsapp/WhatsappInviteService.php | 13 +- app/Services/Whatsapp/WhatsappLinkService.php | 23 +- .../Whatsapp/WhatsappMembershipService.php | 7 +- app/Support/MailHtml.php | 10 +- app/Support/SessionTimeoutConfig.php | 2 + app/ThirdParty/fpdf/font/courier.php | 9 +- app/ThirdParty/fpdf/font/courierb.php | 9 +- app/ThirdParty/fpdf/font/courierbi.php | 9 +- app/ThirdParty/fpdf/font/courieri.php | 9 +- app/ThirdParty/fpdf/font/helvetica.php | 30 +- app/ThirdParty/fpdf/font/helveticab.php | 30 +- app/ThirdParty/fpdf/font/helveticabi.php | 30 +- app/ThirdParty/fpdf/font/helveticai.php | 30 +- app/ThirdParty/fpdf/font/symbol.php | 30 +- app/ThirdParty/fpdf/font/times.php | 30 +- app/ThirdParty/fpdf/font/timesb.php | 30 +- app/ThirdParty/fpdf/font/timesbi.php | 30 +- app/ThirdParty/fpdf/font/timesi.php | 30 +- app/ThirdParty/fpdf/font/zapfdingbats.php | 30 +- app/ThirdParty/fpdf/fpdf.php | 3869 +++++++++-------- bootstrap/app.php | 62 +- bootstrap/providers.php | 7 +- config/auth.php | 4 +- config/jwt.php | 7 +- config/sanctum.php | 9 +- database/factories/ClassSectionFactory.php | 4 +- database/factories/StudentFactory.php | 6 +- database/factories/UserFactory.php | 4 +- .../0001_01_01_000000_create_users_table.php | 1 - .../0001_01_01_000001_create_cache_table.php | 1 - .../0001_01_01_000002_create_jobs_table.php | 2 - .../2026_02_23_204001_create_users_table.php | 7 +- ...026_02_23_204002_create_students_table.php | 10 +- ...026_02_23_204030_create_expenses_table.php | 10 +- ...3_204035_create_current_incident_table.php | 7 +- ...026_02_23_204036_create_incident_table.php | 10 +- ...2_23_204037_create_grading_locks_table.php | 10 +- ...026_02_23_204038_create_homework_table.php | 8 +- ...4039_create_inventory_categories_table.php | 10 +- ...23_204040_create_inventory_items_table.php | 12 +- ...04041_create_inventory_movements_table.php | 12 +- ...2_23_204042_create_invoice_event_table.php | 10 +- ...026_02_23_204043_create_invoices_table.php | 10 +- ...044_create_invoice_students_list_table.php | 10 +- ..._02_23_204045_create_ip_attempts_table.php | 10 +- ..._23_204046_create_late_slip_logs_table.php | 8 +- ..._23_204047_create_login_activity_table.php | 10 +- ...23_204048_create_manual_payments_table.php | 10 +- ...026_02_23_204049_create_messages_table.php | 10 +- ...02_23_204050_create_midterm_exam_table.php | 8 +- ...1_create_missing_score_overrides_table.php | 10 +- ...26_02_23_204052_create_nav_items_table.php | 10 +- ...2_23_204053_create_notifications_table.php | 10 +- ...create_parent_attendance_reports_table.php | 10 +- ..._create_parent_meeting_schedules_table.php | 10 +- ...2026_02_23_204056_create_parents_table.php | 10 +- ...4057_create_parent_notifications_table.php | 10 +- ...2_23_204058_create_participation_table.php | 10 +- ...23_204059_create_password_resets_table.php | 11 +- ...0_create_password_reset_requests_table.php | 10 +- ...100_create_reimbursement_batches_table.php | 12 +- ...23_204101_create_paypal_payments_table.php | 10 +- ..._23_204101_create_reimbursements_table.php | 10 +- ...2_23_204102_create_payment_error_table.php | 10 +- ...026_02_23_204103_create_payments_table.php | 10 +- ...create_payment_notification_logs_table.php | 10 +- ...4105_create_payment_transactions_table.php | 10 +- ...04106_create_paypal_transactions_table.php | 11 +- ..._02_23_204107_create_permissions_table.php | 10 +- ..._204108_create_placement_batches_table.php | 10 +- ...3_204109_create_placement_levels_table.php | 12 +- ...3_204110_create_placement_scores_table.php | 10 +- ...3_204111_create_user_preferences_table.php | 10 +- ..._23_204112_create_print_requests_table.php | 13 +- ...2026_02_23_204113_create_project_table.php | 10 +- ...23_204114_create_promotion_queue_table.php | 10 +- .../2026_02_23_204117_create_quiz_table.php | 10 +- ...2026_02_23_204118_create_refunds_table.php | 10 +- ..._reimbursement_batch_admin_files_table.php | 10 +- .../2026_02_23_204123_create_roles_table.php | 10 +- ..._23_204124_create_role_nav_items_table.php | 10 +- ...3_204125_create_role_permissions_table.php | 10 +- ..._23_204126_create_score_comments_table.php | 10 +- ...026_02_23_204127_create_sections_table.php | 10 +- ...23_204128_create_semester_scores_table.php | 10 +- ...026_02_23_204129_create_settings_table.php | 10 +- ...3_204130_create_staff_attendance_table.php | 12 +- .../2026_02_23_204131_create_staff_table.php | 10 +- ..._204133_create_student_allergies_table.php | 12 +- ...2_23_204134_create_student_class_table.php | 10 +- ...reate_student_medical_conditions_table.php | 12 +- ..._create_subject_curriculum_items_table.php | 10 +- ...2_23_204138_create_teacher_class_table.php | 10 +- ..._submission_notification_history_table.php | 10 +- ...204142_create_user_notifications_table.php | 10 +- ...6_02_23_204143_create_user_roles_table.php | 10 +- ...4144_create_whatsapp_group_links_table.php | 10 +- ...reate_whatsapp_group_memberships_table.php | 8 +- ...4146_create_whatsapp_invites_log_table.php | 10 +- ...50_create_class_progress_reports_table.php | 10 +- ...ate_report_card_acknowledgements_table.php | 7 +- ...reate_class_progress_attachments_table.php | 12 +- ...create_reimbursement_batch_items_table.php | 12 +- ...900001_create_additional_charges_table.php | 10 +- ...eate_admin_notification_subjects_table.php | 10 +- ...eate_attendance_comment_template_table.php | 10 +- ...23_900004_create_attendance_data_table.php | 10 +- ..._23_900005_create_attendance_day_table.php | 10 +- ..._900006_create_attendance_record_table.php | 10 +- ...00007_create_attendance_tracking_table.php | 12 +- ...3_900008_create_authorized_users_table.php | 10 +- ...3_900009_create_badge_print_logs_table.php | 10 +- ...23_900010_create_calendar_events_table.php | 11 +- ...026_02_23_900011_create_chapters_table.php | 10 +- ...02_23_900012_create_classSection_table.php | 10 +- ...13_create_class_prep_adjustments_table.php | 10 +- ...014_create_class_preparation_log_table.php | 10 +- ...2026_02_23_900017_create_classes_table.php | 11 +- ...create_competition_class_winners_table.php | 10 +- ...900019_create_competition_scores_table.php | 10 +- ...00020_create_competition_winners_table.php | 10 +- ...02_23_900021_create_competitions_table.php | 10 +- ...2_23_900022_create_configuration_table.php | 10 +- ...26_02_23_900023_create_contactus_table.php | 10 +- ...02_23_900024_create_current_flag_table.php | 10 +- ...23_900025_create_discount_usages_table.php | 10 +- ..._900026_create_discount_vouchers_table.php | 10 +- ...reate_early_dismissal_signatures_table.php | 10 +- ...23_900028_create_email_templates_table.php | 10 +- ...900029_create_emergency_contacts_table.php | 10 +- ..._02_23_900030_create_enrollments_table.php | 10 +- ...2_23_900031_create_event_charges_table.php | 8 +- .../2026_02_23_900032_create_events_table.php | 10 +- ..._02_23_900033_create_exam_drafts_table.php | 12 +- ..._create_examquestiondistribution_table.php | 10 +- ...2_23_900035_create_examquestions_table.php | 10 +- .../2026_02_23_900036_create_exams_table.php | 10 +- ...026_02_23_900038_create_families_table.php | 11 +- ..._900039_create_family_comm_prefs_table.php | 12 +- ...3_900040_create_family_guardians_table.php | 12 +- ...23_900041_create_family_students_table.php | 12 +- ...6_02_23_900042_create_final_exam_table.php | 10 +- ..._02_23_900043_create_final_score_table.php | 10 +- ...00044_create_longanswerquestions_table.php | 10 +- ...6_create_notification_recipients_table.php | 10 +- ..._02_23_900047_create_preferences_table.php | 10 +- ...02_23_900048_create_qcmquestions_table.php | 10 +- ...26_02_23_900049_create_questions_table.php | 10 +- ...900050_create_questionstruefalse_table.php | 10 +- ...2_23_900051_create_questiontypes_table.php | 10 +- ...026_02_23_900052_create_scan_log_table.php | 10 +- ...0053_create_shortanswerquestions_table.php | 10 +- ...3_900054_create_support_requests_table.php | 10 +- ...5_create_teacher_attendance_data_table.php | 10 +- ...900056_create_communication_logs_table.php | 8 +- ...9_070060_add_fields_to_suppliers_table.php | 8 +- ...20000_create_certificate_records_table.php | 7 +- .../2026_06_04_120010_create_flag_table.php | 7 +- ...000_create_below_sixty_decisions_table.php | 7 +- ..._130010_create_student_decisions_table.php | 7 +- ...lifecycle_and_section_placement_tables.php | 12 +- ...00000_add_badge_scanning_list_nav_item.php | 8 +- ..._strong_grading_backward_compatibility.php | 22 +- ...04_230000_add_finance_lifecycle_tables.php | 18 +- ...0000_create_school_year_closure_tables.php | 13 +- ...5_000000_sort_nav_items_alphabetically.php | 2 +- ...entory_item_id_to_purchase_order_items.php | 2 +- routes/api.php | 221 +- tests/Concerns/AssertsE2EApiResponses.php | 6 +- tests/Concerns/CreatesApiTestUsers.php | 2 +- .../Concerns/SeedsAdministratorApiConfig.php | 2 +- tests/Concerns/SeedsE2ETestFixtures.php | 16 +- .../ApiAuthenticationAndAuthorizationTest.php | 5 +- .../Feature/Api/ApiPreferencesFeatureTest.php | 1 - tests/Feature/Api/ApiPublicEndpointsTest.php | 2 +- .../Api/ApiRolePermissionFeatureTest.php | 2 - tests/Feature/Api/ApiRouteContractTest.php | 10 +- .../Api/ApiUseCaseCoverageMatrixTest.php | 10 +- .../Api/ApiUserManagementFeatureTest.php | 1 - .../AdministratorAbsenceControllerTest.php | 2 +- .../AdministratorEnrollmentControllerTest.php | 2 +- ...stratorEnrollmentUpdateStatusesApiTest.php | 3 +- ...dministratorNotificationControllerTest.php | 2 +- ...stratorTeacherSubmissionControllerTest.php | 2 +- ...istratorTeacherSubmissionReportApiTest.php | 2 +- .../EmergencyContactControllerTest.php | 10 +- .../AssignmentApiControllerTest.php | 8 +- tests/Feature/Api/V1/AssignmentApiTest.php | 98 +- .../Attendance/LateSlipLogsControllerTest.php | 2 +- .../V1/AttendanceCommentTemplateApiTest.php | 12 +- .../AttendanceTrackingControllerTest.php | 3 +- .../Api/V1/Auth/IpBanControllerTest.php | 2 +- tests/Feature/Api/V1/BadgeControllerTest.php | 2 +- .../BroadcastEmailControllerTest.php | 7 +- .../ClassProgressControllerTest.php | 10 +- .../V1/Classes/ClassSectionControllerTest.php | 2 +- .../CommunicationControllerTest.php | 4 +- .../V1/Family/FamilyAdminControllerTest.php | 8 +- .../Api/V1/Family/FamilyControllerTest.php | 6 +- .../V1/Finance/EventChargeControllerTest.php | 12 +- .../V1/Frontend/FrontendControllerTest.php | 2 +- .../V1/Frontend/InfoIconControllerTest.php | 1 - .../V1/Frontend/LandingPageControllerTest.php | 2 +- .../Api/V1/Frontend/PageControllerTest.php | 4 +- ...demicAttendanceFullSurfaceContractTest.php | 76 +- .../ApiAccountStatusLifecycleContractTest.php | 2 +- ...AdminOperationsFullSurfaceContractTest.php | 20 +- ...achmentUploadContentSafetyContractTest.php | 2 +- ...AdministrativeTraceabilityContractTest.php | 4 +- ...thenticationLifecycleDepthContractTest.php | 28 +- ...orizationCacheInvalidationContractTest.php | 4 +- .../ApiBatch11RouteRegressionNetTest.php | 4 +- ...Batch12AttachmentLifecycleContractTest.php | 3 +- ...PromotionRollbackIntegrityContractTest.php | 8 +- ...merationAndIdentityPrivacyContractTest.php | 10 +- ...DelegationApprovalWorkflowContractTest.php | 8 +- ...14ApiConsumerContractCompatibilityTest.php | 16 +- ...endanceDeviceAndKioskAbuseContractTest.php | 8 +- ...AuditTrailTamperResistanceContractTest.php | 8 +- ...h14BulkAssignmentCollisionContractTest.php | 12 +- ...14CrossModuleInvariantRiskRegisterTest.php | 6 +- ...4DataLifecycleArchivePurgeContractTest.php | 11 +- ...conciliationAndRefundAbuseContractTest.php | 14 +- ...ificationTemplateInjectionContractTest.php | 16 +- ...atch14PrintExportTamperingContractTest.php | 8 +- .../ApiBatch14RegressionMegaSweepTest.php | 6 +- ...lationshipOwnershipMatrixExpansionTest.php | 12 +- ...4SessionCookieCsrfBoundaryContractTest.php | 4 +- ...h14StoragePathAndSignedUrlContractTest.php | 10 +- ...personationSessionBoundaryContractTest.php | 2 - ...TokenRotationAndRevocationContractTest.php | 2 - ...kgroundCommandAndSchedulerContractTest.php | 2 - ...rivacyAndDataSubjectRightsContractTest.php | 2 - ...ossSchoolIdentityCollisionContractTest.php | 2 - ...aResidencyBackupAndRestoreContractTest.php | 2 - ...AndNotificationConsistencyContractTest.php | 2 - ...reFlagExperimentAndRolloutContractTest.php | 2 - ...questAuthorizationCoverageContractTest.php | 2 - ...rchAndDirectoryEnumerationContractTest.php | 2 - ...ontentDirectionAndEncodingContractTest.php | 2 - ...gAndSparseFieldsetSecurityContractTest.php | 2 - ...15RateLimitAndAbusePatternContractTest.php | 4 +- .../ApiBatch15RegressionUltraSweepTest.php | 2 - ...illdownAndAggregatePrivacyContractTest.php | 2 - ...15RouteParameterPoisoningExpansionTest.php | 4 +- ...ntSafetyIncidentEscalationContractTest.php | 2 - ...cPrerequisiteAndDependencyContractTest.php | 1 - ...ContractDiffAndCriticalKeyRegisterTest.php | 1 - ...ch17AttendanceTamperWindowContractTest.php | 1 - ...nicationRecipientExpansionContractTest.php | 1 - ...ineageAndSourceAttributionContractTest.php | 1 - ...7ExportColumnLevelSecurityContractTest.php | 1 - ...alIdAndNaturalKeyCollisionContractTest.php | 1 - ...alPeriodCloseAndAdjustmentContractTest.php | 1 - ...tch17InventoryAssetCustodyContractTest.php | 1 - ...iBatch17MultiGuardBoundaryContractTest.php | 1 - ...ailureErrorDetailRedactionContractTest.php | 1 - ...rResourceRateLimitIdentityContractTest.php | 1 - .../ApiBatch17RegressionOmegaSweepTest.php | 1 - ...RankingAndPermissionFilterContractTest.php | 1 - ...7StudentIdentityMergeSplitContractTest.php | 1 - ...h17ZeroTrustInternalHeaderContractTest.php | 1 - ...eAccountAndMachineIdentityContractTest.php | 1 - ...icalIdentifierImmutabilityContractTest.php | 1 - ...aitlistAndScheduleConflictContractTest.php | 1 - ...AndCommunicationComplianceContractTest.php | 1 - ...ossModuleBusinessInvariantRegisterTest.php | 1 - ...rtWatermarkAndAuditabilityContractTest.php | 1 - ...itlementAndLicenseBoundaryContractTest.php | 1 - ...FileVirusScanAndQuarantineContractTest.php | 1 - ...18FinanceChargebackDisputeContractTest.php | 1 - ...edRecordAndCascadeBoundaryContractTest.php | 1 - ...eBoundaryAfterRoleMutationContractTest.php | 1 - .../ApiBatch18RegressionFinalitySweepTest.php | 1 - ...throughAndRowLevelSecurityContractTest.php | 1 - ...lAndSafetyDataMinimizationContractTest.php | 1 - ...ralAuditAndTimezoneForgeryContractTest.php | 1 - ...rsonApprovalAndSegregationContractTest.php | 1 - ...okOrderingAndOutOfSequenceContractTest.php | 1 - ...h19AggregateDashboardScopeContractTest.php | 1 - ...tch19AppendOnlyAuditLedgerContractTest.php | 1 - ...nceExcuseDocumentLifecycleContractTest.php | 1 - ...ributeBasedAccessConditionContractTest.php | 1 - ...ch19BackupRestoreIsolationContractTest.php | 1 - ...ansferTranscriptContinuityContractTest.php | 1 - ...ounceAndSuppressionWebhookContractTest.php | 1 - ...rictionPickupAuthorizationContractTest.php | 1 - ...estedRelationshipRedactionContractTest.php | 1 - ...ationalAndPrivacyInvariantRegisterTest.php | 1 - ...aymentAllocationAndBalanceContractTest.php | 1 - ...ethodAndSettlementBoundaryContractTest.php | 1 - ...piBatch19RegressionContinuitySweepTest.php | 1 - ...h19ReportCacheInvalidationContractTest.php | 1 - ...edDeletedUserTokenBoundaryContractTest.php | 1 - ...eacherSubstitutionCoverageContractTest.php | 1 - ...aryUrlExpirationRevocationContractTest.php | 1 - ...dPartySyncCanonicalizationContractTest.php | 1 - ...0AdminAuditExportRedactionContractTest.php | 1 - ...eprecationAndCompatibilityContractTest.php | 1 - ...anceGeofenceAndDeviceTrustContractTest.php | 1 - ...awerManualPaymentIntegrityContractTest.php | 1 - ...CapacityLotteryAndWaitlistContractTest.php | 1 - ...20ContinuityComplianceRiskRegisterTest.php | 1 - ...taWarehouseAnalyticsExportContractTest.php | 1 - ...0EmergencyClosureBroadcastContractTest.php | 1 - ...0GradebookLockAndAmendmentContractTest.php | 1 - ...dRetentionAndPurgeConflictContractTest.php | 1 - ...ngualContentAndTranslationContractTest.php | 1 - ...ficationDeadLetterAndRetryContractTest.php | 1 - ...entDisputeAndCustodyReviewContractTest.php | 1 - ...ingAndTaxDocumentIntegrityContractTest.php | 1 - ...egressionContinuityComplianceSweepTest.php | 3 +- ...rshipRenewalAndEligibilityContractTest.php | 1 - ...SiblingFamilyDiscountAbuseContractTest.php | 1 - ...icMonitoringAndHealthProbeContractTest.php | 1 - ...portationPickupRouteSafetyContractTest.php | 1 - ...tch20WebhookSecretRotationContractTest.php | 1 - .../ApiBusinessRuleEdgeCaseContractTest.php | 6 +- ...piCalendarScheduleConflictContractTest.php | 2 +- ...entCompatibilityVersioningContractTest.php | 15 +- ...piConcurrencyAndStaleWriteContractTest.php | 2 +- ...piContractCoverageHeatmapExpansionTest.php | 6 +- .../ApiContractSnapshotSeedTest.php | 3 +- ...ossVersionRouteCompatibilityMatrixTest.php | 8 +- ...piCsvExcelFormulaInjectionContractTest.php | 2 +- ...piDataExportLeastPrivilegeContractTest.php | 4 +- .../ApiDataMinimizationByRoleContractTest.php | 4 +- ...ShapeBackwardCompatibilityContractTest.php | 8 +- ...atabaseTransactionRollbackContractTest.php | 2 +- ...piDeepAuthorizationIsolationMatrixTest.php | 8 +- ...ocumentationAndRouteParityContractTest.php | 9 +- ...leImportExportAndPrintableContractTest.php | 11 +- ...encyAndDuplicateSubmissionContractTest.php | 14 +- ...pPaginationSearchAndFilterContractTest.php | 15 +- ...epResponseShapeAndEnvelopeContractTest.php | 14 +- ...epStateTransitionInvariantContractTest.php | 7 +- .../ApiDeepValidationFailureContractTest.php | 14 +- ...piEmptyDatasetAndNullStateContractTest.php | 2 +- .../ApiEnumAndStateMachineContractTest.php | 6 +- ...ApiEtagConditionalMutationContractTest.php | 2 +- .../ApiFailureModeResilienceExpansionTest.php | 12 +- ...ApiFamilyPortalFullSurfaceContractTest.php | 26 +- ...eFlagAndConfigurationDriftContractTest.php | 2 +- ...yCommunicationsFullSurfaceContractTest.php | 26 +- .../ApiFrontendBackendContractParityTest.php | 14 +- .../ApiFullSurfaceEndToEndContractTest.php | 29 +- .../ApiHtmlJavaScriptEscapingContractTest.php | 2 +- ...tpSemanticsAndMethodSafetyContractTest.php | 8 +- ...iIdempotencyScopeAndReplayContractTest.php | 4 +- .../ApiImportTemplateAndCsvContractTest.php | 6 +- .../ApiMigrationSafeDefaultContractTest.php | 4 +- .../ApiMobileClientToleranceContractTest.php | 4 +- ...ificationMessagingDeliveryContractTest.php | 4 +- ...NullBooleanAndTypeCoercionContractTest.php | 2 +- ...piOpenRedirectAndReturnUrlContractTest.php | 4 +- ...perationalEndpointExposureContractTest.php | 2 +- ...ionalResiliencePayloadSizeContractTest.php | 10 +- ...piOwnerBoundaryAndPrivacyExpansionTest.php | 5 +- ...nationCursorAndOffsetAbuseContractTest.php | 2 +- ...lymorphicOwnershipBoundaryContractTest.php | 12 +- ...PrivacyRetentionAndErasureContractTest.php | 8 +- ...piPublicAndAuthFullSurfaceContractTest.php | 14 +- .../ApiQueryParameterFuzzingContractTest.php | 6 +- ...ApiQueueJobAndAsyncTriggerContractTest.php | 8 +- ...itTokenAndSessionHardeningContractTest.php | 4 +- ...ApiRelationshipCardinalityContractTest.php | 2 +- ...tMathAndFinancialInvariantContractTest.php | 8 +- .../ApiRequestHeaderSpoofingContractTest.php | 4 +- ...RoleWorkflowNegativeMatrixContractTest.php | 2 +- ...ApiRouteNamingVersioningAndHygieneTest.php | 3 +- .../ApiRouteParameterAbuseContractTest.php | 6 +- ...ApiRouteUseCaseCatalogCompletenessTest.php | 8 +- ...chemaKeyStabilityExpansionContractTest.php | 8 +- ...lCalendarAcademicBoundaryExpansionTest.php | 8 +- ...SearchIndexAndAutocompleteContractTest.php | 8 +- ...ngFieldSelectionAndIncludeContractTest.php | 2 +- ...olYearAndSemesterIsolationContractTest.php | 4 +- ...icodeNormalizationAndInputContractTest.php | 6 +- ...FetchSsrfAndCallbackSafetyContractTest.php | 2 +- .../ApiWriteAuditAndTimestampContractTest.php | 2 +- .../Support/FullSurfaceE2EContractCase.php | 11 +- .../V1/Incidents/IncidentControllerTest.php | 2 +- .../InventoryCategoriesControllerTest.php | 6 +- .../InventoryItemsControllerTest.php | 16 +- .../InventoryMovementsControllerTest.php | 4 +- .../V1/Inventory/SuppliersControllerTest.php | 4 +- .../SupplyCategoriesControllerTest.php | 4 +- .../V1/Messaging/MessagesControllerTest.php | 8 +- .../Parents/AuthorizedUsersControllerTest.php | 8 +- .../ParentAttendanceReportControllerTest.php | 3 +- .../PrintRequestsWorkflowTest.php | 4 +- .../ParentPromotionControllerTest.php | 9 +- .../V1/Public/PublicWinnersControllerTest.php | 4 +- .../Api/V1/Reports/FilesControllerTest.php | 8 +- .../V1/Reports/ReportCardsControllerTest.php | 10 +- .../V1/Roles/RolePermissionControllerTest.php | 20 +- .../V1/Roles/RoleSwitcherControllerTest.php | 1 - .../V1/Settings/PreferencesControllerTest.php | 6 +- .../Settings/SchoolCalendarControllerTest.php | 12 +- .../V1/Settings/SettingsControllerTest.php | 3 +- .../Api/V1/Staff/StaffControllerTest.php | 6 +- .../Api/V1/Students/StudentControllerTest.php | 2 +- .../Api/V1/Support/SupportControllerTest.php | 2 +- .../Api/V1/Teachers/TeacherControllerTest.php | 2 +- tests/Feature/Api/V1/Ui/UiControllerTest.php | 2 +- .../Whatsapp/WhatsappInviteControllerTest.php | 3 +- .../Whatsapp/WhatsappLinkControllerTest.php | 6 +- .../ScenarioAFamilyEnrollmentTest.php | 6 +- .../ScenarioBSundaySchoolDayTest.php | 4 +- .../Workflows/ScenarioCAcademicTermTest.php | 4 +- .../Workflows/ScenarioDFamilyBillingTest.php | 4 +- .../V1/Workflows/ScenarioEFinanceEdgeTest.php | 18 +- .../ScenarioFTeacherSubmissionTest.php | 1 - .../V1/Workflows/ScenarioGPrintBadgeTest.php | 4 +- ...cenarioHSecurityUnauthorizedAccessTest.php | 2 +- ...cenarioIPublicAuthAndSystemUseCaseTest.php | 4 +- ...cenarioJCrossModuleApiUseCaseSmokeTest.php | 10 +- ...enarioKParentSelfServiceAndPrivacyTest.php | 10 +- ...ScenarioLTeacherAcademicOperationsTest.php | 14 +- ...tratorEnrollmentAndClassOperationsTest.php | 2 +- ...NCommunicationsAndSupportLifecycleTest.php | 4 +- ...oOInventoryFinanceAndAuditControlsTest.php | 12 +- ...ConfigurationAndCalendarGovernanceTest.php | 18 +- ...choolYearPromotionClosureLifecycleTest.php | 22 +- ...anceOperationsAndExceptionHandlingTest.php | 16 +- ...oringGradingAndReportCardLifecycleTest.php | 27 +- ...rintBadgesAndCertificatesLifecycleTest.php | 12 +- ...ymentRefundAndInstallmentLifecycleTest.php | 30 +- ...entReimbursementAndSupplyLifecycleTest.php | 10 +- ...ntrolRolesNavigationAndPreferencesTest.php | 12 +- ...oskScannerAndVerificationLifecycleTest.php | 8 +- ...AuthorizedUserAndWhatsappLifecycleTest.php | 16 +- ...yAliasesValidationAndCompatibilityTest.php | 36 +- .../StudentEnrollmentWorkflowTest.php | 9 +- tests/Feature/BulkUserRoleCreationTest.php | 2 +- .../RequirePermissionMiddlewareTest.php | 2 +- tests/Feature/Web/SchoolYearAdminPageTest.php | 4 +- tests/Unit/ApiLoginSecurityServiceTest.php | 6 +- tests/Unit/AuthSessionServiceTest.php | 2 +- ...ttendanceCommentTemplateControllerTest.php | 4 +- .../Unit/Grading/ScoreValueValidatorTest.php | 6 +- tests/Unit/Models/AdditionalChargeTest.php | 12 +- .../AttendanceCommentTemplateModelTest.php | 2 +- tests/Unit/Models/AttendanceDataTest.php | 8 +- .../Models/AttendanceEmailTemplateTest.php | 8 +- tests/Unit/Models/AttendanceRecordTest.php | 6 +- tests/Unit/Models/AttendanceTrackingTest.php | 6 +- tests/Unit/Models/AuthorizedUserTest.php | 6 +- tests/Unit/Models/BadgePrintLogTest.php | 6 +- tests/Unit/Models/CalendarTest.php | 6 +- tests/Unit/Models/ClassPrepAdjustmentTest.php | 6 +- tests/Unit/Models/ClassPreparationLogTest.php | 6 +- .../Models/ClassProgressAttachmentTest.php | 6 +- tests/Unit/Models/ClassProgressReportTest.php | 6 +- tests/Unit/Models/ClassSectionTest.php | 6 +- tests/Unit/Models/ClassTest.php | 6 +- tests/Unit/Models/CommunicationLogTest.php | 6 +- .../Models/CompetitionClassWinnerTest.php | 6 +- tests/Unit/Models/CompetitionScoreTest.php | 6 +- tests/Unit/Models/CompetitionTest.php | 8 +- tests/Unit/Models/CompetitionWinnerTest.php | 6 +- tests/Unit/Models/ConfigurationTest.php | 6 +- tests/Unit/Models/ContactUsTest.php | 6 +- tests/Unit/Models/CurrentFlagTest.php | 6 +- tests/Unit/Models/DiscountUsageTest.php | 6 +- tests/Unit/Models/DiscountVoucherTest.php | 6 +- .../Models/EarlyDismissalSignatureTest.php | 6 +- tests/Unit/Models/EmergencyContactTest.php | 6 +- tests/Unit/Models/EnrollmentTest.php | 6 +- tests/Unit/Models/EventChargesTest.php | 6 +- tests/Unit/Models/EventTest.php | 6 +- tests/Unit/Models/ExamDraftTest.php | 6 +- tests/Unit/Models/ExamTest.php | 6 +- tests/Unit/Models/ExpenseTest.php | 6 +- tests/Unit/Models/FamilyCommPrefTest.php | 6 +- tests/Unit/Models/FamilyGuardianTest.php | 6 +- tests/Unit/Models/FamilyStudentTest.php | 6 +- tests/Unit/Models/FamilyTest.php | 6 +- tests/Unit/Models/FinalExamTest.php | 6 +- tests/Unit/Models/FinalScoreTest.php | 6 +- tests/Unit/Models/GradingLockTest.php | 6 +- tests/Unit/Models/HomeworkTest.php | 6 +- tests/Unit/Models/IncidentTest.php | 6 +- tests/Unit/Models/InventoryCategoryTest.php | 6 +- tests/Unit/Models/InventoryItemTest.php | 6 +- tests/Unit/Models/InventoryMovementTest.php | 6 +- tests/Unit/Models/InvoiceEventTest.php | 6 +- tests/Unit/Models/InvoiceStudentListTest.php | 6 +- tests/Unit/Models/InvoiceTest.php | 6 +- tests/Unit/Models/IpAttemptTest.php | 6 +- tests/Unit/Models/LateSlipLogTest.php | 6 +- tests/Unit/Models/LoginActivityTest.php | 6 +- tests/Unit/Models/ManualPaymentTest.php | 6 +- tests/Unit/Models/MessageTest.php | 6 +- tests/Unit/Models/MidtermExamTest.php | 6 +- .../Unit/Models/MissingScoreOverrideTest.php | 6 +- tests/Unit/Models/NavItemTest.php | 8 +- tests/Unit/Models/NotificationTest.php | 8 +- .../Models/ParentAttendanceReportTest.php | 6 +- .../Unit/Models/ParentMeetingScheduleTest.php | 6 +- tests/Unit/Models/ParentNotificationTest.php | 6 +- tests/Unit/Models/ParentTest.php | 6 +- tests/Unit/Models/ParticipationTest.php | 6 +- .../Unit/Models/PasswordResetRequestTest.php | 6 +- tests/Unit/Models/PasswordResetTest.php | 6 +- tests/Unit/Models/PayPalPaymentTest.php | 6 +- tests/Unit/Models/PaymentErrorTest.php | 6 +- .../Models/PaymentNotificationLogTest.php | 6 +- tests/Unit/Models/PaymentTest.php | 6 +- tests/Unit/Models/PaymentTransactionTest.php | 6 +- tests/Unit/Models/PaypalTransactionTest.php | 6 +- tests/Unit/Models/PermissionTest.php | 6 +- tests/Unit/Models/PlacementBatchTest.php | 6 +- tests/Unit/Models/PlacementLevelTest.php | 6 +- tests/Unit/Models/PlacementScoreTest.php | 6 +- tests/Unit/Models/PreferencesTest.php | 6 +- tests/Unit/Models/PrintRequestTest.php | 6 +- tests/Unit/Models/ProjectTest.php | 6 +- tests/Unit/Models/PromotionQueueTest.php | 6 +- tests/Unit/Models/PurchaseOrderItemTest.php | 6 +- tests/Unit/Models/PurchaseOrderTest.php | 8 +- tests/Unit/Models/QuizTest.php | 6 +- tests/Unit/Models/RefundTest.php | 6 +- .../ReimbursementBatchAdminFileTest.php | 6 +- .../Models/ReimbursementBatchItemTest.php | 6 +- tests/Unit/Models/ReimbursementBatchTest.php | 6 +- tests/Unit/Models/ReimbursementTest.php | 6 +- tests/Unit/Models/RoleNavItemTest.php | 6 +- tests/Unit/Models/RolePermissionTest.php | 6 +- tests/Unit/Models/RoleTest.php | 6 +- tests/Unit/Models/ScoreCommentTest.php | 6 +- tests/Unit/Models/SectionTest.php | 6 +- tests/Unit/Models/SemesterScoreTest.php | 6 +- tests/Unit/Models/SettingsTest.php | 6 +- tests/Unit/Models/StaffAttendanceTest.php | 6 +- tests/Unit/Models/StaffTest.php | 6 +- tests/Unit/Models/StatsTest.php | 6 +- tests/Unit/Models/StudentAllergyTest.php | 6 +- tests/Unit/Models/StudentClassTest.php | 6 +- .../Models/StudentMedicalConditionTest.php | 6 +- tests/Unit/Models/StudentTest.php | 6 +- tests/Unit/Models/SubjectCurriculumTest.php | 6 +- tests/Unit/Models/TeacherClassTest.php | 6 +- ...acherSubmissionNotificationHistoryTest.php | 6 +- tests/Unit/Models/TeacherTest.php | 6 +- tests/Unit/Models/UserNotificationTest.php | 6 +- tests/Unit/Models/UserRoleTest.php | 6 +- tests/Unit/Models/UserTest.php | 8 +- tests/Unit/Models/WhatsappGroupLinkTest.php | 6 +- .../Models/WhatsappGroupMembershipTest.php | 6 +- tests/Unit/Models/WhatsappInviteLogTest.php | 6 +- ...reAttendanceCommentTemplateRequestTest.php | 4 +- ...teAttendanceCommentTemplateRequestTest.php | 4 +- .../AdminNotificationSubjectServiceTest.php | 3 +- .../AdminNotificationUserServiceTest.php | 2 +- .../AdminPrintRecipientServiceTest.php | 3 +- .../AdministratorAbsenceServiceTest.php | 16 +- .../AdministratorDashboardServiceTest.php | 6 +- ...dministratorEnrollmentEventServiceTest.php | 2 +- ...dministratorEnrollmentQueryServiceTest.php | 15 +- ...ministratorEnrollmentRefundServiceTest.php | 3 +- .../AdministratorEnrollmentServiceTest.php | 6 +- ...ministratorEnrollmentStatusServiceTest.php | 15 +- .../AdministratorMetricsServiceTest.php | 10 +- .../AdministratorNotificationServiceTest.php | 6 +- .../AdministratorSharedServiceTest.php | 3 +- ...inistratorTeacherSubmissionServiceTest.php | 9 +- .../AdministratorUserSearchServiceTest.php | 2 +- ...acherSubmissionNotificationServiceTest.php | 6 +- .../TeacherSubmissionReportServiceTest.php | 6 +- .../TeacherSubmissionSupportServiceTest.php | 4 +- .../AssignmentContextServiceTest.php | 2 +- .../AssignmentDataLoaderServiceTest.php | 4 +- .../Assignment/AssignmentMetaServiceTest.php | 6 +- .../AssignmentSectionServiceTest.php | 2 +- tests/Unit/Services/AssignmentServiceTest.php | 7 + .../AttendanceAutoPublishJobServiceTest.php | 2 +- .../AttendanceAutoPublishServiceTest.php | 4 +- .../AttendanceCommentServiceTest.php | 4 +- .../AttendanceConsequenceServiceTest.php | 3 +- .../AttendancePolicyServiceTest.php | 8 +- .../Attendance/AttendanceQueryServiceTest.php | 88 +- .../AttendanceRecordSyncServiceTest.php | 5 +- .../AttendanceSemesterRangeServiceTest.php | 4 +- .../Attendance/AttendanceServiceTest.php | 20 +- .../AttendanceSummaryRebuildServiceTest.php | 2 +- .../Attendance/AttendanceTrackingService.php | 299 +- .../Attendance/SemesterRangeServiceTest.php | 8 +- .../Attendance/StaffAttendanceServiceTest.php | 15 +- .../StudentAttendanceWriterServiceTest.php | 6 +- ...TeacherAttendanceSubmissionServiceTest.php | 29 +- .../AttendanceManagementServiceTest.php | 4 +- .../AttendanceCaseQueryServiceTest.php | 15 +- ...endanceCommunicationSupportServiceTest.php | 15 +- .../AttendanceEmailComposerServiceTest.php | 5 +- .../AttendanceNotificationLogServiceTest.php | 3 +- ...endanceNotificationWorkflowServiceTest.php | 30 +- .../AttendanceParentLookupServiceTest.php | 4 +- .../AttendancePendingViolationServiceTest.php | 9 +- .../AttendanceTrackingServiceTest.php | 2 +- ...nceViolationStudentResolverServiceTest.php | 9 +- .../ViolationRuleEngineServiceTest.php | 6 +- .../Auth/PasswordResetCleanupServiceTest.php | 2 +- .../Auth/PermissionCheckServiceTest.php | 2 +- .../Auth/RegistrationCaptchaServiceTest.php | 4 +- .../Auth/RegistrationFormatterServiceTest.php | 4 +- .../Services/Auth/RegistrationServiceTest.php | 12 +- .../Services/Auth/UserRoleServiceTest.php | 2 +- .../Badges/BadgeFormDataServiceTest.php | 6 +- .../Services/Badges/BadgePdfServiceTest.php | 6 +- .../Badges/BadgePrintLogServiceTest.php | 8 +- .../Badges/BadgeTextFormatterTest.php | 6 +- .../Billing/BalanceCalculationServiceTest.php | 8 +- .../Billing/BillingTotalsServiceTest.php | 6 +- .../BroadcastEmailComposerServiceTest.php | 4 +- .../BroadcastEmailRecipientServiceTest.php | 4 +- ...BroadcastEmailSenderOptionsServiceTest.php | 4 +- .../ClassPrep/ClassRosterServiceTest.php | 2 +- .../ClassPrep/StickerCountServiceTest.php | 4 +- .../ClassPreparationAdjustmentServiceTest.php | 2 +- .../ClassPreparationCalculatorServiceTest.php | 4 +- .../ClassPreparationContextServiceTest.php | 2 +- .../ClassPreparationInventoryServiceTest.php | 2 +- .../ClassPreparationLogServiceTest.php | 5 +- .../ClassPreparationRosterServiceTest.php | 4 +- .../ClassProgressAttachmentServiceTest.php | 2 +- .../ClassProgressMetaServiceTest.php | 2 +- .../ClassProgressMutationServiceTest.php | 12 +- .../ClassProgressQueryServiceTest.php | 2 +- .../ClassProgressRuleServiceTest.php | 4 +- .../CommunicationTemplateServiceTest.php | 2 +- .../CompetitionScoresSaveServiceTest.php | 5 +- .../Discounts/DiscountApplyServiceTest.php | 2 +- .../Email/EmailExtractorServiceTest.php | 2 +- .../Email/EmailProfileServiceTest.php | 6 +- .../Events/EventChargeQueryServiceTest.php | 2 +- .../Services/Events/EventListServiceTest.php | 2 +- .../Events/EventStudentChargeServiceTest.php | 2 +- .../Exams/ExamDraftTeacherServiceTest.php | 2 +- .../Expenses/ExpenseReceiptServiceTest.php | 4 +- .../Expenses/ExpenseStaffServiceTest.php | 2 +- .../ExtraChargesMetaServiceTest.php | 4 +- .../Families/FamilyFinanceServiceTest.php | 2 +- .../Families/FamilyMutationServiceTest.php | 8 +- .../Families/FamilyQueryServiceTest.php | 10 +- .../Services/Fees/FeeGradeServiceTest.php | 4 +- .../Fees/FeeRefundCalculatorServiceTest.php | 6 +- .../Fees/FeeRefundDetailServiceTest.php | 2 +- .../Fees/FeeStudentFeeServiceTest.php | 2 +- .../Fees/TuitionCalculationServiceTest.php | 5 +- .../ExamDraftDownloadNameServiceTest.php | 2 +- .../Services/Files/FileServeServiceTest.php | 18 +- .../Finance/FinancialChartServiceTest.php | 2 +- .../Finance/FinancialPaymentServiceTest.php | 2 +- .../Finance/FinancialPdfReportServiceTest.php | 2 +- .../Finance/FinancialReportServiceTest.php | 4 +- .../FinancialSchoolYearServiceTest.php | 2 +- .../Finance/FinancialSummaryServiceTest.php | 2 +- .../FinancialUnpaidParentsServiceTest.php | 2 +- .../Frontend/ContactSubmissionServiceTest.php | 2 +- .../Frontend/FrontendPageServiceTest.php | 2 +- .../Frontend/StaticPageServiceTest.php | 6 +- .../Grading/GradingBelowSixtyServiceTest.php | 2 +- .../Grading/GradingLockServiceTest.php | 4 +- .../Grading/HomeworkTrackingServiceTest.php | 2 +- .../Incidents/IncidentAnalysisServiceTest.php | 2 +- .../Incidents/IncidentHistoryServiceTest.php | 2 +- .../Incidents/IncidentLookupServiceTest.php | 4 +- .../InventoryCategoryServiceTest.php | 4 +- .../Inventory/InventoryItemServiceTest.php | 4 +- .../InventoryMovementServiceTest.php | 2 +- .../Inventory/InventorySummaryServiceTest.php | 2 +- ...nventoryTeacherDistributionServiceTest.php | 8 +- .../Inventory/SupplierServiceTest.php | 4 +- .../Inventory/SupplyCategoryServiceTest.php | 4 +- .../Invoices/InvoiceConfigServiceTest.php | 2 +- .../Invoices/InvoiceGenerationServiceTest.php | 2 +- .../Invoices/InvoiceManagementServiceTest.php | 2 +- .../Invoices/InvoicePaymentServiceTest.php | 2 +- .../Invoices/InvoicePdfServiceTest.php | 2 +- .../Messaging/MessageCommandServiceTest.php | 4 +- .../Messaging/MessageQueryServiceTest.php | 2 +- .../Messaging/MessageRecipientServiceTest.php | 4 +- .../Navigation/NavBuilderServiceTest.php | 6 +- .../Services/Navigation/NavbarServiceTest.php | 2 +- .../NotificationActiveServiceTest.php | 2 +- .../NotificationCleanupServiceTest.php | 2 +- .../NotificationDeletedServiceTest.php | 2 +- .../NotificationDispatchServiceTest.php | 2 +- .../NotificationManagementServiceTest.php | 6 +- .../NotificationReadServiceTest.php | 4 +- .../NotificationRecipientServiceTest.php | 2 +- .../NotificationShowServiceTest.php | 4 +- .../NotificationUserListServiceTest.php | 2 +- .../UserNotificationDispatchServiceTest.php | 4 +- .../ParentAttendanceReportServiceTest.php | 6 +- .../Parents/ParentAttendanceServiceTest.php | 3 +- .../Parents/ParentConfigServiceTest.php | 2 +- .../ParentEmergencyContactServiceTest.php | 2 +- .../Parents/ParentEnrollmentServiceTest.php | 2 +- .../Parents/ParentInvoiceServiceTest.php | 2 +- .../Parents/ParentRegistrationServiceTest.php | 2 +- .../Payments/PaymentBalanceServiceTest.php | 2 +- .../PaymentEnrollmentEventServiceTest.php | 2 +- .../Payments/PaymentLookupServiceTest.php | 2 +- ...PaymentNotificationDispatchServiceTest.php | 2 +- .../Payments/PaymentPlanServiceTest.php | 2 +- .../PaymentTransactionServiceTest.php | 8 +- .../Payments/PaypalPaymentServiceTest.php | 2 +- .../PaypalTransactionsServiceTest.php | 2 +- .../Policy/PolicyContentServiceTest.php | 2 +- .../LevelProgressionServiceTest.php | 8 +- .../PromotionEligibilityServiceTest.php | 10 +- .../PromotionEnrollmentServiceTest.php | 2 +- .../Promotions/PromotionStatusServiceTest.php | 2 +- .../PurchaseOrderCreateServiceTest.php | 2 +- .../PurchaseOrderReceiveServiceTest.php | 2 +- .../PurchaseOrderStatusServiceTest.php | 2 +- .../Refunds/RefundDecisionServiceTest.php | 2 +- .../Refunds/RefundNotificationServiceTest.php | 2 +- .../Refunds/RefundOverpaymentServiceTest.php | 2 +- .../Refunds/RefundPayoutServiceTest.php | 2 +- .../Refunds/RefundPolicyServiceTest.php | 8 +- .../Refunds/RefundQueryServiceTest.php | 2 +- .../Refunds/RefundRequestServiceTest.php | 10 +- .../Refunds/RefundSummaryServiceTest.php | 2 +- ...eimbursementBatchAssignmentServiceTest.php | 2 +- .../ReimbursementBatchServiceTest.php | 2 +- .../ReimbursementDonationServiceTest.php | 2 +- .../ReimbursementRecipientServiceTest.php | 2 +- .../ReportCards/ReportCardServiceTest.php | 10 +- .../SlipPrinterFormatterServiceTest.php | 4 +- .../Stickers/StickerPresetServiceTest.php | 2 +- .../Stickers/StickerPrintServiceTest.php | 4 +- .../Stickers/StickerQueryServiceTest.php | 4 +- .../Roles/PermissionCrudServiceTest.php | 2 +- .../Roles/RoleAssignmentServiceTest.php | 3 +- .../Services/Roles/RoleCrudServiceTest.php | 2 +- .../Roles/RoleDashboardServiceTest.php | 2 +- .../Roles/RolePermissionServiceTest.php | 2 +- .../Services/Roles/RoleQueryServiceTest.php | 4 +- .../Services/Roles/RoleSwitchServiceTest.php | 2 +- .../School/SemesterSelectionServiceTest.php | 4 +- .../SchoolIdGenerationServiceTest.php | 2 +- .../Scores/AttendanceCalculatorTest.php | 2 +- .../Services/Scores/ExamScoreServiceTest.php | 4 +- .../Scores/HomeworkCalculatorTest.php | 2 +- .../Scores/HomeworkScoreServiceTest.php | 4 +- .../Scores/ParticipationScoreServiceTest.php | 4 +- .../Services/Scores/ProjectCalculatorTest.php | 2 +- .../Scores/ProjectScoreServiceTest.php | 6 +- .../Services/Scores/QuizCalculatorTest.php | 2 +- .../Services/Scores/QuizScoreServiceTest.php | 6 +- .../Scores/ScoreCommentServiceTest.php | 4 +- .../Scores/ScoreDashboardServiceTest.php | 4 +- .../Scores/ScorePredictorServiceTest.php | 4 +- .../Services/Scores/ScoreTermServiceTest.php | 4 +- .../Scores/SemesterScoreServiceTest.php | 22 +- .../Services/Security/Pbkdf2HasherTest.php | 2 +- .../Semesters/SemesterConfigServiceTest.php | 2 +- .../Semesters/SemesterRangeServiceTest.php | 6 +- .../Settings/ConfigurationServiceTest.php | 2 +- .../SchoolCalendarContextServiceTest.php | 2 +- .../SchoolCalendarFormatterServiceTest.php | 4 +- .../SchoolCalendarMeetingServiceTest.php | 2 +- .../SchoolCalendarMutationServiceTest.php | 4 +- .../SchoolCalendarQueryServiceTest.php | 4 +- .../Services/Settings/SettingsServiceTest.php | 4 +- .../Staff/StaffCommandServiceTest.php | 4 +- .../Services/Staff/StaffQueryServiceTest.php | 2 +- .../Students/StudentAssignmentServiceTest.php | 4 +- .../Students/StudentProfileServiceTest.php | 2 +- .../Students/StudentScoreCardServiceTest.php | 2 +- .../Subjects/SubjectCurriculumServiceTest.php | 4 +- .../Support/SupportRequestServiceTest.php | 2 +- .../System/CleanupSchedulerServiceTest.php | 6 +- .../System/ConfigUpdateServiceTest.php | 4 +- .../System/GlobalConfigServiceTest.php | 2 +- .../Unit/Services/System/TimeServiceTest.php | 2 +- .../Teachers/TeacherAbsenceServiceTest.php | 14 +- .../Teachers/TeacherAssignmentServiceTest.php | 4 +- .../Teachers/TeacherDashboardServiceTest.php | 2 +- tests/Unit/Services/Ui/UiStyleServiceTest.php | 2 +- .../Users/InactiveUserCleanupServiceTest.php | 2 +- .../Services/Users/UserListServiceTest.php | 1 - .../Whatsapp/WhatsappContactServiceTest.php | 2 +- .../Whatsapp/WhatsappContextServiceTest.php | 4 +- .../WhatsappInviteBundleServiceTest.php | 4 +- .../WhatsappInviteNotificationServiceTest.php | 7 +- .../Whatsapp/WhatsappInviteServiceTest.php | 9 +- .../Whatsapp/WhatsappLinkServiceTest.php | 4 +- .../WhatsappMembershipServiceTest.php | 4 +- 1489 files changed, 11349 insertions(+), 10305 deletions(-) diff --git a/app/Console/Commands/AttendanceAutoPublishCommand.php b/app/Console/Commands/AttendanceAutoPublishCommand.php index fabeff3d..e775c110 100644 --- a/app/Console/Commands/AttendanceAutoPublishCommand.php +++ b/app/Console/Commands/AttendanceAutoPublishCommand.php @@ -8,6 +8,7 @@ use Illuminate\Console\Command; class AttendanceAutoPublishCommand extends Command { protected $signature = 'attendance:auto-publish'; + protected $description = 'Auto-publish attendance days per "second Sunday backward" rule.'; public function handle(AttendanceAutoPublishJobService $service): int diff --git a/app/Console/Commands/CheckMissedPaymentsCommand.php b/app/Console/Commands/CheckMissedPaymentsCommand.php index fff09126..ddd883a5 100644 --- a/app/Console/Commands/CheckMissedPaymentsCommand.php +++ b/app/Console/Commands/CheckMissedPaymentsCommand.php @@ -8,6 +8,7 @@ use Illuminate\Console\Command; class CheckMissedPaymentsCommand extends Command { protected $signature = 'payments:check-missed'; + protected $description = 'Checks for users who missed payments and sends reminders.'; public function handle(PaymentMissedCheckService $service): int @@ -15,6 +16,7 @@ class CheckMissedPaymentsCommand extends Command $users = $service->findUsersWithMissedPayments(); if (empty($users)) { $this->info('No missed payments found.'); + return self::SUCCESS; } diff --git a/app/Console/Commands/CleanupExpiredNotificationsCommand.php b/app/Console/Commands/CleanupExpiredNotificationsCommand.php index 94bcabee..7e5a8237 100644 --- a/app/Console/Commands/CleanupExpiredNotificationsCommand.php +++ b/app/Console/Commands/CleanupExpiredNotificationsCommand.php @@ -8,6 +8,7 @@ use Illuminate\Console\Command; class CleanupExpiredNotificationsCommand extends Command { protected $signature = 'notifications:cleanup'; + protected $description = 'Deletes expired notifications from the database.'; public function handle(NotificationCleanupService $service): int @@ -16,6 +17,7 @@ class CleanupExpiredNotificationsCommand extends Command if ($count <= 0) { $this->info('No expired notifications found to soft delete.'); + return self::SUCCESS; } diff --git a/app/Console/Commands/CleanupPasswordResetsCommand.php b/app/Console/Commands/CleanupPasswordResetsCommand.php index 16d1bdbd..136b7845 100644 --- a/app/Console/Commands/CleanupPasswordResetsCommand.php +++ b/app/Console/Commands/CleanupPasswordResetsCommand.php @@ -8,6 +8,7 @@ use Illuminate\Console\Command; class CleanupPasswordResetsCommand extends Command { protected $signature = 'cleanup:password-resets {--days=30}'; + protected $description = 'Delete password reset requests older than N days.'; public function handle(PasswordResetCleanupService $service): int diff --git a/app/Console/Commands/ConfigUpdateCommand.php b/app/Console/Commands/ConfigUpdateCommand.php index ac90ecbc..5a80c1d9 100644 --- a/app/Console/Commands/ConfigUpdateCommand.php +++ b/app/Console/Commands/ConfigUpdateCommand.php @@ -9,6 +9,7 @@ use Illuminate\Console\Command; class ConfigUpdateCommand extends Command { protected $signature = 'config:update {task?} {--task=} {--dry} {--force} {--tz=}'; + protected $description = 'Run a configuration update task (weekly cron).'; public function handle(ConfigUpdateService $service): int @@ -19,18 +20,20 @@ class ConfigUpdateCommand extends Command $tzName = (string) ($this->option('tz') ?: (config('School')->attendance['timezone'] ?? 'UTC')); $tz = new DateTimeZone($tzName); - if ($task === '' || !in_array($task, $service->availableTasks(), true)) { - $this->error('Invalid or missing --task. Available: ' . implode(', ', $service->availableTasks())); + if ($task === '' || ! in_array($task, $service->availableTasks(), true)) { + $this->error('Invalid or missing --task. Available: '.implode(', ', $service->availableTasks())); + return self::FAILURE; } $result = $service->runTask($task, $dry, $force, $tz); - if (!$result['ok']) { + if (! $result['ok']) { $this->error($result['message'] ?? "Task '{$task}' failed."); + return self::FAILURE; } - $this->info("Task '{$task}' finished successfully." . ($dry ? ' [DRY RUN]' : '')); + $this->info("Task '{$task}' finished successfully.".($dry ? ' [DRY RUN]' : '')); return self::SUCCESS; } diff --git a/app/Console/Commands/DeleteInactiveUsersCommand.php b/app/Console/Commands/DeleteInactiveUsersCommand.php index baa3a450..c2a0e199 100644 --- a/app/Console/Commands/DeleteInactiveUsersCommand.php +++ b/app/Console/Commands/DeleteInactiveUsersCommand.php @@ -8,6 +8,7 @@ use Illuminate\Console\Command; class DeleteInactiveUsersCommand extends Command { protected $signature = 'users:delete-inactive-users {--minutes=15}'; + protected $description = 'Delete users that are inactive and created more than N minutes ago.'; public function handle(InactiveUserCleanupService $service): int diff --git a/app/Console/Commands/DeleteUnverifiedUsersCommand.php b/app/Console/Commands/DeleteUnverifiedUsersCommand.php index bb8a58da..1742baf9 100644 --- a/app/Console/Commands/DeleteUnverifiedUsersCommand.php +++ b/app/Console/Commands/DeleteUnverifiedUsersCommand.php @@ -10,6 +10,7 @@ use Illuminate\Support\Facades\Event; class DeleteUnverifiedUsersCommand extends Command { protected $signature = 'users:delete-unverified {--timeout=86400}'; + protected $description = 'Delete unverified users older than the configured timeout (seconds).'; public function handle(): int diff --git a/app/Console/Commands/InventoryReconcile.php b/app/Console/Commands/InventoryReconcile.php index a836e86f..2d040fd2 100644 --- a/app/Console/Commands/InventoryReconcile.php +++ b/app/Console/Commands/InventoryReconcile.php @@ -39,6 +39,7 @@ class InventoryReconcile extends Command if (empty($result['discrepancies'])) { $this->info('✅ All item quantities match movement ledger.'); + return Command::SUCCESS; } @@ -62,7 +63,7 @@ class InventoryReconcile extends Command qtyChange: -$d['variance'], type: 'adjust', reason: 'Auto-reconciliation: stored qty differed from movement sum', - note: 'Reconciled from ' . $d['stored_quantity'] . ' to ' . $d['movement_sum'], + note: 'Reconciled from '.$d['stored_quantity'].' to '.$d['movement_sum'], performedBy: null ); $this->line(" Fixed item #{$d['id']} ({$d['name']}): variance was {$d['variance']}"); diff --git a/app/Console/Commands/RecalculateAttendanceCommand.php b/app/Console/Commands/RecalculateAttendanceCommand.php index f33ccec1..8ed1327a 100644 --- a/app/Console/Commands/RecalculateAttendanceCommand.php +++ b/app/Console/Commands/RecalculateAttendanceCommand.php @@ -8,12 +8,13 @@ use Illuminate\Console\Command; class RecalculateAttendanceCommand extends Command { protected $signature = 'attendance:recalculate-summary'; + protected $description = 'Recalculates the attendance summary records from raw attendance data.'; public function handle(AttendanceSummaryRebuildService $service): int { $result = $service->rebuild(); - $this->info('Attendance summary recalculation finished. Inserted: ' . $result['inserted']); + $this->info('Attendance summary recalculation finished. Inserted: '.$result['inserted']); return self::SUCCESS; } diff --git a/app/Console/Commands/SendAbsenteesSummaryCommand.php b/app/Console/Commands/SendAbsenteesSummaryCommand.php index 6f25f29d..60fc0d78 100644 --- a/app/Console/Commands/SendAbsenteesSummaryCommand.php +++ b/app/Console/Commands/SendAbsenteesSummaryCommand.php @@ -8,6 +8,7 @@ use Illuminate\Console\Command; class SendAbsenteesSummaryCommand extends Command { protected $signature = 'attendance:absentees-summary'; + protected $description = 'Sends daily attendance summaries for absences to parents.'; public function handle(AttendanceDailySummaryService $service): int diff --git a/app/Console/Commands/SendLatesSummaryCommand.php b/app/Console/Commands/SendLatesSummaryCommand.php index 3489ccaf..c5cbd7ad 100644 --- a/app/Console/Commands/SendLatesSummaryCommand.php +++ b/app/Console/Commands/SendLatesSummaryCommand.php @@ -8,6 +8,7 @@ use Illuminate\Console\Command; class SendLatesSummaryCommand extends Command { protected $signature = 'attendance:lates-summary'; + protected $description = 'Sends daily attendance summaries for lates to parents.'; public function handle(AttendanceDailySummaryService $service): int diff --git a/app/Console/Commands/SendMonthlyPaymentNotificationsCommand.php b/app/Console/Commands/SendMonthlyPaymentNotificationsCommand.php index a2ffccf1..07a8ed25 100644 --- a/app/Console/Commands/SendMonthlyPaymentNotificationsCommand.php +++ b/app/Console/Commands/SendMonthlyPaymentNotificationsCommand.php @@ -9,6 +9,7 @@ use Illuminate\Console\Command; class SendMonthlyPaymentNotificationsCommand extends Command { protected $signature = 'payments:monthly-reminder {--force} {--email=} {--type=}'; + protected $description = 'Send monthly payment reminders on the first Saturday of every month.'; public function handle(PaymentNotificationService $service): int @@ -19,8 +20,9 @@ class SendMonthlyPaymentNotificationsCommand extends Command if ($email !== '') { $user = User::query()->where('email', $email)->first(); - if (!$user) { + if (! $user) { $this->error('Invalid --email provided.'); + return self::FAILURE; } @@ -30,12 +32,14 @@ class SendMonthlyPaymentNotificationsCommand extends Command 'force' => true, ]); - $this->info('Test reminder sent for ' . $email . '.'); + $this->info('Test reminder sent for '.$email.'.'); + return $result['failed'] > 0 ? self::FAILURE : self::SUCCESS; } - if (!$force && !$this->isFirstSaturday(now())) { + if (! $force && ! $this->isFirstSaturday(now())) { $this->info('Not the first Saturday of the month. Use --force to override.'); + return self::SUCCESS; } diff --git a/app/Console/Commands/SendTestPaymentNotificationCommand.php b/app/Console/Commands/SendTestPaymentNotificationCommand.php index 63002fbe..16873942 100644 --- a/app/Console/Commands/SendTestPaymentNotificationCommand.php +++ b/app/Console/Commands/SendTestPaymentNotificationCommand.php @@ -8,6 +8,7 @@ use Illuminate\Console\Command; class SendTestPaymentNotificationCommand extends Command { protected $signature = 'payments:send-test {--email=} {--type=no_payment}'; + protected $description = 'Send a single test non-payment or installment reminder to a specific email.'; public function handle(PaymentTestNotificationService $service): int @@ -15,18 +16,20 @@ class SendTestPaymentNotificationCommand extends Command $email = (string) $this->option('email'); $type = (string) $this->option('type'); - if ($email === '' || !filter_var($email, FILTER_VALIDATE_EMAIL)) { + if ($email === '' || ! filter_var($email, FILTER_VALIDATE_EMAIL)) { $this->error('Usage: php artisan payments:send-test --email=addr@example.com [--type=no_payment|installment]'); + return self::FAILURE; } $result = $service->send($email, $type); - if (!$result['ok']) { - $this->error('Failed to send test reminder to ' . $email . '.'); + if (! $result['ok']) { + $this->error('Failed to send test reminder to '.$email.'.'); + return self::FAILURE; } - $this->info('Sent test reminder to ' . $email . '.'); + $this->info('Sent test reminder to '.$email.'.'); return self::SUCCESS; } diff --git a/app/Console/Commands/SyncPaypalPaymentsCommand.php b/app/Console/Commands/SyncPaypalPaymentsCommand.php index 7a5ba23f..f101ae11 100644 --- a/app/Console/Commands/SyncPaypalPaymentsCommand.php +++ b/app/Console/Commands/SyncPaypalPaymentsCommand.php @@ -8,6 +8,7 @@ use Illuminate\Console\Command; class SyncPaypalPaymentsCommand extends Command { protected $signature = 'payments:sync-paypal {--dry-run} {--report-only}'; + protected $description = 'Sync PayPal payments to internal payments table from paypal_payments.'; public function handle(PaypalPaymentSyncService $service): int @@ -18,7 +19,7 @@ class SyncPaypalPaymentsCommand extends Command $result = $service->sync($dryRun, $reportOnly); $this->info(sprintf('[%s] %d PayPal payments processed.', $result['mode'], $result['processed'])); - if (!empty($result['failed'])) { + if (! empty($result['failed'])) { $this->error(sprintf('[%s] Failed transactions: %s', $result['mode'], implode(', ', $result['failed']))); } diff --git a/app/Events/DeleteUnverifiedUser.php b/app/Events/DeleteUnverifiedUser.php index b15e9c7c..e033be77 100644 --- a/app/Events/DeleteUnverifiedUser.php +++ b/app/Events/DeleteUnverifiedUser.php @@ -4,7 +4,5 @@ namespace App\Events; class DeleteUnverifiedUser { - public function __construct(public int $userId) - { - } + public function __construct(public int $userId) {} } diff --git a/app/Events/WhatsappInvitesSend.php b/app/Events/WhatsappInvitesSend.php index af1cf788..2a03b39f 100644 --- a/app/Events/WhatsappInvitesSend.php +++ b/app/Events/WhatsappInvitesSend.php @@ -10,7 +10,5 @@ class WhatsappInvitesSend use Dispatchable; use SerializesModels; - public function __construct(public array $payload) - { - } + public function __construct(public array $payload) {} } diff --git a/app/Helpers/app_helpers.php b/app/Helpers/app_helpers.php index 7cd96b32..1a8e20b8 100644 --- a/app/Helpers/app_helpers.php +++ b/app/Helpers/app_helpers.php @@ -3,33 +3,34 @@ use Carbon\CarbonImmutable; use Illuminate\Support\Facades\Hash; -if (!function_exists('utc_now')) { +if (! function_exists('utc_now')) { function utc_now(): string { return CarbonImmutable::now('UTC')->toDateTimeString(); } } -if (!function_exists('local_date')) { +if (! function_exists('local_date')) { function local_date(string $utcDateTime, string $format = 'Y-m-d H:i:s'): string { try { $carbon = CarbonImmutable::parse($utcDateTime)->setTimezone(config('app.timezone')); + return $carbon->format($format); - } catch (\Throwable $e) { + } catch (Throwable $e) { return CarbonImmutable::parse($utcDateTime)->format($format); } } } -if (!function_exists('user_timezone')) { +if (! function_exists('user_timezone')) { function user_timezone(): string { return (string) (config('School')->attendance['timezone'] ?? config('app.timezone', 'America/New_York')); } } -if (!function_exists('pbkdf2_hash')) { +if (! function_exists('pbkdf2_hash')) { function pbkdf2_hash(string $password, string $algo = 'sha256', int $iterations = 100000, int $length = 64): string { $salt = random_bytes(16); @@ -41,7 +42,7 @@ if (!function_exists('pbkdf2_hash')) { } } -if (!function_exists('pbkdf2_verify')) { +if (! function_exists('pbkdf2_verify')) { function pbkdf2_verify(string $password, string $storedHash): bool { $delim = strpos($storedHash, '$') !== false ? '$' : ':'; @@ -62,14 +63,15 @@ if (!function_exists('pbkdf2_verify')) { } $calc = hash_pbkdf2($algo, $password, $salt, (int) $iterations, strlen($derived), true); + return hash_equals($derived, $calc); } } -if (!function_exists('verify_stored_password')) { +if (! function_exists('verify_stored_password')) { function verify_stored_password(string $password, ?string $storedHash): bool { - if (!$storedHash) { + if (! $storedHash) { return false; } diff --git a/app/Http/Controllers/Api/Administrator/AdministratorAbsenceController.php b/app/Http/Controllers/Api/Administrator/AdministratorAbsenceController.php index 498033ad..696f1ca8 100644 --- a/app/Http/Controllers/Api/Administrator/AdministratorAbsenceController.php +++ b/app/Http/Controllers/Api/Administrator/AdministratorAbsenceController.php @@ -13,8 +13,7 @@ class AdministratorAbsenceController extends Controller { public function __construct( protected AdministratorAbsenceService $service - ) { - } + ) {} public function index(): JsonResponse { @@ -34,7 +33,7 @@ class AdministratorAbsenceController extends Controller } $payload = $request->all(); - if (!array_key_exists('dates', $payload) || !array_key_exists('reason', $payload)) { + if (! array_key_exists('dates', $payload) || ! array_key_exists('reason', $payload)) { $json = json_decode($request->getContent() ?: '', true); if (is_array($json)) { $payload = array_merge($payload, $json); diff --git a/app/Http/Controllers/Api/Administrator/AdministratorDashboardController.php b/app/Http/Controllers/Api/Administrator/AdministratorDashboardController.php index 67a82364..bb327ef1 100644 --- a/app/Http/Controllers/Api/Administrator/AdministratorDashboardController.php +++ b/app/Http/Controllers/Api/Administrator/AdministratorDashboardController.php @@ -11,8 +11,7 @@ class AdministratorDashboardController extends Controller { public function __construct( protected AdministratorDashboardService $service - ) { - } + ) {} public function metrics(): JsonResponse { @@ -25,4 +24,4 @@ class AdministratorDashboardController extends Controller $this->service->userSearch((string) $request->query('query', '')) ); } -} \ No newline at end of file +} diff --git a/app/Http/Controllers/Api/Administrator/AdministratorEnrollmentController.php b/app/Http/Controllers/Api/Administrator/AdministratorEnrollmentController.php index 2bc753e6..44e43c9d 100644 --- a/app/Http/Controllers/Api/Administrator/AdministratorEnrollmentController.php +++ b/app/Http/Controllers/Api/Administrator/AdministratorEnrollmentController.php @@ -14,8 +14,7 @@ class AdministratorEnrollmentController extends Controller { public function __construct( protected AdministratorEnrollmentService $service - ) { - } + ) {} public function index(Request $request): JsonResponse { @@ -39,13 +38,13 @@ class AdministratorEnrollmentController extends Controller } $payload = $request->all(); - if (!array_key_exists('enrollment_status', $payload)) { + if (! array_key_exists('enrollment_status', $payload)) { $json = json_decode($request->getContent() ?: '', true); if (is_array($json) && array_key_exists('enrollment_status', $json)) { $payload['enrollment_status'] = $json['enrollment_status']; } } - if (!array_key_exists('enrollment_status', $payload)) { + if (! array_key_exists('enrollment_status', $payload)) { $payload['enrollment_status'] = $request->query('enrollment_status'); } diff --git a/app/Http/Controllers/Api/Administrator/AdministratorNotificationController.php b/app/Http/Controllers/Api/Administrator/AdministratorNotificationController.php index efc793ff..67a0a5be 100644 --- a/app/Http/Controllers/Api/Administrator/AdministratorNotificationController.php +++ b/app/Http/Controllers/Api/Administrator/AdministratorNotificationController.php @@ -12,8 +12,7 @@ class AdministratorNotificationController extends Controller { public function __construct( protected AdministratorNotificationService $service - ) { - } + ) {} public function alerts(): JsonResponse { @@ -23,7 +22,7 @@ class AdministratorNotificationController extends Controller public function saveAlerts(Request $request): JsonResponse { $payload = $request->all(); - if (!array_key_exists('subjects', $payload)) { + if (! array_key_exists('subjects', $payload)) { $json = json_decode($request->getContent() ?: '', true); if (is_array($json)) { $payload = array_merge($payload, $json); @@ -60,7 +59,7 @@ class AdministratorNotificationController extends Controller public function savePrintRecipients(Request $request): JsonResponse { $payload = $request->all(); - if (!array_key_exists('notify', $payload)) { + if (! array_key_exists('notify', $payload)) { $json = json_decode($request->getContent() ?: '', true); if (is_array($json)) { $payload = array_merge($payload, $json); diff --git a/app/Http/Controllers/Api/Administrator/AdministratorPromotionController.php b/app/Http/Controllers/Api/Administrator/AdministratorPromotionController.php index 320ae177..5631b479 100644 --- a/app/Http/Controllers/Api/Administrator/AdministratorPromotionController.php +++ b/app/Http/Controllers/Api/Administrator/AdministratorPromotionController.php @@ -16,18 +16,18 @@ use App\Http\Resources\Promotions\PromotionReminderResource; use App\Http\Resources\Promotions\StudentPromotionResource; use App\Models\PromotionAuditLog; use App\Models\PromotionReminderLog; +use App\Models\SectionPlacementBatch; use App\Models\StudentPromotionRecord; use App\Services\Promotions\LevelProgressionService; +use App\Services\Promotions\Placement\SectionPlacementPreviewService; use App\Services\Promotions\PromotionAuditService; use App\Services\Promotions\PromotionEligibilityService; use App\Services\Promotions\PromotionEnrollmentService; use App\Services\Promotions\PromotionQueryService; use App\Services\Promotions\PromotionReminderService; use App\Services\Promotions\PromotionStatusService; -use App\Services\Promotions\Placement\SectionPlacementPreviewService; -use App\Models\SectionPlacementBatch; -use Illuminate\Http\Request; use Illuminate\Http\JsonResponse; +use Illuminate\Http\Request; use Illuminate\Support\Facades\DB; use Illuminate\Support\Facades\Log; use Symfony\Component\HttpFoundation\Response; @@ -54,6 +54,7 @@ class AdministratorPromotionController extends BaseApiController public function index(AdminListPromotionsRequest $request): JsonResponse { $result = $this->query->list($request->filters()); + return response()->json([ 'ok' => true, 'data' => StudentPromotionResource::collection($result['data']), @@ -73,6 +74,7 @@ class AdministratorPromotionController extends BaseApiController if ($record instanceof JsonResponse) { return $record; } + return response()->json([ 'ok' => true, 'data' => new StudentPromotionResource($this->query->detail($record)), @@ -82,6 +84,7 @@ class AdministratorPromotionController extends BaseApiController public function summary(AdminListPromotionsRequest $request): JsonResponse { $counts = $this->query->countsByStatus($request->filters()); + return response()->json([ 'ok' => true, 'counts_by_status' => $counts, @@ -125,6 +128,7 @@ class AdministratorPromotionController extends BaseApiController }; } catch (\Throwable $e) { Log::error('Promotion eligibility evaluation failed', ['exception' => $e]); + return response()->json([ 'ok' => false, 'message' => 'Failed to evaluate promotion eligibility.', @@ -137,7 +141,6 @@ class AdministratorPromotionController extends BaseApiController ]); } - public function createPlacementPreview(Request $request): JsonResponse { $payload = $request->validate([ @@ -157,6 +160,7 @@ class AdministratorPromotionController extends BaseApiController ); } catch (\Throwable $e) { Log::error('Section placement preview generation failed', ['exception' => $e]); + return response()->json([ 'ok' => false, 'message' => $e->getMessage(), @@ -172,7 +176,7 @@ class AdministratorPromotionController extends BaseApiController public function showPlacementBatch(int $batchId): JsonResponse { $batch = SectionPlacementBatch::query()->find($batchId); - if (!$batch) { + if (! $batch) { return response()->json(['ok' => false, 'message' => 'Placement batch not found.'], Response::HTTP_NOT_FOUND); } @@ -188,6 +192,7 @@ class AdministratorPromotionController extends BaseApiController $batch = $this->sectionPlacement->finalize($batchId, $this->getCurrentUserId()); } catch (\Throwable $e) { Log::error('Section placement finalization failed', ['batch_id' => $batchId, 'exception' => $e]); + return response()->json([ 'ok' => false, 'message' => $e->getMessage(), @@ -211,7 +216,7 @@ class AdministratorPromotionController extends BaseApiController $userId = $this->getCurrentUserId(); try { - $updated = !empty($payload['force']) + $updated = ! empty($payload['force']) ? $this->statusService->forceStatus($record, $payload['status'], $userId, $payload['notes'] ?? null) : $this->statusService->transition($record, $payload['status'], $userId, $payload['notes'] ?? null); } catch (\InvalidArgumentException $e) { @@ -244,7 +249,7 @@ class AdministratorPromotionController extends BaseApiController return $record; } $records = collect([$record]); - } elseif ($applyTo === 'filter' && !empty($payload['promotion_ids'])) { + } elseif ($applyTo === 'filter' && ! empty($payload['promotion_ids'])) { $records = StudentPromotionRecord::query() ->whereIn('promotion_id', $payload['promotion_ids']) ->get(); @@ -305,6 +310,7 @@ class AdministratorPromotionController extends BaseApiController 'promotion_id' => $promotionId, 'exception' => $e, ]); + return response()->json([ 'ok' => false, 'message' => 'Failed to send reminder.', @@ -321,6 +327,7 @@ class AdministratorPromotionController extends BaseApiController { $userId = $this->getCurrentUserId(); $result = $this->reminders->dispatchScheduledReminders(null, $userId); + return response()->json([ 'ok' => true, 'result' => $result, @@ -337,6 +344,7 @@ class AdministratorPromotionController extends BaseApiController ->forPromotion((int) $record->getKey()) ->orderByDesc('id') ->get(); + return response()->json([ 'ok' => true, 'reminders' => PromotionReminderResource::collection($rows), @@ -353,6 +361,7 @@ class AdministratorPromotionController extends BaseApiController ->forPromotion((int) $record->getKey()) ->orderByDesc('id') ->get(); + return response()->json([ 'ok' => true, 'entries' => PromotionAuditLogResource::collection($rows), @@ -362,6 +371,7 @@ class AdministratorPromotionController extends BaseApiController public function expireDeadlines(): JsonResponse { $result = $this->enrollmentService->markExpiredDeadlines(null, $this->getCurrentUserId()); + return response()->json([ 'ok' => true, 'result' => $result, @@ -404,6 +414,7 @@ class AdministratorPromotionController extends BaseApiController public function levelProgressions(): JsonResponse { $rows = $this->progression->list(false); + return response()->json([ 'ok' => true, 'levels' => LevelProgressionResource::collection($rows), @@ -413,6 +424,7 @@ class AdministratorPromotionController extends BaseApiController public function upsertLevelProgression(UpsertLevelProgressionRequest $request): JsonResponse { $row = $this->progression->upsertMapping($request->validated()); + return response()->json([ 'ok' => true, 'level' => new LevelProgressionResource($row), @@ -422,6 +434,7 @@ class AdministratorPromotionController extends BaseApiController public function seedLevelProgressions(): JsonResponse { $created = $this->progression->seedDefaults(); + return response()->json([ 'ok' => true, 'created' => $created, @@ -431,12 +444,13 @@ class AdministratorPromotionController extends BaseApiController private function findOrFail(int $promotionId): StudentPromotionRecord|JsonResponse { $record = StudentPromotionRecord::query()->find($promotionId); - if (!$record) { + if (! $record) { return response()->json([ 'ok' => false, 'message' => 'Promotion record not found.', ], Response::HTTP_NOT_FOUND); } + return $record; } } diff --git a/app/Http/Controllers/Api/Administrator/AdministratorTeacherSubmissionController.php b/app/Http/Controllers/Api/Administrator/AdministratorTeacherSubmissionController.php index 6960ac32..4a1e7ced 100644 --- a/app/Http/Controllers/Api/Administrator/AdministratorTeacherSubmissionController.php +++ b/app/Http/Controllers/Api/Administrator/AdministratorTeacherSubmissionController.php @@ -13,8 +13,7 @@ class AdministratorTeacherSubmissionController extends Controller { public function __construct( protected AdministratorTeacherSubmissionService $service - ) { - } + ) {} public function index(Request $request): JsonResponse { diff --git a/app/Http/Controllers/Api/Administrator/EmergencyContactController.php b/app/Http/Controllers/Api/Administrator/EmergencyContactController.php index 5c685cf9..022989b3 100644 --- a/app/Http/Controllers/Api/Administrator/EmergencyContactController.php +++ b/app/Http/Controllers/Api/Administrator/EmergencyContactController.php @@ -18,8 +18,7 @@ class EmergencyContactController extends Controller public function __construct( private EmergencyContactDirectoryService $directoryService, private EmergencyContactCrudService $crudService - ) { - } + ) {} public function index(Request $request): JsonResponse { @@ -38,10 +37,10 @@ class EmergencyContactController extends Controller $payload = $validator->validated(); $parentIds = []; - if (!empty($payload['parent_ids'])) { + if (! empty($payload['parent_ids'])) { $parentIds = array_values(array_unique(array_map('intval', $payload['parent_ids']))); } - if (!empty($payload['parent_id'])) { + if (! empty($payload['parent_id'])) { $parentIds[] = (int) $payload['parent_id']; } $parentIds = array_values(array_unique(array_filter($parentIds))); diff --git a/app/Http/Controllers/Api/Administrator/TeacherClassAssignmentController.php b/app/Http/Controllers/Api/Administrator/TeacherClassAssignmentController.php index 7f841d28..494cc994 100644 --- a/app/Http/Controllers/Api/Administrator/TeacherClassAssignmentController.php +++ b/app/Http/Controllers/Api/Administrator/TeacherClassAssignmentController.php @@ -61,9 +61,6 @@ class TeacherClassAssignmentController extends BaseApiController ], $status); } - /** - * @return int|JsonResponse - */ private function authenticatedUserIdOrUnauthorized(): int|JsonResponse { $userId = (int) (auth()->id() ?? 0); diff --git a/app/Http/Controllers/Api/Attendance/AdminAttendanceApiController.php b/app/Http/Controllers/Api/Attendance/AdminAttendanceApiController.php index 9530777e..14bfd63b 100644 --- a/app/Http/Controllers/Api/Attendance/AdminAttendanceApiController.php +++ b/app/Http/Controllers/Api/Attendance/AdminAttendanceApiController.php @@ -23,8 +23,8 @@ class AdminAttendanceApiController extends Controller { return new DailyAttendanceResource( $this->queryService->buildDailyAttendanceData( - (string)$request->query('semester', $this->attendanceService->currentSemester()), - (string)$request->query('school_year', $this->attendanceService->currentSchoolYear()) + (string) $request->query('semester', $this->attendanceService->currentSemester()), + (string) $request->query('school_year', $this->attendanceService->currentSchoolYear()) ) ); } @@ -60,4 +60,4 @@ class AdminAttendanceApiController extends Controller return response()->json(['message' => $e->getMessage()], 422); } } -} \ No newline at end of file +} diff --git a/app/Http/Controllers/Api/Attendance/AttendanceCommentTemplateController.php b/app/Http/Controllers/Api/Attendance/AttendanceCommentTemplateController.php index 6ae9a5ad..33aa8ee2 100644 --- a/app/Http/Controllers/Api/Attendance/AttendanceCommentTemplateController.php +++ b/app/Http/Controllers/Api/Attendance/AttendanceCommentTemplateController.php @@ -14,8 +14,7 @@ class AttendanceCommentTemplateController extends Controller public function __construct( protected AttendanceCommentTemplateService $service, protected ApplicationUrlService $urls, - ) { - } + ) {} public function bootstrapUrls(): JsonResponse { @@ -75,7 +74,7 @@ class AttendanceCommentTemplateController extends Controller } $data = $validator->validated(); - if (!array_key_exists('is_active', $data)) { + if (! array_key_exists('is_active', $data)) { $data['is_active'] = true; } diff --git a/app/Http/Controllers/Api/Attendance/EarlyDismissalsController.php b/app/Http/Controllers/Api/Attendance/EarlyDismissalsController.php index fffae4f7..4c9a6cba 100644 --- a/app/Http/Controllers/Api/Attendance/EarlyDismissalsController.php +++ b/app/Http/Controllers/Api/Attendance/EarlyDismissalsController.php @@ -9,7 +9,6 @@ use Illuminate\Http\JsonResponse; use Illuminate\Http\Request; use Illuminate\Support\Facades\Auth; use Illuminate\Support\Facades\DB; -use Illuminate\Support\Facades\Storage; use Illuminate\Support\Facades\Validator; use Symfony\Component\HttpFoundation\Response; @@ -27,7 +26,7 @@ class EarlyDismissalsController extends BaseApiController public function index(Request $request): JsonResponse { $schoolYear = $request->query('school_year'); - $semester = $request->query('semester'); + $semester = $request->query('semester'); $query = DB::table('parent_attendance_reports as par') ->select([ @@ -62,15 +61,15 @@ class EarlyDismissalsController extends BaseApiController foreach ($rows as $row) { $date = $row->report_date ?? 'Unknown'; $groups[$date][] = [ - 'id' => $row->id, - 'firstname' => $row->firstname, - 'lastname' => $row->lastname, + 'id' => $row->id, + 'firstname' => $row->firstname, + 'lastname' => $row->lastname, 'class_section_name' => $row->class_section_name, - 'dismiss_time' => $row->dismiss_time, - 'reason' => $row->reason, - 'status' => $row->status, - 'school_year' => $row->school_year, - 'semester' => $row->semester, + 'dismiss_time' => $row->dismiss_time, + 'reason' => $row->reason, + 'status' => $row->status, + 'school_year' => $row->school_year, + 'semester' => $row->semester, ]; } @@ -82,13 +81,13 @@ class EarlyDismissalsController extends BaseApiController $signatureByDate = []; foreach ($signatures as $sig) { $signatureByDate[(string) $sig->report_date] = [ - 'filename' => $sig->filename, + 'filename' => $sig->filename, 'original_name' => $sig->original_name, ]; } return $this->success([ - 'groups' => $groups, + 'groups' => $groups, 'signatureByDate' => $signatureByDate, ]); } @@ -108,7 +107,7 @@ class EarlyDismissalsController extends BaseApiController ]) ->leftJoin('student_class as sc', function ($j) { $j->on('sc.student_id', '=', 's.id') - ->where('sc.school_year', '=', Configuration::getConfigValueByKey('school_year')); + ->where('sc.school_year', '=', Configuration::getConfigValueByKey('school_year')); }) ->leftJoin('classSection as cs', 'cs.class_section_id', '=', 'sc.class_section_id') ->where('s.is_active', 1) @@ -125,10 +124,10 @@ class EarlyDismissalsController extends BaseApiController public function store(Request $request): JsonResponse { $validator = Validator::make($request->all(), [ - 'student_id' => ['required', 'integer', 'min:1', 'exists:students,id'], - 'date' => ['required', 'date_format:Y-m-d'], + 'student_id' => ['required', 'integer', 'min:1', 'exists:students,id'], + 'date' => ['required', 'date_format:Y-m-d'], 'dismiss_time' => ['required', 'date_format:H:i'], - 'reason' => ['nullable', 'string', 'max:2000'], + 'reason' => ['nullable', 'string', 'max:2000'], ]); if ($validator->fails()) { @@ -138,7 +137,7 @@ class EarlyDismissalsController extends BaseApiController $data = $validator->validated(); $schoolYear = (string) Configuration::getConfigValueByKey('school_year'); - $semester = (string) Configuration::getConfigValueByKey('semester'); + $semester = (string) Configuration::getConfigValueByKey('semester'); $sc = DB::table('student_class') ->where('student_id', $data['student_id']) @@ -153,18 +152,18 @@ class EarlyDismissalsController extends BaseApiController ->value('parent_id') ?? Auth::id(); DB::table('parent_attendance_reports')->insert([ - 'parent_id' => $parentId ?? Auth::id(), - 'student_id' => $data['student_id'], + 'parent_id' => $parentId ?? Auth::id(), + 'student_id' => $data['student_id'], 'class_section_id' => $classSectionId, - 'report_date' => $data['date'], - 'type' => 'early_dismissal', - 'dismiss_time' => $data['dismiss_time'], - 'reason' => $data['reason'] ?? null, - 'semester' => $semester, - 'school_year' => $schoolYear, - 'status' => 'new', - 'created_at' => now(), - 'updated_at' => now(), + 'report_date' => $data['date'], + 'type' => 'early_dismissal', + 'dismiss_time' => $data['dismiss_time'], + 'reason' => $data['reason'] ?? null, + 'semester' => $semester, + 'school_year' => $schoolYear, + 'status' => 'new', + 'created_at' => now(), + 'updated_at' => now(), ]); return $this->success([], 'Early dismissal saved.', Response::HTTP_CREATED); @@ -177,9 +176,9 @@ class EarlyDismissalsController extends BaseApiController public function uploadSignature(Request $request): JsonResponse { $validator = Validator::make($request->all(), [ - 'report_date' => ['required', 'date_format:Y-m-d'], - 'school_year' => ['nullable', 'string', 'max:16'], - 'semester' => ['nullable', 'string', 'max:32'], + 'report_date' => ['required', 'date_format:Y-m-d'], + 'school_year' => ['nullable', 'string', 'max:16'], + 'semester' => ['nullable', 'string', 'max:32'], 'signature_file' => ['required', 'file', 'mimes:jpg,jpeg,png,webp,gif,pdf', 'max:5120'], ]); @@ -187,13 +186,13 @@ class EarlyDismissalsController extends BaseApiController return $this->respondValidationError($validator->errors()->toArray()); } - $file = $request->file('signature_file'); + $file = $request->file('signature_file'); $reportDate = $request->input('report_date'); $schoolYear = $request->input('school_year') ?: (string) Configuration::getConfigValueByKey('school_year'); - $semester = $request->input('semester') ?: (string) Configuration::getConfigValueByKey('semester'); + $semester = $request->input('semester') ?: (string) Configuration::getConfigValueByKey('semester'); - $dir = storage_path('uploads/early_dismissal_signatures'); - if (!is_dir($dir)) { + $dir = storage_path('uploads/early_dismissal_signatures'); + if (! is_dir($dir)) { mkdir($dir, 0755, true); } @@ -203,21 +202,21 @@ class EarlyDismissalsController extends BaseApiController ->where('semester', $semester) ->first(); - if ($existing?->filename && file_exists($dir . '/' . $existing->filename)) { - unlink($dir . '/' . $existing->filename); + if ($existing?->filename && file_exists($dir.'/'.$existing->filename)) { + unlink($dir.'/'.$existing->filename); } - $filename = uniqid('sig_', true) . '.' . $file->getClientOriginalExtension(); + $filename = uniqid('sig_', true).'.'.$file->getClientOriginalExtension(); $file->move($dir, $filename); EarlyDismissalSignature::updateOrCreate( ['report_date' => $reportDate, 'school_year' => $schoolYear, 'semester' => $semester], [ - 'filename' => $filename, + 'filename' => $filename, 'original_name' => $file->getClientOriginalName(), - 'mime_type' => $file->getClientMimeType(), - 'file_size' => $file->getSize(), - 'uploaded_by' => Auth::id(), + 'mime_type' => $file->getClientMimeType(), + 'file_size' => $file->getSize(), + 'uploaded_by' => Auth::id(), ] ); diff --git a/app/Http/Controllers/Api/Attendance/LateSlipLogsController.php b/app/Http/Controllers/Api/Attendance/LateSlipLogsController.php index ba8d3b8e..38237ebc 100644 --- a/app/Http/Controllers/Api/Attendance/LateSlipLogsController.php +++ b/app/Http/Controllers/Api/Attendance/LateSlipLogsController.php @@ -42,7 +42,7 @@ class LateSlipLogsController extends BaseApiController public function show(int $id): JsonResponse { $log = $this->queryService->find($id); - if (!$log) { + if (! $log) { return $this->error('Late slip log not found.', Response::HTTP_NOT_FOUND); } @@ -56,7 +56,7 @@ class LateSlipLogsController extends BaseApiController public function destroy(int $id): JsonResponse { $log = $this->queryService->find($id); - if (!$log) { + if (! $log) { return $this->error('Late slip log not found.', Response::HTTP_NOT_FOUND); } @@ -65,11 +65,12 @@ class LateSlipLogsController extends BaseApiController try { $deleted = $this->commandService->delete($log); } catch (\Throwable $e) { - Log::error('Late slip log delete failed: ' . $e->getMessage()); + Log::error('Late slip log delete failed: '.$e->getMessage()); + return $this->error('Unable to delete late slip log.', Response::HTTP_INTERNAL_SERVER_ERROR); } - if (!$deleted) { + if (! $deleted) { return $this->error('Unable to delete late slip log.', Response::HTTP_INTERNAL_SERVER_ERROR); } diff --git a/app/Http/Controllers/Api/Attendance/StaffAttendanceApiController.php b/app/Http/Controllers/Api/Attendance/StaffAttendanceApiController.php index 5fa4ae9f..4af30f2a 100644 --- a/app/Http/Controllers/Api/Attendance/StaffAttendanceApiController.php +++ b/app/Http/Controllers/Api/Attendance/StaffAttendanceApiController.php @@ -23,8 +23,8 @@ class StaffAttendanceApiController extends Controller { return new StaffMonthResource( $this->staffAttendanceService->monthData( - (string)$request->query('semester'), - (string)$request->query('school_year') + (string) $request->query('semester'), + (string) $request->query('school_year') ) ); } @@ -33,9 +33,9 @@ class StaffAttendanceApiController extends Controller { return new AdminAttendanceResource( $this->staffAttendanceService->adminsAttendanceData( - (string)$request->query('date'), - (string)$request->query('semester'), - (string)$request->query('school_year') + (string) $request->query('date'), + (string) $request->query('semester'), + (string) $request->query('school_year') ) ); } @@ -75,9 +75,9 @@ class StaffAttendanceApiController extends Controller public function monthCsv(Request $request): StreamedResponse { return $this->staffAttendanceService->monthCsv( - (string)$request->query('semester'), - (string)$request->query('school_year'), - (string)$request->query('scope') + (string) $request->query('semester'), + (string) $request->query('school_year'), + (string) $request->query('scope') ); } -} \ No newline at end of file +} diff --git a/app/Http/Controllers/Api/Attendance/TeacherAttendanceApiController.php b/app/Http/Controllers/Api/Attendance/TeacherAttendanceApiController.php index 84170381..fbefa6cd 100644 --- a/app/Http/Controllers/Api/Attendance/TeacherAttendanceApiController.php +++ b/app/Http/Controllers/Api/Attendance/TeacherAttendanceApiController.php @@ -21,10 +21,10 @@ class TeacherAttendanceApiController extends Controller public function grid(Request $request): TeacherGridResource { - $semester = (string)($request->query('semester') ?: $this->attendanceService->currentSemester()); - $schoolYear = (string)($request->query('school_year') ?: $this->attendanceService->currentSchoolYear()); - $date = (string)($request->query('date') ?: now()->toDateString()); - $sectionCode = (int)$request->query('class_section_id', 0); + $semester = (string) ($request->query('semester') ?: $this->attendanceService->currentSemester()); + $schoolYear = (string) ($request->query('school_year') ?: $this->attendanceService->currentSchoolYear()); + $date = (string) ($request->query('date') ?: now()->toDateString()); + $sectionCode = (int) $request->query('class_section_id', 0); return new TeacherGridResource( $this->queryService->teacherGrid($semester, $schoolYear, $date, $sectionCode) @@ -42,7 +42,7 @@ class TeacherAttendanceApiController extends Controller return new TeacherAttendanceFormResource( $this->queryService->teacherAttendanceFormData( $guard, - (int)$request->query('class_section_id', 0) + (int) $request->query('class_section_id', 0) ) ); } catch (Throwable $e) { @@ -66,9 +66,6 @@ class TeacherAttendanceApiController extends Controller } } - /** - * @return int|JsonResponse - */ private function authenticatedUserIdOrUnauthorized(): int|JsonResponse { $userId = (int) (auth()->id() ?? 0); @@ -78,4 +75,4 @@ class TeacherAttendanceApiController extends Controller return $userId; } -} \ No newline at end of file +} diff --git a/app/Http/Controllers/Api/AttendanceManagement/AttendanceManagementController.php b/app/Http/Controllers/Api/AttendanceManagement/AttendanceManagementController.php index 906dfad8..ab97f748 100644 --- a/app/Http/Controllers/Api/AttendanceManagement/AttendanceManagementController.php +++ b/app/Http/Controllers/Api/AttendanceManagement/AttendanceManagementController.php @@ -41,7 +41,9 @@ class AttendanceManagementController extends BaseApiController 'reason' => ['nullable', 'string', 'max:255'], 'notes' => ['nullable', 'string'], ]); - if ($validator->fails()) return $this->respondValidationError($validator->errors()->toArray()); + if ($validator->fails()) { + return $this->respondValidationError($validator->errors()->toArray()); + } try { return $this->success(['event' => $this->service->manualEntry($validator->validated(), $request->user())], 'Manual attendance entry recorded.', Response::HTTP_CREATED); } catch (\InvalidArgumentException $e) { @@ -63,7 +65,9 @@ class AttendanceManagementController extends BaseApiController 'authorized' => ['nullable'], 'reason' => ['nullable', 'string', 'max:255'], ]); - if ($validator->fails()) return $this->respondValidationError($validator->errors()->toArray()); + if ($validator->fails()) { + return $this->respondValidationError($validator->errors()->toArray()); + } try { return $this->success(['event' => $this->service->badgeScan($validator->validated(), $request->user())], 'Badge scan classified.'); } catch (\InvalidArgumentException $e) { @@ -91,7 +95,9 @@ class AttendanceManagementController extends BaseApiController 'reason' => ['nullable', 'string', 'max:255'], 'notes' => ['nullable', 'string'], ]); - if ($validator->fails()) return $this->respondValidationError($validator->errors()->toArray()); + if ($validator->fails()) { + return $this->respondValidationError($validator->errors()->toArray()); + } try { return $this->success(['event' => $this->service->exitEntry($validator->validated(), $request->user())], 'Exit attendance entry recorded.', Response::HTTP_CREATED); } catch (\InvalidArgumentException $e) { @@ -113,7 +119,9 @@ class AttendanceManagementController extends BaseApiController 'report_status' => ['nullable', 'string', 'max:48'], 'reason' => ['nullable', 'string', 'max:255'], ]); - if ($validator->fails()) return $this->respondValidationError($validator->errors()->toArray()); + if ($validator->fails()) { + return $this->respondValidationError($validator->errors()->toArray()); + } try { return $this->success(['event' => $this->service->markAbsent($validator->validated(), $request->user())], 'Absence recorded.', Response::HTTP_CREATED); } catch (\InvalidArgumentException $e) { @@ -130,7 +138,9 @@ class AttendanceManagementController extends BaseApiController 'final_decision' => ['nullable', 'string', 'max:255'], 'notes' => ['nullable', 'string'], ]); - if ($validator->fails()) return $this->respondValidationError($validator->errors()->toArray()); + if ($validator->fails()) { + return $this->respondValidationError($validator->errors()->toArray()); + } try { return $this->success(['event' => $this->service->completeFollowUp($id, $validator->validated(), $request->user())], 'Follow-up completed.'); } catch (\RuntimeException $e) { @@ -147,7 +157,9 @@ class AttendanceManagementController extends BaseApiController 'slip_number' => ['nullable', 'string', 'max:64'], 'late_slip_log_id' => ['nullable', 'integer'], ]); - if ($validator->fails()) return $this->respondValidationError($validator->errors()->toArray()); + if ($validator->fails()) { + return $this->respondValidationError($validator->errors()->toArray()); + } try { return $this->success(['reprint' => $this->service->reprintLateSlip($id, $validator->validated(), $request->user())], 'Late slip reprint logged.', Response::HTTP_CREATED); } catch (\RuntimeException $e) { diff --git a/app/Http/Controllers/Api/AttendanceTracking/AttendanceTrackingController.php b/app/Http/Controllers/Api/AttendanceTracking/AttendanceTrackingController.php index 6d53db9f..503524f5 100644 --- a/app/Http/Controllers/Api/AttendanceTracking/AttendanceTrackingController.php +++ b/app/Http/Controllers/Api/AttendanceTracking/AttendanceTrackingController.php @@ -12,8 +12,7 @@ class AttendanceTrackingController extends Controller { public function __construct( protected AttendanceTrackingService $service - ) { - } + ) {} public function pendingViolations(Request $request): JsonResponse { diff --git a/app/Http/Controllers/Api/Auth/AuthController.php b/app/Http/Controllers/Api/Auth/AuthController.php index f8977db1..74dd6e99 100644 --- a/app/Http/Controllers/Api/Auth/AuthController.php +++ b/app/Http/Controllers/Api/Auth/AuthController.php @@ -50,7 +50,7 @@ class AuthController extends BaseApiController } $user = User::query()->whereRaw('LOWER(email) = ?', [$email])->first(); - if (!$user) { + if (! $user) { $security->logIpAttempt((string) $ip); Log::notice('api_login: unknown email', [ 'ip' => (string) $ip, @@ -111,7 +111,7 @@ class AuthController extends BaseApiController $security->logSuccessfulLogin($user->fresh(), $request); $fresh = $user->fresh(); - if (!$fresh) { + if (! $fresh) { Log::warning('api_login: fresh user reload failed after successful verification', [ 'ip' => (string) $ip, 'email' => $this->maskEmail($email), @@ -139,7 +139,7 @@ class AuthController extends BaseApiController [$local, $domain] = explode('@', $email, 2); $localPrefix = substr($local, 0, min(2, strlen($local))); - return $localPrefix . str_repeat('*', max(strlen($local) - strlen($localPrefix), 0)) . '@' . $domain; + return $localPrefix.str_repeat('*', max(strlen($local) - strlen($localPrefix), 0)).'@'.$domain; } public function refresh(Request $request): JsonResponse @@ -160,7 +160,7 @@ class AuthController extends BaseApiController public function me(): JsonResponse { $user = Auth::user(); - if (!$user) { + if (! $user) { return $this->respondError('Unauthorized.', 401); } diff --git a/app/Http/Controllers/Api/Auth/AuthSessionController.php b/app/Http/Controllers/Api/Auth/AuthSessionController.php index 4dbca54f..f67bd3f8 100644 --- a/app/Http/Controllers/Api/Auth/AuthSessionController.php +++ b/app/Http/Controllers/Api/Auth/AuthSessionController.php @@ -203,7 +203,7 @@ class AuthSessionController extends Controller [$local, $domain] = explode('@', $email, 2); $localPrefix = substr($local, 0, min(2, strlen($local))); - return $localPrefix . str_repeat('*', max(strlen($local) - strlen($localPrefix), 0)) . '@' . $domain; + return $localPrefix.str_repeat('*', max(strlen($local) - strlen($localPrefix), 0)).'@'.$domain; } /** Closes the current web session. */ diff --git a/app/Http/Controllers/Api/Auth/IpBanController.php b/app/Http/Controllers/Api/Auth/IpBanController.php index 3f45f8c8..4eaf314c 100644 --- a/app/Http/Controllers/Api/Auth/IpBanController.php +++ b/app/Http/Controllers/Api/Auth/IpBanController.php @@ -44,7 +44,7 @@ class IpBanController extends BaseApiController public function show(int $id): JsonResponse { $ban = $this->queryService->find($id); - if (!$ban) { + if (! $ban) { return $this->error('IP record not found.', Response::HTTP_NOT_FOUND); } @@ -67,11 +67,12 @@ class IpBanController extends BaseApiController try { $ban = $this->commandService->banNow($id ? (int) $id : null, $ip ? (string) $ip : null, $hours); } catch (\Throwable $e) { - Log::error('IP ban failed: ' . $e->getMessage()); + Log::error('IP ban failed: '.$e->getMessage()); + return $this->error('Unable to ban IP.', Response::HTTP_INTERNAL_SERVER_ERROR); } - if (!$ban) { + if (! $ban) { return $this->error('IP record not found.', Response::HTTP_NOT_FOUND); } @@ -90,9 +91,10 @@ class IpBanController extends BaseApiController try { if ($all) { $count = $this->commandService->unbanAll(); + return $this->success([ 'count' => $count, - ], $count . ' IP(s) unbanned.'); + ], $count.' IP(s) unbanned.'); } $ban = $this->commandService->unbanOne( @@ -100,11 +102,12 @@ class IpBanController extends BaseApiController $validated['ip'] ?? null ); } catch (\Throwable $e) { - Log::error('IP unban failed: ' . $e->getMessage()); + Log::error('IP unban failed: '.$e->getMessage()); + return $this->error('Unable to unban IP.', Response::HTTP_INTERNAL_SERVER_ERROR); } - if (!$ban) { + if (! $ban) { return $this->error('IP record not found.', Response::HTTP_NOT_FOUND); } diff --git a/app/Http/Controllers/Api/Auth/RegisterController.php b/app/Http/Controllers/Api/Auth/RegisterController.php index 0de4fd44..5e1cafef 100644 --- a/app/Http/Controllers/Api/Auth/RegisterController.php +++ b/app/Http/Controllers/Api/Auth/RegisterController.php @@ -3,6 +3,7 @@ namespace App\Http\Controllers\Api\Auth; use App\Http\Controllers\Api\Core\BaseApiController; +use App\Http\Requests\Auth\RegisterRequest; use App\Http\Resources\Auth\RegisterResource; use App\Services\Auth\ApiRegistrationService; use App\Services\Auth\RegistrationCaptchaService; @@ -50,7 +51,7 @@ class RegisterController extends BaseApiController ]); } - $validator = Validator::make($request->all(), \App\Http\Requests\Auth\RegisterRequest::ruleset()); + $validator = Validator::make($request->all(), RegisterRequest::ruleset()); if ($validator->fails()) { return response()->json([ 'message' => 'Validation failed.', diff --git a/app/Http/Controllers/Api/Auth/RolePermissionController.php b/app/Http/Controllers/Api/Auth/RolePermissionController.php index 3395caac..e4e15825 100644 --- a/app/Http/Controllers/Api/Auth/RolePermissionController.php +++ b/app/Http/Controllers/Api/Auth/RolePermissionController.php @@ -56,7 +56,7 @@ class RolePermissionController extends BaseApiController public function showRole(int $roleId): JsonResponse { $role = Role::query()->find($roleId); - if (!$role) { + if (! $role) { return $this->error('Role not found.', Response::HTTP_NOT_FOUND); } @@ -70,7 +70,8 @@ class RolePermissionController extends BaseApiController try { $role = $this->roleCrudService->create($request->validated()); } catch (\Throwable $e) { - Log::error('Role store failed: ' . $e->getMessage()); + Log::error('Role store failed: '.$e->getMessage()); + return $this->error('Failed to create role.', Response::HTTP_INTERNAL_SERVER_ERROR); } @@ -82,14 +83,15 @@ class RolePermissionController extends BaseApiController public function updateRole(RoleUpdateRequest $request, int $roleId): JsonResponse { $role = Role::query()->find($roleId); - if (!$role) { + if (! $role) { return $this->error('Role not found.', Response::HTTP_NOT_FOUND); } try { $role = $this->roleCrudService->update($role, $request->validated()); } catch (\Throwable $e) { - Log::error('Role update failed: ' . $e->getMessage()); + Log::error('Role update failed: '.$e->getMessage()); + return $this->error('Failed to update role.', Response::HTTP_INTERNAL_SERVER_ERROR); } @@ -101,14 +103,15 @@ class RolePermissionController extends BaseApiController public function deleteRole(int $roleId): JsonResponse { $role = Role::query()->find($roleId); - if (!$role) { + if (! $role) { return $this->error('Role not found.', Response::HTTP_NOT_FOUND); } try { $this->roleCrudService->delete($role); } catch (\Throwable $e) { - Log::error('Role delete failed: ' . $e->getMessage()); + Log::error('Role delete failed: '.$e->getMessage()); + return $this->error('Failed to delete role.', Response::HTTP_INTERNAL_SERVER_ERROR); } @@ -144,11 +147,12 @@ class RolePermissionController extends BaseApiController $actorId ); } catch (\Throwable $e) { - Log::error('Assign roles failed: ' . $e->getMessage()); + Log::error('Assign roles failed: '.$e->getMessage()); + return $this->error('Failed to update roles.', Response::HTTP_INTERNAL_SERVER_ERROR); } - if (!($result['ok'] ?? false)) { + if (! ($result['ok'] ?? false)) { return $this->error($result['message'] ?? 'Failed to update roles.', Response::HTTP_UNPROCESSABLE_ENTITY); } @@ -169,7 +173,7 @@ class RolePermissionController extends BaseApiController public function showPermission(int $permissionId): JsonResponse { $permission = Permission::query()->find($permissionId); - if (!$permission) { + if (! $permission) { return $this->error('Permission not found.', Response::HTTP_NOT_FOUND); } @@ -183,7 +187,8 @@ class RolePermissionController extends BaseApiController try { $permission = $this->permissionCrudService->create($request->validated()); } catch (\Throwable $e) { - Log::error('Permission store failed: ' . $e->getMessage()); + Log::error('Permission store failed: '.$e->getMessage()); + return $this->error('Failed to create permission.', Response::HTTP_INTERNAL_SERVER_ERROR); } @@ -195,14 +200,15 @@ class RolePermissionController extends BaseApiController public function updatePermission(PermissionUpdateRequest $request, int $permissionId): JsonResponse { $permission = Permission::query()->find($permissionId); - if (!$permission) { + if (! $permission) { return $this->error('Permission not found.', Response::HTTP_NOT_FOUND); } try { $permission = $this->permissionCrudService->update($permission, $request->validated()); } catch (\Throwable $e) { - Log::error('Permission update failed: ' . $e->getMessage()); + Log::error('Permission update failed: '.$e->getMessage()); + return $this->error('Failed to update permission.', Response::HTTP_INTERNAL_SERVER_ERROR); } @@ -214,14 +220,15 @@ class RolePermissionController extends BaseApiController public function deletePermission(int $permissionId): JsonResponse { $permission = Permission::query()->find($permissionId); - if (!$permission) { + if (! $permission) { return $this->error('Permission not found.', Response::HTTP_NOT_FOUND); } try { $this->permissionCrudService->delete($permission); } catch (\Throwable $e) { - Log::error('Permission delete failed: ' . $e->getMessage()); + Log::error('Permission delete failed: '.$e->getMessage()); + return $this->error('Failed to delete permission.', Response::HTTP_INTERNAL_SERVER_ERROR); } @@ -242,16 +249,14 @@ class RolePermissionController extends BaseApiController try { $this->permissionService->saveRolePermissions($roleId, $request->validated()['permissions'] ?? []); } catch (\Throwable $e) { - Log::error('Role permissions update failed: ' . $e->getMessage()); + Log::error('Role permissions update failed: '.$e->getMessage()); + return $this->error('Failed to update role permissions.', Response::HTTP_INTERNAL_SERVER_ERROR); } return $this->success(null, 'Permissions saved successfully.'); } - /** - * @return int|JsonResponse - */ private function authenticatedUserIdOrUnauthorized(): int|JsonResponse { $userId = (int) (auth()->id() ?? 0); diff --git a/app/Http/Controllers/Api/Auth/RoleSwitcherController.php b/app/Http/Controllers/Api/Auth/RoleSwitcherController.php index e5427d11..569cbf1d 100644 --- a/app/Http/Controllers/Api/Auth/RoleSwitcherController.php +++ b/app/Http/Controllers/Api/Auth/RoleSwitcherController.php @@ -6,9 +6,9 @@ use App\Http\Controllers\Api\Core\BaseApiController; use App\Http\Requests\Roles\RoleSwitchRequest; use App\Services\Roles\RoleSwitchService; use Illuminate\Http\JsonResponse; +use Symfony\Component\HttpFoundation\Response; use Symfony\Component\HttpKernel\Exception\AccessDeniedHttpException; use Symfony\Component\HttpKernel\Exception\NotFoundHttpException; -use Symfony\Component\HttpFoundation\Response; class RoleSwitcherController extends BaseApiController { @@ -56,9 +56,6 @@ class RoleSwitcherController extends BaseApiController ], 'Role switched successfully.'); } - /** - * @return int|JsonResponse - */ private function authenticatedUserIdOrUnauthorized(): int|JsonResponse { $userId = (int) (auth()->id() ?? 0); diff --git a/app/Http/Controllers/Api/Auth/SessionTimeoutController.php b/app/Http/Controllers/Api/Auth/SessionTimeoutController.php index 6cb3a380..b29f19e9 100644 --- a/app/Http/Controllers/Api/Auth/SessionTimeoutController.php +++ b/app/Http/Controllers/Api/Auth/SessionTimeoutController.php @@ -19,8 +19,7 @@ class SessionTimeoutController extends Controller public function __construct( private ApplicationUrlService $urls, - ) { - } + ) {} /** * Returns timeout metadata for the web portal. diff --git a/app/Http/Controllers/Api/Badges/BadgeController.php b/app/Http/Controllers/Api/Badges/BadgeController.php index 9a2d1345..b9777caa 100644 --- a/app/Http/Controllers/Api/Badges/BadgeController.php +++ b/app/Http/Controllers/Api/Badges/BadgeController.php @@ -17,8 +17,7 @@ class BadgeController extends Controller protected BadgePdfService $badgePdfService, protected BadgePrintLogService $badgePrintLogService, protected BadgeFormDataService $badgeFormDataService - ) { - } + ) {} public function generatePdf(Request $request) { @@ -166,7 +165,7 @@ class BadgeController extends Controller if (is_string($ids)) { $ids = array_filter(array_map('trim', explode(',', $ids)), 'strlen'); - } elseif (!is_array($ids)) { + } elseif (! is_array($ids)) { $ids = $ids ? [$ids] : []; } @@ -199,7 +198,7 @@ class BadgeController extends Controller if (is_string($ids)) { $ids = array_filter(array_map('trim', explode(',', $ids)), 'strlen'); - } elseif (!is_array($ids)) { + } elseif (! is_array($ids)) { $ids = $ids ? [$ids] : []; } @@ -208,9 +207,6 @@ class BadgeController extends Controller return array_values(array_filter($ids, static fn ($v) => $v > 0)); } - /** - * @return int|JsonResponse - */ private function authenticatedUserIdOrUnauthorized(): int|JsonResponse { $userId = (int) (auth()->id() ?? 0); diff --git a/app/Http/Controllers/Api/BaseApiController.php b/app/Http/Controllers/Api/BaseApiController.php index 00e32be0..2134d855 100644 --- a/app/Http/Controllers/Api/BaseApiController.php +++ b/app/Http/Controllers/Api/BaseApiController.php @@ -4,6 +4,4 @@ namespace App\Http\Controllers\Api; use App\Http\Controllers\Api\Core\BaseApiController as CoreBaseApiController; -class BaseApiController extends CoreBaseApiController -{ -} +class BaseApiController extends CoreBaseApiController {} diff --git a/app/Http/Controllers/Api/ClassPreparation/ClassPreparationController.php b/app/Http/Controllers/Api/ClassPreparation/ClassPreparationController.php index 86732288..42bbbc45 100644 --- a/app/Http/Controllers/Api/ClassPreparation/ClassPreparationController.php +++ b/app/Http/Controllers/Api/ClassPreparation/ClassPreparationController.php @@ -20,15 +20,16 @@ use Symfony\Component\HttpFoundation\Response; class ClassPreparationController extends BaseApiController { private ClassPreparationService $service; + private StickerCountService $stickerCounts; + private ClassRosterService $roster; public function __construct( ClassPreparationService $service, StickerCountService $stickerCounts, ClassRosterService $roster - ) - { + ) { parent::__construct(); $this->service = $service; $this->stickerCounts = $stickerCounts; @@ -77,7 +78,8 @@ class ClassPreparationController extends BaseApiController try { $count = $this->service->saveAdjustments($classSectionId, $schoolYear, $validated['adjustments']); } catch (\Throwable $e) { - Log::error('Class prep adjustment save failed: ' . $e->getMessage()); + Log::error('Class prep adjustment save failed: '.$e->getMessage()); + return $this->error('Unable to save adjustments.', Response::HTTP_INTERNAL_SERVER_ERROR); } diff --git a/app/Http/Controllers/Api/ClassProgress/ClassProgressController.php b/app/Http/Controllers/Api/ClassProgress/ClassProgressController.php index 45ddf489..cda25c37 100644 --- a/app/Http/Controllers/Api/ClassProgress/ClassProgressController.php +++ b/app/Http/Controllers/Api/ClassProgress/ClassProgressController.php @@ -94,8 +94,9 @@ class ClassProgressController extends BaseApiController $filters['semester'] = $filters['semester'] ?? ($meta['semester'] ?? null); $paginator = $this->queryService->listReports($auth, $filters); - if (!empty($filters['group_by_week'])) { + if (! empty($filters['group_by_week'])) { $grouped = ClassProgressGroupCollection::fromPaginator($paginator); + return $this->respondSuccess($grouped, 'Progress reports retrieved.'); } @@ -135,6 +136,7 @@ class ClassProgressController extends BaseApiController Log::error('Failed to create class progress reports.', [ 'error' => $e->getMessage(), ]); + return $this->respondError('Unable to save progress reports.', 500); } @@ -170,6 +172,7 @@ class ClassProgressController extends BaseApiController Log::error('Failed to update class progress report.', [ 'error' => $e->getMessage(), ]); + return $this->respondError('Unable to update progress report.', 500); } @@ -184,6 +187,7 @@ class ClassProgressController extends BaseApiController Log::error('Failed to delete class progress report.', [ 'error' => $e->getMessage(), ]); + return $this->respondError('Unable to delete progress report.', 500); } @@ -193,7 +197,7 @@ class ClassProgressController extends BaseApiController public function downloadAttachment(int $attachment): BinaryFileResponse|JsonResponse { $record = ClassProgressAttachment::query()->find($attachment); - if (!$record || !$record->file_path) { + if (! $record || ! $record->file_path) { return $this->respondError('Attachment not found.', 404); } @@ -202,11 +206,12 @@ class ClassProgressController extends BaseApiController } $path = $this->attachmentService->resolvePath($record->file_path); - if (!$path) { + if (! $path) { return $this->respondError('Attachment missing.', 404); } $downloadName = $record->original_name ?: basename($path); + return response()->download($path, $downloadName); } @@ -214,12 +219,12 @@ class ClassProgressController extends BaseApiController { $this->authorize('view', $class_progress); - if (!$class_progress->attachment_path) { + if (! $class_progress->attachment_path) { return $this->respondError('Attachment not found.', 404); } $path = $this->attachmentService->resolvePath($class_progress->attachment_path); - if (!$path) { + if (! $path) { return $this->respondError('Attachment missing.', 404); } @@ -228,19 +233,18 @@ class ClassProgressController extends BaseApiController /** * @param mixed $user - * @return User|JsonResponse */ private function requireAuthenticatedUser($user): User|JsonResponse { - if (!$user instanceof User) { + if (! $user instanceof User) { $user = auth()->user(); } - if (!$user instanceof User) { + if (! $user instanceof User) { $user = $this->laravelRequest->user(); } - if (!$user instanceof User) { + if (! $user instanceof User) { return $this->respondError('Unauthorized.', Response::HTTP_UNAUTHORIZED); } diff --git a/app/Http/Controllers/Api/Classes/ClassController.php b/app/Http/Controllers/Api/Classes/ClassController.php index 09991bc2..1a5f86c0 100644 --- a/app/Http/Controllers/Api/Classes/ClassController.php +++ b/app/Http/Controllers/Api/Classes/ClassController.php @@ -53,7 +53,7 @@ class ClassController extends BaseApiController $section = $this->queryService->find((int) $classSection->id); - if (!$section) { + if (! $section) { return $this->error('Class section not found.', Response::HTTP_NOT_FOUND); } @@ -88,7 +88,7 @@ class ClassController extends BaseApiController { $this->authorize('delete', $classSection); - if (!$this->commandService->delete($classSection)) { + if (! $this->commandService->delete($classSection)) { return $this->error('Class section not found.', Response::HTTP_NOT_FOUND); } @@ -125,7 +125,8 @@ class ClassController extends BaseApiController try { $created = $this->seedService->seedDefaults(); } catch (\Throwable $e) { - Log::error('Seeding default classes failed: ' . $e->getMessage()); + Log::error('Seeding default classes failed: '.$e->getMessage()); + return $this->error('Unable to seed default classes.', Response::HTTP_INTERNAL_SERVER_ERROR); } diff --git a/app/Http/Controllers/Api/Communication/CommunicationController.php b/app/Http/Controllers/Api/Communication/CommunicationController.php index e55655bc..b8281e11 100644 --- a/app/Http/Controllers/Api/Communication/CommunicationController.php +++ b/app/Http/Controllers/Api/Communication/CommunicationController.php @@ -14,9 +14,13 @@ use Illuminate\Http\Request; class CommunicationController extends BaseApiController { private CommunicationStudentService $students; + private CommunicationTemplateService $templates; + private CommunicationFamilyService $families; + private CommunicationPreviewService $previewService; + private CommunicationSendService $sendService; public function __construct( @@ -76,7 +80,7 @@ class CommunicationController extends BaseApiController $teacherName = $this->resolveTeacherName(); $result = $this->previewService->buildPreview($templateKey, $studentId, $familyId, $vars, $teacherName); - if (!$result['ok']) { + if (! $result['ok']) { return response()->json([ 'ok' => false, 'message' => $result['message'] ?? 'Preview failed.', @@ -116,7 +120,7 @@ class CommunicationController extends BaseApiController } $student = $this->students->getStudentBasic($studentId); - if (!$student) { + if (! $student) { return response()->json([ 'ok' => false, 'message' => 'Student not found.', @@ -137,11 +141,11 @@ class CommunicationController extends BaseApiController 'recipients' => $recipients, 'cc' => $cc, 'bcc' => $bcc, - 'student_name' => trim(($student['firstname'] ?? '') . ' ' . ($student['lastname'] ?? '')), + 'student_name' => trim(($student['firstname'] ?? '').' '.($student['lastname'] ?? '')), 'sent_by' => $senderId, ]); - if (!$result['ok']) { + if (! $result['ok']) { return response()->json([ 'ok' => false, 'message' => 'Failed to send email.', @@ -175,15 +179,14 @@ class CommunicationController extends BaseApiController { $user = auth()->user(); if ($user) { - $name = trim(($user->firstname ?? '') . ' ' . ($user->lastname ?? '')); + $name = trim(($user->firstname ?? '').' '.($user->lastname ?? '')); + return $name !== '' ? $name : 'Teacher'; } + return 'Teacher'; } - /** - * @return int|JsonResponse - */ private function authenticatedUserIdOrUnauthorized(): int|JsonResponse { $userId = (int) (auth()->id() ?? 0); diff --git a/app/Http/Controllers/Api/CompetitionScores/CompetitionScoresController.php b/app/Http/Controllers/Api/CompetitionScores/CompetitionScoresController.php index 09dc3cfa..a1feb69b 100644 --- a/app/Http/Controllers/Api/CompetitionScores/CompetitionScoresController.php +++ b/app/Http/Controllers/Api/CompetitionScores/CompetitionScoresController.php @@ -14,8 +14,11 @@ use Illuminate\Http\Request; class CompetitionScoresController extends BaseApiController { private CompetitionScoresContextService $contextService; + private CompetitionScoresQueryService $queryService; + private CompetitionScoresRosterService $rosterService; + private CompetitionScoresSaveService $saveService; public function __construct( @@ -100,7 +103,7 @@ class CompetitionScoresController extends BaseApiController } $competition = Competition::query()->find($id); - if (!$competition) { + if (! $competition) { return response()->json([ 'ok' => false, 'message' => 'Competition not found.', @@ -120,7 +123,7 @@ class CompetitionScoresController extends BaseApiController ], 422); } - if (!in_array($classSectionId, $allowedClassIds, true)) { + if (! in_array($classSectionId, $allowedClassIds, true)) { return response()->json([ 'ok' => false, 'message' => 'You are not assigned to that class.', @@ -170,7 +173,7 @@ class CompetitionScoresController extends BaseApiController } $competition = Competition::query()->find($id); - if (!$competition) { + if (! $competition) { return response()->json([ 'ok' => false, 'message' => 'Competition not found.', @@ -194,7 +197,7 @@ class CompetitionScoresController extends BaseApiController ], 422); } - if (!in_array($classSectionId, $allowedClassIds, true)) { + if (! in_array($classSectionId, $allowedClassIds, true)) { return response()->json([ 'ok' => false, 'message' => 'You are not assigned to that class.', @@ -205,7 +208,7 @@ class CompetitionScoresController extends BaseApiController $scores = is_array($scores) ? $scores : []; [$cleanScores, $invalidScores] = $this->saveService->filterScores($scores); - if (!empty($invalidScores)) { + if (! empty($invalidScores)) { return response()->json([ 'ok' => false, 'message' => 'Scores must be whole numbers (no decimals).', @@ -222,9 +225,6 @@ class CompetitionScoresController extends BaseApiController ]); } - /** - * @return int|JsonResponse - */ private function authenticatedUserIdOrUnauthorized(): int|JsonResponse { $userId = (int) (auth()->id() ?? 0); diff --git a/app/Http/Controllers/Api/CompetitionWinners/CompetitionWinnersController.php b/app/Http/Controllers/Api/CompetitionWinners/CompetitionWinnersController.php index 4efc2c51..7eca8c84 100644 --- a/app/Http/Controllers/Api/CompetitionWinners/CompetitionWinnersController.php +++ b/app/Http/Controllers/Api/CompetitionWinners/CompetitionWinnersController.php @@ -313,9 +313,6 @@ class CompetitionWinnersController extends BaseApiController return response()->json($result, $result['ok'] ? Response::HTTP_OK : Response::HTTP_UNPROCESSABLE_ENTITY); } - /** - * @return int|JsonResponse - */ private function authenticatedAdminOrForbidden(): int|JsonResponse { $user = $this->getCurrentUser(); @@ -361,7 +358,6 @@ class CompetitionWinnersController extends BaseApiController } /** - * @param mixed $row * @return array */ private function rowToArray(mixed $row): array diff --git a/app/Http/Controllers/Api/Core/BaseApiController.php b/app/Http/Controllers/Api/Core/BaseApiController.php index 3b05ff3c..6cb46bb2 100644 --- a/app/Http/Controllers/Api/Core/BaseApiController.php +++ b/app/Http/Controllers/Api/Core/BaseApiController.php @@ -6,6 +6,7 @@ use App\Http\Controllers\Controller; use App\Models\User; use Illuminate\Contracts\Validation\Validator as ValidatorContract; use Illuminate\Database\Eloquent\Builder as EloquentBuilder; +use Illuminate\Database\Query\Builder; use Illuminate\Http\JsonResponse; use Illuminate\Http\Request; use Illuminate\Support\Facades\DB; @@ -16,7 +17,8 @@ use Symfony\Component\HttpFoundation\Response; class BaseApiController extends Controller { protected Request $laravelRequest; - protected ValidatorContract|null $validator = null; + + protected ?ValidatorContract $validator = null; public function __construct() { @@ -26,19 +28,19 @@ class BaseApiController extends Controller protected function success($data = null, string $message = 'Success', int $code = Response::HTTP_OK): JsonResponse { return response()->json([ - 'status' => true, + 'status' => true, 'message' => $message, - 'data' => $data, + 'data' => $data, ], $code); } protected function error(string $message = 'An error occurred', int $code = Response::HTTP_BAD_REQUEST, ?array $errors = null): JsonResponse { $payload = [ - 'status' => false, + 'status' => false, 'message' => $message, ]; - if (!empty($errors)) { + if (! empty($errors)) { $payload['errors'] = $errors; } @@ -91,6 +93,7 @@ class BaseApiController extends Controller { $payload = $this->payloadData(); $errors = $this->validateRequest($payload, $rules); + return empty($errors); } @@ -100,6 +103,7 @@ class BaseApiController extends Controller if (is_array($json)) { return $json; } + return array_merge($this->laravelRequest->query->all(), $this->laravelRequest->request->all()); } @@ -116,71 +120,88 @@ class BaseApiController extends Controller continue; } if (preg_match('/^max_length\\[(\\d+)\\]$/', $part, $m)) { - $result[] = 'max:' . $m[1]; + $result[] = 'max:'.$m[1]; + continue; } if (preg_match('/^min_length\\[(\\d+)\\]$/', $part, $m)) { - $result[] = 'min:' . $m[1]; + $result[] = 'min:'.$m[1]; + continue; } if (preg_match('/^in_list\\[(.+)\\]$/', $part, $m)) { - $result[] = 'in:' . $m[1]; + $result[] = 'in:'.$m[1]; + continue; } if (preg_match('/^greater_than_equal_to\\[(.+)\\]$/', $part, $m)) { - $result[] = 'gte:' . $m[1]; + $result[] = 'gte:'.$m[1]; + continue; } if (preg_match('/^less_than_equal_to\\[(.+)\\]$/', $part, $m)) { - $result[] = 'lte:' . $m[1]; + $result[] = 'lte:'.$m[1]; + continue; } if (preg_match('/^valid_date\\[(.+)\\]$/', $part, $m)) { - $result[] = 'date_format:' . $m[1]; + $result[] = 'date_format:'.$m[1]; + continue; } if ($part === 'valid_email') { $result[] = 'email'; + continue; } if ($part === 'permit_empty') { $result[] = 'nullable'; + continue; } if (preg_match('/^is_unique\\[([^\\.]+)\\.([^\\]]+)\\]$/', $part, $m)) { - $result[] = 'unique:' . $m[1] . ',' . $m[2]; + $result[] = 'unique:'.$m[1].','.$m[2]; + continue; } if ($part === 'string') { $result[] = 'string'; + continue; } if ($part === 'integer') { $result[] = 'integer'; + continue; } if ($part === 'numeric') { $result[] = 'numeric'; + continue; } if ($part === 'required') { $result[] = 'required'; + continue; } if ($part === 'alpha_numeric') { $result[] = 'alpha_num'; + continue; } if ($part === 'in_array') { $result[] = 'in_array'; + continue; } if ($part === 'valid_url') { $result[] = 'url'; + continue; } if (strpos($part, 'max_size') === 0) { $result[] = str_replace('max_size', 'max', $part); + continue; } $result[] = $part; @@ -191,11 +212,11 @@ class BaseApiController extends Controller protected function paginate($source, int $page = 1, int $perPage = 20): array { - $page = max(1, $page); + $page = max(1, $page); $perPage = max(1, $perPage); - $offset = ($page - 1) * $perPage; + $offset = ($page - 1) * $perPage; - if ($source instanceof EloquentBuilder || $source instanceof \Illuminate\Database\Query\Builder) { + if ($source instanceof EloquentBuilder || $source instanceof Builder) { $total = $source->toBase()->getCountForPagination(); $items = $source->offset($offset)->limit($perPage)->get()->toArray(); } elseif (is_array($source)) { @@ -206,9 +227,9 @@ class BaseApiController extends Controller 'data' => [], 'pagination' => [ 'current_page' => $page, - 'per_page' => $perPage, - 'total' => 0, - 'total_pages' => 0, + 'per_page' => $perPage, + 'total' => 0, + 'total_pages' => 0, ], ]; } @@ -217,9 +238,9 @@ class BaseApiController extends Controller 'data' => array_values($items), 'pagination' => [ 'current_page' => $page, - 'per_page' => $perPage, - 'total' => $total, - 'total_pages' => (int) ceil($total / $perPage), + 'per_page' => $perPage, + 'total' => $total, + 'total_pages' => (int) ceil($total / $perPage), ], ]; } @@ -233,29 +254,30 @@ class BaseApiController extends Controller if ($userId <= 0) { $userId = (int) (optional($this->laravelRequest->user())->id ?? 0); } + return $userId > 0 ? $userId : null; } protected function getCurrentUser(): ?object { $userId = $this->getCurrentUserId(); - if (!$userId) { + if (! $userId) { return null; } /** @var User|null $row */ $row = User::query()->find($userId); - if (!$row) { + if (! $row) { return null; } $firstname = (string) ($row->firstname ?? ''); - $lastname = (string) ($row->lastname ?? ''); - $email = $row->email; + $lastname = (string) ($row->lastname ?? ''); + $email = $row->email; - $name = trim($firstname . ' ' . $lastname); + $name = trim($firstname.' '.$lastname); if ($name === '') { - $name = $email ?? 'User #' . $userId; + $name = $email ?? 'User #'.$userId; } try { @@ -269,13 +291,13 @@ class BaseApiController extends Controller ->values() ->all(); } catch (\Throwable $e) { - Log::error('Unable to load user roles: ' . $e->getMessage()); + Log::error('Unable to load user roles: '.$e->getMessage()); $roles = []; } return (object) [ - 'id' => (int) $userId, - 'name' => $name, + 'id' => (int) $userId, + 'name' => $name, 'email' => $email, 'roles' => $roles, ]; diff --git a/app/Http/Controllers/Api/Discounts/DiscountController.php b/app/Http/Controllers/Api/Discounts/DiscountController.php index f1fb5183..0784d899 100644 --- a/app/Http/Controllers/Api/Discounts/DiscountController.php +++ b/app/Http/Controllers/Api/Discounts/DiscountController.php @@ -17,8 +17,11 @@ use Illuminate\Http\JsonResponse; class DiscountController extends BaseApiController { private DiscountContextService $context; + private DiscountVoucherService $voucherService; + private DiscountParentService $parentService; + private DiscountApplyService $applyService; public function __construct( @@ -69,6 +72,7 @@ class DiscountController extends BaseApiController public function listVouchers(): JsonResponse { $vouchers = $this->voucherService->listAll(); + return response()->json([ 'ok' => true, 'vouchers' => DiscountVoucherResource::collection($vouchers), @@ -78,6 +82,7 @@ class DiscountController extends BaseApiController public function storeVoucher(StoreDiscountVoucherRequest $request): JsonResponse { $voucher = $this->voucherService->create($request->validated()); + return response()->json([ 'ok' => true, 'voucher' => new DiscountVoucherResource($voucher), @@ -87,6 +92,7 @@ class DiscountController extends BaseApiController public function updateVoucher(UpdateDiscountVoucherRequest $request, int $id): JsonResponse { $voucher = $this->voucherService->update($id, $request->validated()); + return response()->json([ 'ok' => true, 'voucher' => new DiscountVoucherResource($voucher), @@ -96,6 +102,7 @@ class DiscountController extends BaseApiController public function showVoucher(int $id): JsonResponse { $voucher = $this->voucherService->find($id); + return response()->json([ 'ok' => true, 'voucher' => new DiscountVoucherResource($voucher), @@ -105,14 +112,12 @@ class DiscountController extends BaseApiController public function deleteVoucher(int $id): JsonResponse { $this->voucherService->delete($id); + return response()->json([ 'ok' => true, ]); } - /** - * @return int|JsonResponse - */ private function authenticatedUserIdOrUnauthorized(): int|JsonResponse { $userId = (int) (auth()->id() ?? 0); diff --git a/app/Http/Controllers/Api/Email/BroadcastEmailController.php b/app/Http/Controllers/Api/Email/BroadcastEmailController.php index 797d579c..200506b4 100644 --- a/app/Http/Controllers/Api/Email/BroadcastEmailController.php +++ b/app/Http/Controllers/Api/Email/BroadcastEmailController.php @@ -14,9 +14,13 @@ use Illuminate\Http\Request; class BroadcastEmailController extends BaseApiController { private BroadcastEmailSenderOptionsService $senderOptions; + private BroadcastEmailRecipientService $recipients; + private BroadcastEmailComposerService $composer; + private BroadcastEmailDispatchService $dispatch; + private BroadcastEmailImageService $imageService; public function __construct( @@ -140,7 +144,7 @@ class BroadcastEmailController extends BaseApiController public function uploadImage(Request $request): JsonResponse { $file = $request->file('image'); - if (!$file || !$file->isValid()) { + if (! $file || ! $file->isValid()) { return response()->json([ 'success' => false, 'error' => 'No image uploaded', @@ -148,7 +152,7 @@ class BroadcastEmailController extends BaseApiController } $result = $this->imageService->store($file); - if (!$result['ok']) { + if (! $result['ok']) { return response()->json([ 'success' => false, 'error' => $result['error'] ?? 'Upload failed', @@ -174,6 +178,7 @@ class BroadcastEmailController extends BaseApiController foreach (explode(',', $value) as $piece) { $ids[] = (int) $piece; } + continue; } $ids[] = (int) $value; diff --git a/app/Http/Controllers/Api/Email/EmailController.php b/app/Http/Controllers/Api/Email/EmailController.php index 696316a9..78d1d5de 100644 --- a/app/Http/Controllers/Api/Email/EmailController.php +++ b/app/Http/Controllers/Api/Email/EmailController.php @@ -46,7 +46,7 @@ class EmailController extends BaseApiController $attachments ); - if (!$ok) { + if (! $ok) { return response()->json([ 'ok' => false, 'message' => 'Email failed to send.', diff --git a/app/Http/Controllers/Api/Email/EmailExtractorController.php b/app/Http/Controllers/Api/Email/EmailExtractorController.php index f63cbd33..e1b797cd 100644 --- a/app/Http/Controllers/Api/Email/EmailExtractorController.php +++ b/app/Http/Controllers/Api/Email/EmailExtractorController.php @@ -8,7 +8,6 @@ use App\Http\Resources\Email\EmailExtractorCompareResource; use App\Http\Resources\Email\EmailExtractorEmailsResource; use App\Services\Email\EmailExtractorService; use Illuminate\Http\JsonResponse; -use Illuminate\Http\Request; class EmailExtractorController extends BaseApiController { diff --git a/app/Http/Controllers/Api/Exams/ExamDraftController.php b/app/Http/Controllers/Api/Exams/ExamDraftController.php index b434813c..4798260f 100644 --- a/app/Http/Controllers/Api/Exams/ExamDraftController.php +++ b/app/Http/Controllers/Api/Exams/ExamDraftController.php @@ -49,7 +49,7 @@ class ExamDraftController extends BaseApiController } $result = $this->teacherService->store($guard, $request->validated(), $request->file('draft_file')); - if (!$result['ok']) { + if (! $result['ok']) { return response()->json(['ok' => false, 'message' => $result['message'] ?? 'Unable to save.'], 422); } @@ -85,7 +85,7 @@ class ExamDraftController extends BaseApiController } $result = $this->adminService->uploadLegacy($guard, $request->validated(), $request->file('old_exam_file')); - if (!$result['ok']) { + if (! $result['ok']) { return response()->json(['ok' => false, 'message' => $result['message'] ?? 'Unable to save.'], 422); } @@ -103,7 +103,7 @@ class ExamDraftController extends BaseApiController } $result = $this->adminService->review($guard, $request->validated(), $request->file('final_file')); - if (!$result['ok']) { + if (! $result['ok']) { return response()->json(['ok' => false, 'message' => $result['message'] ?? 'Unable to save.'], 422); } @@ -113,9 +113,6 @@ class ExamDraftController extends BaseApiController ]); } - /** - * @return int|JsonResponse - */ private function authenticatedUserIdOrUnauthorized(): int|JsonResponse { $userId = (int) (auth()->id() ?? 0); diff --git a/app/Http/Controllers/Api/Expenses/ExpenseController.php b/app/Http/Controllers/Api/Expenses/ExpenseController.php index a7440736..771b573e 100644 --- a/app/Http/Controllers/Api/Expenses/ExpenseController.php +++ b/app/Http/Controllers/Api/Expenses/ExpenseController.php @@ -51,6 +51,7 @@ class ExpenseController extends BaseApiController $rows = $this->queryService->listAll(); $rows = array_map(function ($row) { $row['receipt_url'] = $this->receiptService->receiptUrl($row['receipt_path'] ?? null); + return $row; }, $rows); @@ -63,7 +64,7 @@ class ExpenseController extends BaseApiController public function show(int $id): JsonResponse { $row = $this->queryService->findById($id); - if (!$row) { + if (! $row) { return response()->json(['ok' => false, 'message' => 'Expense not found.'], 404); } @@ -121,7 +122,7 @@ class ExpenseController extends BaseApiController } $expense = Expense::query()->find($id); - if (!$expense) { + if (! $expense) { return response()->json(['ok' => false, 'message' => 'Expense not found.'], 404); } @@ -131,7 +132,7 @@ class ExpenseController extends BaseApiController if ($request->hasFile('receipt')) { $receiptName = $this->receiptService->storeReceipt($request->file('receipt')); } - if (!empty($payload['remove_receipt'])) { + if (! empty($payload['remove_receipt'])) { $receiptName = null; } @@ -176,7 +177,7 @@ class ExpenseController extends BaseApiController } $expense = Expense::query()->find($id); - if (!$expense) { + if (! $expense) { return response()->json(['ok' => false, 'message' => 'Expense not found.'], 404); } @@ -190,9 +191,6 @@ class ExpenseController extends BaseApiController ]); } - /** - * @return int|JsonResponse - */ private function authenticatedUserIdOrUnauthorized(): int|JsonResponse { $userId = (int) (auth()->id() ?? 0); diff --git a/app/Http/Controllers/Api/ExtraCharges/ExtraChargesController.php b/app/Http/Controllers/Api/ExtraCharges/ExtraChargesController.php index fac7d96d..696880de 100644 --- a/app/Http/Controllers/Api/ExtraCharges/ExtraChargesController.php +++ b/app/Http/Controllers/Api/ExtraCharges/ExtraChargesController.php @@ -103,7 +103,7 @@ class ExtraChargesController extends BaseApiController public function update(UpdateExtraChargeRequest $request, int $id): JsonResponse { $charge = AdditionalCharge::query()->find($id); - if (!$charge) { + if (! $charge) { return response()->json(['ok' => false, 'error' => 'Charge not found'], 404); } @@ -126,7 +126,7 @@ class ExtraChargesController extends BaseApiController public function void(int $id): JsonResponse { $charge = AdditionalCharge::query()->find($id); - if (!$charge) { + if (! $charge) { return response()->json(['ok' => false, 'error' => 'Charge not found'], 404); } @@ -140,7 +140,7 @@ class ExtraChargesController extends BaseApiController public function reverse(int $id): JsonResponse { $charge = AdditionalCharge::query()->find($id); - if (!$charge) { + if (! $charge) { return response()->json(['ok' => false, 'error' => 'Charge not found'], 404); } @@ -151,9 +151,6 @@ class ExtraChargesController extends BaseApiController ], $ok ? 200 : 422); } - /** - * @return int|JsonResponse - */ private function authenticatedUserIdOrUnauthorized(): int|JsonResponse { $userId = (int) (auth()->id() ?? 0); diff --git a/app/Http/Controllers/Api/Family/FamilyAdminController.php b/app/Http/Controllers/Api/Family/FamilyAdminController.php index 02d2007c..cc0ff8e9 100644 --- a/app/Http/Controllers/Api/Family/FamilyAdminController.php +++ b/app/Http/Controllers/Api/Family/FamilyAdminController.php @@ -72,7 +72,7 @@ class FamilyAdminController extends BaseApiController $schoolYear ); - if (!$family) { + if (! $family) { return $this->error('Family not found.', Response::HTTP_NOT_FOUND); } @@ -92,8 +92,9 @@ class FamilyAdminController extends BaseApiController $payload['reply_to_name'] ?? null ); - if (!$ok) { + if (! $ok) { Log::warning('Compose email failed for family admin', ['recipient' => $payload['recipient']]); + return $this->error('Unable to send email.', Response::HTTP_INTERNAL_SERVER_ERROR); } diff --git a/app/Http/Controllers/Api/Family/FamilyController.php b/app/Http/Controllers/Api/Family/FamilyController.php index 63f3c6ad..047755c4 100644 --- a/app/Http/Controllers/Api/Family/FamilyController.php +++ b/app/Http/Controllers/Api/Family/FamilyController.php @@ -53,7 +53,8 @@ class FamilyController extends BaseApiController try { $result = $this->mutationService->bootstrapFamilies(); } catch (\Throwable $e) { - Log::error('Family bootstrap failed: ' . $e->getMessage()); + Log::error('Family bootstrap failed: '.$e->getMessage()); + return $this->error('Family bootstrap failed.', Response::HTTP_INTERNAL_SERVER_ERROR); } @@ -69,7 +70,7 @@ class FamilyController extends BaseApiController (string) ($payload['relation'] ?? 'secondary') ); - if (!($result['ok'] ?? false)) { + if (! ($result['ok'] ?? false)) { return $this->error($result['message'] ?? 'Unable to attach guardian.', Response::HTTP_UNPROCESSABLE_ENTITY); } @@ -88,11 +89,12 @@ class FamilyController extends BaseApiController (string) ($payload['relation'] ?? 'secondary') ); } catch (\Throwable $e) { - Log::error('Attach guardian by email failed: ' . $e->getMessage()); + Log::error('Attach guardian by email failed: '.$e->getMessage()); + return $this->error('Unable to attach guardian.', Response::HTTP_INTERNAL_SERVER_ERROR); } - if (!($result['ok'] ?? false)) { + if (! ($result['ok'] ?? false)) { return $this->error($result['message'] ?? 'Unable to attach guardian.', Response::HTTP_UNPROCESSABLE_ENTITY); } @@ -156,11 +158,12 @@ class FamilyController extends BaseApiController try { $result = $this->mutationService->importSecondParentsFromLegacy(); } catch (\Throwable $e) { - Log::error('Legacy second parent import failed: ' . $e->getMessage()); + Log::error('Legacy second parent import failed: '.$e->getMessage()); + return $this->error('Legacy import failed.', Response::HTTP_INTERNAL_SERVER_ERROR); } - if (!($result['ok'] ?? true)) { + if (! ($result['ok'] ?? true)) { return $this->error($result['message'] ?? 'Legacy import failed.', Response::HTTP_UNPROCESSABLE_ENTITY); } diff --git a/app/Http/Controllers/Api/Finance/BalanceCarryforwardController.php b/app/Http/Controllers/Api/Finance/BalanceCarryforwardController.php index 2a8af7d8..43b268c9 100644 --- a/app/Http/Controllers/Api/Finance/BalanceCarryforwardController.php +++ b/app/Http/Controllers/Api/Finance/BalanceCarryforwardController.php @@ -38,12 +38,14 @@ class BalanceCarryforwardController extends Controller public function waive(CarryforwardAdjustmentRequest $request, int $carryforward): JsonResponse { $data = $request->validated(); + return response()->json(['ok' => true, 'carryforward' => $this->service->waive($carryforward, (float) $data['amount'], $data['reason'] ?? null, optional($request->user())->id)]); } public function adjust(CarryforwardAdjustmentRequest $request, int $carryforward): JsonResponse { $data = $request->validated(); + return response()->json(['ok' => true, 'carryforward' => $this->service->adjust($carryforward, (float) $data['amount'], $data['reason'] ?? null)]); } @@ -55,10 +57,13 @@ class BalanceCarryforwardController extends Controller public function reportCsv(Request $request): StreamedResponse { $rows = $this->service->csvRows($this->service->report($request->query())); + return response()->streamDownload(function () use ($rows) { $out = fopen('php://output', 'w'); - foreach ($rows as $row) fputcsv($out, $row); + foreach ($rows as $row) { + fputcsv($out, $row); + } fclose($out); - }, 'carryforward_report_' . date('Ymd_His') . '.csv', ['Content-Type' => 'text/csv']); + }, 'carryforward_report_'.date('Ymd_His').'.csv', ['Content-Type' => 'text/csv']); } } diff --git a/app/Http/Controllers/Api/Finance/ChargeController.php b/app/Http/Controllers/Api/Finance/ChargeController.php index ccb535ca..6ae5ea37 100644 --- a/app/Http/Controllers/Api/Finance/ChargeController.php +++ b/app/Http/Controllers/Api/Finance/ChargeController.php @@ -143,9 +143,6 @@ class ChargeController extends BaseApiController ], $status); } - /** - * @return int|JsonResponse - */ private function authenticatedUserIdOrUnauthorized(): int|JsonResponse { $userId = (int) (auth()->id() ?? 0); diff --git a/app/Http/Controllers/Api/Finance/EventChargeController.php b/app/Http/Controllers/Api/Finance/EventChargeController.php index 8d85d5e3..e5ed0654 100644 --- a/app/Http/Controllers/Api/Finance/EventChargeController.php +++ b/app/Http/Controllers/Api/Finance/EventChargeController.php @@ -15,15 +15,24 @@ class EventChargeController extends Controller public function index(Request $request): JsonResponse { $q = EventCharge::query(); - foreach (['parent_id','student_id','event_id','school_year','semester'] as $field) { - if ($request->filled($field)) $q->where($field, $request->query($field)); + foreach (['parent_id', 'student_id', 'event_id', 'school_year', 'semester'] as $field) { + if ($request->filled($field)) { + $q->where($field, $request->query($field)); + } } if ($request->filled('status')) { $status = $request->query('status'); - if (Schema::hasColumn('event_charges', 'status')) $q->where('status', $status); - elseif ($status === 'paid') $q->where('event_paid', 1); - elseif ($status === 'pending') $q->where(function ($qq) { $qq->whereNull('event_paid')->orWhere('event_paid', 0); }); + if (Schema::hasColumn('event_charges', 'status')) { + $q->where('status', $status); + } elseif ($status === 'paid') { + $q->where('event_paid', 1); + } elseif ($status === 'pending') { + $q->where(function ($qq) { + $qq->whereNull('event_paid')->orWhere('event_paid', 0); + }); + } } + return response()->json(['ok' => true, 'event_charges' => $q->orderByDesc('id')->paginate((int) $request->query('per_page', 25))]); } @@ -31,8 +40,11 @@ class EventChargeController extends Controller { $data = $this->normalizePayload($request->validated()); $data['created_by'] = optional($request->user())->id; - if (Schema::hasColumn('event_charges', 'status')) $data['status'] = $data['status'] ?? 'draft'; + if (Schema::hasColumn('event_charges', 'status')) { + $data['status'] = $data['status'] ?? 'draft'; + } $charge = EventCharge::query()->create($data); + return response()->json(['ok' => true, 'event_charge' => $charge], 201); } @@ -45,6 +57,7 @@ class EventChargeController extends Controller { $charge = EventCharge::query()->findOrFail($eventCharge); $charge->fill($this->normalizePayload($request->validated()))->save(); + return response()->json(['ok' => true, 'event_charge' => $charge->fresh()]); } @@ -56,38 +69,58 @@ class EventChargeController extends Controller public function approve(int $eventCharge): JsonResponse { $charge = EventCharge::query()->findOrFail($eventCharge); - if (Schema::hasColumn('event_charges', 'status')) $charge->status = 'approved'; - if (Schema::hasColumn('event_charges', 'charged')) $charge->charged = 1; + if (Schema::hasColumn('event_charges', 'status')) { + $charge->status = 'approved'; + } + if (Schema::hasColumn('event_charges', 'charged')) { + $charge->charged = 1; + } $charge->save(); + return response()->json(['ok' => true, 'event_charge' => $charge->fresh()]); } public function void(int $eventCharge): JsonResponse { $charge = EventCharge::query()->findOrFail($eventCharge); - if (Schema::hasColumn('event_charges', 'status')) $charge->status = 'voided'; - if (Schema::hasColumn('event_charges', 'charged')) $charge->charged = 0; + if (Schema::hasColumn('event_charges', 'status')) { + $charge->status = 'voided'; + } + if (Schema::hasColumn('event_charges', 'charged')) { + $charge->charged = 0; + } $charge->save(); + return response()->json(['ok' => true, 'event_charge' => $charge->fresh()]); } public function attachToInvoice(Request $request, int $eventCharge): JsonResponse { - $request->validate(['invoice_id' => ['required','integer']]); + $request->validate(['invoice_id' => ['required', 'integer']]); $charge = EventCharge::query()->findOrFail($eventCharge); $invoiceId = (int) $request->input('invoice_id'); - abort_if(!DB::table('invoices')->where('id', $invoiceId)->exists(), 404, 'Invoice not found.'); - if (Schema::hasColumn('event_charges', 'invoice_id')) $charge->invoice_id = $invoiceId; - if (Schema::hasColumn('event_charges', 'status')) $charge->status = 'invoiced'; - if (Schema::hasColumn('event_charges', 'charged')) $charge->charged = 1; + abort_if(! DB::table('invoices')->where('id', $invoiceId)->exists(), 404, 'Invoice not found.'); + if (Schema::hasColumn('event_charges', 'invoice_id')) { + $charge->invoice_id = $invoiceId; + } + if (Schema::hasColumn('event_charges', 'status')) { + $charge->status = 'invoiced'; + } + if (Schema::hasColumn('event_charges', 'charged')) { + $charge->charged = 1; + } $charge->save(); - return response()->json(['ok' => true, 'event_charge' => $charge->fresh(), 'warning' => Schema::hasColumn('event_charges','invoice_id') ? null : 'invoice_id column does not exist; charge marked as charged only.']); + + return response()->json(['ok' => true, 'event_charge' => $charge->fresh(), 'warning' => Schema::hasColumn('event_charges', 'invoice_id') ? null : 'invoice_id column does not exist; charge marked as charged only.']); } private function normalizePayload(array $data): array { - if (isset($data['title']) && !isset($data['event_name'])) $data['event_name'] = $data['title']; + if (isset($data['title']) && ! isset($data['event_name'])) { + $data['event_name'] = $data['title']; + } unset($data['title'], $data['invoice_id']); - return array_filter($data, fn($v) => $v !== null); + + return array_filter($data, fn ($v) => $v !== null); } } diff --git a/app/Http/Controllers/Api/Finance/FinanceNotificationController.php b/app/Http/Controllers/Api/Finance/FinanceNotificationController.php index 6b17e1a6..6b0e7740 100644 --- a/app/Http/Controllers/Api/Finance/FinanceNotificationController.php +++ b/app/Http/Controllers/Api/Finance/FinanceNotificationController.php @@ -16,7 +16,7 @@ class FinanceNotificationController extends Controller public function sendPaymentReceipt(Request $request, int $payment): JsonResponse { $p = DB::table('payments')->where('id', $payment)->first(); - abort_if(!$p, 404, 'Payment not found.'); + abort_if(! $p, 404, 'Payment not found.'); $receipt = $this->service->nextReceiptNumber($p->school_year ?? date('Y')); $log = $this->service->log([ 'parent_id' => $p->parent_id ?? null, @@ -25,34 +25,39 @@ class FinanceNotificationController extends Controller 'notification_type' => 'payment_receipt', 'channel' => 'database', 'recipient' => (string) ($p->parent_id ?? ''), - 'subject' => 'Payment receipt ' . $receipt, + 'subject' => 'Payment receipt '.$receipt, ], optional($request->user())->id); + return response()->json(['ok' => true, 'receipt_number' => $receipt, 'notification_log' => $log]); } public function sendRefundReceipt(Request $request, int $refund): JsonResponse { $log = $this->service->log(['refund_id' => $refund, 'notification_type' => 'refund_receipt', 'channel' => 'database', 'subject' => 'Refund receipt'], optional($request->user())->id); + return response()->json(['ok' => true, 'notification_log' => $log]); } public function sendInvoiceStatement(Request $request, int $invoice): JsonResponse { $inv = DB::table('invoices')->where('id', $invoice)->first(); - abort_if(!$inv, 404, 'Invoice not found.'); + abort_if(! $inv, 404, 'Invoice not found.'); $log = $this->service->log(['parent_id' => $inv->parent_id ?? null, 'invoice_id' => $invoice, 'notification_type' => 'statement_available', 'channel' => 'database', 'subject' => 'Statement available'], optional($request->user())->id); + return response()->json(['ok' => true, 'notification_log' => $log]); } public function sendOverdueReminder(Request $request, int $parent): JsonResponse { $log = $this->service->log(['parent_id' => $parent, 'notification_type' => 'overdue_balance_reminder', 'channel' => 'database', 'subject' => 'Overdue balance reminder'], optional($request->user())->id); + return response()->json(['ok' => true, 'notification_log' => $log]); } public function sendInstallmentReminder(Request $request, int $installment): JsonResponse { $log = $this->service->log(['installment_id' => $installment, 'notification_type' => 'installment_reminder', 'channel' => 'database', 'subject' => 'Installment reminder'], optional($request->user())->id); + return response()->json(['ok' => true, 'notification_log' => $log]); } diff --git a/app/Http/Controllers/Api/Finance/FinancialController.php b/app/Http/Controllers/Api/Finance/FinancialController.php index a78c8e36..ba354af4 100644 --- a/app/Http/Controllers/Api/Finance/FinancialController.php +++ b/app/Http/Controllers/Api/Finance/FinancialController.php @@ -5,10 +5,10 @@ namespace App\Http\Controllers\Api\Finance; use App\Http\Controllers\Api\Core\BaseApiController; use App\Http\Requests\Finance\FinancialReportRequest; use App\Http\Requests\Finance\FinancialSummaryRequest; -use App\Http\Requests\Finance\StakeholderFinancialAnalysisRequest; use App\Http\Requests\Finance\FinancialUnpaidParentsRequest; -use App\Http\Requests\Finance\ParentPaymentFollowUpRequest; use App\Http\Requests\Finance\FollowUpNoteRequest; +use App\Http\Requests\Finance\ParentPaymentFollowUpRequest; +use App\Http\Requests\Finance\StakeholderFinancialAnalysisRequest; use App\Http\Resources\Finance\FinancialReportResource; use App\Http\Resources\Finance\FinancialSummaryResource; use App\Http\Resources\Finance\FinancialUnpaidParentResource; @@ -16,9 +16,9 @@ use App\Services\Finance\FinancialPdfReportService; use App\Services\Finance\FinancialReportService; use App\Services\Finance\FinancialSchoolYearService; use App\Services\Finance\FinancialSummaryService; -use App\Services\Finance\StakeholderFinancialAnalysisService; use App\Services\Finance\FinancialUnpaidParentsService; use App\Services\Finance\ParentPaymentFollowUpService; +use App\Services\Finance\StakeholderFinancialAnalysisService; use Illuminate\Http\JsonResponse; use Symfony\Component\HttpFoundation\StreamedResponse; @@ -82,9 +82,6 @@ class FinancialController extends BaseApiController ]); } - - - public function parentPaymentFollowups(ParentPaymentFollowUpRequest $request): JsonResponse { return response()->json([ @@ -104,7 +101,7 @@ class FinancialController extends BaseApiController fputcsv($out, $row); } fclose($out); - }, 'parent_payment_followups_' . ($report['schoolYear'] ?? 'report') . '_' . date('Ymd_His') . '.csv', ['Content-Type' => 'text/csv']); + }, 'parent_payment_followups_'.($report['schoolYear'] ?? 'report').'_'.date('Ymd_His').'.csv', ['Content-Type' => 'text/csv']); } public function storeParentFollowUpNote(FollowUpNoteRequest $request, int $parent): JsonResponse @@ -118,18 +115,21 @@ class FinancialController extends BaseApiController public function markParentContacted(FollowUpNoteRequest $request, int $parent): JsonResponse { $payload = array_merge($request->validated(), ['status' => $request->input('status', 'contacted')]); + return response()->json(['ok' => true, 'note' => $this->parentPaymentFollowUpService->storeNote($parent, $payload, optional($request->user())->id)], 201); } public function storePromiseToPay(FollowUpNoteRequest $request, int $parent): JsonResponse { $payload = array_merge($request->validated(), ['status' => 'promise_to_pay']); + return response()->json(['ok' => true, 'note' => $this->parentPaymentFollowUpService->storeNote($parent, $payload, optional($request->user())->id)], 201); } public function resolveParentFollowUp(FollowUpNoteRequest $request, int $parent): JsonResponse { $payload = array_merge($request->validated(), ['status' => 'resolved']); + return response()->json(['ok' => true, 'note' => $this->parentPaymentFollowUpService->storeNote($parent, $payload, optional($request->user())->id)], 201); } @@ -168,7 +168,7 @@ class FinancialController extends BaseApiController fputcsv($out, $row); } fclose($out); - }, 'stakeholder_financial_analysis_' . $schoolYear . '_' . date('Ymd_His') . '.csv', ['Content-Type' => 'text/csv']); + }, 'stakeholder_financial_analysis_'.$schoolYear.'_'.date('Ymd_His').'.csv', ['Content-Type' => 'text/csv']); } public function downloadCsv(FinancialReportRequest $request): StreamedResponse @@ -200,7 +200,7 @@ class FinancialController extends BaseApiController $breakdown = $report['paymentBreakdown'] ?? []; - $filename = 'financial_report_' . date('Ymd_His') . '.csv'; + $filename = 'financial_report_'.date('Ymd_His').'.csv'; return response()->streamDownload(function () use ($report, $paymentsMap, $refundsMap, $discountsMap, $breakdown) { $out = fopen('php://output', 'w'); @@ -309,6 +309,6 @@ class FinancialController extends BaseApiController return response()->streamDownload(function () use ($pdfBytes) { echo $pdfBytes; - }, 'Financial_Report_' . $schoolYear . '.pdf', ['Content-Type' => 'application/pdf']); + }, 'Financial_Report_'.$schoolYear.'.pdf', ['Content-Type' => 'application/pdf']); } } diff --git a/app/Http/Controllers/Api/Finance/InstallmentPlanController.php b/app/Http/Controllers/Api/Finance/InstallmentPlanController.php index 796c66e5..98e44832 100644 --- a/app/Http/Controllers/Api/Finance/InstallmentPlanController.php +++ b/app/Http/Controllers/Api/Finance/InstallmentPlanController.php @@ -13,17 +13,52 @@ class InstallmentPlanController extends Controller { public function __construct(private InstallmentPlanService $service) {} - public function index(Request $request): JsonResponse { return response()->json(['ok' => true, 'plans' => $this->service->list($request->query())]); } - public function store(InstallmentPlanRequest $request, int $invoice): JsonResponse { return response()->json(['ok' => true, 'plan' => $this->service->createForInvoice($invoice, $request->validated(), optional($request->user())->id)], 201); } - public function show(int $plan): JsonResponse { return response()->json(['ok' => true, 'plan' => $this->service->show($plan)]); } - public function activate(Request $request, int $plan): JsonResponse { return response()->json(['ok' => true, 'plan' => $this->service->activate($plan, optional($request->user())->id)]); } - public function cancel(int $plan): JsonResponse { return response()->json(['ok' => true, 'plan' => $this->service->cancel($plan)]); } + public function index(Request $request): JsonResponse + { + return response()->json(['ok' => true, 'plans' => $this->service->list($request->query())]); + } + + public function store(InstallmentPlanRequest $request, int $invoice): JsonResponse + { + return response()->json(['ok' => true, 'plan' => $this->service->createForInvoice($invoice, $request->validated(), optional($request->user())->id)], 201); + } + + public function show(int $plan): JsonResponse + { + return response()->json(['ok' => true, 'plan' => $this->service->show($plan)]); + } + + public function activate(Request $request, int $plan): JsonResponse + { + return response()->json(['ok' => true, 'plan' => $this->service->activate($plan, optional($request->user())->id)]); + } + + public function cancel(int $plan): JsonResponse + { + return response()->json(['ok' => true, 'plan' => $this->service->cancel($plan)]); + } + public function restructure(InstallmentPlanRequest $request, int $plan): JsonResponse { $old = $this->service->cancel($plan); + return response()->json(['ok' => true, 'old_plan' => $old, 'message' => 'Old plan cancelled. Create a replacement plan against the invoice to preserve audit history.']); } - public function allocatePayment(PaymentAllocationRequest $request, int $payment): JsonResponse { return response()->json(['ok' => true, 'result' => $this->service->allocatePayment($payment, $request->validated())]); } - public function due(): JsonResponse { return response()->json(['ok' => true, 'installments' => $this->service->due(false)]); } - public function overdue(): JsonResponse { $this->service->markOverdue(); return response()->json(['ok' => true, 'installments' => $this->service->due(true)]); } + + public function allocatePayment(PaymentAllocationRequest $request, int $payment): JsonResponse + { + return response()->json(['ok' => true, 'result' => $this->service->allocatePayment($payment, $request->validated())]); + } + + public function due(): JsonResponse + { + return response()->json(['ok' => true, 'installments' => $this->service->due(false)]); + } + + public function overdue(): JsonResponse + { + $this->service->markOverdue(); + + return response()->json(['ok' => true, 'installments' => $this->service->due(true)]); + } } diff --git a/app/Http/Controllers/Api/Finance/InvoiceController.php b/app/Http/Controllers/Api/Finance/InvoiceController.php index 153ba53a..0a2dc585 100644 --- a/app/Http/Controllers/Api/Finance/InvoiceController.php +++ b/app/Http/Controllers/Api/Finance/InvoiceController.php @@ -111,7 +111,7 @@ class InvoiceController extends BaseApiController $status = $request->validated()['status']; $updated = Invoice::updateInvoiceStatus($invoiceId, $status); - if (!$updated) { + if (! $updated) { return response()->json(['ok' => false, 'message' => 'Invoice not found.'], 404); } @@ -151,12 +151,9 @@ class InvoiceController extends BaseApiController return response()->streamDownload(function () use ($pdfBytes) { echo $pdfBytes; - }, 'invoice_' . $invoiceId . '.pdf', ['Content-Type' => 'application/pdf']); + }, 'invoice_'.$invoiceId.'.pdf', ['Content-Type' => 'application/pdf']); } - /** - * @return int|JsonResponse - */ private function authenticatedUserIdOrUnauthorized(): int|JsonResponse { $userId = (int) (auth()->id() ?? 0); diff --git a/app/Http/Controllers/Api/Finance/PaymentController.php b/app/Http/Controllers/Api/Finance/PaymentController.php index db5c5477..100e6462 100644 --- a/app/Http/Controllers/Api/Finance/PaymentController.php +++ b/app/Http/Controllers/Api/Finance/PaymentController.php @@ -82,7 +82,7 @@ class PaymentController extends BaseApiController $payload = $validator->validated(); $updated = $this->balanceService->updateBalance($paymentId, (float) $payload['paid_amount']); - if (!$updated) { + if (! $updated) { return response()->json(['ok' => false, 'message' => 'Payment not found.'], 404); } diff --git a/app/Http/Controllers/Api/Finance/PaymentEventChargesController.php b/app/Http/Controllers/Api/Finance/PaymentEventChargesController.php index 5eeff71e..bc8dc02f 100644 --- a/app/Http/Controllers/Api/Finance/PaymentEventChargesController.php +++ b/app/Http/Controllers/Api/Finance/PaymentEventChargesController.php @@ -69,9 +69,6 @@ class PaymentEventChargesController extends BaseApiController return response()->json(['ok' => true, 'students' => $students]); } - /** - * @return int|JsonResponse - */ private function authenticatedUserIdOrUnauthorized(): int|JsonResponse { $userId = (int) (auth()->id() ?? 0); diff --git a/app/Http/Controllers/Api/Finance/PaymentManualController.php b/app/Http/Controllers/Api/Finance/PaymentManualController.php index baa1a4fa..b945d6a6 100644 --- a/app/Http/Controllers/Api/Finance/PaymentManualController.php +++ b/app/Http/Controllers/Api/Finance/PaymentManualController.php @@ -8,8 +8,8 @@ use App\Http\Requests\Payments\PaymentManualUpdateRequest; use App\Services\Payments\PaymentManualService; use Illuminate\Http\JsonResponse; use Illuminate\Http\Request; -use Illuminate\Support\Facades\Validator; use Illuminate\Support\Facades\Log; +use Illuminate\Support\Facades\Validator; class PaymentManualController extends BaseApiController { @@ -87,7 +87,8 @@ class PaymentManualController extends BaseApiController } catch (\InvalidArgumentException $e) { return response()->json(['ok' => false, 'message' => $e->getMessage()], 422); } catch (\Throwable $e) { - Log::error('Manual payment record failed: ' . $e->getMessage()); + Log::error('Manual payment record failed: '.$e->getMessage()); + return response()->json(['ok' => false, 'message' => 'Unable to record payment.'], 400); } @@ -115,7 +116,8 @@ class PaymentManualController extends BaseApiController } catch (\InvalidArgumentException $e) { return response()->json(['ok' => false, 'message' => $e->getMessage()], 422); } catch (\Throwable $e) { - Log::error('Manual payment edit failed: ' . $e->getMessage()); + Log::error('Manual payment edit failed: '.$e->getMessage()); + return response()->json(['ok' => false, 'message' => 'Unable to update payment.'], 400); } @@ -125,12 +127,10 @@ class PaymentManualController extends BaseApiController public function file(Request $request, string $filename) { $mode = (string) ($request->query('mode') ?? 'download'); + return $this->service->serveCheckFile($filename, $mode); } - /** - * @return int|JsonResponse - */ private function authenticatedUserIdOrUnauthorized(): int|JsonResponse { $userId = (int) (auth()->id() ?? 0); diff --git a/app/Http/Controllers/Api/Finance/PaymentTransactionController.php b/app/Http/Controllers/Api/Finance/PaymentTransactionController.php index f52645a1..25ff846b 100644 --- a/app/Http/Controllers/Api/Finance/PaymentTransactionController.php +++ b/app/Http/Controllers/Api/Finance/PaymentTransactionController.php @@ -83,7 +83,7 @@ class PaymentTransactionController extends BaseApiController $updated = $this->service->updateStatus($transactionId, (string) $status); - if (!$updated) { + if (! $updated) { return response()->json(['ok' => false, 'message' => 'Transaction not found.'], 404); } diff --git a/app/Http/Controllers/Api/Finance/PaypalTransactionsController.php b/app/Http/Controllers/Api/Finance/PaypalTransactionsController.php index d6a75e3b..f9bdf486 100644 --- a/app/Http/Controllers/Api/Finance/PaypalTransactionsController.php +++ b/app/Http/Controllers/Api/Finance/PaypalTransactionsController.php @@ -39,7 +39,7 @@ class PaypalTransactionsController extends BaseApiController $payload = $request->validated(); $rows = $this->service->listAll($payload['q'] ?? null); - $filename = 'paypal_transactions_' . date('Ymd_His') . '.csv'; + $filename = 'paypal_transactions_'.date('Ymd_His').'.csv'; return response()->streamDownload(function () use ($rows) { $out = fopen('php://output', 'w'); diff --git a/app/Http/Controllers/Api/Finance/PurchaseOrderController.php b/app/Http/Controllers/Api/Finance/PurchaseOrderController.php index 7be4dd29..e8ef46b9 100644 --- a/app/Http/Controllers/Api/Finance/PurchaseOrderController.php +++ b/app/Http/Controllers/Api/Finance/PurchaseOrderController.php @@ -52,7 +52,7 @@ class PurchaseOrderController extends BaseApiController public function show(int $id): JsonResponse { $data = $this->queryService->find($id); - if (!$data) { + if (! $data) { return response()->json(['ok' => false, 'message' => 'Purchase order not found.'], 404); } @@ -69,7 +69,7 @@ class PurchaseOrderController extends BaseApiController $validator = Validator::make($data, [ 'po_number' => ['required', 'string', 'max:60'], 'supplier_id' => ['required', 'integer', 'min:1', 'exists:suppliers,id'], - 'status' => ['nullable', 'string', 'in:' . implode(',', PurchaseOrder::allowedStatuses())], + 'status' => ['nullable', 'string', 'in:'.implode(',', PurchaseOrder::allowedStatuses())], 'order_date' => ['nullable', 'date'], 'expected_date' => ['nullable', 'date'], 'notes' => ['nullable', 'string', 'max:5000'], @@ -88,7 +88,7 @@ class PurchaseOrderController extends BaseApiController } $payload = $validator->validated(); - if (!empty($payload['order_date']) && !empty($payload['expected_date'])) { + if (! empty($payload['order_date']) && ! empty($payload['expected_date'])) { $orderTs = strtotime((string) $payload['order_date']); $expectedTs = strtotime((string) $payload['expected_date']); if ($orderTs !== false && $expectedTs !== false && $expectedTs < $orderTs) { @@ -136,7 +136,7 @@ class PurchaseOrderController extends BaseApiController $payload = $validator->validated(); $po = PurchaseOrder::query()->find($id); - if (!$po) { + if (! $po) { return response()->json(['ok' => false, 'message' => 'Purchase order not found.'], 404); } @@ -169,7 +169,7 @@ class PurchaseOrderController extends BaseApiController } $po = PurchaseOrder::query()->find($id); - if (!$po) { + if (! $po) { return response()->json(['ok' => false, 'message' => 'Purchase order not found.'], 404); } diff --git a/app/Http/Controllers/Api/Finance/RefundController.php b/app/Http/Controllers/Api/Finance/RefundController.php index 6c503880..76ffbbf2 100644 --- a/app/Http/Controllers/Api/Finance/RefundController.php +++ b/app/Http/Controllers/Api/Finance/RefundController.php @@ -48,7 +48,7 @@ class RefundController extends BaseApiController public function show(int $refundId): JsonResponse { $refund = $this->queryService->find($refundId); - if (!$refund) { + if (! $refund) { return response()->json(['ok' => false, 'message' => 'Refund not found.'], 404); } @@ -71,6 +71,7 @@ class RefundController extends BaseApiController $result = $this->requestService->requestRefund($payload, $guard); } catch (\Throwable $e) { Log::error('Refund request failed.', ['error' => $e->getMessage()]); + return response()->json(['ok' => false, 'message' => 'Failed to submit refund request.'], 500); } @@ -212,9 +213,6 @@ class RefundController extends BaseApiController ]); } - /** - * @return int|JsonResponse - */ private function authenticatedUserIdOrUnauthorized(): int|JsonResponse { $userId = (int) (auth()->id() ?? 0); diff --git a/app/Http/Controllers/Api/Finance/ReimbursementController.php b/app/Http/Controllers/Api/Finance/ReimbursementController.php index 114b9fdb..f343854d 100644 --- a/app/Http/Controllers/Api/Finance/ReimbursementController.php +++ b/app/Http/Controllers/Api/Finance/ReimbursementController.php @@ -93,6 +93,7 @@ class ReimbursementController extends BaseApiController } catch (RuntimeException $e) { $message = $e->getMessage(); $status = str_contains($message, 'not found') ? 404 : (str_contains($message, 'already') ? 409 : 422); + return response()->json(['ok' => false, 'message' => $message], $status); } catch (\Throwable $e) { return response()->json(['ok' => false, 'message' => 'Unable to mark donation right now.'], 500); @@ -144,7 +145,7 @@ class ReimbursementController extends BaseApiController $adminIdRaw = $payload['admin_id'] ?? null; $adminId = ($adminIdRaw === '' || $adminIdRaw === null) ? null : (int) $adminIdRaw; - $reimbursementId = !empty($payload['reimbursement_id']) ? (int) $payload['reimbursement_id'] : null; + $reimbursementId = ! empty($payload['reimbursement_id']) ? (int) $payload['reimbursement_id'] : null; try { $result = $this->assignmentService->updateAssignment( @@ -184,6 +185,7 @@ class ReimbursementController extends BaseApiController } catch (RuntimeException $e) { $message = $e->getMessage(); $status = str_contains($message, 'check file') ? 409 : 404; + return response()->json(['ok' => false, 'message' => $message], $status); } catch (\Throwable $e) { return response()->json(['ok' => false, 'message' => 'Unable to lock batch right now.'], 500); @@ -204,7 +206,7 @@ class ReimbursementController extends BaseApiController $adminId = (int) $payload['admin_id']; $batch = ReimbursementBatch::query()->find($batchId); - if (!$batch) { + if (! $batch) { return response()->json(['ok' => false, 'message' => 'Batch not found.'], 404); } if (strtolower((string) ($batch->status ?? '')) !== ReimbursementBatch::STATUS_OPEN) { @@ -231,7 +233,7 @@ class ReimbursementController extends BaseApiController return response(file_get_contents($meta['path']), 200, [ 'Content-Type' => $meta['mime'], - 'Content-Disposition' => $disposition . '; filename="' . $meta['download_name'] . '"', + 'Content-Disposition' => $disposition.'; filename="'.$meta['download_name'].'"', 'Content-Length' => (string) $meta['size'], 'ETag' => $meta['etag'], 'Last-Modified' => $meta['last_modified'], @@ -281,7 +283,7 @@ class ReimbursementController extends BaseApiController return response()->json(['ok' => false, 'message' => $e->getMessage()], 404); } - if (!$ok) { + if (! $ok) { return response()->json(['ok' => false, 'message' => 'Failed to send email. Please try again.'], 500); } @@ -301,7 +303,7 @@ class ReimbursementController extends BaseApiController return response()->streamDownload(function () use ($csv) { $out = fopen('php://output', 'w'); - fprintf($out, chr(0xEF) . chr(0xBB) . chr(0xBF)); + fprintf($out, chr(0xEF).chr(0xBB).chr(0xBF)); foreach ($csv['rows'] as $row) { fputcsv($out, $row); } @@ -318,7 +320,7 @@ class ReimbursementController extends BaseApiController return response()->streamDownload(function () use ($csv) { $out = fopen('php://output', 'w'); - fprintf($out, chr(0xEF) . chr(0xBB) . chr(0xBF)); + fprintf($out, chr(0xEF).chr(0xBB).chr(0xBF)); foreach ($csv['rows'] as $row) { fputcsv($out, $row); } @@ -436,7 +438,7 @@ class ReimbursementController extends BaseApiController } $reimb = Reimbursement::query()->find($id); - if (!$reimb) { + if (! $reimb) { return response()->json(['ok' => false, 'message' => 'Reimbursement not found.'], 404); } @@ -464,7 +466,7 @@ class ReimbursementController extends BaseApiController if ($request->hasFile('receipt')) { $receiptName = $this->fileService->storeReceipt($request->file('receipt')); } - if (!empty($payload['remove_receipt'])) { + if (! empty($payload['remove_receipt'])) { $receiptName = null; } @@ -478,9 +480,6 @@ class ReimbursementController extends BaseApiController ]); } - /** - * @return int|JsonResponse - */ private function authenticatedUserIdOrUnauthorized(): int|JsonResponse { $userId = (int) (auth()->id() ?? 0); diff --git a/app/Http/Controllers/Api/Frontend/FrontendController.php b/app/Http/Controllers/Api/Frontend/FrontendController.php index 95664035..12977b61 100644 --- a/app/Http/Controllers/Api/Frontend/FrontendController.php +++ b/app/Http/Controllers/Api/Frontend/FrontendController.php @@ -61,7 +61,7 @@ class FrontendController extends BaseApiController public function fetchUser(): JsonResponse { $user = auth()->user(); - if (!$user) { + if (! $user) { return $this->error('Unauthorized.', Response::HTTP_UNAUTHORIZED); } diff --git a/app/Http/Controllers/Api/Frontend/InfoIconController.php b/app/Http/Controllers/Api/Frontend/InfoIconController.php index 150dcdda..c6649574 100644 --- a/app/Http/Controllers/Api/Frontend/InfoIconController.php +++ b/app/Http/Controllers/Api/Frontend/InfoIconController.php @@ -29,9 +29,6 @@ class InfoIconController extends BaseApiController ]); } - /** - * @return int|JsonResponse - */ private function authenticatedUserIdOrUnauthorized(): int|JsonResponse { $userId = (int) (auth()->id() ?? 0); diff --git a/app/Http/Controllers/Api/Frontend/LandingPageController.php b/app/Http/Controllers/Api/Frontend/LandingPageController.php index cc792f02..5e019a68 100644 --- a/app/Http/Controllers/Api/Frontend/LandingPageController.php +++ b/app/Http/Controllers/Api/Frontend/LandingPageController.php @@ -19,7 +19,7 @@ class LandingPageController extends BaseApiController public function index(LandingTeacherRequest $request): JsonResponse { $user = $request->user(); - if (!$user) { + if (! $user) { return $this->error('Unauthorized.', Response::HTTP_UNAUTHORIZED); } @@ -36,7 +36,7 @@ class LandingPageController extends BaseApiController public function teacher(LandingTeacherRequest $request): JsonResponse { $user = $request->user(); - if (!$user) { + if (! $user) { return $this->error('Unauthorized.', Response::HTTP_UNAUTHORIZED); } @@ -53,7 +53,7 @@ class LandingPageController extends BaseApiController public function parent(): JsonResponse { $user = auth()->user(); - if (!$user) { + if (! $user) { return $this->error('Unauthorized.', Response::HTTP_UNAUTHORIZED); } diff --git a/app/Http/Controllers/Api/Frontend/PageController.php b/app/Http/Controllers/Api/Frontend/PageController.php index 0aefcccf..2beeb093 100644 --- a/app/Http/Controllers/Api/Frontend/PageController.php +++ b/app/Http/Controllers/Api/Frontend/PageController.php @@ -51,7 +51,7 @@ class PageController extends BaseApiController private function renderStatic(string $filename, string $type): JsonResponse { $result = $this->pageService->load($filename, $type); - if (!$result['ok']) { + if (! $result['ok']) { return $this->error($result['error'], Response::HTTP_NOT_FOUND); } diff --git a/app/Http/Controllers/Api/Grading/GradingController.php b/app/Http/Controllers/Api/Grading/GradingController.php index 869ab0d2..312f0985 100644 --- a/app/Http/Controllers/Api/Grading/GradingController.php +++ b/app/Http/Controllers/Api/Grading/GradingController.php @@ -3,17 +3,17 @@ namespace App\Http\Controllers\Api\Grading; use App\Http\Controllers\Api\Core\BaseApiController; -use App\Http\Requests\Grading\BelowSixtyEmailRequest; use App\Http\Requests\Grading\AllDecisionsRequest; use App\Http\Requests\Grading\BelowSixtyDecisionDetailsRequest; use App\Http\Requests\Grading\BelowSixtyDecisionListRequest; use App\Http\Requests\Grading\BelowSixtyDecisionSaveRequest; use App\Http\Requests\Grading\BelowSixtyDecisionSendRequest; +use App\Http\Requests\Grading\BelowSixtyEmailRequest; +use App\Http\Requests\Grading\BelowSixtyListRequest; use App\Http\Requests\Grading\BelowSixtyMeetingRequest; use App\Http\Requests\Grading\BelowSixtyMeetingSaveRequest; use App\Http\Requests\Grading\BelowSixtySendRequest; use App\Http\Requests\Grading\BelowSixtyStatusRequest; -use App\Http\Requests\Grading\BelowSixtyListRequest; use App\Http\Requests\Grading\GenerateAllDecisionsRequest; use App\Http\Requests\Grading\GradingLockAllRequest; use App\Http\Requests\Grading\GradingLockRequest; @@ -23,8 +23,8 @@ use App\Http\Requests\Grading\GradingScoreShowRequest; use App\Http\Requests\Grading\GradingScoreUpdateRequest; use App\Http\Requests\Grading\PlacementBatchRequest; use App\Http\Requests\Grading\PlacementBatchUpdateRequest; -use App\Http\Requests\Grading\PlacementLevelsRequest; use App\Http\Requests\Grading\PlacementLevelRequest; +use App\Http\Requests\Grading\PlacementLevelsRequest; use App\Http\Requests\Grading\PlacementRequest; use App\Http\Resources\Grading\BelowSixtyStudentResource; use App\Http\Resources\Grading\GradingScoreResource; @@ -419,9 +419,6 @@ class GradingController extends BaseApiController return response()->json(['ok' => true]); } - /** - * @return int|JsonResponse - */ private function authenticatedUserIdOrUnauthorized(): int|JsonResponse { $userId = (int) (auth()->id() ?? 0); diff --git a/app/Http/Controllers/Api/Incidents/IncidentController.php b/app/Http/Controllers/Api/Incidents/IncidentController.php index e2849d2d..b74dfedc 100644 --- a/app/Http/Controllers/Api/Incidents/IncidentController.php +++ b/app/Http/Controllers/Api/Incidents/IncidentController.php @@ -4,7 +4,6 @@ namespace App\Http\Controllers\Api\Incidents; use App\Http\Controllers\Api\Core\BaseApiController; use App\Http\Requests\Incidents\IncidentCancelRequest; -use App\Http\Requests\Incidents\IncidentCloseRequest; use App\Http\Requests\Incidents\IncidentListRequest; use App\Http\Requests\Incidents\IncidentStateRequest; use App\Http\Resources\Incidents\IncidentAnalysisStudentResource; @@ -209,9 +208,6 @@ class IncidentController extends BaseApiController ]); } - /** - * @return int|JsonResponse - */ private function authenticatedUserIdOrUnauthorized(): int|JsonResponse { $userId = (int) (auth()->id() ?? 0); diff --git a/app/Http/Controllers/Api/Inventory/InventoryCategoryController.php b/app/Http/Controllers/Api/Inventory/InventoryCategoryController.php index 39501c5c..6f54b921 100644 --- a/app/Http/Controllers/Api/Inventory/InventoryCategoryController.php +++ b/app/Http/Controllers/Api/Inventory/InventoryCategoryController.php @@ -31,7 +31,7 @@ class InventoryCategoryController extends BaseApiController public function store(InventoryCategoryStoreRequest $request): JsonResponse { $result = $this->categories->create($request->validated()); - if (!($result['ok'] ?? false)) { + if (! ($result['ok'] ?? false)) { return $this->error($result['message'] ?? 'Failed to save category.', Response::HTTP_UNPROCESSABLE_ENTITY); } @@ -46,7 +46,7 @@ class InventoryCategoryController extends BaseApiController return $this->error($e->getMessage(), Response::HTTP_UNPROCESSABLE_ENTITY); } - if (!($result['ok'] ?? false)) { + if (! ($result['ok'] ?? false)) { return $this->error($result['message'] ?? 'Failed to update category.', Response::HTTP_UNPROCESSABLE_ENTITY); } @@ -56,7 +56,7 @@ class InventoryCategoryController extends BaseApiController public function destroy(int $id): JsonResponse { $result = $this->categories->delete($id); - if (!($result['ok'] ?? false)) { + if (! ($result['ok'] ?? false)) { return $this->error($result['message'] ?? 'Failed to delete category.', Response::HTTP_UNPROCESSABLE_ENTITY); } diff --git a/app/Http/Controllers/Api/Inventory/InventoryController.php b/app/Http/Controllers/Api/Inventory/InventoryController.php index de4057dc..1a06a86c 100644 --- a/app/Http/Controllers/Api/Inventory/InventoryController.php +++ b/app/Http/Controllers/Api/Inventory/InventoryController.php @@ -51,7 +51,7 @@ class InventoryController extends BaseApiController public function show(int $id): JsonResponse { $item = $this->items->find($id); - if (!$item) { + if (! $item) { return $this->error('Item not found.', Response::HTTP_NOT_FOUND); } @@ -66,7 +66,7 @@ class InventoryController extends BaseApiController } $result = $this->items->create($request->validated(), $actor); - if (!($result['ok'] ?? false)) { + if (! ($result['ok'] ?? false)) { return $this->error($result['message'] ?? 'Failed to create item.', Response::HTTP_UNPROCESSABLE_ENTITY); } @@ -81,7 +81,7 @@ class InventoryController extends BaseApiController } $result = $this->items->update($id, $request->validated(), $actor); - if (!($result['ok'] ?? false)) { + if (! ($result['ok'] ?? false)) { return $this->error($result['message'] ?? 'Failed to update item.', Response::HTTP_UNPROCESSABLE_ENTITY); } @@ -91,7 +91,7 @@ class InventoryController extends BaseApiController public function destroy(int $id): JsonResponse { $result = $this->items->delete($id); - if (!($result['ok'] ?? false)) { + if (! ($result['ok'] ?? false)) { return $this->error($result['message'] ?? 'Failed to delete item.', Response::HTTP_UNPROCESSABLE_ENTITY); } @@ -106,7 +106,7 @@ class InventoryController extends BaseApiController } $result = $this->items->auditClassroom($id, $request->validated(), $actor); - if (!($result['ok'] ?? false)) { + if (! ($result['ok'] ?? false)) { return $this->error($result['message'] ?? 'Failed to audit item.', Response::HTTP_UNPROCESSABLE_ENTITY); } @@ -121,7 +121,7 @@ class InventoryController extends BaseApiController } $result = $this->items->adjustStock($id, $request->validated(), $actor); - if (!($result['ok'] ?? false)) { + if (! ($result['ok'] ?? false)) { return $this->error($result['message'] ?? 'Failed to adjust stock.', Response::HTTP_UNPROCESSABLE_ENTITY); } @@ -131,6 +131,7 @@ class InventoryController extends BaseApiController public function summary(string $type): JsonResponse { $payload = $this->summary->summary($type, request()->query('school_year')); + return $this->success($payload); } @@ -140,6 +141,7 @@ class InventoryController extends BaseApiController request()->query('school_year'), request()->query('type') ); + return $this->success($payload); } @@ -157,7 +159,7 @@ class InventoryController extends BaseApiController $payload['item_id'] ?? null ); - if (!($result['ok'] ?? false)) { + if (! ($result['ok'] ?? false)) { return $this->error($result['message'] ?? 'Unable to load distribution form.', Response::HTTP_UNPROCESSABLE_ENTITY); } @@ -174,11 +176,12 @@ class InventoryController extends BaseApiController try { $result = $this->distribution->distribute($actor, $request->validated()); } catch (\Throwable $e) { - Log::error('Inventory distribution failed: ' . $e->getMessage()); + Log::error('Inventory distribution failed: '.$e->getMessage()); + return $this->error('Unable to distribute inventory.', Response::HTTP_INTERNAL_SERVER_ERROR); } - if (!($result['ok'] ?? false)) { + if (! ($result['ok'] ?? false)) { return $this->error($result['message'] ?? 'Unable to distribute inventory.', Response::HTTP_UNPROCESSABLE_ENTITY); } @@ -247,7 +250,7 @@ class InventoryController extends BaseApiController public function reorderRequest(int $id): JsonResponse { $item = InventoryItem::with('preferredSupplier')->find($id); - if (!$item) { + if (! $item) { return $this->error('Item not found.', Response::HTTP_NOT_FOUND); } @@ -340,9 +343,6 @@ class InventoryController extends BaseApiController ]); } - /** - * @return int|JsonResponse - */ private function authenticatedUserIdOrUnauthorized(): int|JsonResponse { $userId = (int) (auth()->id() ?? 0); diff --git a/app/Http/Controllers/Api/Inventory/InventoryMovementController.php b/app/Http/Controllers/Api/Inventory/InventoryMovementController.php index 0a2881b8..ec77787c 100644 --- a/app/Http/Controllers/Api/Inventory/InventoryMovementController.php +++ b/app/Http/Controllers/Api/Inventory/InventoryMovementController.php @@ -36,7 +36,7 @@ class InventoryMovementController extends BaseApiController } $result = $this->movements->create($request->validated(), $actor); - if (!($result['ok'] ?? false)) { + if (! ($result['ok'] ?? false)) { return $this->error($result['message'] ?? 'Failed to create movement.', Response::HTTP_UNPROCESSABLE_ENTITY); } @@ -46,7 +46,7 @@ class InventoryMovementController extends BaseApiController public function update(InventoryMovementUpdateRequest $request, int $id): JsonResponse { $result = $this->movements->update($id, $request->validated()); - if (!($result['ok'] ?? false)) { + if (! ($result['ok'] ?? false)) { return $this->error($result['message'] ?? 'Failed to update movement.', Response::HTTP_UNPROCESSABLE_ENTITY); } @@ -56,7 +56,7 @@ class InventoryMovementController extends BaseApiController public function destroy(int $id): JsonResponse { $result = $this->movements->delete($id); - if (!($result['ok'] ?? false)) { + if (! ($result['ok'] ?? false)) { return $this->error($result['message'] ?? 'Failed to delete movement.', Response::HTTP_UNPROCESSABLE_ENTITY); } @@ -66,7 +66,7 @@ class InventoryMovementController extends BaseApiController public function bulkDelete(InventoryMovementBulkDeleteRequest $request): JsonResponse { $result = $this->movements->bulkDelete($request->validated()['ids']); - if (!($result['ok'] ?? false)) { + if (! ($result['ok'] ?? false)) { return $this->error($result['message'] ?? 'Bulk delete failed.', Response::HTTP_UNPROCESSABLE_ENTITY); } @@ -89,7 +89,7 @@ class InventoryMovementController extends BaseApiController } $result = $this->movements->voidMovement($id, $reason, $actor); - if (!($result['ok'] ?? false)) { + if (! ($result['ok'] ?? false)) { return $this->error($result['message'] ?? 'Failed to void movement.', Response::HTTP_UNPROCESSABLE_ENTITY); } @@ -117,16 +117,13 @@ class InventoryMovementController extends BaseApiController } $result = $this->movements->correctMovement($id, $qtyChange, $reason, $actor); - if (!($result['ok'] ?? false)) { + if (! ($result['ok'] ?? false)) { return $this->error($result['message'] ?? 'Failed to correct movement.', Response::HTTP_UNPROCESSABLE_ENTITY); } return $this->success(['message' => 'Correction movement created.']); } - /** - * @return int|JsonResponse - */ private function authenticatedUserIdOrUnauthorized(): int|JsonResponse { $userId = (int) (auth()->id() ?? 0); diff --git a/app/Http/Controllers/Api/Inventory/SupplierController.php b/app/Http/Controllers/Api/Inventory/SupplierController.php index 509488a9..c64c9061 100644 --- a/app/Http/Controllers/Api/Inventory/SupplierController.php +++ b/app/Http/Controllers/Api/Inventory/SupplierController.php @@ -33,7 +33,7 @@ class SupplierController extends BaseApiController public function store(SupplierStoreRequest $request): JsonResponse { $result = $this->suppliers->create($request->validated()); - if (!($result['ok'] ?? false)) { + if (! ($result['ok'] ?? false)) { return $this->error($result['message'] ?? 'Failed to save supplier.', Response::HTTP_UNPROCESSABLE_ENTITY); } @@ -43,7 +43,7 @@ class SupplierController extends BaseApiController public function update(SupplierUpdateRequest $request, int $id): JsonResponse { $result = $this->suppliers->update($id, $request->validated()); - if (!($result['ok'] ?? false)) { + if (! ($result['ok'] ?? false)) { return $this->error($result['message'] ?? 'Failed to update supplier.', Response::HTTP_UNPROCESSABLE_ENTITY); } @@ -53,7 +53,7 @@ class SupplierController extends BaseApiController public function destroy(int $id): JsonResponse { $result = $this->suppliers->delete($id); - if (!($result['ok'] ?? false)) { + if (! ($result['ok'] ?? false)) { return $this->error($result['message'] ?? 'Failed to delete supplier.', Response::HTTP_UNPROCESSABLE_ENTITY); } diff --git a/app/Http/Controllers/Api/Inventory/SupplyCategoryController.php b/app/Http/Controllers/Api/Inventory/SupplyCategoryController.php index 4a0b0e89..25a5bc87 100644 --- a/app/Http/Controllers/Api/Inventory/SupplyCategoryController.php +++ b/app/Http/Controllers/Api/Inventory/SupplyCategoryController.php @@ -29,7 +29,7 @@ class SupplyCategoryController extends BaseApiController public function store(SupplyCategoryStoreRequest $request): JsonResponse { $result = $this->categories->create($request->validated()); - if (!($result['ok'] ?? false)) { + if (! ($result['ok'] ?? false)) { return $this->error($result['message'] ?? 'Failed to save category.', Response::HTTP_UNPROCESSABLE_ENTITY); } @@ -39,7 +39,7 @@ class SupplyCategoryController extends BaseApiController public function update(SupplyCategoryUpdateRequest $request, int $id): JsonResponse { $result = $this->categories->update($id, $request->validated()); - if (!($result['ok'] ?? false)) { + if (! ($result['ok'] ?? false)) { return $this->error($result['message'] ?? 'Failed to update category.', Response::HTTP_UNPROCESSABLE_ENTITY); } @@ -49,7 +49,7 @@ class SupplyCategoryController extends BaseApiController public function destroy(int $id): JsonResponse { $result = $this->categories->delete($id); - if (!($result['ok'] ?? false)) { + if (! ($result['ok'] ?? false)) { return $this->error($result['message'] ?? 'Failed to delete category.', Response::HTTP_UNPROCESSABLE_ENTITY); } diff --git a/app/Http/Controllers/Api/Messaging/MessagesController.php b/app/Http/Controllers/Api/Messaging/MessagesController.php index 6f7374ee..f6fffc8e 100644 --- a/app/Http/Controllers/Api/Messaging/MessagesController.php +++ b/app/Http/Controllers/Api/Messaging/MessagesController.php @@ -75,7 +75,7 @@ class MessagesController extends BaseApiController public function show(int $id): JsonResponse { $message = $this->queryService->findForUser($id); - if (!$message) { + if (! $message) { return $this->error('Message not found.', Response::HTTP_NOT_FOUND); } @@ -104,7 +104,8 @@ class MessagesController extends BaseApiController try { $message = $this->commandService->create($guard, $request->validated(), $request->file('attachment')); } catch (\Throwable $e) { - Log::error('Message send failed: ' . $e->getMessage()); + Log::error('Message send failed: '.$e->getMessage()); + return $this->error($e->getMessage(), Response::HTTP_BAD_REQUEST); } @@ -150,7 +151,7 @@ class MessagesController extends BaseApiController public function update(MessageUpdateRequest $request, int $id): JsonResponse { $message = Message::query()->find($id); - if (!$message) { + if (! $message) { return $this->error('Message not found.', Response::HTTP_NOT_FOUND); } @@ -159,7 +160,8 @@ class MessagesController extends BaseApiController try { $updated = $this->commandService->update($message, $request->validated()); } catch (\Throwable $e) { - Log::error('Message update failed: ' . $e->getMessage()); + Log::error('Message update failed: '.$e->getMessage()); + return $this->error('Unable to update message.', Response::HTTP_INTERNAL_SERVER_ERROR); } @@ -171,7 +173,7 @@ class MessagesController extends BaseApiController public function destroy(int $id): JsonResponse { $message = Message::query()->find($id); - if (!$message) { + if (! $message) { return $this->error('Message not found.', Response::HTTP_NOT_FOUND); } @@ -180,11 +182,12 @@ class MessagesController extends BaseApiController try { $trashed = $this->commandService->trash($message); } catch (\Throwable $e) { - Log::error('Message delete failed: ' . $e->getMessage()); + Log::error('Message delete failed: '.$e->getMessage()); + return $this->error('Unable to delete message.', Response::HTTP_INTERNAL_SERVER_ERROR); } - if (!$trashed) { + if (! $trashed) { return $this->error('Unable to delete message.', Response::HTTP_INTERNAL_SERVER_ERROR); } @@ -220,9 +223,6 @@ class MessagesController extends BaseApiController ]); } - /** - * @return int|JsonResponse - */ private function authenticatedUserIdOrUnauthorized(): int|JsonResponse { $userId = (int) (auth()->id() ?? 0); diff --git a/app/Http/Controllers/Api/Messaging/WhatsappController.php b/app/Http/Controllers/Api/Messaging/WhatsappController.php index a7d52d28..0a225c8d 100644 --- a/app/Http/Controllers/Api/Messaging/WhatsappController.php +++ b/app/Http/Controllers/Api/Messaging/WhatsappController.php @@ -117,11 +117,12 @@ class WhatsappController extends BaseApiController try { $result = $this->inviteService->send($request->validated()); } catch (\Throwable $e) { - Log::error('WhatsApp invite send failed: ' . $e->getMessage()); + Log::error('WhatsApp invite send failed: '.$e->getMessage()); + return $this->error('Invite processing failed.', Response::HTTP_INTERNAL_SERVER_ERROR); } - if (!($result['ok'] ?? false)) { + if (! ($result['ok'] ?? false)) { return $this->error($result['message'] ?? 'Invite processing failed.', Response::HTTP_UNPROCESSABLE_ENTITY); } @@ -140,11 +141,12 @@ class WhatsappController extends BaseApiController try { $result = $this->membershipService->updateMembership($request->validated(), $userId); } catch (\Throwable $e) { - Log::error('WhatsApp membership update failed: ' . $e->getMessage()); + Log::error('WhatsApp membership update failed: '.$e->getMessage()); + return $this->error('Failed to save membership.', Response::HTTP_INTERNAL_SERVER_ERROR); } - if (!($result['ok'] ?? false)) { + if (! ($result['ok'] ?? false)) { $code = str_contains((string) ($result['message'] ?? ''), 'missing') ? Response::HTTP_INTERNAL_SERVER_ERROR : Response::HTTP_UNPROCESSABLE_ENTITY; @@ -158,9 +160,6 @@ class WhatsappController extends BaseApiController ], 'Membership saved.'); } - /** - * @return int|JsonResponse - */ private function authenticatedUserIdOrUnauthorized(): int|JsonResponse { $userId = (int) (auth()->id() ?? 0); diff --git a/app/Http/Controllers/Api/Notifications/NotificationController.php b/app/Http/Controllers/Api/Notifications/NotificationController.php index f6e189c9..aaa70ec8 100644 --- a/app/Http/Controllers/Api/Notifications/NotificationController.php +++ b/app/Http/Controllers/Api/Notifications/NotificationController.php @@ -59,7 +59,7 @@ class NotificationController extends BaseApiController } $row = $this->showService->getForUser($guard, $notificationId); - if (!$row) { + if (! $row) { return response()->json(['ok' => false, 'message' => 'Notification not found.'], 404); } @@ -82,6 +82,7 @@ class NotificationController extends BaseApiController $result = $this->sendService->send($payload, $guard); } catch (\Throwable $e) { Log::error('Notification send failed.', ['error' => $e->getMessage()]); + return response()->json(['ok' => false, 'message' => 'Failed to send notification.'], 500); } @@ -114,7 +115,7 @@ class NotificationController extends BaseApiController public function destroy(int $notificationId): JsonResponse { $deleted = $this->managementService->delete($notificationId); - if (!$deleted) { + if (! $deleted) { return response()->json(['ok' => false, 'message' => 'Notification not found.'], 404); } @@ -124,7 +125,7 @@ class NotificationController extends BaseApiController public function restore(int $notificationId): JsonResponse { $restored = $this->managementService->restore($notificationId); - if (!$restored) { + if (! $restored) { return response()->json(['ok' => false, 'message' => 'Notification not found.'], 404); } @@ -139,7 +140,7 @@ class NotificationController extends BaseApiController } $ok = $this->readService->markRead($guard, $notificationId); - if (!$ok) { + if (! $ok) { return response()->json(['ok' => false, 'message' => 'Notification not found.'], 404); } @@ -167,9 +168,6 @@ class NotificationController extends BaseApiController ]); } - /** - * @return int|JsonResponse - */ private function authenticatedUserIdOrUnauthorized(): int|JsonResponse { $userId = (int) (auth()->id() ?? 0); diff --git a/app/Http/Controllers/Api/Parents/ParentAttendanceReportController.php b/app/Http/Controllers/Api/Parents/ParentAttendanceReportController.php index 271ba93c..0710ea7f 100644 --- a/app/Http/Controllers/Api/Parents/ParentAttendanceReportController.php +++ b/app/Http/Controllers/Api/Parents/ParentAttendanceReportController.php @@ -3,9 +3,9 @@ namespace App\Http\Controllers\Api\Parents; use App\Http\Controllers\Api\Core\BaseApiController; -use App\Http\Requests\Parents\ParentAttendanceReportSubmitRequest; -use App\Http\Requests\Parents\ParentAttendanceReportListRequest; use App\Http\Requests\Parents\ParentAttendanceReportCheckRequest; +use App\Http\Requests\Parents\ParentAttendanceReportListRequest; +use App\Http\Requests\Parents\ParentAttendanceReportSubmitRequest; use App\Http\Requests\Parents\ParentAttendanceReportUpdateRequest; use App\Http\Resources\Parents\ParentAttendanceReportResource; use App\Services\Parents\ParentAttendanceReportService; @@ -116,9 +116,6 @@ class ParentAttendanceReportController extends BaseApiController return response()->json(['ok' => true]); } - /** - * @return int|JsonResponse - */ private function parentIdOrUnauthorized(): int|JsonResponse { $parentId = (int) (auth()->id() ?? 0); diff --git a/app/Http/Controllers/Api/Parents/ParentController.php b/app/Http/Controllers/Api/Parents/ParentController.php index 1dd0f3b8..d1b0f2d8 100644 --- a/app/Http/Controllers/Api/Parents/ParentController.php +++ b/app/Http/Controllers/Api/Parents/ParentController.php @@ -4,18 +4,18 @@ namespace App\Http\Controllers\Api\Parents; use App\Http\Controllers\Api\Core\BaseApiController; use App\Http\Requests\Parents\ParentAttendanceRequest; +use App\Http\Requests\Parents\ParentEmergencyContactRequest; use App\Http\Requests\Parents\ParentEnrollmentActionRequest; use App\Http\Requests\Parents\ParentEnrollmentRequest; -use App\Http\Requests\Parents\ParentEmergencyContactRequest; use App\Http\Requests\Parents\ParentEventParticipationRequest; use App\Http\Requests\Parents\ParentInvoiceRequest; use App\Http\Requests\Parents\ParentProfileUpdateRequest; use App\Http\Requests\Parents\ParentRegistrationRequest; use App\Http\Requests\Parents\ParentStudentUpdateRequest; +use App\Http\Resources\Invoices\InvoiceResource; use App\Http\Resources\Parents\ParentAttendanceResource; use App\Http\Resources\Parents\ParentEmergencyContactResource; use App\Http\Resources\Parents\ParentStudentResource; -use App\Http\Resources\Invoices\InvoiceResource; use App\Services\Parents\ParentAttendanceService; use App\Services\Parents\ParentEmergencyContactService; use App\Services\Parents\ParentEnrollmentService; diff --git a/app/Http/Controllers/Api/Parents/ParentPromotionController.php b/app/Http/Controllers/Api/Parents/ParentPromotionController.php index 6b861b4a..24bb93fa 100644 --- a/app/Http/Controllers/Api/Parents/ParentPromotionController.php +++ b/app/Http/Controllers/Api/Parents/ParentPromotionController.php @@ -140,20 +140,21 @@ class ParentPromotionController extends BaseApiController private function loadOwnedRecord(int $promotionId, int $parentId): StudentPromotionRecord|JsonResponse { $record = StudentPromotionRecord::query()->find($promotionId); - if (!$record) { + if (! $record) { return response()->json(['ok' => false, 'message' => 'Promotion record not found.'], Response::HTTP_NOT_FOUND); } $owns = (int) $record->parent_id === $parentId; - if (!$owns) { + if (! $owns) { $studentParentId = (int) (Student::query() ->where('id', $record->student_id) ->value('parent_id') ?? 0); $owns = $studentParentId === $parentId; } - if (!$owns) { + if (! $owns) { return response()->json(['ok' => false, 'message' => 'You are not authorised to view this promotion record.'], Response::HTTP_FORBIDDEN); } + return $record; } @@ -168,6 +169,7 @@ class ParentPromotionController extends BaseApiController if ($parentId <= 0) { return response()->json(['ok' => false, 'message' => 'Unauthorized.'], Response::HTTP_UNAUTHORIZED); } + return $parentId; } } diff --git a/app/Http/Controllers/Api/PrintRequests/PrintRequestsController.php b/app/Http/Controllers/Api/PrintRequests/PrintRequestsController.php index ca3ba992..8fce19e9 100644 --- a/app/Http/Controllers/Api/PrintRequests/PrintRequestsController.php +++ b/app/Http/Controllers/Api/PrintRequests/PrintRequestsController.php @@ -8,6 +8,7 @@ use App\Models\User; use App\Services\PrintRequests\PrintRequestsPortalService; use Illuminate\Http\JsonResponse; use Illuminate\Http\Request; +use Illuminate\Http\UploadedFile; use Illuminate\Support\Facades\Auth; use Illuminate\Support\Facades\Validator; use Illuminate\Validation\Rule; @@ -59,7 +60,7 @@ class PrintRequestsController extends BaseApiController return $this->respondValidationError($validator->errors()->toArray()); } - /** @var \Illuminate\Http\UploadedFile $file */ + /** @var UploadedFile $file */ $file = $request->file('file'); $model = $this->portal->storeTeacherUpload($validator->validated(), $file, $uid); diff --git a/app/Http/Controllers/Api/Reports/ReportCardsController.php b/app/Http/Controllers/Api/Reports/ReportCardsController.php index 1fc98ea0..d9145948 100644 --- a/app/Http/Controllers/Api/Reports/ReportCardsController.php +++ b/app/Http/Controllers/Api/Reports/ReportCardsController.php @@ -52,7 +52,7 @@ class ReportCardsController extends BaseApiController $classSectionId = (int) ($payload['class_section_id'] ?? 0); $result = $this->service->completeness($schoolYear, $semester, $classSectionId); - if (!($result['ok'] ?? false)) { + if (! ($result['ok'] ?? false)) { return $this->error($result['message'] ?? 'Unable to generate completeness report.', Response::HTTP_UNPROCESSABLE_ENTITY); } @@ -101,11 +101,12 @@ class ReportCardsController extends BaseApiController try { $result = $this->service->generateSingleReport($studentId, $request->validated()); } catch (\Throwable $e) { - Log::error('Report card PDF generation failed: ' . $e->getMessage()); + Log::error('Report card PDF generation failed: '.$e->getMessage()); + return $this->error('Failed to generate report card.', Response::HTTP_INTERNAL_SERVER_ERROR); } - if (!($result['ok'] ?? false)) { + if (! ($result['ok'] ?? false)) { return $this->error($result['message'] ?? 'Unable to generate report card.', Response::HTTP_UNPROCESSABLE_ENTITY); } @@ -113,7 +114,7 @@ class ReportCardsController extends BaseApiController return response($result['pdf'], 200, [ 'Content-Type' => 'application/pdf', - 'Content-Disposition' => $disposition . '; filename="' . ($result['filename'] ?? 'ReportCard.pdf') . '"', + 'Content-Disposition' => $disposition.'; filename="'.($result['filename'] ?? 'ReportCard.pdf').'"', 'Cache-Control' => 'private, max-age=0, must-revalidate', 'Pragma' => 'public', ]); @@ -124,11 +125,12 @@ class ReportCardsController extends BaseApiController try { $result = $this->service->generateClassReport($classSectionId, $request->validated()); } catch (\Throwable $e) { - Log::error('Class report card PDF generation failed: ' . $e->getMessage()); + Log::error('Class report card PDF generation failed: '.$e->getMessage()); + return $this->error('Failed to generate class report cards.', Response::HTTP_INTERNAL_SERVER_ERROR); } - if (!($result['ok'] ?? false)) { + if (! ($result['ok'] ?? false)) { return $this->error($result['message'] ?? 'Unable to generate class report cards.', Response::HTTP_UNPROCESSABLE_ENTITY); } @@ -136,7 +138,7 @@ class ReportCardsController extends BaseApiController return response($result['pdf'], 200, [ 'Content-Type' => 'application/pdf', - 'Content-Disposition' => $disposition . '; filename="' . ($result['filename'] ?? 'ClassReportCards.pdf') . '"', + 'Content-Disposition' => $disposition.'; filename="'.($result['filename'] ?? 'ClassReportCards.pdf').'"', 'Cache-Control' => 'private, max-age=0, must-revalidate', 'Pragma' => 'public', ]); @@ -148,6 +150,7 @@ class ReportCardsController extends BaseApiController if ($value !== '') { return $value; } + return trim((string) (Configuration::getConfig('school_year') ?? '')); } @@ -157,6 +160,7 @@ class ReportCardsController extends BaseApiController if ($value !== '') { return $value; } + return trim((string) (Configuration::getConfig('semester') ?? '')); } } diff --git a/app/Http/Controllers/Api/Reports/SlipPrinterController.php b/app/Http/Controllers/Api/Reports/SlipPrinterController.php index 44b3685e..b285b9f7 100644 --- a/app/Http/Controllers/Api/Reports/SlipPrinterController.php +++ b/app/Http/Controllers/Api/Reports/SlipPrinterController.php @@ -27,13 +27,13 @@ class SlipPrinterController extends BaseApiController $result = $this->service->print($request->validated(), $guard); - if (!$result['ok']) { + if (! $result['ok']) { return response()->json(['ok' => false, 'message' => $result['message'] ?? 'Print failed.'], 422); } return response($result['pdf'], 200, [ 'Content-Type' => 'application/pdf', - 'Content-Disposition' => 'inline; filename="' . $result['filename'] . '"', + 'Content-Disposition' => 'inline; filename="'.$result['filename'].'"', ]); } @@ -78,19 +78,16 @@ class SlipPrinterController extends BaseApiController $result = $this->service->reprint((int) $request->validated()['id'], $guard); - if (!$result['ok']) { + if (! $result['ok']) { return response()->json(['ok' => false, 'message' => $result['message'] ?? 'Slip not found.'], 404); } return response($result['pdf'], 200, [ 'Content-Type' => 'application/pdf', - 'Content-Disposition' => 'inline; filename="' . $result['filename'] . '"', + 'Content-Disposition' => 'inline; filename="'.$result['filename'].'"', ]); } - /** - * @return int|JsonResponse - */ private function authenticatedUserIdOrUnauthorized(): int|JsonResponse { $userId = (int) (auth()->id() ?? 0); diff --git a/app/Http/Controllers/Api/Reports/StickersController.php b/app/Http/Controllers/Api/Reports/StickersController.php index a5129532..8cfa11d2 100644 --- a/app/Http/Controllers/Api/Reports/StickersController.php +++ b/app/Http/Controllers/Api/Reports/StickersController.php @@ -64,17 +64,18 @@ class StickersController extends BaseApiController try { $result = $this->printService->generate($request->validated()); } catch (\Throwable $e) { - Log::error('Sticker print failed: ' . $e->getMessage()); + Log::error('Sticker print failed: '.$e->getMessage()); + return $this->error('Failed to generate stickers.', Response::HTTP_INTERNAL_SERVER_ERROR); } - if (!($result['ok'] ?? false)) { + if (! ($result['ok'] ?? false)) { return $this->error($result['message'] ?? 'Unable to generate stickers.', Response::HTTP_UNPROCESSABLE_ENTITY); } return response($result['pdf'], 200, [ 'Content-Type' => 'application/pdf', - 'Content-Disposition' => 'inline; filename="' . ($result['filename'] ?? 'Student_Stickers.pdf') . '"', + 'Content-Disposition' => 'inline; filename="'.($result['filename'] ?? 'Student_Stickers.pdf').'"', ]); } } diff --git a/app/Http/Controllers/Api/RolePermissionController.php b/app/Http/Controllers/Api/RolePermissionController.php index 4b331fd4..0013504e 100644 --- a/app/Http/Controllers/Api/RolePermissionController.php +++ b/app/Http/Controllers/Api/RolePermissionController.php @@ -55,7 +55,7 @@ class RolePermissionController extends BaseApiController public function showRole(int $roleId): JsonResponse { $role = Role::query()->find($roleId); - if (!$role) { + if (! $role) { return $this->error('Role not found.', Response::HTTP_NOT_FOUND); } @@ -69,7 +69,8 @@ class RolePermissionController extends BaseApiController try { $role = $this->roleCrudService->create($request->validated()); } catch (\Throwable $e) { - Log::error('Role store failed: ' . $e->getMessage()); + Log::error('Role store failed: '.$e->getMessage()); + return $this->error('Failed to create role.', Response::HTTP_INTERNAL_SERVER_ERROR); } @@ -81,14 +82,15 @@ class RolePermissionController extends BaseApiController public function updateRole(RoleUpdateRequest $request, int $roleId): JsonResponse { $role = Role::query()->find($roleId); - if (!$role) { + if (! $role) { return $this->error('Role not found.', Response::HTTP_NOT_FOUND); } try { $role = $this->roleCrudService->update($role, $request->validated()); } catch (\Throwable $e) { - Log::error('Role update failed: ' . $e->getMessage()); + Log::error('Role update failed: '.$e->getMessage()); + return $this->error('Failed to update role.', Response::HTTP_INTERNAL_SERVER_ERROR); } @@ -100,14 +102,15 @@ class RolePermissionController extends BaseApiController public function deleteRole(int $roleId): JsonResponse { $role = Role::query()->find($roleId); - if (!$role) { + if (! $role) { return $this->error('Role not found.', Response::HTTP_NOT_FOUND); } try { $this->roleCrudService->delete($role); } catch (\Throwable $e) { - Log::error('Role delete failed: ' . $e->getMessage()); + Log::error('Role delete failed: '.$e->getMessage()); + return $this->error('Failed to delete role.', Response::HTTP_INTERNAL_SERVER_ERROR); } @@ -138,11 +141,12 @@ class RolePermissionController extends BaseApiController (int) (auth()->id() ?? 0) ); } catch (\Throwable $e) { - Log::error('Assign roles failed: ' . $e->getMessage()); + Log::error('Assign roles failed: '.$e->getMessage()); + return $this->error('Failed to update roles.', Response::HTTP_INTERNAL_SERVER_ERROR); } - if (!($result['ok'] ?? false)) { + if (! ($result['ok'] ?? false)) { return $this->error($result['message'] ?? 'Failed to update roles.', Response::HTTP_UNPROCESSABLE_ENTITY); } @@ -163,7 +167,7 @@ class RolePermissionController extends BaseApiController public function showPermission(int $permissionId): JsonResponse { $permission = Permission::query()->find($permissionId); - if (!$permission) { + if (! $permission) { return $this->error('Permission not found.', Response::HTTP_NOT_FOUND); } @@ -177,7 +181,8 @@ class RolePermissionController extends BaseApiController try { $permission = $this->permissionCrudService->create($request->validated()); } catch (\Throwable $e) { - Log::error('Permission store failed: ' . $e->getMessage()); + Log::error('Permission store failed: '.$e->getMessage()); + return $this->error('Failed to create permission.', Response::HTTP_INTERNAL_SERVER_ERROR); } @@ -189,14 +194,15 @@ class RolePermissionController extends BaseApiController public function updatePermission(PermissionUpdateRequest $request, int $permissionId): JsonResponse { $permission = Permission::query()->find($permissionId); - if (!$permission) { + if (! $permission) { return $this->error('Permission not found.', Response::HTTP_NOT_FOUND); } try { $permission = $this->permissionCrudService->update($permission, $request->validated()); } catch (\Throwable $e) { - Log::error('Permission update failed: ' . $e->getMessage()); + Log::error('Permission update failed: '.$e->getMessage()); + return $this->error('Failed to update permission.', Response::HTTP_INTERNAL_SERVER_ERROR); } @@ -208,14 +214,15 @@ class RolePermissionController extends BaseApiController public function deletePermission(int $permissionId): JsonResponse { $permission = Permission::query()->find($permissionId); - if (!$permission) { + if (! $permission) { return $this->error('Permission not found.', Response::HTTP_NOT_FOUND); } try { $this->permissionCrudService->delete($permission); } catch (\Throwable $e) { - Log::error('Permission delete failed: ' . $e->getMessage()); + Log::error('Permission delete failed: '.$e->getMessage()); + return $this->error('Failed to delete permission.', Response::HTTP_INTERNAL_SERVER_ERROR); } @@ -236,7 +243,8 @@ class RolePermissionController extends BaseApiController try { $this->permissionService->saveRolePermissions($roleId, $request->validated()['permissions'] ?? []); } catch (\Throwable $e) { - Log::error('Role permissions update failed: ' . $e->getMessage()); + Log::error('Role permissions update failed: '.$e->getMessage()); + return $this->error('Failed to update role permissions.', Response::HTTP_INTERNAL_SERVER_ERROR); } diff --git a/app/Http/Controllers/Api/RoleSwitcherController.php b/app/Http/Controllers/Api/RoleSwitcherController.php index 2f22e748..0758dae3 100644 --- a/app/Http/Controllers/Api/RoleSwitcherController.php +++ b/app/Http/Controllers/Api/RoleSwitcherController.php @@ -5,9 +5,9 @@ namespace App\Http\Controllers\Api; use App\Http\Requests\Roles\RoleSwitchRequest; use App\Services\Roles\RoleSwitchService; use Illuminate\Http\JsonResponse; +use Symfony\Component\HttpFoundation\Response; use Symfony\Component\HttpKernel\Exception\AccessDeniedHttpException; use Symfony\Component\HttpKernel\Exception\NotFoundHttpException; -use Symfony\Component\HttpFoundation\Response; class RoleSwitcherController extends BaseApiController { diff --git a/app/Http/Controllers/Api/ScannerController.php b/app/Http/Controllers/Api/ScannerController.php index d8d3264d..26dde969 100644 --- a/app/Http/Controllers/Api/ScannerController.php +++ b/app/Http/Controllers/Api/ScannerController.php @@ -23,12 +23,13 @@ use Illuminate\Support\Facades\Schema; class ScannerController extends BaseApiController { private const LATE_CUTOFF_CONFIG_KEY = 'attendance_late_cutoff_time'; + private const DEFAULT_LATE_CUTOFF = '10:00 AM'; public function process(Request $request): JsonResponse { $data = $request->json()->all(); - if (!is_array($data) || $data === []) { + if (! is_array($data) || $data === []) { $data = $request->all(); } @@ -41,10 +42,11 @@ class ScannerController extends BaseApiController if ($badgeCode === '') { $this->logScan($badgeCode, null, null, $action, 'rejected', $operatorId, $stationId, 'Badge code is required.', $scannedAt); + return response()->json($this->failure('Badge code is required.'), 400); } - if (!in_array($scanStatus, ['present', 'late'], true)) { + if (! in_array($scanStatus, ['present', 'late'], true)) { $scanStatus = 'present'; } @@ -59,6 +61,7 @@ class ScannerController extends BaseApiController } $this->logScan($badgeCode, null, null, $action, 'not_found', $operatorId, $stationId, 'Badge not found.', $scannedAt); + return response()->json($this->failure('Badge not found.'), 404); } @@ -74,8 +77,9 @@ class ScannerController extends BaseApiController } $assignment = $this->studentAssignment((int) $student->id, $term['semester'], $term['school_year']); - if (!$assignment) { + if (! $assignment) { $this->logScan($badgeCode, 'student', (int) $student->id, $action, 'rejected', $operatorId, $stationId, 'Student has no class assignment for current term.', $scannedAt); + return response()->json($this->failure('Student has no class assignment for current term.'), 409); } @@ -150,7 +154,7 @@ class ScannerController extends BaseApiController $operatorId, $existing ? (string) ($oldStatus ?? '') : null ); - $warnings[] = 'Student arrived after ' . $lateCutoff->format('h:i A') . '. Late slip receipt generated.'; + $warnings[] = 'Student arrived after '.$lateCutoff->format('h:i A').'. Late slip receipt generated.'; } return response()->json($this->successPayload([ @@ -194,7 +198,7 @@ class ScannerController extends BaseApiController $person = [ 'id' => (int) $user->id, - 'name' => trim((string) ($user->firstname ?? '') . ' ' . (string) ($user->lastname ?? '')), + 'name' => trim((string) ($user->firstname ?? '').' '.(string) ($user->lastname ?? '')), 'email' => $user->email, 'role' => $roleName, 'badgeCode' => $badgeCode, @@ -238,7 +242,7 @@ class ScannerController extends BaseApiController }) ->first(); - if (!$user || !$this->isStaffUser((int) $user->id)) { + if (! $user || ! $this->isStaffUser((int) $user->id)) { return null; } @@ -255,7 +259,7 @@ class ScannerController extends BaseApiController foreach ($roles as $role) { $name = trim((string) ($role->role_name ?? '')); - if ($name !== '' && !in_array($name, ['parent', 'guest', 'student'], true)) { + if ($name !== '' && ! in_array($name, ['parent', 'guest', 'student'], true)) { return true; } } @@ -287,7 +291,7 @@ class ScannerController extends BaseApiController } $row = $builder->orderByDesc('sc.id')->first(); - if (!$row && Schema::hasColumn('student_class', 'school_year')) { + if (! $row && Schema::hasColumn('student_class', 'school_year')) { $row = DB::table('student_class as sc') ->select('sc.class_section_id', 'cs.class_id', 'cs.class_section_name') ->leftJoin('classSection as cs', 'cs.class_section_id', '=', 'sc.class_section_id') @@ -329,8 +333,9 @@ class ScannerController extends BaseApiController ->where('school_year', $schoolYear) ->first(); - if (!$record) { + if (! $record) { $this->addAttendanceRecord($classSectionId, $studentId, $schoolId, $newStatus, $semester, $schoolYear, $operatorId); + return; } @@ -383,6 +388,7 @@ class ScannerController extends BaseApiController 'updated_at' => utc_now(), ]); $record->save(); + return; } @@ -424,13 +430,13 @@ class ScannerController extends BaseApiController return [ 'id' => (int) ($student['id'] ?? 0), 'schoolId' => $student['school_id'] ?? null, - 'name' => trim((string) ($student['firstname'] ?? '') . ' ' . (string) ($student['lastname'] ?? '')), + 'name' => trim((string) ($student['firstname'] ?? '').' '.(string) ($student['lastname'] ?? '')), 'firstName' => $student['firstname'] ?? null, 'lastName' => $student['lastname'] ?? null, 'grade' => $student['registration_grade'] ?? null, 'classSectionId' => isset($assignment['class_section_id']) ? (int) $assignment['class_section_id'] : null, 'classSectionName' => $assignment['class_section_name'] ?? null, - 'badgeCode' => !empty($student['rfid_tag']) ? $student['rfid_tag'] : ($student['school_id'] ?? null), + 'badgeCode' => ! empty($student['rfid_tag']) ? $student['rfid_tag'] : ($student['school_id'] ?? null), ]; } @@ -527,7 +533,7 @@ class ScannerController extends BaseApiController $outstandingBalance = 0.0; $paymentStatus = 'Unknown'; - if (!empty($payments)) { + if (! empty($payments)) { $latest = $payments[0]; $paymentStatus = (string) ($latest['status'] ?? 'Unknown'); $outstandingBalance = (float) ($latest['balance'] ?? 0); @@ -550,7 +556,7 @@ class ScannerController extends BaseApiController { $issues = []; $payments = $summary['payments'] ?? []; - if (!empty($payments)) { + if (! empty($payments)) { $latest = $payments[0]; $balance = (float) ($latest['balance'] ?? 0); $status = strtolower(trim((string) ($latest['status'] ?? ''))); @@ -559,9 +565,9 @@ class ScannerController extends BaseApiController 'category' => 'payment', 'severity' => 'critical', 'title' => 'Payment balance due', - 'detail' => '$' . number_format($balance, 2) . ' outstanding', + 'detail' => '$'.number_format($balance, 2).' outstanding', ]; - } elseif ($status !== '' && !in_array($status, ['paid', 'full', 'full payment', 'complete', 'completed'], true)) { + } elseif ($status !== '' && ! in_array($status, ['paid', 'full', 'full payment', 'complete', 'completed'], true)) { $issues[] = [ 'category' => 'payment', 'severity' => 'warning', @@ -578,7 +584,7 @@ class ScannerController extends BaseApiController 'category' => 'grades', 'severity' => (float) $semesterScore < 60 ? 'critical' : 'warning', 'title' => 'Grade concern', - 'detail' => 'Semester score ' . number_format((float) $semesterScore, 1), + 'detail' => 'Semester score '.number_format((float) $semesterScore, 1), ]; } @@ -587,7 +593,7 @@ class ScannerController extends BaseApiController $issues[] = [ 'category' => 'grades', 'severity' => 'warning', - 'title' => $label . ' average below target', + 'title' => $label.' average below target', 'detail' => number_format((float) $grading[$key], 1), ]; } @@ -599,7 +605,7 @@ class ScannerController extends BaseApiController 'category' => 'attendance', 'severity' => 'warning', 'title' => 'Repeated lateness', - 'detail' => (int) $attendance['late'] . ' late attendance records', + 'detail' => (int) $attendance['late'].' late attendance records', ]; } @@ -647,7 +653,7 @@ class ScannerController extends BaseApiController { $label = trim(str_replace('_', ' ', $flagType)); if ($label !== '') { - return ucwords($label) . ' flag'; + return ucwords($label).' flag'; } return match ($category) { @@ -679,10 +685,10 @@ class ScannerController extends BaseApiController private function createLateSlipPayload(array $student, array $assignment, array $term, \DateTimeImmutable $schoolNow, \DateTimeImmutable $lateCutoff, int $operatorId, ?string $previousStatus): array { - $studentName = trim((string) ($student['firstname'] ?? '') . ' ' . (string) ($student['lastname'] ?? '')); + $studentName = trim((string) ($student['firstname'] ?? '').' '.(string) ($student['lastname'] ?? '')); $grade = (string) ($student['registration_grade'] ?? $assignment['class_section_name'] ?? ''); $cutoffLabel = $lateCutoff->format('h:i A'); - $reason = 'Arrived after ' . $cutoffLabel; + $reason = 'Arrived after '.$cutoffLabel; $adminName = $this->operatorName($operatorId); $slip = [ 'school_year' => $term['school_year'], @@ -714,7 +720,7 @@ class ScannerController extends BaseApiController $logId = (int) ($created->id ?? 0); $logged = $logId > 0; } catch (\Throwable $e) { - Log::error('Badge scan late slip log failed: ' . $e->getMessage()); + Log::error('Badge scan late slip log failed: '.$e->getMessage()); } } @@ -735,15 +741,15 @@ class ScannerController extends BaseApiController 'reason' => $reason, 'adminName' => $adminName, 'cutoffTime' => $cutoffLabel, - 'message' => 'Late slip receipt generated for arrival after ' . $cutoffLabel . '.', + 'message' => 'Late slip receipt generated for arrival after '.$cutoffLabel.'.', 'text' => implode("\n", [ 'Al Rahma School at ISGL', - 'School Year: ' . $slip['school_year'], - 'Student Name: ' . $studentName, - 'Date: ' . $displayDate . ' Time In: ' . $displayTime, - 'Grade: ' . $grade, - 'Reason: ' . $reason, - 'Admin: ' . $adminName, + 'School Year: '.$slip['school_year'], + 'Student Name: '.$studentName, + 'Date: '.$displayDate.' Time In: '.$displayTime, + 'Grade: '.$grade, + 'Reason: '.$reason, + 'Admin: '.$adminName, ]), ]; } @@ -760,18 +766,19 @@ class ScannerController extends BaseApiController $date = $schoolNow->format('Y-m-d'); foreach (['H:i:s', 'H:i', 'g:i A', 'h:i A', 'g:i a', 'h:i a'] as $format) { - $parsed = \DateTimeImmutable::createFromFormat('!' . $format, $configured, $timezone); + $parsed = \DateTimeImmutable::createFromFormat('!'.$format, $configured, $timezone); if ($parsed instanceof \DateTimeImmutable) { return $schoolNow->setTime((int) $parsed->format('H'), (int) $parsed->format('i'), (int) $parsed->format('s')); } } - $timestamp = strtotime($date . ' ' . $configured); + $timestamp = strtotime($date.' '.$configured); if ($timestamp !== false) { - return (new \DateTimeImmutable('@' . $timestamp))->setTimezone($timezone); + return (new \DateTimeImmutable('@'.$timestamp))->setTimezone($timezone); } - Log::warning(self::LATE_CUTOFF_CONFIG_KEY . ' has invalid value "' . $configured . '"; using ' . self::DEFAULT_LATE_CUTOFF); + Log::warning(self::LATE_CUTOFF_CONFIG_KEY.' has invalid value "'.$configured.'"; using '.self::DEFAULT_LATE_CUTOFF); + return $schoolNow->setTime(10, 0, 0); } @@ -780,6 +787,7 @@ class ScannerController extends BaseApiController $timezone = new \DateTimeZone('America/New_York'); try { $utc = new \DateTimeZone('UTC'); + return (new \DateTimeImmutable($scannedAt, $utc))->setTimezone($timezone); } catch (\Throwable) { return new \DateTimeImmutable('now', $timezone); @@ -792,7 +800,7 @@ class ScannerController extends BaseApiController try { $user = User::query()->select('firstname', 'lastname', 'email')->find($operatorId); if ($user) { - $name = trim((string) ($user->firstname ?? '') . ' ' . (string) ($user->lastname ?? '')); + $name = trim((string) ($user->firstname ?? '').' '.(string) ($user->lastname ?? '')); if ($name !== '') { return $name; } @@ -800,7 +808,7 @@ class ScannerController extends BaseApiController return (string) ($user->email ?? ''); } } catch (\Throwable $e) { - Log::error('Badge scan operator lookup failed: ' . $e->getMessage()); + Log::error('Badge scan operator lookup failed: '.$e->getMessage()); } } @@ -833,7 +841,7 @@ class ScannerController extends BaseApiController private function logScan(string $badgeCode, ?string $personType, ?int $personId, string $action, string $status, int $operatorId, string $stationId, string $message, string $scannedAt): void { try { - if (!Schema::hasTable('badge_scan_logs')) { + if (! Schema::hasTable('badge_scan_logs')) { return; } @@ -850,7 +858,7 @@ class ScannerController extends BaseApiController 'created_at' => utc_now(), ]); } catch (\Throwable $e) { - Log::error('Badge scan log failed: ' . $e->getMessage()); + Log::error('Badge scan log failed: '.$e->getMessage()); } } diff --git a/app/Http/Controllers/Api/Scores/FinalController.php b/app/Http/Controllers/Api/Scores/FinalController.php index 4b9b705c..14812192 100644 --- a/app/Http/Controllers/Api/Scores/FinalController.php +++ b/app/Http/Controllers/Api/Scores/FinalController.php @@ -135,9 +135,6 @@ class FinalController extends BaseApiController } } - /** - * @return int|JsonResponse - */ private function authenticatedUserIdOrUnauthorized(): int|JsonResponse { $userId = (int) (auth()->id() ?? 0); diff --git a/app/Http/Controllers/Api/Scores/HomeworkController.php b/app/Http/Controllers/Api/Scores/HomeworkController.php index f38e491c..8c44ab69 100644 --- a/app/Http/Controllers/Api/Scores/HomeworkController.php +++ b/app/Http/Controllers/Api/Scores/HomeworkController.php @@ -158,9 +158,6 @@ class HomeworkController extends BaseApiController } } - /** - * @return int|JsonResponse - */ private function authenticatedUserIdOrUnauthorized(): int|JsonResponse { $userId = (int) (auth()->id() ?? 0); diff --git a/app/Http/Controllers/Api/Scores/MidtermController.php b/app/Http/Controllers/Api/Scores/MidtermController.php index 8ddcb304..a88d13b7 100644 --- a/app/Http/Controllers/Api/Scores/MidtermController.php +++ b/app/Http/Controllers/Api/Scores/MidtermController.php @@ -135,9 +135,6 @@ class MidtermController extends BaseApiController } } - /** - * @return int|JsonResponse - */ private function authenticatedUserIdOrUnauthorized(): int|JsonResponse { $userId = (int) (auth()->id() ?? 0); diff --git a/app/Http/Controllers/Api/Scores/ParticipationController.php b/app/Http/Controllers/Api/Scores/ParticipationController.php index 98b18796..fa5ed8d2 100644 --- a/app/Http/Controllers/Api/Scores/ParticipationController.php +++ b/app/Http/Controllers/Api/Scores/ParticipationController.php @@ -138,9 +138,6 @@ class ParticipationController extends BaseApiController } } - /** - * @return int|JsonResponse - */ private function authenticatedUserIdOrUnauthorized(): int|JsonResponse { $userId = (int) (auth()->id() ?? 0); diff --git a/app/Http/Controllers/Api/Scores/ProjectController.php b/app/Http/Controllers/Api/Scores/ProjectController.php index 9954763f..85566cdc 100644 --- a/app/Http/Controllers/Api/Scores/ProjectController.php +++ b/app/Http/Controllers/Api/Scores/ProjectController.php @@ -159,9 +159,6 @@ class ProjectController extends BaseApiController } } - /** - * @return int|JsonResponse - */ private function authenticatedUserIdOrUnauthorized(): int|JsonResponse { $userId = (int) (auth()->id() ?? 0); diff --git a/app/Http/Controllers/Api/Scores/QuizController.php b/app/Http/Controllers/Api/Scores/QuizController.php index 2a17f95d..4b979b65 100644 --- a/app/Http/Controllers/Api/Scores/QuizController.php +++ b/app/Http/Controllers/Api/Scores/QuizController.php @@ -158,9 +158,6 @@ class QuizController extends BaseApiController } } - /** - * @return int|JsonResponse - */ private function authenticatedUserIdOrUnauthorized(): int|JsonResponse { $userId = (int) (auth()->id() ?? 0); diff --git a/app/Http/Controllers/Api/Scores/ScoreCommentController.php b/app/Http/Controllers/Api/Scores/ScoreCommentController.php index 24a1c873..ad2e3a82 100644 --- a/app/Http/Controllers/Api/Scores/ScoreCommentController.php +++ b/app/Http/Controllers/Api/Scores/ScoreCommentController.php @@ -88,7 +88,7 @@ class ScoreCommentController extends BaseApiController $userId ); - if (!empty($result['errors'])) { + if (! empty($result['errors'])) { return response()->json(['ok' => false, 'errors' => $result['errors']], 422); } @@ -112,16 +112,13 @@ class ScoreCommentController extends BaseApiController $userId ); - if (!empty($result['errors'])) { + if (! empty($result['errors'])) { return response()->json(['ok' => false, 'errors' => $result['errors']], 422); } return response()->json(['ok' => true]); } - /** - * @return int|JsonResponse - */ private function authenticatedUserIdOrUnauthorized(): int|JsonResponse { $userId = (int) (auth()->id() ?? 0); diff --git a/app/Http/Controllers/Api/Scores/ScoreController.php b/app/Http/Controllers/Api/Scores/ScoreController.php index c8b465a2..825bd21c 100644 --- a/app/Http/Controllers/Api/Scores/ScoreController.php +++ b/app/Http/Controllers/Api/Scores/ScoreController.php @@ -81,9 +81,6 @@ class ScoreController extends BaseApiController return response()->json(['ok' => true] + $data); } - /** - * @return int|JsonResponse - */ private function authenticatedUserIdOrUnauthorized(): int|JsonResponse { $userId = (int) (request()->user()?->id ?? auth()->id() ?? 0); diff --git a/app/Http/Controllers/Api/Settings/ConfigurationAdminController.php b/app/Http/Controllers/Api/Settings/ConfigurationAdminController.php index b95add77..62b1ef1b 100644 --- a/app/Http/Controllers/Api/Settings/ConfigurationAdminController.php +++ b/app/Http/Controllers/Api/Settings/ConfigurationAdminController.php @@ -39,7 +39,7 @@ class ConfigurationAdminController extends BaseApiController public function update(ConfigurationUpdateRequest $request, int $id): JsonResponse { $config = $this->service->update($id, $request->validated()); - if (!$config) { + if (! $config) { return response()->json(['ok' => false, 'message' => 'Configuration not found.'], 404); } @@ -52,7 +52,7 @@ class ConfigurationAdminController extends BaseApiController public function destroy(int $id): JsonResponse { $deleted = $this->service->delete($id); - if (!$deleted) { + if (! $deleted) { return response()->json(['ok' => false, 'message' => 'Configuration not found.'], 404); } @@ -62,7 +62,7 @@ class ConfigurationAdminController extends BaseApiController public function getByKey(string $configKey): JsonResponse { $config = $this->service->getByKey($configKey); - if (!$config) { + if (! $config) { return response()->json(['ok' => false, 'message' => 'Configuration not found.'], 404); } diff --git a/app/Http/Controllers/Api/Settings/EventController.php b/app/Http/Controllers/Api/Settings/EventController.php index f245ccf8..6bbb4f35 100644 --- a/app/Http/Controllers/Api/Settings/EventController.php +++ b/app/Http/Controllers/Api/Settings/EventController.php @@ -50,7 +50,7 @@ class EventController extends BaseApiController public function show(int $eventId): JsonResponse { $event = $this->listService->find($eventId); - if (!$event) { + if (! $event) { return response()->json(['ok' => false, 'message' => 'Event not found.'], 404); } @@ -73,6 +73,7 @@ class EventController extends BaseApiController $result = $this->managementService->create($payload, $request->file('flyer'), $guard); } catch (\Throwable $e) { Log::error('Event creation failed.', ['error' => $e->getMessage()]); + return response()->json(['ok' => false, 'message' => 'Failed to create event.'], 500); } @@ -176,9 +177,6 @@ class EventController extends BaseApiController ]); } - /** - * @return int|JsonResponse - */ private function authenticatedUserIdOrUnauthorized(): int|JsonResponse { $userId = (int) (auth()->id() ?? 0); diff --git a/app/Http/Controllers/Api/Settings/PreferencesController.php b/app/Http/Controllers/Api/Settings/PreferencesController.php index 2a93b8c2..633414bb 100644 --- a/app/Http/Controllers/Api/Settings/PreferencesController.php +++ b/app/Http/Controllers/Api/Settings/PreferencesController.php @@ -58,7 +58,7 @@ class PreferencesController extends BaseApiController public function showForUser(int $userId): JsonResponse { $row = Preferences::query()->where('user_id', $userId)->first(); - if (!$row) { + if (! $row) { return $this->error('Preferences not found.', Response::HTTP_NOT_FOUND); } @@ -82,7 +82,8 @@ class PreferencesController extends BaseApiController try { $saved = $this->commandService->upsert($guard, $request->validated()); } catch (\Throwable $e) { - Log::error('Preferences save failed: ' . $e->getMessage()); + Log::error('Preferences save failed: '.$e->getMessage()); + return $this->error('Unable to save preferences.', Response::HTTP_INTERNAL_SERVER_ERROR); } @@ -103,7 +104,8 @@ class PreferencesController extends BaseApiController try { $saved = $this->commandService->upsert($userId, $request->validated()); } catch (\Throwable $e) { - Log::error('Preferences update failed: ' . $e->getMessage()); + Log::error('Preferences update failed: '.$e->getMessage()); + return $this->error('Unable to save preferences.', Response::HTTP_INTERNAL_SERVER_ERROR); } @@ -117,7 +119,7 @@ class PreferencesController extends BaseApiController public function destroy(int $userId): JsonResponse { $row = Preferences::query()->where('user_id', $userId)->first(); - if (!$row) { + if (! $row) { return $this->error('Preferences not found.', Response::HTTP_NOT_FOUND); } @@ -126,20 +128,18 @@ class PreferencesController extends BaseApiController try { $deleted = $this->commandService->delete($row); } catch (\Throwable $e) { - Log::error('Preferences delete failed: ' . $e->getMessage()); + Log::error('Preferences delete failed: '.$e->getMessage()); + return $this->error('Unable to delete preferences.', Response::HTTP_INTERNAL_SERVER_ERROR); } - if (!$deleted) { + if (! $deleted) { return $this->error('Unable to delete preferences.', Response::HTTP_INTERNAL_SERVER_ERROR); } return $this->success(null, 'Preferences deleted.'); } - /** - * @return int|JsonResponse - */ private function authenticatedUserIdOrUnauthorized(): int|JsonResponse { $userId = (int) (auth()->id() ?? 0); diff --git a/app/Http/Controllers/Api/Settings/SchoolCalendarController.php b/app/Http/Controllers/Api/Settings/SchoolCalendarController.php index d6cf29e8..53eccb0a 100644 --- a/app/Http/Controllers/Api/Settings/SchoolCalendarController.php +++ b/app/Http/Controllers/Api/Settings/SchoolCalendarController.php @@ -99,7 +99,7 @@ class SchoolCalendarController extends BaseApiController public function show(int $eventId): JsonResponse { $event = $this->queryService->findEvent($eventId); - if (!$event) { + if (! $event) { return $this->error('Event not found.', Response::HTTP_NOT_FOUND); } @@ -116,7 +116,8 @@ class SchoolCalendarController extends BaseApiController try { $result = $this->mutationService->create($payload, $targets); } catch (\Throwable $e) { - Log::error('SchoolCalendar store failed: ' . $e->getMessage()); + Log::error('SchoolCalendar store failed: '.$e->getMessage()); + return $this->error('Failed to save event.', Response::HTTP_INTERNAL_SERVER_ERROR); } @@ -129,7 +130,7 @@ class SchoolCalendarController extends BaseApiController public function update(SchoolCalendarUpdateRequest $request, int $eventId): JsonResponse { $event = $this->queryService->findEvent($eventId); - if (!$event) { + if (! $event) { return $this->error('Event not found.', Response::HTTP_NOT_FOUND); } @@ -139,7 +140,8 @@ class SchoolCalendarController extends BaseApiController try { $result = $this->mutationService->update($event, $payload, $targets); } catch (\Throwable $e) { - Log::error('SchoolCalendar update failed: ' . $e->getMessage()); + Log::error('SchoolCalendar update failed: '.$e->getMessage()); + return $this->error('Failed to update event.', Response::HTTP_INTERNAL_SERVER_ERROR); } @@ -152,14 +154,15 @@ class SchoolCalendarController extends BaseApiController public function destroy(int $eventId): JsonResponse { $event = $this->queryService->findEvent($eventId); - if (!$event) { + if (! $event) { return $this->error('Event not found.', Response::HTTP_NOT_FOUND); } try { $this->mutationService->delete($event); } catch (\Throwable $e) { - Log::error('SchoolCalendar delete failed: ' . $e->getMessage()); + Log::error('SchoolCalendar delete failed: '.$e->getMessage()); + return $this->error('Failed to delete event.', Response::HTTP_INTERNAL_SERVER_ERROR); } @@ -169,15 +172,12 @@ class SchoolCalendarController extends BaseApiController private function extractEmailTargets(array $payload): array { return [ - 'parents' => !empty($payload['send_email_parent']), - 'teachers' => !empty($payload['send_email_teacher']), - 'admins' => !empty($payload['send_email_admin']), + 'parents' => ! empty($payload['send_email_parent']), + 'teachers' => ! empty($payload['send_email_teacher']), + 'admins' => ! empty($payload['send_email_admin']), ]; } - /** - * @return int|JsonResponse - */ private function authenticatedUserIdOrUnauthorized(): int|JsonResponse { $userId = (int) (auth()->id() ?? 0); diff --git a/app/Http/Controllers/Api/Settings/SettingsController.php b/app/Http/Controllers/Api/Settings/SettingsController.php index 2c37f826..396b9ae9 100644 --- a/app/Http/Controllers/Api/Settings/SettingsController.php +++ b/app/Http/Controllers/Api/Settings/SettingsController.php @@ -40,7 +40,8 @@ class SettingsController extends BaseApiController try { $updated = $this->settingsService->update($request->validated(), $guard); } catch (\Throwable $e) { - Log::error('Settings update failed: ' . $e->getMessage()); + Log::error('Settings update failed: '.$e->getMessage()); + return $this->error('Unable to update settings.', Response::HTTP_INTERNAL_SERVER_ERROR); } @@ -49,9 +50,6 @@ class SettingsController extends BaseApiController ], 'Settings updated.'); } - /** - * @return int|JsonResponse - */ private function authenticatedUserIdOrUnauthorized(): int|JsonResponse { $userId = (int) (auth()->id() ?? 0); diff --git a/app/Http/Controllers/Api/Staff/StaffController.php b/app/Http/Controllers/Api/Staff/StaffController.php index 38418b0e..38e53478 100644 --- a/app/Http/Controllers/Api/Staff/StaffController.php +++ b/app/Http/Controllers/Api/Staff/StaffController.php @@ -9,6 +9,7 @@ use App\Http\Requests\Staff\StaffUpdateRequest; use App\Http\Resources\Staff\StaffCollection; use App\Http\Resources\Staff\StaffResource; use App\Models\Staff; +use App\Models\User; use App\Services\Staff\StaffCommandService; use App\Services\Staff\StaffQueryService; use Illuminate\Http\JsonResponse; @@ -47,12 +48,12 @@ class StaffController extends BaseApiController public function show(int $id): JsonResponse { $staff = $this->queryService->find($id); - if (!$staff) { + if (! $staff) { return $this->error('Staff member not found.', Response::HTTP_NOT_FOUND); } $this->authorize('view', $staff); - $staff->school_id = \App\Models\User::getSchoolIdByUserId((int) ($staff->user_id ?? 0)); + $staff->school_id = User::getSchoolIdByUserId((int) ($staff->user_id ?? 0)); return $this->success([ 'staff' => new StaffResource($staff), @@ -66,7 +67,8 @@ class StaffController extends BaseApiController try { $staff = $this->commandService->create($request->validated()); } catch (\Throwable $e) { - Log::error('Staff create failed: ' . $e->getMessage()); + Log::error('Staff create failed: '.$e->getMessage()); + return $this->error($e->getMessage(), Response::HTTP_BAD_REQUEST); } @@ -78,7 +80,7 @@ class StaffController extends BaseApiController public function update(StaffUpdateRequest $request, int $id): JsonResponse { $staff = $this->queryService->find($id); - if (!$staff) { + if (! $staff) { return $this->error('Staff member not found.', Response::HTTP_NOT_FOUND); } @@ -87,7 +89,8 @@ class StaffController extends BaseApiController try { $updated = $this->commandService->update($staff, $request->validated()); } catch (\Throwable $e) { - Log::error('Staff update failed: ' . $e->getMessage()); + Log::error('Staff update failed: '.$e->getMessage()); + return $this->error('Unable to update staff.', Response::HTTP_INTERNAL_SERVER_ERROR); } @@ -99,7 +102,7 @@ class StaffController extends BaseApiController public function destroy(int $id): JsonResponse { $staff = $this->queryService->find($id); - if (!$staff) { + if (! $staff) { return $this->error('Staff member not found.', Response::HTTP_NOT_FOUND); } @@ -108,11 +111,12 @@ class StaffController extends BaseApiController try { $deleted = $this->commandService->delete($staff); } catch (\Throwable $e) { - Log::error('Staff delete failed: ' . $e->getMessage()); + Log::error('Staff delete failed: '.$e->getMessage()); + return $this->error('Unable to delete staff.', Response::HTTP_INTERNAL_SERVER_ERROR); } - if (!$deleted) { + if (! $deleted) { return $this->error('Unable to delete staff.', Response::HTTP_INTERNAL_SERVER_ERROR); } diff --git a/app/Http/Controllers/Api/Staff/TeacherController.php b/app/Http/Controllers/Api/Staff/TeacherController.php index 83c2ea10..fa761c7f 100644 --- a/app/Http/Controllers/Api/Staff/TeacherController.php +++ b/app/Http/Controllers/Api/Staff/TeacherController.php @@ -124,12 +124,12 @@ class TeacherController extends BaseApiController private function teacherPayload(?array $teacher, int $userId): ?array { - if (!$teacher) { + if (! $teacher) { $model = User::query()->find($userId); $teacher = $model?->toArray(); } - if (!$teacher) { + if (! $teacher) { return null; } @@ -142,9 +142,6 @@ class TeacherController extends BaseApiController ]; } - /** - * @return int|JsonResponse - */ private function authenticatedUserIdOrUnauthorized(): int|JsonResponse { $userId = (int) (auth()->id() ?? 0); diff --git a/app/Http/Controllers/Api/StatsController.php b/app/Http/Controllers/Api/StatsController.php index 412aa165..68bf1e53 100644 --- a/app/Http/Controllers/Api/StatsController.php +++ b/app/Http/Controllers/Api/StatsController.php @@ -2,7 +2,6 @@ namespace App\Http\Controllers\Api; -use App\Http\Controllers\Api\BaseApiController; use App\Models\Stats; use Illuminate\Support\Facades\Log; use Illuminate\Support\Facades\Schema; @@ -18,9 +17,11 @@ class StatsController extends BaseApiController } $stats = Stats::query()->get()->toArray(); + return $this->success($stats, 'Statistics retrieved successfully'); } catch (\Throwable $e) { Log::error('Stats retrieval error: '.$e->getMessage()); + return $this->respondError('Failed to retrieve statistics', Response::HTTP_INTERNAL_SERVER_ERROR); } } diff --git a/app/Http/Controllers/Api/Students/StudentController.php b/app/Http/Controllers/Api/Students/StudentController.php index 80280e5b..e81aaed9 100644 --- a/app/Http/Controllers/Api/Students/StudentController.php +++ b/app/Http/Controllers/Api/Students/StudentController.php @@ -15,7 +15,6 @@ use App\Http\Resources\Students\StudentClassSectionResource; use App\Http\Resources\Students\StudentRemovedResource; use App\Http\Resources\Students\StudentScoreCardRowResource; use App\Models\AttendanceRecord; -use App\Models\ClassSection; use App\Models\EmergencyContact; use App\Models\Incident; use App\Models\SemesterScore; @@ -82,10 +81,10 @@ class StudentController extends BaseApiController $payload = $validator->validated(); $parentIds = []; - if (!empty($payload['parent_ids'])) { + if (! empty($payload['parent_ids'])) { $parentIds = array_values(array_unique(array_map('intval', $payload['parent_ids']))); } - if (!empty($payload['parent_id'])) { + if (! empty($payload['parent_id'])) { $parentIds[] = (int) $payload['parent_id']; } $parentIds = array_values(array_unique(array_filter($parentIds))); @@ -149,7 +148,7 @@ class StudentController extends BaseApiController public function show(int $studentId): JsonResponse { $student = Student::query()->find($studentId); - if (!$student) { + if (! $student) { return response()->json(['ok' => false, 'message' => 'Student not found.'], 404); } @@ -263,7 +262,7 @@ class StudentController extends BaseApiController if (array_key_exists('allergies', $payload)) { $healthPayload['allergies'] = $payload['allergies']; } - if (!empty($healthPayload)) { + if (! empty($healthPayload)) { $this->profileService->updateStudent($student->id, $healthPayload); } @@ -380,7 +379,7 @@ class StudentController extends BaseApiController public function destroy(int $studentId): JsonResponse { $student = Student::query()->find($studentId); - if (!$student) { + if (! $student) { return response()->json(['ok' => false, 'message' => 'Student not found.'], 404); } @@ -522,8 +521,8 @@ class StudentController extends BaseApiController $rows = AttendanceRecord::query() ->where('student_id', $studentId) - ->when(!empty($payload['school_year']), fn ($q) => $q->where('school_year', $payload['school_year'])) - ->when(!empty($payload['semester']), fn ($q) => $q->where('semester', $payload['semester'])) + ->when(! empty($payload['school_year']), fn ($q) => $q->where('school_year', $payload['school_year'])) + ->when(! empty($payload['semester']), fn ($q) => $q->where('semester', $payload['semester'])) ->orderBy('school_year', 'desc') ->orderBy('semester', 'desc') ->get() @@ -554,8 +553,8 @@ class StudentController extends BaseApiController $rows = Incident::query() ->where('student_id', $studentId) - ->when(!empty($payload['school_year']), fn ($q) => $q->where('school_year', $payload['school_year'])) - ->when(!empty($payload['semester']), fn ($q) => $q->where('semester', $payload['semester'])) + ->when(! empty($payload['school_year']), fn ($q) => $q->where('school_year', $payload['school_year'])) + ->when(! empty($payload['semester']), fn ($q) => $q->where('semester', $payload['semester'])) ->orderByDesc('incident_datetime') ->get() ->toArray(); @@ -585,8 +584,8 @@ class StudentController extends BaseApiController $rows = SemesterScore::query() ->where('student_id', $studentId) - ->when(!empty($payload['school_year']), fn ($q) => $q->where('school_year', $payload['school_year'])) - ->when(!empty($payload['semester']), fn ($q) => $q->where('semester', $payload['semester'])) + ->when(! empty($payload['school_year']), fn ($q) => $q->where('school_year', $payload['school_year'])) + ->when(! empty($payload['semester']), fn ($q) => $q->where('semester', $payload['semester'])) ->orderBy('school_year', 'desc') ->orderBy('semester', 'desc') ->get() @@ -611,12 +610,12 @@ class StudentController extends BaseApiController public function parents(int $studentId): JsonResponse { $student = Student::query()->find($studentId); - if (!$student) { + if (! $student) { return response()->json(['ok' => false, 'message' => 'Student not found.'], 404); } $parent = null; - if (!empty($student->parent_id)) { + if (! empty($student->parent_id)) { $parent = User::query() ->select(['id', 'firstname', 'lastname', 'email', 'cellphone']) ->find((int) $student->parent_id); @@ -632,7 +631,7 @@ class StudentController extends BaseApiController public function emergencyContacts(int $studentId): JsonResponse { $student = Student::query()->find($studentId); - if (!$student) { + if (! $student) { return response()->json(['ok' => false, 'message' => 'Student not found.'], 404); } @@ -640,7 +639,7 @@ class StudentController extends BaseApiController if (Schema::hasColumn('emergency_contacts', 'student_id')) { $query->where('student_id', $studentId); } - if (!empty($student->parent_id)) { + if (! empty($student->parent_id)) { $query->orWhere('parent_id', $student->parent_id); } @@ -659,7 +658,7 @@ class StudentController extends BaseApiController public function addEmergencyContact(Request $request, int $studentId): JsonResponse { $student = Student::query()->find($studentId); - if (!$student) { + if (! $student) { return response()->json(['ok' => false, 'message' => 'Student not found.'], 404); } @@ -702,7 +701,7 @@ class StudentController extends BaseApiController public function updateEmergencyContact(Request $request, int $studentId, int $contactId): JsonResponse { $student = Student::query()->find($studentId); - if (!$student) { + if (! $student) { return response()->json(['ok' => false, 'message' => 'Student not found.'], 404); } @@ -724,7 +723,7 @@ class StudentController extends BaseApiController } $contact = EmergencyContact::query()->find($contactId); - if (!$contact) { + if (! $contact) { return response()->json(['ok' => false, 'message' => 'Contact not found.'], 404); } @@ -755,7 +754,7 @@ class StudentController extends BaseApiController } $student = Student::query()->find($studentId); - if (!$student) { + if (! $student) { return response()->json(['ok' => false, 'message' => 'Student not found.'], 404); } @@ -778,14 +777,14 @@ class StudentController extends BaseApiController } $student = Student::query()->find($studentId); - if (!$student) { + if (! $student) { return response()->json(['ok' => false, 'message' => 'Student not found.'], 404); } $file = $request->file('photo'); $ext = $file->getClientOriginalExtension() ?: 'jpg'; - $path = "student-photos/{$studentId}." . strtolower($ext); - Storage::disk('public')->putFileAs('student-photos', $file, "{$studentId}." . strtolower($ext)); + $path = "student-photos/{$studentId}.".strtolower($ext); + Storage::disk('public')->putFileAs('student-photos', $file, "{$studentId}.".strtolower($ext)); return response()->json([ 'ok' => true, @@ -800,7 +799,7 @@ class StudentController extends BaseApiController $disk = Storage::disk('public'); foreach ($candidates as $ext) { - $path = $base . '.' . $ext; + $path = $base.'.'.$ext; if ($disk->exists($path)) { return response()->json([ 'ok' => true, @@ -815,13 +814,14 @@ class StudentController extends BaseApiController private function generateSchoolId(int $studentId): string { $year = (int) date('y'); - return 'STU' . $year . str_pad((string) $studentId, 4, '0', STR_PAD_LEFT); + + return 'STU'.$year.str_pad((string) $studentId, 4, '0', STR_PAD_LEFT); } public function scoreCard(int $studentId): JsonResponse { $result = $this->scoreCardService->scoreCard($studentId); - if (!$result['ok']) { + if (! $result['ok']) { return response()->json(['ok' => false, 'message' => $result['message'] ?? 'Not found.'], 404); } diff --git a/app/Http/Controllers/Api/Subjects/SubjectCurriculumController.php b/app/Http/Controllers/Api/Subjects/SubjectCurriculumController.php index d9681de7..9602d637 100644 --- a/app/Http/Controllers/Api/Subjects/SubjectCurriculumController.php +++ b/app/Http/Controllers/Api/Subjects/SubjectCurriculumController.php @@ -35,7 +35,7 @@ class SubjectCurriculumController extends BaseApiController public function show(int $id): JsonResponse { $entry = SubjectCurriculum::query()->find($id); - if (!$entry) { + if (! $entry) { return response()->json(['ok' => false, 'message' => 'Curriculum entry not found.'], 404); } @@ -58,7 +58,7 @@ class SubjectCurriculumController extends BaseApiController public function update(SubjectCurriculumUpdateRequest $request, int $id): JsonResponse { $entry = $this->service->update($id, $request->validated()); - if (!$entry) { + if (! $entry) { return response()->json(['ok' => false, 'message' => 'Curriculum entry not found.'], 404); } @@ -71,7 +71,7 @@ class SubjectCurriculumController extends BaseApiController public function destroy(int $id): JsonResponse { $deleted = $this->service->delete($id); - if (!$deleted) { + if (! $deleted) { return response()->json(['ok' => false, 'message' => 'Curriculum entry not found.'], 404); } diff --git a/app/Http/Controllers/Api/Support/SupportController.php b/app/Http/Controllers/Api/Support/SupportController.php index beb0522d..075a8fe2 100644 --- a/app/Http/Controllers/Api/Support/SupportController.php +++ b/app/Http/Controllers/Api/Support/SupportController.php @@ -64,12 +64,13 @@ class SupportController extends BaseApiController $payload = $request->validated(); $payload['user_email'] = $user->email ?? null; - $payload['user_name'] = trim(($user->firstname ?? '') . ' ' . ($user->lastname ?? '')); + $payload['user_name'] = trim(($user->firstname ?? '').' '.($user->lastname ?? '')); try { $result = $this->supportService->create((int) $user->id, $payload); } catch (\Throwable $e) { - Log::error('Support request failed: ' . $e->getMessage()); + Log::error('Support request failed: '.$e->getMessage()); + return $this->error('Unable to submit support request.', Response::HTTP_INTERNAL_SERVER_ERROR); } @@ -86,9 +87,6 @@ class SupportController extends BaseApiController ]); } - /** - * @return int|JsonResponse - */ private function authenticatedUserIdOrUnauthorized(): int|JsonResponse { $userId = (int) (auth()->id() ?? 0); diff --git a/app/Http/Controllers/Api/System/DashboardController.php b/app/Http/Controllers/Api/System/DashboardController.php index 0acb1e99..5e4f9544 100644 --- a/app/Http/Controllers/Api/System/DashboardController.php +++ b/app/Http/Controllers/Api/System/DashboardController.php @@ -18,7 +18,7 @@ class DashboardController extends BaseApiController public function route(): JsonResponse { $user = auth()->user(); - if (!$user) { + if (! $user) { return $this->error('Unauthorized.', Response::HTTP_UNAUTHORIZED); } diff --git a/app/Http/Controllers/Api/System/DashboardRedirectController.php b/app/Http/Controllers/Api/System/DashboardRedirectController.php index d4de52d8..a4c1a75d 100644 --- a/app/Http/Controllers/Api/System/DashboardRedirectController.php +++ b/app/Http/Controllers/Api/System/DashboardRedirectController.php @@ -15,13 +15,12 @@ class DashboardRedirectController extends Controller public function __construct( private DashboardRouteService $service, private ApplicationUrlService $urls, - ) { - } + ) {} public function dashboard(): RedirectResponse { $user = auth()->user(); - if (!$user) { + if (! $user) { return redirect()->guest($this->urls->webLoginUrl()); } diff --git a/app/Http/Controllers/Api/System/DatabaseHealthController.php b/app/Http/Controllers/Api/System/DatabaseHealthController.php index 92f9995f..70b9bfbd 100644 --- a/app/Http/Controllers/Api/System/DatabaseHealthController.php +++ b/app/Http/Controllers/Api/System/DatabaseHealthController.php @@ -17,7 +17,7 @@ class DatabaseHealthController extends BaseApiController public function index(): JsonResponse { $result = $this->healthService->check(); - if (!$result['ok']) { + if (! $result['ok']) { return $this->error('Database connection failed.', Response::HTTP_SERVICE_UNAVAILABLE, [ 'error' => $result['error'], ]); diff --git a/app/Http/Controllers/Api/System/NavBuilderController.php b/app/Http/Controllers/Api/System/NavBuilderController.php index eecb1ca7..944bcfbf 100644 --- a/app/Http/Controllers/Api/System/NavBuilderController.php +++ b/app/Http/Controllers/Api/System/NavBuilderController.php @@ -8,8 +8,8 @@ use App\Http\Requests\System\NavItemStoreRequest; use App\Http\Resources\System\NavBuilderDataResource; use App\Http\Resources\System\NavItemResource; use App\Models\NavItem; -use App\Services\Navigation\NavBuilderService; use App\Services\Navigation\NavbarService; +use App\Services\Navigation\NavBuilderService; use Illuminate\Http\JsonResponse; use Illuminate\Support\Facades\Log; use Symfony\Component\HttpFoundation\Response; @@ -67,7 +67,7 @@ class NavBuilderController extends BaseApiController $deleted = $this->builderService->delete($id); - if (!$deleted) { + if (! $deleted) { return $this->error('Menu item not found.', Response::HTTP_NOT_FOUND); } @@ -81,7 +81,8 @@ class NavBuilderController extends BaseApiController try { $this->builderService->reorder($request->validated()['orders']); } catch (\Throwable $e) { - Log::error('Nav reorder failed: ' . $e->getMessage()); + Log::error('Nav reorder failed: '.$e->getMessage()); + return $this->error('Unable to reorder items.', Response::HTTP_INTERNAL_SERVER_ERROR); } diff --git a/app/Http/Controllers/Api/System/StatsController.php b/app/Http/Controllers/Api/System/StatsController.php index 9c0c648b..70554b2c 100644 --- a/app/Http/Controllers/Api/System/StatsController.php +++ b/app/Http/Controllers/Api/System/StatsController.php @@ -18,9 +18,11 @@ class StatsController extends BaseApiController } $stats = Stats::query()->get()->toArray(); + return $this->success($stats, 'Statistics retrieved successfully'); } catch (\Throwable $e) { Log::error('Stats retrieval error: '.$e->getMessage()); + return $this->respondError('Failed to retrieve statistics', Response::HTTP_INTERNAL_SERVER_ERROR); } } diff --git a/app/Http/Controllers/Api/Ui/UiController.php b/app/Http/Controllers/Api/Ui/UiController.php index 9a3f177c..e0c5e405 100644 --- a/app/Http/Controllers/Api/Ui/UiController.php +++ b/app/Http/Controllers/Api/Ui/UiController.php @@ -30,9 +30,6 @@ class UiController extends BaseApiController ], 'Style updated.'); } - /** - * @return int|JsonResponse - */ private function authenticatedUserIdOrUnauthorized(): int|JsonResponse { $userId = (int) (auth()->id() ?? 0); diff --git a/app/Http/Controllers/Api/Users/UserController.php b/app/Http/Controllers/Api/Users/UserController.php index f477641a..f12d4d32 100644 --- a/app/Http/Controllers/Api/Users/UserController.php +++ b/app/Http/Controllers/Api/Users/UserController.php @@ -151,9 +151,6 @@ class UserController extends BaseApiController ]); } - /** - * @return int|JsonResponse - */ private function authenticatedUserIdOrUnauthorized(): int|JsonResponse { $userId = (int) (auth()->id() ?? 0); diff --git a/app/Http/Controllers/Web/SchoolYearAdminPageController.php b/app/Http/Controllers/Web/SchoolYearAdminPageController.php index 84c0714a..7aac1b71 100644 --- a/app/Http/Controllers/Web/SchoolYearAdminPageController.php +++ b/app/Http/Controllers/Web/SchoolYearAdminPageController.php @@ -17,8 +17,7 @@ class SchoolYearAdminPageController extends Controller public function __construct( private SchoolYearClosureService $service, private SchoolYearResolver $resolver - ) { - } + ) {} public function show(Request $request): View { diff --git a/app/Http/Controllers/Web/TeacherCalendarPageController.php b/app/Http/Controllers/Web/TeacherCalendarPageController.php index d9a9b807..e1555272 100644 --- a/app/Http/Controllers/Web/TeacherCalendarPageController.php +++ b/app/Http/Controllers/Web/TeacherCalendarPageController.php @@ -16,8 +16,7 @@ class TeacherCalendarPageController extends Controller private SchoolCalendarQueryService $queryService, private SchoolCalendarMeetingService $meetingService, private SchoolCalendarFormatterService $formatterService, - ) { - } + ) {} public function show(): View { diff --git a/app/Http/Middleware/ApiDocsAuth.php b/app/Http/Middleware/ApiDocsAuth.php index 21e7fae4..14ee1c02 100644 --- a/app/Http/Middleware/ApiDocsAuth.php +++ b/app/Http/Middleware/ApiDocsAuth.php @@ -15,7 +15,7 @@ class ApiDocsAuth public function handle(Request $request, Closure $next): Response { $user = Auth::user(); - if (!$user) { + if (! $user) { $authHeader = (string) $request->header('Authorization'); if ($authHeader !== '' && stripos($authHeader, 'Bearer ') === 0) { $token = trim(substr($authHeader, 7)); @@ -34,11 +34,11 @@ class ApiDocsAuth } } - if (!$user) { + if (! $user) { return $this->deny('Unauthorized access to documentation.', 401); } - if (!$this->userIsAdmin((int) $user->id)) { + if (! $this->userIsAdmin((int) $user->id)) { return $this->deny('Admin privileges required.', 403); } diff --git a/app/Http/Middleware/ApiJwtAuth.php b/app/Http/Middleware/ApiJwtAuth.php index f6f2d6c1..afcfaa48 100644 --- a/app/Http/Middleware/ApiJwtAuth.php +++ b/app/Http/Middleware/ApiJwtAuth.php @@ -15,7 +15,7 @@ class ApiJwtAuth { try { $user = JWTAuth::parseToken()->authenticate(); - if (!$user) { + if (! $user) { return response()->json(['status' => false, 'message' => 'Unauthorized.'], 401); } diff --git a/app/Http/Middleware/CleanupScheduler.php b/app/Http/Middleware/CleanupScheduler.php index f2fe3ac1..f383011a 100644 --- a/app/Http/Middleware/CleanupScheduler.php +++ b/app/Http/Middleware/CleanupScheduler.php @@ -9,9 +9,7 @@ use Symfony\Component\HttpFoundation\Response; class CleanupScheduler { - public function __construct(private CleanupSchedulerService $service) - { - } + public function __construct(private CleanupSchedulerService $service) {} public function handle(Request $request, Closure $next): Response { diff --git a/app/Http/Middleware/EnsurePrintRequestsAdminAccess.php b/app/Http/Middleware/EnsurePrintRequestsAdminAccess.php index 32055dc3..467089e5 100644 --- a/app/Http/Middleware/EnsurePrintRequestsAdminAccess.php +++ b/app/Http/Middleware/EnsurePrintRequestsAdminAccess.php @@ -28,8 +28,7 @@ class EnsurePrintRequestsAdminAccess ->toArray(); foreach ( - ['administrator', 'admin', 'principal', 'vice_principal', 'vice-principal'] - as $allowed + ['administrator', 'admin', 'principal', 'vice_principal', 'vice-principal'] as $allowed ) { if (in_array($allowed, $roles, true)) { return $next($request); diff --git a/app/Http/Middleware/EnsureSchoolYearEditable.php b/app/Http/Middleware/EnsureSchoolYearEditable.php index 7bbfc881..dc96c1a9 100644 --- a/app/Http/Middleware/EnsureSchoolYearEditable.php +++ b/app/Http/Middleware/EnsureSchoolYearEditable.php @@ -12,13 +12,11 @@ use Symfony\Component\HttpFoundation\Response; class EnsureSchoolYearEditable { - public function __construct(private SchoolYearWriteGuard $guard) - { - } + public function __construct(private SchoolYearWriteGuard $guard) {} public function handle(Request $request, Closure $next): Response { - if (!in_array(strtoupper($request->method()), ['POST', 'PUT', 'PATCH', 'DELETE'], true)) { + if (! in_array(strtoupper($request->method()), ['POST', 'PUT', 'PATCH', 'DELETE'], true)) { return $next($request); } @@ -84,7 +82,7 @@ class EnsureSchoolYearEditable private function lookupYear(string $id, string $table, string $column): ?string { - if (!ctype_digit($id) || !DB::getSchemaBuilder()->hasTable($table)) { + if (! ctype_digit($id) || ! DB::getSchemaBuilder()->hasTable($table)) { return null; } diff --git a/app/Http/Middleware/MultiAuth.php b/app/Http/Middleware/MultiAuth.php index ee12e081..bc1cd473 100644 --- a/app/Http/Middleware/MultiAuth.php +++ b/app/Http/Middleware/MultiAuth.php @@ -16,6 +16,7 @@ class MultiAuth $sanctumUser = Auth::guard('sanctum')->user(); if ($sanctumUser) { Auth::setUser($sanctumUser); + return $next($request); } @@ -25,6 +26,7 @@ class MultiAuth $jwtUser = JWTAuth::setToken($token)->authenticate(); if ($jwtUser) { Auth::setUser($jwtUser); + return $next($request); } } catch (JWTException $e) { diff --git a/app/Http/Middleware/PrimeTimezone.php b/app/Http/Middleware/PrimeTimezone.php index 34811419..ab71d84c 100644 --- a/app/Http/Middleware/PrimeTimezone.php +++ b/app/Http/Middleware/PrimeTimezone.php @@ -9,9 +9,7 @@ use Symfony\Component\HttpFoundation\Response; class PrimeTimezone { - public function __construct(private TimeService $timeService) - { - } + public function __construct(private TimeService $timeService) {} public function handle(Request $request, Closure $next): Response { diff --git a/app/Http/Middleware/RequirePermission.php b/app/Http/Middleware/RequirePermission.php index bd5ae72b..b78d9f0a 100644 --- a/app/Http/Middleware/RequirePermission.php +++ b/app/Http/Middleware/RequirePermission.php @@ -9,9 +9,7 @@ use Symfony\Component\HttpFoundation\Response; class RequirePermission { - public function __construct(private PermissionCheckService $permissions) - { - } + public function __construct(private PermissionCheckService $permissions) {} public function handle(Request $request, Closure $next, string $permission): Response { @@ -20,10 +18,11 @@ class RequirePermission return response()->json(['status' => false, 'message' => 'Unauthorized.'], 401); } - if (!$this->permissions->hasPermission($userId, $permission)) { + if (! $this->permissions->hasPermission($userId, $permission)) { if ($request->expectsJson()) { return response()->json(['status' => false, 'message' => 'Access denied.'], 403); } + return response()->json(['status' => false, 'message' => 'Access denied.'], 403); } diff --git a/app/Http/Requests/Admin/SaveCompetitionScoresRequest.php b/app/Http/Requests/Admin/SaveCompetitionScoresRequest.php index c9b71b46..74d8e7de 100644 --- a/app/Http/Requests/Admin/SaveCompetitionScoresRequest.php +++ b/app/Http/Requests/Admin/SaveCompetitionScoresRequest.php @@ -2,10 +2,11 @@ namespace App\Http\Requests\Admin; +use App\Controllers\Admin\CompetitionWinnersController; use Illuminate\Foundation\Http\FormRequest; /** - * Mirrors legacy {@see \App\Controllers\Admin\CompetitionWinnersController::saveScores()} accepted fields. + * Mirrors legacy {@see CompetitionWinnersController::saveScores()} accepted fields. */ class SaveCompetitionScoresRequest extends FormRequest { diff --git a/app/Http/Requests/Admin/UpdateCompetitionWinnerRequest.php b/app/Http/Requests/Admin/UpdateCompetitionWinnerRequest.php index 5610abd3..11da37fc 100644 --- a/app/Http/Requests/Admin/UpdateCompetitionWinnerRequest.php +++ b/app/Http/Requests/Admin/UpdateCompetitionWinnerRequest.php @@ -3,6 +3,4 @@ namespace App\Http\Requests\Admin; /** Validation for updating competition settings (legacy `CompetitionWinnersController::update`). */ -class UpdateCompetitionWinnerRequest extends StoreCompetitionWinnerRequest -{ -} +class UpdateCompetitionWinnerRequest extends StoreCompetitionWinnerRequest {} diff --git a/app/Http/Requests/Administrator/SubmitAdministratorAbsenceRequest.php b/app/Http/Requests/Administrator/SubmitAdministratorAbsenceRequest.php index fc2ace6f..78efdab5 100644 --- a/app/Http/Requests/Administrator/SubmitAdministratorAbsenceRequest.php +++ b/app/Http/Requests/Administrator/SubmitAdministratorAbsenceRequest.php @@ -31,4 +31,4 @@ class SubmitAdministratorAbsenceRequest extends ApiFormRequest 'reason.required' => 'Reason is required.', ]; } -} \ No newline at end of file +} diff --git a/app/Http/Requests/Api/Admin/IndexAdminProgressRequest.php b/app/Http/Requests/Api/Admin/IndexAdminProgressRequest.php index 0ef66ddf..2a54a14a 100644 --- a/app/Http/Requests/Api/Admin/IndexAdminProgressRequest.php +++ b/app/Http/Requests/Api/Admin/IndexAdminProgressRequest.php @@ -2,7 +2,6 @@ namespace App\Http\Requests\Api\Admin; -use App\Http\Requests\ApiFormRequest; use Illuminate\Validation\Rule; class IndexAdminProgressRequest extends FormRequest @@ -36,4 +35,4 @@ class IndexAdminProgressRequest extends FormRequest 'status' => $this->filled('status') ? (string) $this->query('status') : '', ]); } -} \ No newline at end of file +} diff --git a/app/Http/Requests/Api/Admin/SaveAdminNotificationSubjectsRequest.php b/app/Http/Requests/Api/Admin/SaveAdminNotificationSubjectsRequest.php index 7b00fa43..f9ea9b87 100644 --- a/app/Http/Requests/Api/Admin/SaveAdminNotificationSubjectsRequest.php +++ b/app/Http/Requests/Api/Admin/SaveAdminNotificationSubjectsRequest.php @@ -2,8 +2,6 @@ namespace App\Http\Requests\Admin; -use App\Http\Requests\ApiFormRequest; - class SaveAdminNotificationSubjectsRequest extends FormRequest { public function authorize(): bool @@ -18,4 +16,4 @@ class SaveAdminNotificationSubjectsRequest extends FormRequest // dynamic structure: subjects[adminId][subjectKey]=true OR array of subject strings ]; } -} \ No newline at end of file +} diff --git a/app/Http/Requests/Api/Admin/SavePrintNotificationRecipientsRequest.php b/app/Http/Requests/Api/Admin/SavePrintNotificationRecipientsRequest.php index b5c72993..f37a6ee8 100644 --- a/app/Http/Requests/Api/Admin/SavePrintNotificationRecipientsRequest.php +++ b/app/Http/Requests/Api/Admin/SavePrintNotificationRecipientsRequest.php @@ -2,8 +2,6 @@ namespace App\Http\Requests\Admin; -use App\Http\Requests\ApiFormRequest; - class SavePrintNotificationRecipientsRequest extends FormRequest { public function authorize(): bool @@ -17,4 +15,4 @@ class SavePrintNotificationRecipientsRequest extends FormRequest 'notify' => ['required', 'array'], ]; } -} \ No newline at end of file +} diff --git a/app/Http/Requests/Api/Admin/SendTeacherSubmissionNotificationsRequest.php b/app/Http/Requests/Api/Admin/SendTeacherSubmissionNotificationsRequest.php index 5819b909..5e58cc8a 100644 --- a/app/Http/Requests/Api/Admin/SendTeacherSubmissionNotificationsRequest.php +++ b/app/Http/Requests/Api/Admin/SendTeacherSubmissionNotificationsRequest.php @@ -2,8 +2,6 @@ namespace App\Http\Requests\Admin; -use App\Http\Requests\ApiFormRequest; - class SendTeacherSubmissionNotificationsRequest extends FormRequest { public function authorize(): bool @@ -19,4 +17,4 @@ class SendTeacherSubmissionNotificationsRequest extends FormRequest 'missing_items' => ['nullable', 'array'], ]; } -} \ No newline at end of file +} diff --git a/app/Http/Requests/Assignment/StoreStudentAssignmentRequest.php b/app/Http/Requests/Assignment/StoreStudentAssignmentRequest.php index 2e9069e5..8c7485a0 100644 --- a/app/Http/Requests/Assignment/StoreStudentAssignmentRequest.php +++ b/app/Http/Requests/Assignment/StoreStudentAssignmentRequest.php @@ -2,8 +2,6 @@ namespace App\Http\Requests; -use App\Http\Requests\ApiFormRequest; - class StoreStudentAssignmentRequest extends FormRequest { public function authorize(): bool @@ -14,11 +12,11 @@ class StoreStudentAssignmentRequest extends FormRequest public function rules(): array { return [ - 'student_id' => ['required', 'integer', 'exists:students,id'], + 'student_id' => ['required', 'integer', 'exists:students,id'], 'class_section_id' => ['required', 'integer', 'exists:class_sections,id'], - 'semester' => ['nullable', 'string', 'max:50'], - 'school_year' => ['nullable', 'string', 'max:50'], - 'description' => ['nullable', 'string'], + 'semester' => ['nullable', 'string', 'max:50'], + 'school_year' => ['nullable', 'string', 'max:50'], + 'description' => ['nullable', 'string'], ]; } -} \ No newline at end of file +} diff --git a/app/Http/Requests/Attendance/AdminAddAttendanceEntryRequest.php b/app/Http/Requests/Attendance/AdminAddAttendanceEntryRequest.php index fe24e995..bdf7828d 100644 --- a/app/Http/Requests/Attendance/AdminAddAttendanceEntryRequest.php +++ b/app/Http/Requests/Attendance/AdminAddAttendanceEntryRequest.php @@ -24,4 +24,4 @@ class AdminAddAttendanceEntryRequest extends FormRequest 'is_reported' => ['nullable', 'in:yes,no'], ]; } -} \ No newline at end of file +} diff --git a/app/Http/Requests/Attendance/SaveAdminAttendanceRequest.php b/app/Http/Requests/Attendance/SaveAdminAttendanceRequest.php index 630d5e2f..b6cbc816 100644 --- a/app/Http/Requests/Attendance/SaveAdminAttendanceRequest.php +++ b/app/Http/Requests/Attendance/SaveAdminAttendanceRequest.php @@ -21,4 +21,4 @@ class SaveAdminAttendanceRequest extends FormRequest 'staff' => ['nullable', 'array'], ]; } -} \ No newline at end of file +} diff --git a/app/Http/Requests/Attendance/SaveStaffAttendanceCellRequest.php b/app/Http/Requests/Attendance/SaveStaffAttendanceCellRequest.php index bea0e6ef..40c79fdb 100644 --- a/app/Http/Requests/Attendance/SaveStaffAttendanceCellRequest.php +++ b/app/Http/Requests/Attendance/SaveStaffAttendanceCellRequest.php @@ -23,4 +23,4 @@ class SaveStaffAttendanceCellRequest extends FormRequest 'reason' => ['nullable', 'string'], ]; } -} \ No newline at end of file +} diff --git a/app/Http/Requests/Attendance/TeacherSubmitAttendanceRequest.php b/app/Http/Requests/Attendance/TeacherSubmitAttendanceRequest.php index fafef9bb..dd32766d 100644 --- a/app/Http/Requests/Attendance/TeacherSubmitAttendanceRequest.php +++ b/app/Http/Requests/Attendance/TeacherSubmitAttendanceRequest.php @@ -26,4 +26,4 @@ class TeacherSubmitAttendanceRequest extends FormRequest 'teachers' => ['required', 'array', 'min:1'], ]; } -} \ No newline at end of file +} diff --git a/app/Http/Requests/Attendance/UpdateAttendanceManagementRequest.php b/app/Http/Requests/Attendance/UpdateAttendanceManagementRequest.php index eded3b8c..325c34af 100644 --- a/app/Http/Requests/Attendance/UpdateAttendanceManagementRequest.php +++ b/app/Http/Requests/Attendance/UpdateAttendanceManagementRequest.php @@ -26,4 +26,4 @@ class UpdateAttendanceManagementRequest extends FormRequest 'is_reported' => ['nullable'], ]; } -} \ No newline at end of file +} diff --git a/app/Http/Requests/AttendanceTracking/ComposeAttendanceEmailRequest.php b/app/Http/Requests/AttendanceTracking/ComposeAttendanceEmailRequest.php index ad954676..6d8c2614 100644 --- a/app/Http/Requests/AttendanceTracking/ComposeAttendanceEmailRequest.php +++ b/app/Http/Requests/AttendanceTracking/ComposeAttendanceEmailRequest.php @@ -14,9 +14,9 @@ class ComposeAttendanceEmailRequest extends ApiFormRequest public function rules(): array { return [ - 'student_id' => ['required', 'integer', 'min:1'], - 'code' => ['nullable', 'string'], - 'variant' => ['nullable', 'string'], + 'student_id' => ['required', 'integer', 'min:1'], + 'code' => ['nullable', 'string'], + 'variant' => ['nullable', 'string'], 'incident_date' => ['nullable', 'date_format:Y-m-d'], ]; } diff --git a/app/Http/Requests/AttendanceTracking/RecordAttendanceTrackingRequest.php b/app/Http/Requests/AttendanceTracking/RecordAttendanceTrackingRequest.php index 9c00dec7..9e71011d 100644 --- a/app/Http/Requests/AttendanceTracking/RecordAttendanceTrackingRequest.php +++ b/app/Http/Requests/AttendanceTracking/RecordAttendanceTrackingRequest.php @@ -14,13 +14,13 @@ class RecordAttendanceTrackingRequest extends ApiFormRequest public function rules(): array { return [ - 'student_id' => ['required', 'integer', 'min:1'], - 'date' => ['required', 'date_format:Y-m-d'], + 'student_id' => ['required', 'integer', 'min:1'], + 'date' => ['required', 'date_format:Y-m-d'], 'parent_email' => ['nullable', 'email'], - 'parent_name' => ['nullable', 'string'], + 'parent_name' => ['nullable', 'string'], 'subject_type' => ['nullable', 'string'], - 'semester' => ['nullable', 'string'], - 'school_year' => ['nullable', 'string'], + 'semester' => ['nullable', 'string'], + 'school_year' => ['nullable', 'string'], ]; } } diff --git a/app/Http/Requests/AttendanceTracking/SaveAttendanceNotificationNoteRequest.php b/app/Http/Requests/AttendanceTracking/SaveAttendanceNotificationNoteRequest.php index 1d57f480..9276272a 100644 --- a/app/Http/Requests/AttendanceTracking/SaveAttendanceNotificationNoteRequest.php +++ b/app/Http/Requests/AttendanceTracking/SaveAttendanceNotificationNoteRequest.php @@ -14,13 +14,13 @@ class SaveAttendanceNotificationNoteRequest extends ApiFormRequest public function rules(): array { return [ - 'student_id' => ['required', 'integer', 'min:1'], - 'code' => ['required', 'string'], - 'note' => ['required', 'string'], + 'student_id' => ['required', 'integer', 'min:1'], + 'code' => ['required', 'string'], + 'note' => ['required', 'string'], 'incident_date' => ['nullable', 'date_format:Y-m-d'], - 'to' => ['nullable', 'email'], - 'subject' => ['nullable', 'string'], - 'variant' => ['nullable', 'string'], + 'to' => ['nullable', 'email'], + 'subject' => ['nullable', 'string'], + 'variant' => ['nullable', 'string'], ]; } } diff --git a/app/Http/Requests/AttendanceTracking/SendAttendanceManualEmailRequest.php b/app/Http/Requests/AttendanceTracking/SendAttendanceManualEmailRequest.php index 0994b8d9..bca44af7 100644 --- a/app/Http/Requests/AttendanceTracking/SendAttendanceManualEmailRequest.php +++ b/app/Http/Requests/AttendanceTracking/SendAttendanceManualEmailRequest.php @@ -14,12 +14,12 @@ class SendAttendanceManualEmailRequest extends ApiFormRequest public function rules(): array { return [ - 'student_id' => ['required', 'integer', 'min:1'], - 'to' => ['required', 'email'], - 'subject' => ['required', 'string'], - 'body_html' => ['required', 'string'], - 'code' => ['required', 'string'], - 'variant' => ['nullable', 'string'], + 'student_id' => ['required', 'integer', 'min:1'], + 'to' => ['required', 'email'], + 'subject' => ['required', 'string'], + 'body_html' => ['required', 'string'], + 'code' => ['required', 'string'], + 'variant' => ['nullable', 'string'], 'incident_date' => ['nullable', 'date_format:Y-m-d'], ]; } diff --git a/app/Http/Requests/Auth/RegisterRequest.php b/app/Http/Requests/Auth/RegisterRequest.php index 13401f00..ff57feda 100644 --- a/app/Http/Requests/Auth/RegisterRequest.php +++ b/app/Http/Requests/Auth/RegisterRequest.php @@ -8,7 +8,7 @@ class RegisterRequest extends ApiFormRequest { public static function ruleset(): array { - return (new self())->rules(); + return (new self)->rules(); } public function authorize(): bool diff --git a/app/Http/Requests/Badges/BadgePrintStatusRequest.php b/app/Http/Requests/Badges/BadgePrintStatusRequest.php index 4cf174a0..86429035 100644 --- a/app/Http/Requests/Badges/BadgePrintStatusRequest.php +++ b/app/Http/Requests/Badges/BadgePrintStatusRequest.php @@ -17,7 +17,7 @@ class BadgePrintStatusRequest extends FormRequest if (is_string($userIds)) { $userIds = array_filter(array_map('trim', explode(',', $userIds)), 'strlen'); - } elseif (!is_array($userIds)) { + } elseif (! is_array($userIds)) { $userIds = $userIds ? [$userIds] : []; } @@ -40,4 +40,4 @@ class BadgePrintStatusRequest extends FormRequest { return $this->input('user_ids', []); } -} \ No newline at end of file +} diff --git a/app/Http/Requests/Badges/GenerateBadgePdfRequest.php b/app/Http/Requests/Badges/GenerateBadgePdfRequest.php index 20f7684b..c1bc01a8 100644 --- a/app/Http/Requests/Badges/GenerateBadgePdfRequest.php +++ b/app/Http/Requests/Badges/GenerateBadgePdfRequest.php @@ -17,7 +17,7 @@ class GenerateBadgePdfRequest extends FormRequest if (is_string($userIds)) { $userIds = array_filter(array_map('trim', explode(',', $userIds)), 'strlen'); - } elseif (!is_array($userIds)) { + } elseif (! is_array($userIds)) { $userIds = $userIds ? [$userIds] : []; } @@ -38,4 +38,4 @@ class GenerateBadgePdfRequest extends FormRequest 'classes' => ['nullable', 'array'], ]; } -} \ No newline at end of file +} diff --git a/app/Http/Requests/Badges/LogBadgePrintRequest.php b/app/Http/Requests/Badges/LogBadgePrintRequest.php index 2e5362c3..b2fabafd 100644 --- a/app/Http/Requests/Badges/LogBadgePrintRequest.php +++ b/app/Http/Requests/Badges/LogBadgePrintRequest.php @@ -17,7 +17,7 @@ class LogBadgePrintRequest extends FormRequest if (is_string($userIds)) { $userIds = array_filter(array_map('trim', explode(',', $userIds)), 'strlen'); - } elseif (!is_array($userIds)) { + } elseif (! is_array($userIds)) { $userIds = $userIds ? [$userIds] : []; } @@ -38,4 +38,4 @@ class LogBadgePrintRequest extends FormRequest 'classes' => ['nullable', 'array'], ]; } -} \ No newline at end of file +} diff --git a/app/Http/Requests/ClassProgress/ClassProgressIndexRequest.php b/app/Http/Requests/ClassProgress/ClassProgressIndexRequest.php index aded861c..fd6ac3e0 100644 --- a/app/Http/Requests/ClassProgress/ClassProgressIndexRequest.php +++ b/app/Http/Requests/ClassProgress/ClassProgressIndexRequest.php @@ -2,8 +2,6 @@ namespace App\Http\Requests\ClassProgress; -use App\Http\Requests\ClassProgress\ClassProgressFormRequest; - class ClassProgressIndexRequest extends ClassProgressFormRequest { public function authorize(): bool @@ -23,7 +21,7 @@ class ClassProgressIndexRequest extends ClassProgressFormRequest 'week_start' => ['nullable', 'date_format:Y-m-d'], 'week_end' => ['nullable', 'date_format:Y-m-d'], 'subject' => ['nullable', 'string', 'max:120'], - 'status' => $statuses ? ['nullable', 'in:' . implode(',', $statuses)] : ['nullable', 'string'], + 'status' => $statuses ? ['nullable', 'in:'.implode(',', $statuses)] : ['nullable', 'string'], 'group_by_week' => ['nullable', 'boolean'], 'page' => ['nullable', 'integer', 'min:1'], 'per_page' => ['nullable', 'integer', 'min:1', 'max:200'], diff --git a/app/Http/Requests/ClassProgress/ClassProgressMetaRequest.php b/app/Http/Requests/ClassProgress/ClassProgressMetaRequest.php index cb7f61ad..ba56c177 100644 --- a/app/Http/Requests/ClassProgress/ClassProgressMetaRequest.php +++ b/app/Http/Requests/ClassProgress/ClassProgressMetaRequest.php @@ -2,8 +2,6 @@ namespace App\Http\Requests\ClassProgress; -use App\Http\Requests\ClassProgress\ClassProgressFormRequest; - class ClassProgressMetaRequest extends ClassProgressFormRequest { public function authorize(): bool diff --git a/app/Http/Requests/ClassProgress/ClassProgressStoreRequest.php b/app/Http/Requests/ClassProgress/ClassProgressStoreRequest.php index ea3d0e69..81dbbdb1 100644 --- a/app/Http/Requests/ClassProgress/ClassProgressStoreRequest.php +++ b/app/Http/Requests/ClassProgress/ClassProgressStoreRequest.php @@ -2,8 +2,6 @@ namespace App\Http\Requests\ClassProgress; -use App\Http\Requests\ClassProgress\ClassProgressFormRequest; - class ClassProgressStoreRequest extends ClassProgressFormRequest { public function authorize(): bool @@ -27,7 +25,7 @@ class ClassProgressStoreRequest extends ClassProgressFormRequest $sections = (array) config('progress.subject_sections', []); $maxFileKb = (int) config('progress.attachments.max_kb', 5120); $mimes = (array) config('progress.attachments.mimes', ['pdf', 'jpg', 'jpeg', 'png']); - $mimeRule = 'mimes:' . implode(',', $mimes); + $mimeRule = 'mimes:'.implode(',', $mimes); foreach ($sections as $slug => $section) { $rules["covered_{$slug}"] = ['nullable', 'string']; @@ -37,7 +35,7 @@ class ClassProgressStoreRequest extends ClassProgressFormRequest $rules["chapter_{$slug}"] = ['nullable', 'array']; $rules["chapter_{$slug}.*"] = ['nullable', 'string', 'max:120']; $rules["attachment_{$slug}"] = ['nullable', 'array']; - $rules["attachment_{$slug}.*"] = ['nullable', 'file', $mimeRule, 'max:' . $maxFileKb]; + $rules["attachment_{$slug}.*"] = ['nullable', 'file', $mimeRule, 'max:'.$maxFileKb]; } return $rules; diff --git a/app/Http/Requests/ClassProgress/ClassProgressUpdateRequest.php b/app/Http/Requests/ClassProgress/ClassProgressUpdateRequest.php index 1919b7e5..c72f8030 100644 --- a/app/Http/Requests/ClassProgress/ClassProgressUpdateRequest.php +++ b/app/Http/Requests/ClassProgress/ClassProgressUpdateRequest.php @@ -2,8 +2,6 @@ namespace App\Http\Requests\ClassProgress; -use App\Http\Requests\ClassProgress\ClassProgressFormRequest; - class ClassProgressUpdateRequest extends ClassProgressFormRequest { public function authorize(): bool @@ -16,7 +14,7 @@ class ClassProgressUpdateRequest extends ClassProgressFormRequest $statuses = array_keys((array) config('progress.status_options', [])); $maxFileKb = (int) config('progress.attachments.max_kb', 5120); $mimes = (array) config('progress.attachments.mimes', ['pdf', 'jpg', 'jpeg', 'png']); - $mimeRule = 'mimes:' . implode(',', $mimes); + $mimeRule = 'mimes:'.implode(',', $mimes); return [ 'week_start' => ['nullable', 'date_format:Y-m-d'], @@ -24,11 +22,11 @@ class ClassProgressUpdateRequest extends ClassProgressFormRequest 'covered' => ['nullable', 'string'], 'homework' => ['nullable', 'string'], 'unit_title' => ['nullable', 'string', 'max:120'], - 'status' => $statuses ? ['nullable', 'in:' . implode(',', $statuses)] : ['nullable', 'string'], + 'status' => $statuses ? ['nullable', 'in:'.implode(',', $statuses)] : ['nullable', 'string'], 'support_needed' => ['nullable', 'string'], 'flags' => ['nullable', 'array'], 'attachments' => ['nullable', 'array'], - 'attachments.*' => ['nullable', 'file', $mimeRule, 'max:' . $maxFileKb], + 'attachments.*' => ['nullable', 'file', $mimeRule, 'max:'.$maxFileKb], ]; } diff --git a/app/Http/Requests/Families/FamilyComposeEmailRequest.php b/app/Http/Requests/Families/FamilyComposeEmailRequest.php index 3058a88c..caf48ad7 100644 --- a/app/Http/Requests/Families/FamilyComposeEmailRequest.php +++ b/app/Http/Requests/Families/FamilyComposeEmailRequest.php @@ -10,7 +10,7 @@ class FamilyComposeEmailRequest extends ApiFormRequest { parent::prepareForValidation(); - if (!$this->has('html_message') && $this->has('html')) { + if (! $this->has('html_message') && $this->has('html')) { $this->merge(['html_message' => $this->input('html')]); } } diff --git a/app/Http/Requests/Finance/CarryforwardAdjustmentRequest.php b/app/Http/Requests/Finance/CarryforwardAdjustmentRequest.php index 251e49e9..7e3054bb 100644 --- a/app/Http/Requests/Finance/CarryforwardAdjustmentRequest.php +++ b/app/Http/Requests/Finance/CarryforwardAdjustmentRequest.php @@ -6,12 +6,16 @@ use Illuminate\Foundation\Http\FormRequest; class CarryforwardAdjustmentRequest extends FormRequest { - public function authorize(): bool { return true; } + public function authorize(): bool + { + return true; + } + public function rules(): array { return [ - 'amount' => ['required','numeric','min:0'], - 'reason' => ['nullable','string','max:255'], + 'amount' => ['required', 'numeric', 'min:0'], + 'reason' => ['nullable', 'string', 'max:255'], ]; } } diff --git a/app/Http/Requests/Finance/CarryforwardFinalizeRequest.php b/app/Http/Requests/Finance/CarryforwardFinalizeRequest.php index 2cd75e8f..bdd72381 100644 --- a/app/Http/Requests/Finance/CarryforwardFinalizeRequest.php +++ b/app/Http/Requests/Finance/CarryforwardFinalizeRequest.php @@ -6,14 +6,18 @@ use Illuminate\Foundation\Http\FormRequest; class CarryforwardFinalizeRequest extends FormRequest { - public function authorize(): bool { return true; } + public function authorize(): bool + { + return true; + } + public function rules(): array { return [ - 'from_school_year' => ['required_without:id','string','max:20'], - 'to_school_year' => ['required_without:id','string','max:20'], - 'parent_id' => ['nullable','integer'], - 'reason' => ['nullable','string','max:255'], + 'from_school_year' => ['required_without:id', 'string', 'max:20'], + 'to_school_year' => ['required_without:id', 'string', 'max:20'], + 'parent_id' => ['nullable', 'integer'], + 'reason' => ['nullable', 'string', 'max:255'], ]; } } diff --git a/app/Http/Requests/Finance/CarryforwardPreviewRequest.php b/app/Http/Requests/Finance/CarryforwardPreviewRequest.php index 7d660220..813fc380 100644 --- a/app/Http/Requests/Finance/CarryforwardPreviewRequest.php +++ b/app/Http/Requests/Finance/CarryforwardPreviewRequest.php @@ -6,14 +6,18 @@ use Illuminate\Foundation\Http\FormRequest; class CarryforwardPreviewRequest extends FormRequest { - public function authorize(): bool { return true; } + public function authorize(): bool + { + return true; + } + public function rules(): array { return [ - 'from_school_year' => ['required','string','max:20'], - 'to_school_year' => ['required','string','max:20'], - 'parent_id' => ['nullable','integer'], - 'minimum_balance' => ['nullable','numeric','min:0'], + 'from_school_year' => ['required', 'string', 'max:20'], + 'to_school_year' => ['required', 'string', 'max:20'], + 'parent_id' => ['nullable', 'integer'], + 'minimum_balance' => ['nullable', 'numeric', 'min:0'], ]; } } diff --git a/app/Http/Requests/Finance/EventChargeLifecycleRequest.php b/app/Http/Requests/Finance/EventChargeLifecycleRequest.php index e2a4371c..d689a18a 100644 --- a/app/Http/Requests/Finance/EventChargeLifecycleRequest.php +++ b/app/Http/Requests/Finance/EventChargeLifecycleRequest.php @@ -6,20 +6,24 @@ use Illuminate\Foundation\Http\FormRequest; class EventChargeLifecycleRequest extends FormRequest { - public function authorize(): bool { return true; } + public function authorize(): bool + { + return true; + } + public function rules(): array { return [ - 'event_id' => ['nullable','integer'], - 'parent_id' => ['nullable','integer'], - 'student_id' => ['nullable','integer'], - 'school_year' => ['nullable','string','max:20'], - 'semester' => ['nullable','string','max:40'], - 'event_name' => ['nullable','string','max:255'], - 'title' => ['nullable','string','max:255'], - 'description' => ['nullable','string'], - 'amount' => ['nullable','numeric','min:0'], - 'invoice_id' => ['nullable','integer'], + 'event_id' => ['nullable', 'integer'], + 'parent_id' => ['nullable', 'integer'], + 'student_id' => ['nullable', 'integer'], + 'school_year' => ['nullable', 'string', 'max:20'], + 'semester' => ['nullable', 'string', 'max:40'], + 'event_name' => ['nullable', 'string', 'max:255'], + 'title' => ['nullable', 'string', 'max:255'], + 'description' => ['nullable', 'string'], + 'amount' => ['nullable', 'numeric', 'min:0'], + 'invoice_id' => ['nullable', 'integer'], ]; } } diff --git a/app/Http/Requests/Finance/FinanceNotificationLogRequest.php b/app/Http/Requests/Finance/FinanceNotificationLogRequest.php index e5e38aff..970299b4 100644 --- a/app/Http/Requests/Finance/FinanceNotificationLogRequest.php +++ b/app/Http/Requests/Finance/FinanceNotificationLogRequest.php @@ -6,15 +6,19 @@ use Illuminate\Foundation\Http\FormRequest; class FinanceNotificationLogRequest extends FormRequest { - public function authorize(): bool { return true; } + public function authorize(): bool + { + return true; + } + public function rules(): array { return [ - 'status' => ['nullable','string','max:40'], - 'notification_type' => ['nullable','string','max:80'], - 'parent_id' => ['nullable','integer'], - 'date_from' => ['nullable','date'], - 'date_to' => ['nullable','date'], + 'status' => ['nullable', 'string', 'max:40'], + 'notification_type' => ['nullable', 'string', 'max:80'], + 'parent_id' => ['nullable', 'integer'], + 'date_from' => ['nullable', 'date'], + 'date_to' => ['nullable', 'date'], ]; } } diff --git a/app/Http/Requests/Finance/FinancialSummaryRequest.php b/app/Http/Requests/Finance/FinancialSummaryRequest.php index 1f1c43dd..36548dfb 100644 --- a/app/Http/Requests/Finance/FinancialSummaryRequest.php +++ b/app/Http/Requests/Finance/FinancialSummaryRequest.php @@ -2,6 +2,4 @@ namespace App\Http\Requests\Finance; -class FinancialSummaryRequest extends FinancialReportRequest -{ -} +class FinancialSummaryRequest extends FinancialReportRequest {} diff --git a/app/Http/Requests/Finance/FollowUpNoteRequest.php b/app/Http/Requests/Finance/FollowUpNoteRequest.php index 23f0caa6..e4979a54 100644 --- a/app/Http/Requests/Finance/FollowUpNoteRequest.php +++ b/app/Http/Requests/Finance/FollowUpNoteRequest.php @@ -6,19 +6,23 @@ use Illuminate\Foundation\Http\FormRequest; class FollowUpNoteRequest extends FormRequest { - public function authorize(): bool { return true; } + public function authorize(): bool + { + return true; + } + public function rules(): array { return [ - 'invoice_id' => ['nullable','integer'], - 'school_year' => ['nullable','string','max:20'], - 'status' => ['nullable','string','max:60'], - 'contact_method' => ['nullable','string','max:60'], - 'promise_to_pay_date' => ['nullable','date'], - 'promise_to_pay_amount' => ['nullable','numeric','min:0'], - 'next_follow_up_date' => ['nullable','date'], - 'escalation_level' => ['nullable','integer','min:0','max:10'], - 'note' => ['nullable','string','max:10000'], + 'invoice_id' => ['nullable', 'integer'], + 'school_year' => ['nullable', 'string', 'max:20'], + 'status' => ['nullable', 'string', 'max:60'], + 'contact_method' => ['nullable', 'string', 'max:60'], + 'promise_to_pay_date' => ['nullable', 'date'], + 'promise_to_pay_amount' => ['nullable', 'numeric', 'min:0'], + 'next_follow_up_date' => ['nullable', 'date'], + 'escalation_level' => ['nullable', 'integer', 'min:0', 'max:10'], + 'note' => ['nullable', 'string', 'max:10000'], ]; } } diff --git a/app/Http/Requests/Finance/InstallmentPlanRequest.php b/app/Http/Requests/Finance/InstallmentPlanRequest.php index e53b74d9..57ff6913 100644 --- a/app/Http/Requests/Finance/InstallmentPlanRequest.php +++ b/app/Http/Requests/Finance/InstallmentPlanRequest.php @@ -6,15 +6,19 @@ use Illuminate\Foundation\Http\FormRequest; class InstallmentPlanRequest extends FormRequest { - public function authorize(): bool { return true; } + public function authorize(): bool + { + return true; + } + public function rules(): array { return [ - 'plan_name' => ['nullable','string','max:120'], - 'total_amount' => ['nullable','numeric','min:0'], - 'number_of_installments' => ['required','integer','min:1','max:24'], - 'first_due_date' => ['required','date'], - 'interval_months' => ['nullable','integer','min:1','max:12'], + 'plan_name' => ['nullable', 'string', 'max:120'], + 'total_amount' => ['nullable', 'numeric', 'min:0'], + 'number_of_installments' => ['required', 'integer', 'min:1', 'max:24'], + 'first_due_date' => ['required', 'date'], + 'interval_months' => ['nullable', 'integer', 'min:1', 'max:12'], ]; } } diff --git a/app/Http/Requests/Finance/ParentPaymentFollowUpRequest.php b/app/Http/Requests/Finance/ParentPaymentFollowUpRequest.php index 7309f768..24fedbe1 100644 --- a/app/Http/Requests/Finance/ParentPaymentFollowUpRequest.php +++ b/app/Http/Requests/Finance/ParentPaymentFollowUpRequest.php @@ -6,23 +6,27 @@ use Illuminate\Foundation\Http\FormRequest; class ParentPaymentFollowUpRequest extends FormRequest { - public function authorize(): bool { return true; } + public function authorize(): bool + { + return true; + } + public function rules(): array { return [ - 'school_year' => ['nullable','string','max:20'], - 'date_from' => ['nullable','date'], - 'date_to' => ['nullable','date'], - 'minimum_balance' => ['nullable','numeric','min:0'], - 'aging_bucket' => ['nullable','string','max:40'], - 'parent_id' => ['nullable','integer'], - 'student_id' => ['nullable','integer'], - 'follow_up_status' => ['nullable','string','max:60'], - 'last_contacted_before' => ['nullable','date'], - 'next_follow_up_due_before' => ['nullable','date'], - 'promise_to_pay_due_before' => ['nullable','date'], - 'include_paid_invoices' => ['nullable','boolean'], - 'include_inactive_students' => ['nullable','boolean'], + 'school_year' => ['nullable', 'string', 'max:20'], + 'date_from' => ['nullable', 'date'], + 'date_to' => ['nullable', 'date'], + 'minimum_balance' => ['nullable', 'numeric', 'min:0'], + 'aging_bucket' => ['nullable', 'string', 'max:40'], + 'parent_id' => ['nullable', 'integer'], + 'student_id' => ['nullable', 'integer'], + 'follow_up_status' => ['nullable', 'string', 'max:60'], + 'last_contacted_before' => ['nullable', 'date'], + 'next_follow_up_due_before' => ['nullable', 'date'], + 'promise_to_pay_due_before' => ['nullable', 'date'], + 'include_paid_invoices' => ['nullable', 'boolean'], + 'include_inactive_students' => ['nullable', 'boolean'], ]; } } diff --git a/app/Http/Requests/Finance/PaymentAllocationRequest.php b/app/Http/Requests/Finance/PaymentAllocationRequest.php index 36f714bf..a201e3f8 100644 --- a/app/Http/Requests/Finance/PaymentAllocationRequest.php +++ b/app/Http/Requests/Finance/PaymentAllocationRequest.php @@ -6,13 +6,17 @@ use Illuminate\Foundation\Http\FormRequest; class PaymentAllocationRequest extends FormRequest { - public function authorize(): bool { return true; } + public function authorize(): bool + { + return true; + } + public function rules(): array { return [ - 'installments' => ['nullable','array'], - 'installments.*.installment_id' => ['required_with:installments','integer'], - 'installments.*.amount' => ['required_with:installments','numeric','min:0.01'], + 'installments' => ['nullable', 'array'], + 'installments.*.installment_id' => ['required_with:installments', 'integer'], + 'installments.*.amount' => ['required_with:installments', 'numeric', 'min:0.01'], ]; } } diff --git a/app/Http/Requests/Incidents/IncidentStoreRequest.php b/app/Http/Requests/Incidents/IncidentStoreRequest.php index b9de4dbb..0368835c 100644 --- a/app/Http/Requests/Incidents/IncidentStoreRequest.php +++ b/app/Http/Requests/Incidents/IncidentStoreRequest.php @@ -44,7 +44,7 @@ class IncidentStoreRequest extends ApiFormRequest return [ 'student_id' => ['required', 'integer', 'min:1', 'exists:students,id'], 'grade' => ['required', 'string', 'max:50'], - 'incident' => ['required', 'string', 'in:' . implode(',', $incidentTypes)], + 'incident' => ['required', 'string', 'in:'.implode(',', $incidentTypes)], 'description' => ['required', 'string', 'max:2000'], 'incident_state' => ['nullable', 'string', 'in:Open,Closed,Canceled'], 'state_description' => ['nullable', 'string', 'max:2000'], diff --git a/app/Http/Requests/Promotions/AdminListPromotionsRequest.php b/app/Http/Requests/Promotions/AdminListPromotionsRequest.php index f6b54c47..e90290e3 100644 --- a/app/Http/Requests/Promotions/AdminListPromotionsRequest.php +++ b/app/Http/Requests/Promotions/AdminListPromotionsRequest.php @@ -30,7 +30,7 @@ class AdminListPromotionsRequest extends ApiFormRequest $status = $data['status'] ?? null; if (is_string($status) && $status !== '') { $status = [$status]; - } elseif (!is_array($status)) { + } elseif (! is_array($status)) { $status = null; } diff --git a/app/Http/Requests/Promotions/ParentEnrollmentStepRequest.php b/app/Http/Requests/Promotions/ParentEnrollmentStepRequest.php index 8689d033..9b4103d7 100644 --- a/app/Http/Requests/Promotions/ParentEnrollmentStepRequest.php +++ b/app/Http/Requests/Promotions/ParentEnrollmentStepRequest.php @@ -25,6 +25,7 @@ class ParentEnrollmentStepRequest extends ApiFormRequest $steps[$field] = (bool) $data[$field]; } } + return $steps; } } diff --git a/app/Http/Requests/PurchaseOrders/PurchaseOrderStoreRequest.php b/app/Http/Requests/PurchaseOrders/PurchaseOrderStoreRequest.php index aea68722..6bc12b16 100644 --- a/app/Http/Requests/PurchaseOrders/PurchaseOrderStoreRequest.php +++ b/app/Http/Requests/PurchaseOrders/PurchaseOrderStoreRequest.php @@ -17,7 +17,7 @@ class PurchaseOrderStoreRequest extends ApiFormRequest return [ 'po_number' => ['required', 'string', 'max:60'], 'supplier_id' => ['required', 'integer', 'min:1', 'exists:suppliers,id'], - 'status' => ['nullable', 'string', 'in:' . implode(',', PurchaseOrder::allowedStatuses())], + 'status' => ['nullable', 'string', 'in:'.implode(',', PurchaseOrder::allowedStatuses())], 'order_date' => ['nullable', 'date'], 'expected_date' => ['nullable', 'date', 'after_or_equal:order_date'], 'notes' => ['nullable', 'string', 'max:5000'], diff --git a/app/Http/Requests/Refunds/RefundDecisionRequest.php b/app/Http/Requests/Refunds/RefundDecisionRequest.php index e5db2dea..788a76ec 100644 --- a/app/Http/Requests/Refunds/RefundDecisionRequest.php +++ b/app/Http/Requests/Refunds/RefundDecisionRequest.php @@ -15,7 +15,7 @@ class RefundDecisionRequest extends ApiFormRequest public function rules(): array { return [ - 'status' => ['required', 'string', 'in:' . implode(',', [ + 'status' => ['required', 'string', 'in:'.implode(',', [ Refund::STATUS_APPROVED, Refund::STATUS_REJECTED, ])], diff --git a/app/Http/Requests/Refunds/RefundIndexRequest.php b/app/Http/Requests/Refunds/RefundIndexRequest.php index 3f18109b..17ffb2cd 100644 --- a/app/Http/Requests/Refunds/RefundIndexRequest.php +++ b/app/Http/Requests/Refunds/RefundIndexRequest.php @@ -15,7 +15,7 @@ class RefundIndexRequest extends ApiFormRequest public function rules(): array { return [ - 'status' => ['nullable', 'string', 'in:' . implode(',', [ + 'status' => ['nullable', 'string', 'in:'.implode(',', [ Refund::STATUS_PENDING, Refund::STATUS_APPROVED, Refund::STATUS_PARTIAL, diff --git a/app/Http/Requests/Reports/Stickers/StickerFormDataRequest.php b/app/Http/Requests/Reports/Stickers/StickerFormDataRequest.php index 9644faf2..a1b04b3f 100644 --- a/app/Http/Requests/Reports/Stickers/StickerFormDataRequest.php +++ b/app/Http/Requests/Reports/Stickers/StickerFormDataRequest.php @@ -10,7 +10,7 @@ class StickerFormDataRequest extends ApiFormRequest { parent::prepareForValidation(); - if (!$this->has('class_section_id') && $this->has('class_id')) { + if (! $this->has('class_section_id') && $this->has('class_id')) { $this->merge(['class_section_id' => $this->input('class_id')]); } } diff --git a/app/Http/Requests/Reports/Stickers/StickerPrintRequest.php b/app/Http/Requests/Reports/Stickers/StickerPrintRequest.php index 651ec973..2522926e 100644 --- a/app/Http/Requests/Reports/Stickers/StickerPrintRequest.php +++ b/app/Http/Requests/Reports/Stickers/StickerPrintRequest.php @@ -11,7 +11,7 @@ class StickerPrintRequest extends ApiFormRequest { parent::prepareForValidation(); - if (!$this->has('class_section_id') && $this->has('class_id')) { + if (! $this->has('class_section_id') && $this->has('class_id')) { $this->merge(['class_section_id' => $this->input('class_id')]); } } @@ -53,7 +53,7 @@ class StickerPrintRequest extends ApiFormRequest $hasClass = $this->filled('class_section_id'); $printAll = (bool) $this->input('print_all', false); - if (!$hasStudent && !$hasClass && !$printAll) { + if (! $hasStudent && ! $hasClass && ! $printAll) { $validator->errors()->add('selection', 'Please select a student or class, or set print_all.'); } }); diff --git a/app/Http/Requests/Reports/Stickers/StickerStudentsByClassRequest.php b/app/Http/Requests/Reports/Stickers/StickerStudentsByClassRequest.php index 089ff9a0..5ae919ca 100644 --- a/app/Http/Requests/Reports/Stickers/StickerStudentsByClassRequest.php +++ b/app/Http/Requests/Reports/Stickers/StickerStudentsByClassRequest.php @@ -10,7 +10,7 @@ class StickerStudentsByClassRequest extends ApiFormRequest { parent::prepareForValidation(); - if (!$this->has('class_section_id') && $this->has('class_id')) { + if (! $this->has('class_section_id') && $this->has('class_id')) { $this->merge(['class_section_id' => $this->input('class_id')]); } } diff --git a/app/Http/Requests/Roles/PermissionStoreRequest.php b/app/Http/Requests/Roles/PermissionStoreRequest.php index cd97a21c..ee365b15 100644 --- a/app/Http/Requests/Roles/PermissionStoreRequest.php +++ b/app/Http/Requests/Roles/PermissionStoreRequest.php @@ -3,7 +3,6 @@ namespace App\Http\Requests\Roles; use App\Http\Requests\ApiFormRequest; -use Illuminate\Validation\Rule; class PermissionStoreRequest extends ApiFormRequest { diff --git a/app/Http/Requests/Security/IpBanRequest.php b/app/Http/Requests/Security/IpBanRequest.php index 1c89a523..e599414d 100644 --- a/app/Http/Requests/Security/IpBanRequest.php +++ b/app/Http/Requests/Security/IpBanRequest.php @@ -26,7 +26,7 @@ class IpBanRequest extends ApiFormRequest $validator->after(function (Validator $validator) { $id = $this->input('id'); $ip = $this->input('ip'); - if (!$id && !$ip) { + if (! $id && ! $ip) { $validator->errors()->add('id', 'Either id or ip is required.'); } }); diff --git a/app/Http/Requests/Security/IpUnbanRequest.php b/app/Http/Requests/Security/IpUnbanRequest.php index e5ad0e3a..31291e76 100644 --- a/app/Http/Requests/Security/IpUnbanRequest.php +++ b/app/Http/Requests/Security/IpUnbanRequest.php @@ -28,7 +28,7 @@ class IpUnbanRequest extends ApiFormRequest $ip = $this->input('ip'); $all = filter_var($this->input('all'), FILTER_VALIDATE_BOOLEAN); - if (!$all && !$id && !$ip) { + if (! $all && ! $id && ! $ip) { $validator->errors()->add('id', 'Either id, ip, or all=true is required.'); } }); diff --git a/app/Http/Resources/Admin/AdminNotificationAlertsResource.php b/app/Http/Resources/Admin/AdminNotificationAlertsResource.php index 0f0a1dc6..cf371d8a 100644 --- a/app/Http/Resources/Admin/AdminNotificationAlertsResource.php +++ b/app/Http/Resources/Admin/AdminNotificationAlertsResource.php @@ -16,4 +16,4 @@ class AdminNotificationAlertsResource extends JsonResource 'table_ready' => (bool) ($this['table_ready'] ?? false), ]; } -} \ No newline at end of file +} diff --git a/app/Http/Resources/Admin/PrintRecipientsResource.php b/app/Http/Resources/Admin/PrintRecipientsResource.php index 867d2dc8..db8dd1e4 100644 --- a/app/Http/Resources/Admin/PrintRecipientsResource.php +++ b/app/Http/Resources/Admin/PrintRecipientsResource.php @@ -15,4 +15,4 @@ class PrintRecipientsResource extends JsonResource 'table_ready' => (bool) ($this['table_ready'] ?? false), ]; } -} \ No newline at end of file +} diff --git a/app/Http/Resources/Admin/Progress/ProgressGroupResource.php b/app/Http/Resources/Admin/Progress/ProgressGroupResource.php index e768b766..8b1c25d9 100644 --- a/app/Http/Resources/Admin/Progress/ProgressGroupResource.php +++ b/app/Http/Resources/Admin/Progress/ProgressGroupResource.php @@ -19,4 +19,4 @@ class ProgressGroupResource extends JsonResource 'reports' => $reports, // keyed by subject for compatibility ]; } -} \ No newline at end of file +} diff --git a/app/Http/Resources/Admin/Progress/ProgressReportDetailResource.php b/app/Http/Resources/Admin/Progress/ProgressReportDetailResource.php index 67db6e36..40f31809 100644 --- a/app/Http/Resources/Admin/Progress/ProgressReportDetailResource.php +++ b/app/Http/Resources/Admin/Progress/ProgressReportDetailResource.php @@ -15,4 +15,4 @@ class ProgressReportDetailResource extends JsonResource 'subject_sections' => $this['subject_sections'] ?? [], ]; } -} \ No newline at end of file +} diff --git a/app/Http/Resources/Admin/TeacherSubmissionReportResource.php b/app/Http/Resources/Admin/TeacherSubmissionReportResource.php index 17a1b776..47b673f9 100644 --- a/app/Http/Resources/Admin/TeacherSubmissionReportResource.php +++ b/app/Http/Resources/Admin/TeacherSubmissionReportResource.php @@ -22,4 +22,4 @@ class TeacherSubmissionReportResource extends JsonResource ], ]; } -} \ No newline at end of file +} diff --git a/app/Http/Resources/Attendance/AdminAttendanceResource.php b/app/Http/Resources/Attendance/AdminAttendanceResource.php index c48f9509..9241743e 100644 --- a/app/Http/Resources/Attendance/AdminAttendanceResource.php +++ b/app/Http/Resources/Attendance/AdminAttendanceResource.php @@ -16,4 +16,4 @@ class AdminAttendanceResource extends JsonResource 'admins_grid' => $this['admins_grid'] ?? [], ]; } -} \ No newline at end of file +} diff --git a/app/Http/Resources/Attendance/DailyAttendanceResource.php b/app/Http/Resources/Attendance/DailyAttendanceResource.php index ce0a40ad..ef3f5e94 100644 --- a/app/Http/Resources/Attendance/DailyAttendanceResource.php +++ b/app/Http/Resources/Attendance/DailyAttendanceResource.php @@ -25,4 +25,4 @@ class DailyAttendanceResource extends JsonResource 'current_admin_name' => $this['currentAdminName'] ?? null, ]; } -} \ No newline at end of file +} diff --git a/app/Http/Resources/Attendance/TeacherAttendanceFormResource.php b/app/Http/Resources/Attendance/TeacherAttendanceFormResource.php index 0e72ddfc..f6b1d306 100644 --- a/app/Http/Resources/Attendance/TeacherAttendanceFormResource.php +++ b/app/Http/Resources/Attendance/TeacherAttendanceFormResource.php @@ -9,7 +9,7 @@ class TeacherAttendanceFormResource extends JsonResource { public function toArray(Request $request): array { - $enable = (int)($this['enable_attendance'] ?? 0); + $enable = (int) ($this['enable_attendance'] ?? 0); $res = $this->resource; $hasCanEditKey = is_array($res) && array_key_exists('can_edit', $res); @@ -29,4 +29,4 @@ class TeacherAttendanceFormResource extends JsonResource 'locked_by_student' => $this['locked_by_student'] ?? [], ]; } -} \ No newline at end of file +} diff --git a/app/Http/Resources/Attendance/TeacherGridResource.php b/app/Http/Resources/Attendance/TeacherGridResource.php index 3daa03bf..313ab743 100644 --- a/app/Http/Resources/Attendance/TeacherGridResource.php +++ b/app/Http/Resources/Attendance/TeacherGridResource.php @@ -16,7 +16,7 @@ class TeacherGridResource extends JsonResource 'class_section_id' => $this['class_section_id'] ?? null, 'sections' => $this['sections'] ?? [], 'grid' => $this['grid'] ?? [], - 'locked' => (bool)($this['locked'] ?? false), + 'locked' => (bool) ($this['locked'] ?? false), ]; } -} \ No newline at end of file +} diff --git a/app/Http/Resources/ClassProgress/ClassProgressGroupCollection.php b/app/Http/Resources/ClassProgress/ClassProgressGroupCollection.php index daa20591..8ba9c2df 100644 --- a/app/Http/Resources/ClassProgress/ClassProgressGroupCollection.php +++ b/app/Http/Resources/ClassProgress/ClassProgressGroupCollection.php @@ -23,9 +23,9 @@ class ClassProgressGroupCollection extends ResourceCollection continue; } - $key = $sectionId . '|' . $weekStart; + $key = $sectionId.'|'.$weekStart; - if (!isset($groups[$key])) { + if (! isset($groups[$key])) { $groups[$key] = [ 'week_start' => $weekStart, 'week_end' => $report->week_end?->format('Y-m-d'), diff --git a/app/Http/Resources/Events/EventChargeResource.php b/app/Http/Resources/Events/EventChargeResource.php index d9622d53..9b062c88 100644 --- a/app/Http/Resources/Events/EventChargeResource.php +++ b/app/Http/Resources/Events/EventChargeResource.php @@ -14,9 +14,9 @@ class EventChargeResource extends JsonResource 'event_id' => (int) ($this->event_id ?? 0), 'event_name' => $this->event_name ?? null, 'parent_id' => (int) ($this->parent_id ?? 0), - 'parent_name' => trim(($this->parent_firstname ?? '') . ' ' . ($this->parent_lastname ?? '')), + 'parent_name' => trim(($this->parent_firstname ?? '').' '.($this->parent_lastname ?? '')), 'student_id' => (int) ($this->student_id ?? 0), - 'student_name' => trim(($this->student_firstname ?? '') . ' ' . ($this->student_lastname ?? '')), + 'student_name' => trim(($this->student_firstname ?? '').' '.($this->student_lastname ?? '')), 'participation' => $this->getRawOriginal('participation') ?? $this->participation, 'charged' => (float) ($this->getRawOriginal('charged') ?? $this->charged ?? 0), 'school_year' => $this->school_year, diff --git a/app/Http/Resources/Messaging/MessageCollection.php b/app/Http/Resources/Messaging/MessageCollection.php index 3703c1a4..7b5c7ad9 100644 --- a/app/Http/Resources/Messaging/MessageCollection.php +++ b/app/Http/Resources/Messaging/MessageCollection.php @@ -8,7 +8,7 @@ class MessageCollection extends ResourceCollection { public function with($request): array { - if (!method_exists($this->resource, 'total')) { + if (! method_exists($this->resource, 'total')) { return []; } diff --git a/app/Http/Resources/Messaging/MessageResource.php b/app/Http/Resources/Messaging/MessageResource.php index 3acb8ac3..3c0c0478 100644 --- a/app/Http/Resources/Messaging/MessageResource.php +++ b/app/Http/Resources/Messaging/MessageResource.php @@ -24,8 +24,8 @@ class MessageResource extends JsonResource 'status' => $this->status ?? null, 'semester' => $this->semester ?? null, 'school_year' => $this->school_year ?? null, - 'sender_name' => $this->sender ? trim($this->sender->firstname . ' ' . $this->sender->lastname) : null, - 'recipient_name' => $this->recipient ? trim($this->recipient->firstname . ' ' . $this->recipient->lastname) : null, + 'sender_name' => $this->sender ? trim($this->sender->firstname.' '.$this->sender->lastname) : null, + 'recipient_name' => $this->recipient ? trim($this->recipient->firstname.' '.$this->recipient->lastname) : null, ]; } } diff --git a/app/Http/Resources/Payments/PaymentNotificationLogResource.php b/app/Http/Resources/Payments/PaymentNotificationLogResource.php index 78b5a744..bec8d35a 100644 --- a/app/Http/Resources/Payments/PaymentNotificationLogResource.php +++ b/app/Http/Resources/Payments/PaymentNotificationLogResource.php @@ -10,7 +10,7 @@ class PaymentNotificationLogResource extends JsonResource public function toArray(Request $request): array { $parent = $this->resource->parent ?? null; - $parentName = $parent ? trim(($parent->firstname ?? '') . ' ' . ($parent->lastname ?? '')) : null; + $parentName = $parent ? trim(($parent->firstname ?? '').' '.($parent->lastname ?? '')) : null; return [ 'id' => (int) ($this->resource->id ?? 0), diff --git a/app/Http/Resources/Promotions/StudentPromotionResource.php b/app/Http/Resources/Promotions/StudentPromotionResource.php index ec6455c0..c0711435 100644 --- a/app/Http/Resources/Promotions/StudentPromotionResource.php +++ b/app/Http/Resources/Promotions/StudentPromotionResource.php @@ -19,6 +19,7 @@ class StudentPromotionResource extends JsonResource if (is_array($r)) { return $r[$key] ?? $default; } + return $r->{$key} ?? $default; }; diff --git a/app/Http/Resources/Reimbursements/ReimbursementRecipientResource.php b/app/Http/Resources/Reimbursements/ReimbursementRecipientResource.php index 70f2bfd4..0f16fbff 100644 --- a/app/Http/Resources/Reimbursements/ReimbursementRecipientResource.php +++ b/app/Http/Resources/Reimbursements/ReimbursementRecipientResource.php @@ -12,7 +12,7 @@ class ReimbursementRecipientResource extends JsonResource $id = (int) ($this->resource['id'] ?? 0); $first = $this->resource['firstname'] ?? ''; $last = $this->resource['lastname'] ?? ''; - $name = trim($first . ' ' . $last); + $name = trim($first.' '.$last); return [ 'id' => $id, diff --git a/app/Http/Resources/Staff/StaffCollection.php b/app/Http/Resources/Staff/StaffCollection.php index 7eb263e1..71532432 100644 --- a/app/Http/Resources/Staff/StaffCollection.php +++ b/app/Http/Resources/Staff/StaffCollection.php @@ -15,7 +15,7 @@ class StaffCollection extends ResourceCollection public function with($request): array { - if (!method_exists($this->resource, 'total')) { + if (! method_exists($this->resource, 'total')) { return []; } diff --git a/app/Http/Resources/Support/SupportRequestCollection.php b/app/Http/Resources/Support/SupportRequestCollection.php index fda04237..3066c892 100644 --- a/app/Http/Resources/Support/SupportRequestCollection.php +++ b/app/Http/Resources/Support/SupportRequestCollection.php @@ -15,7 +15,7 @@ class SupportRequestCollection extends ResourceCollection public function with($request): array { - if (!method_exists($this->resource, 'total')) { + if (! method_exists($this->resource, 'total')) { return []; } diff --git a/app/Http/Resources/System/NavItemResource.php b/app/Http/Resources/System/NavItemResource.php index 67b0d82b..60360056 100644 --- a/app/Http/Resources/System/NavItemResource.php +++ b/app/Http/Resources/System/NavItemResource.php @@ -20,7 +20,7 @@ class NavItemResource extends JsonResource 'target' => $item['target'] ?? null, 'sort_order' => (int) ($item['sort_order'] ?? 0), 'is_enabled' => (int) ($item['is_enabled'] ?? 0), - 'children' => !empty($item['children']) + 'children' => ! empty($item['children']) ? NavItemResource::collection($item['children']) : [], ]; diff --git a/app/Http/Resources/TeacherSubmissionRowResource.php b/app/Http/Resources/TeacherSubmissionRowResource.php index c6c15918..9c7e1ef9 100644 --- a/app/Http/Resources/TeacherSubmissionRowResource.php +++ b/app/Http/Resources/TeacherSubmissionRowResource.php @@ -24,4 +24,4 @@ class TeacherSubmissionRowResource extends JsonResource 'missing_items' => $this['missing_items'] ?? [], ]; } -} \ No newline at end of file +} diff --git a/app/Libraries/StaffTimeOffLinkService.php b/app/Libraries/StaffTimeOffLinkService.php index e44fc38d..b46e8ed2 100644 --- a/app/Libraries/StaffTimeOffLinkService.php +++ b/app/Libraries/StaffTimeOffLinkService.php @@ -6,9 +6,7 @@ use App\Services\Staff\StaffTimeOffLinkService as StaffTimeOffLinkTokenService; class StaffTimeOffLinkService { - public function __construct(private StaffTimeOffLinkTokenService $service = new StaffTimeOffLinkTokenService()) - { - } + public function __construct(private StaffTimeOffLinkTokenService $service = new StaffTimeOffLinkTokenService) {} public function createToken(array $payload): string { diff --git a/app/Listeners/AttendanceConsequenceListener.php b/app/Listeners/AttendanceConsequenceListener.php index 1238751c..b8f56b73 100644 --- a/app/Listeners/AttendanceConsequenceListener.php +++ b/app/Listeners/AttendanceConsequenceListener.php @@ -6,9 +6,7 @@ use App\Services\Attendance\AttendanceConsequenceService; class AttendanceConsequenceListener { - public function __construct(private AttendanceConsequenceService $service) - { - } + public function __construct(private AttendanceConsequenceService $service) {} public function followUp(array $payload): void { diff --git a/app/Listeners/BelowSixtyEmailListener.php b/app/Listeners/BelowSixtyEmailListener.php index 0e9c54b9..fe021b58 100644 --- a/app/Listeners/BelowSixtyEmailListener.php +++ b/app/Listeners/BelowSixtyEmailListener.php @@ -6,9 +6,7 @@ use App\Services\Grading\BelowSixtyEmailService; class BelowSixtyEmailListener { - public function __construct(private BelowSixtyEmailService $service) - { - } + public function __construct(private BelowSixtyEmailService $service) {} public function handle(array $payload): void { diff --git a/app/Listeners/DeleteUnverifiedUserListener.php b/app/Listeners/DeleteUnverifiedUserListener.php index e84bb7f6..4108eed6 100644 --- a/app/Listeners/DeleteUnverifiedUserListener.php +++ b/app/Listeners/DeleteUnverifiedUserListener.php @@ -7,9 +7,7 @@ use App\Services\Users\DeleteUnverifiedUserService; class DeleteUnverifiedUserListener { - public function __construct(private DeleteUnverifiedUserService $service) - { - } + public function __construct(private DeleteUnverifiedUserService $service) {} public function handle(DeleteUnverifiedUser $event): void { diff --git a/app/Listeners/SchoolEventListener.php b/app/Listeners/SchoolEventListener.php index 3ed583d5..2ca41471 100644 --- a/app/Listeners/SchoolEventListener.php +++ b/app/Listeners/SchoolEventListener.php @@ -12,8 +12,7 @@ class SchoolEventListener private EnrollmentEventService $enrollment, private PaymentEventService $payments, private AccountEventService $accounts - ) { - } + ) {} public function handleAdmissionUnderReview(array $data, array $studentdata): void { diff --git a/app/Listeners/WhatsappInviteListener.php b/app/Listeners/WhatsappInviteListener.php index da7c4409..f7925cd3 100644 --- a/app/Listeners/WhatsappInviteListener.php +++ b/app/Listeners/WhatsappInviteListener.php @@ -7,9 +7,7 @@ use App\Services\Whatsapp\WhatsappInviteEmailService; class WhatsappInviteListener { - public function __construct(private WhatsappInviteEmailService $service) - { - } + public function __construct(private WhatsappInviteEmailService $service) {} public function handle(WhatsappInvitesSend $event): void { diff --git a/app/Mail/TeacherSubmissionReminderMail.php b/app/Mail/TeacherSubmissionReminderMail.php index a1d71a3e..7161e567 100644 --- a/app/Mail/TeacherSubmissionReminderMail.php +++ b/app/Mail/TeacherSubmissionReminderMail.php @@ -14,8 +14,7 @@ class TeacherSubmissionReminderMail extends Mailable public function __construct( protected string $subjectLine, protected string $htmlBody - ) { - } + ) {} public function build(): self { diff --git a/app/Models/AdditionalCharge.php b/app/Models/AdditionalCharge.php index 1c50fd08..91e5f242 100644 --- a/app/Models/AdditionalCharge.php +++ b/app/Models/AdditionalCharge.php @@ -3,7 +3,6 @@ namespace App\Models; use Illuminate\Database\Eloquent\Builder; -use App\Models\BaseModel; use Illuminate\Support\Facades\DB; class AdditionalCharge extends BaseModel @@ -28,10 +27,10 @@ class AdditionalCharge extends BaseModel ]; protected $casts = [ - 'parent_id' => 'integer', + 'parent_id' => 'integer', 'invoice_id' => 'integer', - 'amount' => 'decimal:2', // adjust if you store more precision - 'due_date' => 'date', // if due_date is DATE/DATETIME + 'amount' => 'decimal:2', // adjust if you store more precision + 'due_date' => 'date', // if due_date is DATE/DATETIME 'created_by' => 'integer', ]; @@ -120,7 +119,7 @@ class AdditionalCharge extends BaseModel $affected = static::query() ->whereIn('id', $ids) ->update([ - 'status' => 'applied', + 'status' => 'applied', 'invoice_id' => $invoiceId, ]); @@ -157,18 +156,18 @@ class AdditionalCharge extends BaseModel $query->where('additional_charges.semester', $semFilter); } - if (!empty($status)) { + if (! empty($status)) { $query->where('additional_charges.status', $status); } - if (!empty($q)) { + if (! empty($q)) { $term = trim($q); $query->where(function ($w) use ($term) { $w->where('additional_charges.title', 'like', "%{$term}%") - ->orWhere('additional_charges.description', 'like', "%{$term}%") - ->orWhere('users.firstname', 'like', "%{$term}%") - ->orWhere('users.lastname', 'like', "%{$term}%") - ->orWhere('invoices.invoice_number', 'like', "%{$term}%"); + ->orWhere('additional_charges.description', 'like', "%{$term}%") + ->orWhere('users.firstname', 'like', "%{$term}%") + ->orWhere('users.lastname', 'like', "%{$term}%") + ->orWhere('invoices.invoice_number', 'like', "%{$term}%"); }); } diff --git a/app/Models/AdminNotificationSubject.php b/app/Models/AdminNotificationSubject.php index 469a54c3..747ccf6f 100644 --- a/app/Models/AdminNotificationSubject.php +++ b/app/Models/AdminNotificationSubject.php @@ -2,8 +2,6 @@ namespace App\Models; -use App\Models\BaseModel; - class AdminNotificationSubject extends BaseModel { protected $table = 'admin_notification_subjects'; @@ -16,7 +14,8 @@ class AdminNotificationSubject extends BaseModel ]; // legacy uses created_at / updated_at - public $timestamps = true; + public $timestamps = true; + protected $casts = [ 'admin_id' => 'integer', ]; @@ -26,4 +25,4 @@ class AdminNotificationSubject extends BaseModel { return $this->belongsTo(User::class, 'admin_id'); } -} \ No newline at end of file +} diff --git a/app/Models/AttendanceCommentTemplate.php b/app/Models/AttendanceCommentTemplate.php index bfd0df1b..96551754 100644 --- a/app/Models/AttendanceCommentTemplate.php +++ b/app/Models/AttendanceCommentTemplate.php @@ -2,8 +2,6 @@ namespace App\Models; -use App\Models\BaseModel; - class AttendanceCommentTemplate extends BaseModel { protected $table = 'attendance_comment_template'; @@ -43,4 +41,4 @@ class AttendanceCommentTemplate extends BaseModel { return $q->where('is_active', 1); } -} \ No newline at end of file +} diff --git a/app/Models/AttendanceData.php b/app/Models/AttendanceData.php index 51770c6c..a5e55d94 100644 --- a/app/Models/AttendanceData.php +++ b/app/Models/AttendanceData.php @@ -2,13 +2,13 @@ namespace App\Models; -use App\Models\BaseModel; use Illuminate\Support\Carbon; use Illuminate\Support\Facades\DB; class AttendanceData extends BaseModel { protected $table = 'attendance_data'; + public $timestamps = true; // uses created_at / updated_at protected $fillable = [ @@ -30,12 +30,12 @@ class AttendanceData extends BaseModel ]; protected $casts = [ - 'class_id' => 'integer', + 'class_id' => 'integer', 'class_section_id' => 'integer', - 'school_id' => 'integer', - 'student_id' => 'integer', - 'modified_by' => 'integer', - 'date' => 'datetime', // ✅ DATETIME column + 'school_id' => 'integer', + 'student_id' => 'integer', + 'modified_by' => 'integer', + 'date' => 'datetime', // ✅ DATETIME column ]; /* ========================= @@ -59,6 +59,7 @@ class AttendanceData extends BaseModel $day = substr($day, 0, 10); // YYYY-MM-DD $start = Carbon::parse($day)->startOfDay(); $endEx = Carbon::parse($day)->addDay()->startOfDay(); // exclusive + return [$start, $endEx]; } @@ -87,6 +88,7 @@ class AttendanceData extends BaseModel public static function updateAttendance(int $attendanceId, array $data): bool { $affected = static::query()->whereKey($attendanceId)->update($data); + return $affected >= 0; } @@ -113,7 +115,7 @@ class AttendanceData extends BaseModel $q = static::query(); static::applyReportedFilter($q); - [$start, ] = static::dayRange($startDate); + [$start] = static::dayRange($startDate); if ($endDate) { [, $endEx] = static::dayRange($endDate); // end exclusive of next day @@ -134,13 +136,19 @@ class AttendanceData extends BaseModel ->where('date', '>=', $start) ->where('date', '<', $endEx) ->where(function ($w) { - $w->whereIn(DB::raw("UPPER(TRIM(status))"), ['ABSENT', 'ABS', 'A']) - ->orWhereRaw("UPPER(TRIM(status)) LIKE 'ABS%'"); + $w->whereIn(DB::raw('UPPER(TRIM(status))'), ['ABSENT', 'ABS', 'A']) + ->orWhereRaw("UPPER(TRIM(status)) LIKE 'ABS%'"); }); - if ($classSectionId !== null) $q->where('class_section_id', $classSectionId); - if ($schoolYear !== null) $q->where('school_year', $schoolYear); - if ($semester !== null) $q->where('semester', $semester); + if ($classSectionId !== null) { + $q->where('class_section_id', $classSectionId); + } + if ($schoolYear !== null) { + $q->where('school_year', $schoolYear); + } + if ($semester !== null) { + $q->where('semester', $semester); + } return $q->get(); } @@ -153,13 +161,19 @@ class AttendanceData extends BaseModel ->where('date', '>=', $start) ->where('date', '<', $endEx) ->where(function ($w) { - $w->whereIn(DB::raw("UPPER(TRIM(status))"), ['LATE', 'L']) - ->orWhereRaw("UPPER(TRIM(status)) LIKE 'LATE%'"); + $w->whereIn(DB::raw('UPPER(TRIM(status))'), ['LATE', 'L']) + ->orWhereRaw("UPPER(TRIM(status)) LIKE 'LATE%'"); }); - if ($classSectionId !== null) $q->where('class_section_id', $classSectionId); - if ($schoolYear !== null) $q->where('school_year', $schoolYear); - if ($semester !== null) $q->where('semester', $semester); + if ($classSectionId !== null) { + $q->where('class_section_id', $classSectionId); + } + if ($schoolYear !== null) { + $q->where('school_year', $schoolYear); + } + if ($semester !== null) { + $q->where('semester', $semester); + } return $q->get(); } @@ -171,8 +185,8 @@ class AttendanceData extends BaseModel ->where('school_year', $schoolYear) ->where('semester', $semester) ->where(function ($w) { - $w->whereIn(DB::raw("UPPER(TRIM(status))"), ['ABSENT', 'ABS', 'A']) - ->orWhereRaw("UPPER(TRIM(status)) LIKE 'ABS%'"); + $w->whereIn(DB::raw('UPPER(TRIM(status))'), ['ABSENT', 'ABS', 'A']) + ->orWhereRaw("UPPER(TRIM(status)) LIKE 'ABS%'"); }) ->orderByDesc('date') ->get(); @@ -185,22 +199,26 @@ class AttendanceData extends BaseModel public static function getStudentAttendance( int $studentId, ?string $startDate = null, - ?string $endDate = null, + ?string $endDate = null, ?string $schoolYear = null, - ?string $semester = null + ?string $semester = null ) { $q = static::query()->where('student_id', $studentId); - if (!empty($startDate)) { - [$start, ] = static::dayRange($startDate); + if (! empty($startDate)) { + [$start] = static::dayRange($startDate); $q->where('date', '>=', $start); } - if (!empty($endDate)) { + if (! empty($endDate)) { [, $endEx] = static::dayRange($endDate); $q->where('date', '<', $endEx); } - if (!empty($schoolYear)) $q->where('school_year', $schoolYear); - if (!empty($semester)) $q->where('semester', $semester); + if (! empty($schoolYear)) { + $q->where('school_year', $schoolYear); + } + if (! empty($semester)) { + $q->where('semester', $semester); + } return $q->orderByDesc('date')->get(); } @@ -225,13 +243,13 @@ class AttendanceData extends BaseModel public static function unreportedTermRows(array $studentIds, string $schoolYear, ?string $semester) { $q = static::query() - ->select(['id','student_id','class_id','class_section_id','date','status','reason','school_year','semester','is_reported']) + ->select(['id', 'student_id', 'class_id', 'class_section_id', 'date', 'status', 'reason', 'school_year', 'semester', 'is_reported']) ->where('school_year', $schoolYear); if ($semester !== null && $semester !== '') { $q->where('semester', $semester); } - if (!empty($studentIds)) { + if (! empty($studentIds)) { $q->whereIn('student_id', array_map('intval', $studentIds)); } @@ -247,26 +265,30 @@ class AttendanceData extends BaseModel public static function getUnreportedAbsencesAndLates(?string $schoolYear = null, ?string $semester = null): array { $q = static::query() - ->select(['id','student_id','class_id','class_section_id','date','status','reason','school_year','semester','is_reported']); + ->select(['id', 'student_id', 'class_id', 'class_section_id', 'date', 'status', 'reason', 'school_year', 'semester', 'is_reported']); static::applyUnreportedFilter($q); // Not notified yet: supports 0/'0'/NULL and string enums like 'yes' $q->where(function ($w) { $w->whereNull('is_notified') - ->orWhere('is_notified', 0) - ->orWhere('is_notified', '0') - ->orWhereRaw("LOWER(TRIM(COALESCE(is_notified, ''))) != 'yes'"); + ->orWhere('is_notified', 0) + ->orWhere('is_notified', '0') + ->orWhereRaw("LOWER(TRIM(COALESCE(is_notified, ''))) != 'yes'"); }); - if (!empty($schoolYear)) $q->where('school_year', $schoolYear); - if (!empty($semester)) $q->where('semester', $semester); + if (! empty($schoolYear)) { + $q->where('school_year', $schoolYear); + } + if (! empty($semester)) { + $q->where('semester', $semester); + } // Status filter: exact + prefixes (ABS_3, LATE_2, etc.) $q->where(function ($w) { - $w->whereIn(DB::raw("UPPER(TRIM(status))"), ['ABS','ABSENT','LATE','A','L']) - ->orWhereRaw("UPPER(TRIM(status)) LIKE 'ABS%'") - ->orWhereRaw("UPPER(TRIM(status)) LIKE 'LATE%'"); + $w->whereIn(DB::raw('UPPER(TRIM(status))'), ['ABS', 'ABSENT', 'LATE', 'A', 'L']) + ->orWhereRaw("UPPER(TRIM(status)) LIKE 'ABS%'") + ->orWhereRaw("UPPER(TRIM(status)) LIKE 'LATE%'"); }); $rows = $q->orderBy('student_id')->orderByDesc('date')->get()->toArray(); @@ -275,27 +297,27 @@ class AttendanceData extends BaseModel foreach ($rows as $r) { $sid = (int) ($r['student_id'] ?? 0); - if (!isset($out[$sid])) { + if (! isset($out[$sid])) { $out[$sid] = [ 'absences' => [], - 'lates' => [], - 'counts' => ['absences' => 0, 'lates' => 0, 'total' => 0], + 'lates' => [], + 'counts' => ['absences' => 0, 'lates' => 0, 'total' => 0], ]; } - $st = strtoupper(trim((string)($r['status'] ?? ''))); - $isAbs = ($st === 'ABS' || $st === 'ABSENT' || $st === 'A' || str_starts_with($st, 'ABS')); + $st = strtoupper(trim((string) ($r['status'] ?? ''))); + $isAbs = ($st === 'ABS' || $st === 'ABSENT' || $st === 'A' || str_starts_with($st, 'ABS')); $isLate = ($st === 'LATE' || $st === 'L' || str_starts_with($st, 'LATE')); $rec = [ - 'id' => $r['id'] ?? null, - 'date' => $r['date'] ?? null, - 'status' => $r['status'] ?? null, - 'reason' => $r['reason'] ?? null, - 'class_id' => $r['class_id'] ?? null, + 'id' => $r['id'] ?? null, + 'date' => $r['date'] ?? null, + 'status' => $r['status'] ?? null, + 'reason' => $r['reason'] ?? null, + 'class_id' => $r['class_id'] ?? null, 'class_section_id' => $r['class_section_id'] ?? null, - 'school_year' => $r['school_year'] ?? null, - 'semester' => $r['semester'] ?? null, + 'school_year' => $r['school_year'] ?? null, + 'semester' => $r['semester'] ?? null, ]; if ($isAbs) { @@ -329,12 +351,16 @@ class AttendanceData extends BaseModel ->where('date', '>=', $start) ->where('date', '<', $endEx); - if (!empty($semester)) $q->where('semester', $semester); - if (!empty($schoolYear)) $q->where('school_year', $schoolYear); + if (! empty($semester)) { + $q->where('semester', $semester); + } + if (! empty($schoolYear)) { + $q->where('school_year', $schoolYear); + } $affected = $q->update([ 'is_notified' => 'yes', - 'updated_at' => now(), + 'updated_at' => now(), ]); return $affected >= 0; @@ -349,14 +375,20 @@ class AttendanceData extends BaseModel return is_string($d) && preg_match('/^\d{4}-\d{2}-\d{2}$/', $d); }))); - if (empty($dates)) return true; + if (empty($dates)) { + return true; + } $q = static::query() ->where('student_id', $studentId) ->whereRaw("LOWER(TRIM(status)) IN ('absent','late')"); - if (!empty($semester)) $q->where('semester', $semester); - if (!empty($schoolYear)) $q->where('school_year', $schoolYear); + if (! empty($semester)) { + $q->where('semester', $semester); + } + if (! empty($schoolYear)) { + $q->where('school_year', $schoolYear); + } // OR day ranges instead of DATE(date) $q->where(function ($w) use ($dates) { @@ -370,13 +402,17 @@ class AttendanceData extends BaseModel }); $payload = [ - 'updated_at' => now(), + 'updated_at' => now(), 'is_notified' => 'yes', ]; $cols = static::reportedColumns(); - if (!empty($cols['is_reported'])) $payload['is_reported'] = 'yes'; - if (!empty($cols['reported'])) $payload['reported'] = 1; + if (! empty($cols['is_reported'])) { + $payload['is_reported'] = 'yes'; + } + if (! empty($cols['reported'])) { + $payload['reported'] = 1; + } $affected = $q->update($payload); @@ -394,7 +430,9 @@ class AttendanceData extends BaseModel ?string $semester = null, ?string $schoolYear = null ): array { - if ($studentId <= 0 || empty($statuses)) return []; + if ($studentId <= 0 || empty($statuses)) { + return []; + } $statuses = array_map('strtolower', $statuses); @@ -403,7 +441,7 @@ class AttendanceData extends BaseModel : now()->toDateString(); $startDay = Carbon::parse($day)->subDays(max(0, $lookbackDays))->toDateString(); - [$start, ] = static::dayRange($startDay); + [$start] = static::dayRange($startDay); [, $endEx] = static::dayRange($day); $q = static::query() @@ -411,17 +449,21 @@ class AttendanceData extends BaseModel ->where('student_id', $studentId) ->where('date', '>=', $start) ->where('date', '<', $endEx) - ->whereIn(DB::raw("LOWER(TRIM(status))"), $statuses); + ->whereIn(DB::raw('LOWER(TRIM(status))'), $statuses); static::applyUnreportedFilter($q); - if (!empty($semester)) $q->where('semester', $semester); - if (!empty($schoolYear)) $q->where('school_year', $schoolYear); + if (! empty($semester)) { + $q->where('semester', $semester); + } + if (! empty($schoolYear)) { + $q->where('school_year', $schoolYear); + } $rows = $q->orderByDesc('date')->pluck('date')->all(); return array_values(array_unique(array_map(static function ($d) { - return substr((string)$d, 0, 10); // YYYY-MM-DD + return substr((string) $d, 0, 10); // YYYY-MM-DD }, $rows))); } @@ -432,13 +474,15 @@ class AttendanceData extends BaseModel private static function reportedColumns(): array { static $cache = null; - if ($cache !== null) return $cache; + if ($cache !== null) { + return $cache; + } $table = (new static)->getTable(); $cache = [ 'is_reported' => static::columnExists($table, 'is_reported'), - 'reported' => static::columnExists($table, 'reported'), + 'reported' => static::columnExists($table, 'reported'), ]; return $cache; @@ -464,16 +508,16 @@ class AttendanceData extends BaseModel { $cols = static::reportedColumns(); - if (!empty($cols['is_reported'])) { + if (! empty($cols['is_reported'])) { $q->where(function ($w) { $w->whereNull('is_reported') - ->orWhere('is_reported', 0) - ->orWhere('is_reported', '0') - ->orWhereRaw("LOWER(TRIM(COALESCE(is_reported, ''))) = 'no'"); + ->orWhere('is_reported', 0) + ->orWhere('is_reported', '0') + ->orWhereRaw("LOWER(TRIM(COALESCE(is_reported, ''))) = 'no'"); }); } - if (!empty($cols['reported'])) { + if (! empty($cols['reported'])) { $q->whereRaw("(LOWER(TRIM(COALESCE(reported, ''))) NOT IN ('yes','1','true','y','on'))"); } } @@ -485,8 +529,8 @@ class AttendanceData extends BaseModel { $q->where(function ($w) { $w->where('is_reported', 1) - ->orWhere('is_reported', '1') - ->orWhereRaw("LOWER(TRIM(COALESCE(is_reported, ''))) = 'yes'"); + ->orWhere('is_reported', '1') + ->orWhereRaw("LOWER(TRIM(COALESCE(is_reported, ''))) = 'yes'"); }); } } diff --git a/app/Models/AttendanceDay.php b/app/Models/AttendanceDay.php index b9c00c22..4c82dfea 100644 --- a/app/Models/AttendanceDay.php +++ b/app/Models/AttendanceDay.php @@ -2,9 +2,8 @@ namespace App\Models; -use App\Models\BaseModel; -use Illuminate\Support\Carbon; use Illuminate\Database\QueryException; +use Illuminate\Support\Carbon; class AttendanceDay extends BaseModel { @@ -39,20 +38,20 @@ class AttendanceDay extends BaseModel protected $casts = [ 'class_section_id' => 'integer', - 'submitted_by' => 'integer', - 'published_by' => 'integer', - 'reopened_by' => 'integer', + 'submitted_by' => 'integer', + 'published_by' => 'integer', + 'reopened_by' => 'integer', // If these are DATETIME columns: - 'submitted_at' => 'datetime', - 'published_at' => 'datetime', - 'auto_publish_at' => 'datetime', - 'reopened_at' => 'datetime', - 'created_at' => 'datetime', - 'updated_at' => 'datetime', + 'submitted_at' => 'datetime', + 'published_at' => 'datetime', + 'auto_publish_at' => 'datetime', + 'reopened_at' => 'datetime', + 'created_at' => 'datetime', + 'updated_at' => 'datetime', // If date is stored as DATE string; if it's DATETIME, change to 'datetime' - 'date' => 'date', + 'date' => 'date', ]; /* ========================= @@ -75,11 +74,15 @@ class AttendanceDay extends BaseModel ->whereDate('date', $date) ->where(function ($w) { $w->where('status', 'published') - ->orWhere('status', 'finalized'); // legacy + ->orWhere('status', 'finalized'); // legacy }); - if ($semester !== null) $q->where('semester', $semester); - if ($schoolYear !== null) $q->where('school_year', $schoolYear); + if ($semester !== null) { + $q->where('semester', $semester); + } + if ($schoolYear !== null) { + $q->where('school_year', $schoolYear); + } return $q->exists(); } @@ -113,7 +116,9 @@ class AttendanceDay extends BaseModel int $userId = 0 ): self { $row = static::findBySectionDate($classSectionId, $date, $semester, $schoolYear); - if ($row) return $row; + if ($row) { + return $row; + } $now = now(); $normalizedDate = Carbon::parse($date)->toDateString(); @@ -121,25 +126,27 @@ class AttendanceDay extends BaseModel try { return static::create([ 'class_section_id' => $classSectionId, - 'date' => $normalizedDate, - 'status' => 'draft', - 'submitted_by' => null, - 'submitted_at' => null, - 'published_by' => null, - 'published_at' => null, - 'auto_publish_at' => null, - 'reopened_by' => null, - 'reopened_at' => null, - 'reopen_reason' => null, - 'semester' => $semester, - 'school_year' => $schoolYear, - 'created_at' => $now, - 'updated_at' => $now, + 'date' => $normalizedDate, + 'status' => 'draft', + 'submitted_by' => null, + 'submitted_at' => null, + 'published_by' => null, + 'published_at' => null, + 'auto_publish_at' => null, + 'reopened_by' => null, + 'reopened_at' => null, + 'reopen_reason' => null, + 'semester' => $semester, + 'school_year' => $schoolYear, + 'created_at' => $now, + 'updated_at' => $now, ]); } catch (QueryException $e) { // Race: someone inserted same unique key. $row = static::findBySectionDate($classSectionId, $date, $semester, $schoolYear); - if ($row) return $row; + if ($row) { + return $row; + } throw $e; } } @@ -160,10 +167,10 @@ class AttendanceDay extends BaseModel $now = now(); $payload = [ - 'status' => 'submitted', + 'status' => 'submitted', 'submitted_by' => $userId, 'submitted_at' => $now, - 'updated_at' => $now, + 'updated_at' => $now, ]; if ($autoPublishAt !== null) { @@ -181,10 +188,10 @@ class AttendanceDay extends BaseModel $now = now(); return static::query()->whereKey($this->id)->update([ - 'status' => 'published', + 'status' => 'published', 'published_by' => $userId, 'published_at' => $now, - 'updated_at' => $now, + 'updated_at' => $now, ]) >= 0; } @@ -193,18 +200,18 @@ class AttendanceDay extends BaseModel */ public function reopen(int $userId, string $toStatus = 'draft', ?string $reason = null): bool { - if (!in_array($toStatus, ['draft', 'submitted'], true)) { + if (! in_array($toStatus, ['draft', 'submitted'], true)) { throw new \InvalidArgumentException('toStatus must be "draft" or "submitted".'); } $now = now(); return static::query()->whereKey($this->id)->update([ - 'status' => $toStatus, - 'reopened_by' => $userId, - 'reopened_at' => $now, + 'status' => $toStatus, + 'reopened_by' => $userId, + 'reopened_at' => $now, 'reopen_reason' => $reason, - 'updated_at' => $now, + 'updated_at' => $now, ]) >= 0; } } diff --git a/app/Models/AttendanceEmailTemplate.php b/app/Models/AttendanceEmailTemplate.php index 99496cb7..43b4f0a4 100644 --- a/app/Models/AttendanceEmailTemplate.php +++ b/app/Models/AttendanceEmailTemplate.php @@ -2,8 +2,6 @@ namespace App\Models; -use App\Models\BaseModel; - class AttendanceEmailTemplate extends BaseModel { protected $table = 'email_templates'; @@ -22,7 +20,7 @@ class AttendanceEmailTemplate extends BaseModel ]; protected $casts = [ - 'is_active' => 'boolean', + 'is_active' => 'boolean', 'updated_by' => 'integer', ]; diff --git a/app/Models/AttendanceRecord.php b/app/Models/AttendanceRecord.php index 79756eec..2f3a46d4 100644 --- a/app/Models/AttendanceRecord.php +++ b/app/Models/AttendanceRecord.php @@ -2,7 +2,6 @@ namespace App\Models; -use App\Models\BaseModel; use Illuminate\Support\Facades\DB; class AttendanceRecord extends BaseModel @@ -28,14 +27,14 @@ class AttendanceRecord extends BaseModel ]; protected $casts = [ - 'class_section_id' => 'integer', - 'student_id' => 'integer', - 'school_id' => 'integer', - 'total_absence' => 'integer', - 'total_late' => 'integer', - 'total_presence' => 'integer', - 'total_attendance' => 'integer', - 'modified_by' => 'integer', + 'class_section_id' => 'integer', + 'student_id' => 'integer', + 'school_id' => 'integer', + 'total_absence' => 'integer', + 'total_late' => 'integer', + 'total_presence' => 'integer', + 'total_attendance' => 'integer', + 'modified_by' => 'integer', ]; /* ========================= @@ -86,6 +85,7 @@ class AttendanceRecord extends BaseModel public static function updateAttendanceRecord(int $attendanceId, array $data): bool { $affected = static::query()->whereKey($attendanceId)->update($data); + return $affected >= 0; } @@ -101,20 +101,26 @@ class AttendanceRecord extends BaseModel string $schoolYear, array $increments ): bool { - $allowed = ['total_absence','total_late','total_presence','total_attendance']; + $allowed = ['total_absence', 'total_late', 'total_presence', 'total_attendance']; $inc = array_intersect_key($increments, array_flip($allowed)); - if (empty($inc)) return true; + if (empty($inc)) { + return true; + } // Build atomic update: field = field + X $update = []; foreach ($inc as $field => $value) { $value = (int) $value; - if ($value === 0) continue; + if ($value === 0) { + continue; + } $update[$field] = DB::raw("COALESCE($field,0) + {$value}"); } - if (empty($update)) return true; + if (empty($update)) { + return true; + } $affected = static::query() ->where('student_id', $studentId) @@ -139,4 +145,4 @@ class AttendanceRecord extends BaseModel ->where('school_year', $schoolYear) ->sum('total_absence'); } -} \ No newline at end of file +} diff --git a/app/Models/AttendanceTracking.php b/app/Models/AttendanceTracking.php index c7148bd9..496a680a 100644 --- a/app/Models/AttendanceTracking.php +++ b/app/Models/AttendanceTracking.php @@ -2,7 +2,6 @@ namespace App\Models; -use App\Models\BaseModel; use Illuminate\Support\Carbon; use Illuminate\Support\Facades\DB; @@ -26,11 +25,11 @@ class AttendanceTracking extends BaseModel ]; protected $casts = [ - 'student_id' => 'integer', - 'date' => 'datetime', // legacy dateFormat = datetime - 'is_reported' => 'integer', - 'is_notified' => 'integer', - 'notif_counter' => 'integer', + 'student_id' => 'integer', + 'date' => 'datetime', // legacy dateFormat = datetime + 'is_reported' => 'integer', + 'is_notified' => 'integer', + 'notif_counter' => 'integer', ]; /* ----------------------- Helpers ----------------------- */ @@ -42,7 +41,7 @@ class AttendanceTracking extends BaseModel return $date; } if (preg_match('/^\d{4}-\d{2}-\d{2}$/', $date)) { - return $date . ' 00:00:00'; + return $date.' 00:00:00'; } try { @@ -61,6 +60,7 @@ class AttendanceTracking extends BaseModel if ($schoolYear !== null && $schoolYear !== '') { $q->where('school_year', $schoolYear); } + return $q; } @@ -77,8 +77,8 @@ class AttendanceTracking extends BaseModel $y = (int) $d->format('Y'); $semester = ($m >= 8 && $m <= 12) ? 'Fall' : 'Spring'; - $startY = ($m >= 8) ? $y : $y - 1; - $endY = $startY + 1; + $startY = ($m >= 8) ? $y : $y - 1; + $endY = $startY + 1; return [$semester, sprintf('%d-%d', $startY, $endY)]; } @@ -89,6 +89,7 @@ class AttendanceTracking extends BaseModel $day = substr($ymd, 0, 10); $start = Carbon::parse($day, 'UTC')->startOfDay(); $endEx = Carbon::parse($day, 'UTC')->addDay()->startOfDay(); // exclusive + return [$start, $endEx]; } @@ -111,19 +112,19 @@ class AttendanceTracking extends BaseModel if ($semester === null || $schoolYear === null) { [$sem, $yr] = static::deriveTermFromDate($dt); - $semester = $semester ?? $sem; + $semester = $semester ?? $sem; $schoolYear = $schoolYear ?? $yr; } $row = static::create([ - 'student_id' => $studentId, - 'date' => $dt, - 'is_reported' => $isExcused ? 1 : 0, - 'reason' => $reason, - 'is_notified' => 0, - 'notif_counter' => 0, - 'semester' => $semester, - 'school_year' => $schoolYear, + 'student_id' => $studentId, + 'date' => $dt, + 'is_reported' => $isExcused ? 1 : 0, + 'reason' => $reason, + 'is_notified' => 0, + 'notif_counter' => 0, + 'semester' => $semester, + 'school_year' => $schoolYear, ]); return (int) $row->id; @@ -144,7 +145,7 @@ class AttendanceTracking extends BaseModel if ($startDate) { if (preg_match('/^\d{4}-\d{2}-\d{2}$/', $startDate)) { - [$start, ] = static::dayRange($startDate); + [$start] = static::dayRange($startDate); $q->where('date', '>=', $start); } else { $q->where('date', '>=', static::normalizeDateTime($startDate)); @@ -201,7 +202,7 @@ class AttendanceTracking extends BaseModel ?string $schoolYear = null ): bool { $startDay = Carbon::now('UTC')->subDays($days)->toDateString(); - [$start, ] = static::dayRange($startDay); + [$start] = static::dayRange($startDay); $q = static::query() ->where('student_id', $studentId) @@ -231,13 +232,17 @@ class AttendanceTracking extends BaseModel $absences = $q->orderByDesc('date')->limit($threshold)->get()->toArray(); - if (count($absences) < $threshold) return false; + if (count($absences) < $threshold) { + return false; + } for ($i = 0; $i < $threshold - 1; $i++) { $d1 = new \DateTime($absences[$i]['date']); $d2 = new \DateTime($absences[$i + 1]['date']); $diffDays = (int) $d2->diff($d1)->format('%a'); - if ($diffDays !== 7) return false; + if ($diffDays !== 7) { + return false; + } } return true; @@ -264,11 +269,13 @@ class AttendanceTracking extends BaseModel $records = $q->orderByDesc('date')->limit($weeksStreak)->get()->toArray(); - if (count($records) < $weeksStreak) return false; + if (count($records) < $weeksStreak) { + return false; + } $count = 0; foreach ($records as $r) { - if ((int)($r['is_reported'] ?? 0) === 0 && (int)($r['is_notified'] ?? 0) === 0) { + if ((int) ($r['is_reported'] ?? 0) === 0 && (int) ($r['is_notified'] ?? 0) === 0) { $count++; } } @@ -297,9 +304,9 @@ class AttendanceTracking extends BaseModel $last = $q3->orderByDesc('date')->first(); return [ - 'total_absences' => $total, + 'total_absences' => $total, 'unexcused_absences' => $unexcused, - 'last_absence' => $last ? $last->toArray() : null, + 'last_absence' => $last ? $last->toArray() : null, ]; } @@ -313,7 +320,10 @@ class AttendanceTracking extends BaseModel ) { $q = static::query(); static::applyTermFilters($q, $semester, $schoolYear); - if ($limit > 0) $q->limit($limit); + if ($limit > 0) { + $q->limit($limit); + } + return $q->orderByDesc('date')->get(); } @@ -335,8 +345,8 @@ class AttendanceTracking extends BaseModel $students = (int) $q3->distinct('student_id')->count('student_id'); return [ - 'total_absences' => $total, - 'unexcused_absences' => $unexcused, + 'total_absences' => $total, + 'unexcused_absences' => $unexcused, 'students_with_absences' => $students, ]; } @@ -352,26 +362,28 @@ class AttendanceTracking extends BaseModel foreach ($studentsWithViolations as $student) { if (empty($student['absences'])) { $results['skipped']++; + continue; } foreach ($student['absences'] as $absence) { - $ymd = substr((string)($absence['date'] ?? now()->toDateString()), 0, 10); + $ymd = substr((string) ($absence['date'] ?? now()->toDateString()), 0, 10); [$start, $endEx] = static::dayRange($ymd); $data = [ - 'student_id' => (int)($student['id'] ?? 0), - 'date' => static::normalizeDateTime($ymd), - 'is_reported' => 0, - 'reason' => (string)($student['violation'] ?? ''), - 'is_notified' => 0, - 'notif_counter' => 0, - 'semester' => $semester, - 'school_year' => $schoolYear, + 'student_id' => (int) ($student['id'] ?? 0), + 'date' => static::normalizeDateTime($ymd), + 'is_reported' => 0, + 'reason' => (string) ($student['violation'] ?? ''), + 'is_notified' => 0, + 'notif_counter' => 0, + 'semester' => $semester, + 'school_year' => $schoolYear, ]; if ($data['student_id'] <= 0) { $results['skipped']++; + continue; } @@ -391,8 +403,8 @@ class AttendanceTracking extends BaseModel $existing->fill($data)->save(); $results['updated']++; - if (!empty($existing->is_notified)) { - static::resetNotification((int)$existing->id); + if (! empty($existing->is_notified)) { + static::resetNotification((int) $existing->id); $results['notified']++; } } else { @@ -404,7 +416,7 @@ class AttendanceTracking extends BaseModel // Only match standalone "4" if (preg_match('/\b4\b/', $data['reason'])) { - static::markAsNotified((int)$new->id); + static::markAsNotified((int) $new->id); $results['notified']++; } } @@ -427,8 +439,8 @@ class AttendanceTracking extends BaseModel ->whereKey($id) ->update([ 'notif_counter' => DB::raw('COALESCE(notif_counter,0)+1'), - 'is_notified' => 1, - 'updated_at' => now(), + 'is_notified' => 1, + 'updated_at' => now(), ]); return $affected > 0; @@ -439,9 +451,9 @@ class AttendanceTracking extends BaseModel $affected = static::query() ->whereKey($id) ->update([ - 'is_notified' => 0, + 'is_notified' => 0, 'notif_counter' => 0, - 'updated_at' => now(), + 'updated_at' => now(), ]); return $affected >= 0; @@ -463,7 +475,7 @@ class AttendanceTracking extends BaseModel // Here we require term to be passed or derived from date. if ($semester === null || $schoolYear === null) { [$sem, $yr] = static::deriveTermFromDate($ymd); - $semester = $semester ?? $sem; + $semester = $semester ?? $sem; $schoolYear = $schoolYear ?? $yr; } @@ -475,33 +487,33 @@ class AttendanceTracking extends BaseModel ->where('semester', $semester) ->where('school_year', $schoolYear) ->where('date', '>=', $start) - ->where('date', '<', $endEx) + ->where('date', '<', $endEx) ->where('reason', 'like', "%{$code}%") ->orderByDesc('date') ->first(); // 2) Fallback: match by day only - if (!$row) { + if (! $row) { $row = static::query() ->where('student_id', $studentId) ->where('semester', $semester) ->where('school_year', $schoolYear) ->where('date', '>=', $start) - ->where('date', '<', $endEx) + ->where('date', '<', $endEx) ->orderByDesc('date') ->first(); } if ($row) { - $counter = ((int)($row->notif_counter ?? 0)) + 1; + $counter = ((int) ($row->notif_counter ?? 0)) + 1; $affected = static::query() - ->whereKey((int)$row->id) + ->whereKey((int) $row->id) ->update([ - 'is_notified' => 1, + 'is_notified' => 1, 'notif_counter' => $counter, - 'is_reported' => 0, - 'updated_at' => now(), + 'is_reported' => 0, + 'updated_at' => now(), ]); return $affected > 0; @@ -509,18 +521,18 @@ class AttendanceTracking extends BaseModel // Insert a row so future runs see it as notified $created = static::create([ - 'student_id' => $studentId, - 'date' => static::normalizeDateTime($ymd), - 'is_reported' => 0, - 'reason' => $code, - 'is_notified' => 1, - 'notif_counter' => 1, - 'semester' => $semester, - 'school_year' => $schoolYear, - 'note' => null, + 'student_id' => $studentId, + 'date' => static::normalizeDateTime($ymd), + 'is_reported' => 0, + 'reason' => $code, + 'is_notified' => 1, + 'notif_counter' => 1, + 'semester' => $semester, + 'school_year' => $schoolYear, + 'note' => null, ]); - return !empty($created->id); + return ! empty($created->id); } /** @@ -538,11 +550,11 @@ class AttendanceTracking extends BaseModel ->where('student_id', $studentId) ->where('school_year', $schoolYear); - if (!empty($semester)) { + if (! empty($semester)) { $q->where('semester', $semester); } - if (!empty($lastDate)) { + if (! empty($lastDate)) { $q->where('date', '<=', static::normalizeDateTime($lastDate)); } @@ -550,18 +562,20 @@ class AttendanceTracking extends BaseModel ->orderByDesc('date') ->first(); - if (!$row) return 0; + if (! $row) { + return 0; + } - $counter = ((int)($row->notif_counter ?? 0)) + 1; + $counter = ((int) ($row->notif_counter ?? 0)) + 1; $affected = static::query() - ->whereKey((int)$row->id) + ->whereKey((int) $row->id) ->update([ - 'is_notified' => 1, + 'is_notified' => 1, 'notif_counter' => $counter, - 'updated_at' => now(), + 'updated_at' => now(), ]); return $affected > 0 ? 1 : 0; } -} \ No newline at end of file +} diff --git a/app/Models/AuthorizedUser.php b/app/Models/AuthorizedUser.php index d51ddb81..3f9333c8 100644 --- a/app/Models/AuthorizedUser.php +++ b/app/Models/AuthorizedUser.php @@ -2,8 +2,6 @@ namespace App\Models; -use App\Models\BaseModel; - class AuthorizedUser extends BaseModel { protected $table = 'authorized_users'; @@ -27,7 +25,7 @@ class AuthorizedUser extends BaseModel ]; protected $casts = [ - 'user_id' => 'integer', + 'user_id' => 'integer', 'authorized_user_id' => 'integer', ]; @@ -44,4 +42,4 @@ class AuthorizedUser extends BaseModel { return $this->belongsTo(User::class, 'authorized_user_id'); } -} \ No newline at end of file +} diff --git a/app/Models/BadgePrintLog.php b/app/Models/BadgePrintLog.php index af7d3fcd..88a5206a 100644 --- a/app/Models/BadgePrintLog.php +++ b/app/Models/BadgePrintLog.php @@ -2,7 +2,6 @@ namespace App\Models; -use App\Models\BaseModel; use Illuminate\Database\QueryException; use Illuminate\Support\Facades\DB; use Illuminate\Support\Facades\Log; @@ -25,21 +24,19 @@ class BadgePrintLog extends BaseModel ]; protected $casts = [ - 'user_id' => 'integer', - 'printed_by' => 'integer', - 'printed_at' => 'datetime', - 'copies' => 'integer', + 'user_id' => 'integer', + 'printed_by' => 'integer', + 'printed_at' => 'datetime', + 'copies' => 'integer', ]; /** * Insert a batch of print logs. Swallows errors if table is missing. * - * @param int[] $userIds - * @param int|null $printedBy - * @param string|null $schoolYear - * @param array $roleMap userId => role label - * @param array $classMap userId => class name - * @param int $copies copies per user (defaults to 1) + * @param int[] $userIds + * @param array $roleMap userId => role label + * @param array $classMap userId => class name + * @param int $copies copies per user (defaults to 1) * @return int rows inserted (best-effort) */ public static function logPrints( @@ -55,31 +52,38 @@ class BadgePrintLog extends BaseModel foreach ($userIds as $uid) { $uid = (int) $uid; - if ($uid <= 0) continue; + if ($uid <= 0) { + continue; + } $rows[] = [ - 'user_id' => $uid, - 'printed_by' => $printedBy, - 'school_year' => $schoolYear, - 'printed_at' => $now, - 'role' => (string)($roleMap[$uid] ?? ''), - 'class_section_name' => (string)($classMap[$uid] ?? ''), - 'copies' => max(1, (int) $copies), + 'user_id' => $uid, + 'printed_by' => $printedBy, + 'school_year' => $schoolYear, + 'printed_at' => $now, + 'role' => (string) ($roleMap[$uid] ?? ''), + 'class_section_name' => (string) ($classMap[$uid] ?? ''), + 'copies' => max(1, (int) $copies), ]; } - if (empty($rows)) return 0; + if (empty($rows)) { + return 0; + } try { // insert returns bool; we return count best-effort like legacy DB::table((new static)->getTable())->insert($rows); + return count($rows); } catch (QueryException $e) { // Avoid breaking badge/PDF generation if the table is missing or other DB error - Log::error('BadgePrintLog::logPrints failed: ' . $e->getMessage()); + Log::error('BadgePrintLog::logPrints failed: '.$e->getMessage()); + return 0; } catch (\Throwable $e) { - Log::error('BadgePrintLog::logPrints failed: ' . $e->getMessage()); + Log::error('BadgePrintLog::logPrints failed: '.$e->getMessage()); + return 0; } } @@ -87,31 +91,35 @@ class BadgePrintLog extends BaseModel /** * Return counts and last printed info for given user IDs. * - * @param int[] $userIds - * @param string|null $schoolYear Optional filter by school year + * @param int[] $userIds + * @param string|null $schoolYear Optional filter by school year * @return array userId => [count, last_printed_at, last_printed_by] */ public static function getStatus(array $userIds, ?string $schoolYear = null): array { $userIds = array_values(array_unique(array_map('intval', array_filter($userIds)))); - if (empty($userIds)) return []; + if (empty($userIds)) { + return []; + } try { - $q = DB::table((new static)->getTable() . ' as l') + $q = DB::table((new static)->getTable().' as l') ->selectRaw('l.user_id, COUNT(*) AS prints, MAX(l.printed_at) AS last_printed_at, MAX(l.printed_by) AS last_printed_by') ->whereIn('l.user_id', $userIds) ->groupBy('l.user_id'); - if (!empty($schoolYear)) { + if (! empty($schoolYear)) { $q->where('l.school_year', $schoolYear); } $rows = $q->get(); } catch (QueryException $e) { - Log::error('BadgePrintLog::getStatus failed: ' . $e->getMessage()); + Log::error('BadgePrintLog::getStatus failed: '.$e->getMessage()); + return []; } catch (\Throwable $e) { - Log::error('BadgePrintLog::getStatus failed: ' . $e->getMessage()); + Log::error('BadgePrintLog::getStatus failed: '.$e->getMessage()); + return []; } @@ -119,7 +127,7 @@ class BadgePrintLog extends BaseModel foreach ($rows as $r) { $uid = (int) ($r->user_id ?? 0); $out[$uid] = [ - 'count' => (int) ($r->prints ?? 0), + 'count' => (int) ($r->prints ?? 0), 'last_printed_at' => (string) ($r->last_printed_at ?? ''), 'last_printed_by' => isset($r->last_printed_by) ? (int) $r->last_printed_by : null, ]; @@ -138,4 +146,4 @@ class BadgePrintLog extends BaseModel { return $this->belongsTo(User::class, 'printed_by'); } -} \ No newline at end of file +} diff --git a/app/Models/BaseModel.php b/app/Models/BaseModel.php index 6d35fb69..bcdf9fa0 100644 --- a/app/Models/BaseModel.php +++ b/app/Models/BaseModel.php @@ -3,6 +3,7 @@ namespace App\Models; use Illuminate\Database\Eloquent\Model; +use Illuminate\Foundation\Application; class BaseModel extends Model { @@ -12,8 +13,9 @@ class BaseModel extends Model { if ($this->isTestingEnv()) { $columns = $this->tableColumns(); - if (!empty($columns)) { + if (! empty($columns)) { $key = $this->getKeyName(); + return array_values(array_filter($columns, static function ($col) use ($key) { return $col !== $key; })); @@ -22,10 +24,10 @@ class BaseModel extends Model $fillable = parent::getFillable(); if ($this->timestamps) { - if (!in_array('created_at', $fillable, true)) { + if (! in_array('created_at', $fillable, true)) { $fillable[] = 'created_at'; } - if (!in_array('updated_at', $fillable, true)) { + if (! in_array('updated_at', $fillable, true)) { $fillable[] = 'updated_at'; } } @@ -49,7 +51,7 @@ class BaseModel extends Model if (function_exists('app')) { try { $app = app(); - if ($app instanceof \Illuminate\Foundation\Application && $app->runningUnitTests()) { + if ($app instanceof Application && $app->runningUnitTests()) { return true; } } catch (\Throwable $e) { @@ -82,7 +84,7 @@ class BaseModel extends Model } if ($pattern === null) { - $pattern = __DIR__ . '/../../database/migrations/*.php'; + $pattern = __DIR__.'/../../database/migrations/*.php'; } $files = glob($pattern) ?: []; @@ -98,7 +100,7 @@ class BaseModel extends Model } foreach ($this->parseSchemaCreateTables($contents) as $table => $columns) { - if (!isset($map[$table]) || empty($map[$table])) { + if (! isset($map[$table]) || empty($map[$table])) { $map[$table] = $columns; } } @@ -116,7 +118,7 @@ class BaseModel extends Model $colsBlock = $match[2]; preg_match_all('/`([^`]+)`\\s+[^,]+/m', $colsBlock, $colsMatch); $columns = $colsMatch[1] ?? []; - if (!empty($columns)) { + if (! empty($columns)) { $map[$table] = $columns; } } @@ -128,7 +130,7 @@ class BaseModel extends Model private function parseSchemaCreateTables(string $contents): array { $map = []; - if (!preg_match_all('/Schema::create\\(\\s*[\'"]([^\'"]+)[\'"]\\s*,\\s*function\\s*\\([^)]*\\)\\s*\\{(.*?)\\n\\s*\\}\\);/is', $contents, $matches, PREG_SET_ORDER)) { + if (! preg_match_all('/Schema::create\\(\\s*[\'"]([^\'"]+)[\'"]\\s*,\\s*function\\s*\\([^)]*\\)\\s*\\{(.*?)\\n\\s*\\}\\);/is', $contents, $matches, PREG_SET_ORDER)) { return $map; } @@ -150,7 +152,7 @@ class BaseModel extends Model $columns[] = 'deleted_at'; } - if (!empty($columns)) { + if (! empty($columns)) { $map[$table] = $columns; } } diff --git a/app/Models/Calendar.php b/app/Models/Calendar.php index e1692f17..a1dfd891 100644 --- a/app/Models/Calendar.php +++ b/app/Models/Calendar.php @@ -2,6 +2,4 @@ namespace App\Models; -class Calendar extends CalendarEvent -{ -} +class Calendar extends CalendarEvent {} diff --git a/app/Models/CalendarEvent.php b/app/Models/CalendarEvent.php index bcb814bf..bab56d26 100644 --- a/app/Models/CalendarEvent.php +++ b/app/Models/CalendarEvent.php @@ -2,7 +2,6 @@ namespace App\Models; -use App\Models\BaseModel; use Illuminate\Support\Facades\DB; use Illuminate\Support\Facades\Schema; @@ -31,12 +30,12 @@ class CalendarEvent extends BaseModel protected $casts = [ // If your column is DATE use 'date', if it's DATETIME use 'datetime' - 'date' => 'date', - 'notify_parent' => 'boolean', - 'notify_admin' => 'boolean', + 'date' => 'date', + 'notify_parent' => 'boolean', + 'notify_admin' => 'boolean', 'notify_teacher' => 'boolean', - 'no_school' => 'integer', - 'event_type' => 'string', + 'no_school' => 'integer', + 'event_type' => 'string', ]; private static ?bool $hasEventTypeColumn = null; @@ -48,18 +47,21 @@ class CalendarEvent extends BaseModel public static function getEvents() { $events = static::query()->get()->toArray(); + return static::withNoSchoolFlag($events); } public static function getEventByDate(string $date): array { $events = static::query()->where('date', $date)->get()->toArray(); + return static::withNoSchoolFlag($events); } public static function addEvent(array $data): int { $row = static::create($data); + return (int) $row->id; } @@ -92,6 +94,7 @@ class CalendarEvent extends BaseModel if (static::$hasEventTypeColumn === null) { static::$hasEventTypeColumn = static::columnExists((new static)->getTable(), 'event_type'); } + return (bool) static::$hasEventTypeColumn; } @@ -123,9 +126,9 @@ class CalendarEvent extends BaseModel */ protected static function isNoSchoolEvent(array $event): bool { - $title = strtolower((string)($event['title'] ?? '')); - $desc = strtolower((string)($event['description'] ?? '')); - $text = $title . ' ' . $desc; + $title = strtolower((string) ($event['title'] ?? '')); + $desc = strtolower((string) ($event['description'] ?? '')); + $text = $title.' '.$desc; $keywords = ['no school', 'no-school', 'cancel', 'vacation', 'holiday', 'break']; diff --git a/app/Models/ClassModel.php b/app/Models/ClassModel.php index 2c2d287f..b63baabc 100644 --- a/app/Models/ClassModel.php +++ b/app/Models/ClassModel.php @@ -2,6 +2,4 @@ namespace App\Models; -class ClassModel extends SchoolClass -{ -} +class ClassModel extends SchoolClass {} diff --git a/app/Models/ClassPrepAdjustment.php b/app/Models/ClassPrepAdjustment.php index 5fafae83..cc353221 100644 --- a/app/Models/ClassPrepAdjustment.php +++ b/app/Models/ClassPrepAdjustment.php @@ -2,8 +2,6 @@ namespace App\Models; -use App\Models\BaseModel; - class ClassPrepAdjustment extends BaseModel { protected $table = 'class_prep_adjustments'; @@ -21,7 +19,7 @@ class ClassPrepAdjustment extends BaseModel protected $casts = [ 'class_section_id' => 'integer', - 'adjustment' => 'integer', // change to 'decimal:2' if it's not an int + 'adjustment' => 'integer', // change to 'decimal:2' if it's not an int ]; // Optional relationship @@ -29,4 +27,4 @@ class ClassPrepAdjustment extends BaseModel { return $this->belongsTo(ClassSection::class, 'class_section_id'); } -} \ No newline at end of file +} diff --git a/app/Models/ClassPreparationLog.php b/app/Models/ClassPreparationLog.php index 2601b91b..11891993 100644 --- a/app/Models/ClassPreparationLog.php +++ b/app/Models/ClassPreparationLog.php @@ -2,8 +2,6 @@ namespace App\Models; -use App\Models\BaseModel; - class ClassPreparationLog extends BaseModel { protected $table = 'class_preparation_log'; @@ -22,7 +20,7 @@ class ClassPreparationLog extends BaseModel protected $casts = [ 'class_section_id' => 'integer', // If prep_data is JSON in DB, use 'array' (or 'json') cast: - 'prep_data' => 'array', + 'prep_data' => 'array', ]; // Optional relationship @@ -30,4 +28,4 @@ class ClassPreparationLog extends BaseModel { return $this->belongsTo(ClassSection::class, 'class_section_id'); } -} \ No newline at end of file +} diff --git a/app/Models/ClassProgressAttachment.php b/app/Models/ClassProgressAttachment.php index b0c77514..bfd1ed8d 100644 --- a/app/Models/ClassProgressAttachment.php +++ b/app/Models/ClassProgressAttachment.php @@ -2,12 +2,12 @@ namespace App\Models; -use App\Models\BaseModel; use Illuminate\Database\Eloquent\Factories\HasFactory; class ClassProgressAttachment extends BaseModel { use HasFactory; + protected $table = 'class_progress_attachments'; // legacy: useTimestamps = false @@ -23,8 +23,8 @@ class ClassProgressAttachment extends BaseModel ]; protected $casts = [ - 'report_id' => 'integer', - 'file_size' => 'integer', + 'report_id' => 'integer', + 'file_size' => 'integer', ]; // Optional relationship diff --git a/app/Models/ClassProgressReport.php b/app/Models/ClassProgressReport.php index 9badfcd8..89e224b3 100644 --- a/app/Models/ClassProgressReport.php +++ b/app/Models/ClassProgressReport.php @@ -2,12 +2,12 @@ namespace App\Models; -use App\Models\BaseModel; use Illuminate\Database\Eloquent\Factories\HasFactory; class ClassProgressReport extends BaseModel { use HasFactory; + protected $table = 'class_progress_reports'; // ✅ legacy: useTimestamps = true (created_at/updated_at) @@ -36,14 +36,14 @@ class ClassProgressReport extends BaseModel protected $casts = [ 'class_section_id' => 'integer', - 'teacher_id' => 'integer', + 'teacher_id' => 'integer', // change to 'datetime' if your columns are DATETIME - 'week_start' => 'date', - 'week_end' => 'date', + 'week_start' => 'date', + 'week_end' => 'date', // If flags_json is JSON in DB, this makes it an array automatically - 'flags_json' => 'array', + 'flags_json' => 'array', ]; /* Optional relationships */ diff --git a/app/Models/ClassSection.php b/app/Models/ClassSection.php index 0f517359..0c6477d5 100644 --- a/app/Models/ClassSection.php +++ b/app/Models/ClassSection.php @@ -2,13 +2,13 @@ namespace App\Models; -use App\Models\BaseModel; use Illuminate\Database\Eloquent\Factories\HasFactory; use Illuminate\Support\Facades\DB; class ClassSection extends BaseModel { use HasFactory; + // legacy table name is "classSection" protected $table = 'classSection'; @@ -24,7 +24,7 @@ class ClassSection extends BaseModel ]; protected $casts = [ - 'class_id' => 'integer', + 'class_id' => 'integer', 'class_section_id' => 'integer', ]; diff --git a/app/Models/Communication.php b/app/Models/Communication.php index abdcab5a..8703135a 100644 --- a/app/Models/Communication.php +++ b/app/Models/Communication.php @@ -2,6 +2,4 @@ namespace App\Models; -class Communication extends CommunicationLog -{ -} +class Communication extends CommunicationLog {} diff --git a/app/Models/CommunicationLog.php b/app/Models/CommunicationLog.php index 6b4cb08a..db0c87c6 100644 --- a/app/Models/CommunicationLog.php +++ b/app/Models/CommunicationLog.php @@ -2,8 +2,6 @@ namespace App\Models; -use App\Models\BaseModel; - class CommunicationLog extends BaseModel { protected $table = 'communication_logs'; @@ -34,16 +32,16 @@ class CommunicationLog extends BaseModel } protected $casts = [ - 'student_id' => 'integer', - 'family_id' => 'integer', - 'sent_by' => 'integer', + 'student_id' => 'integer', + 'family_id' => 'integer', + 'sent_by' => 'integer', // If these columns are JSON in DB, this will auto convert to arrays - 'recipients' => 'array', - 'cc' => 'array', - 'bcc' => 'array', - 'attachments' => 'array', - 'metadata' => 'array', + 'recipients' => 'array', + 'cc' => 'array', + 'bcc' => 'array', + 'attachments' => 'array', + 'metadata' => 'array', ]; /* Optional relationships */ diff --git a/app/Models/Competition.php b/app/Models/Competition.php index 40430f46..660a682b 100644 --- a/app/Models/Competition.php +++ b/app/Models/Competition.php @@ -2,7 +2,6 @@ namespace App\Models; -use App\Models\BaseModel; use Illuminate\Database\Eloquent\SoftDeletes; class Competition extends BaseModel @@ -34,20 +33,20 @@ class Competition extends BaseModel protected $casts = [ 'class_section_id' => 'integer', - 'winners_count' => 'integer', - 'prize_per_point' => 'decimal:2', // adjust precision if needed + 'winners_count' => 'integer', + 'prize_per_point' => 'decimal:2', // adjust precision if needed - 'is_published' => 'boolean', - 'is_locked' => 'boolean', + 'is_published' => 'boolean', + 'is_locked' => 'boolean', // change to 'date' if your columns are DATE (not DATETIME) - 'start_date' => 'date', - 'end_date' => 'date', - 'published_at' => 'datetime', - 'locked_at' => 'datetime', + 'start_date' => 'date', + 'end_date' => 'date', + 'published_at' => 'datetime', + 'locked_at' => 'datetime', - 'locked_by' => 'integer', - 'created_by' => 'integer', + 'locked_by' => 'integer', + 'created_by' => 'integer', ]; /* Optional relationships */ @@ -70,4 +69,4 @@ class Competition extends BaseModel { return $this->hasMany(CompetitionClassWinner::class, 'competition_id'); } -} \ No newline at end of file +} diff --git a/app/Models/CompetitionClassWinner.php b/app/Models/CompetitionClassWinner.php index 3d3c5ef4..a718a992 100644 --- a/app/Models/CompetitionClassWinner.php +++ b/app/Models/CompetitionClassWinner.php @@ -2,8 +2,6 @@ namespace App\Models; -use App\Models\BaseModel; - class CompetitionClassWinner extends BaseModel { protected $table = 'competition_class_winners'; @@ -25,10 +23,10 @@ class CompetitionClassWinner extends BaseModel ]; protected $casts = [ - 'competition_id' => 'integer', - 'class_section_id' => 'integer', - 'question_count' => 'integer', - 'winners_override' => 'integer', + 'competition_id' => 'integer', + 'class_section_id' => 'integer', + 'question_count' => 'integer', + 'winners_override' => 'integer', ]; /* Optional relationships */ @@ -41,4 +39,4 @@ class CompetitionClassWinner extends BaseModel { return $this->belongsTo(ClassSection::class, 'class_section_id'); } -} \ No newline at end of file +} diff --git a/app/Models/CompetitionScore.php b/app/Models/CompetitionScore.php index 0d745cd0..d4a49133 100644 --- a/app/Models/CompetitionScore.php +++ b/app/Models/CompetitionScore.php @@ -2,8 +2,6 @@ namespace App\Models; -use App\Models\BaseModel; - class CompetitionScore extends BaseModel { protected $table = 'competition_scores'; @@ -20,10 +18,10 @@ class CompetitionScore extends BaseModel ]; protected $casts = [ - 'competition_id' => 'integer', - 'student_id' => 'integer', + 'competition_id' => 'integer', + 'student_id' => 'integer', 'class_section_id' => 'integer', - 'score' => 'integer', // change to 'decimal:2' if score is not int + 'score' => 'integer', // change to 'decimal:2' if score is not int ]; /* Optional relationships */ @@ -41,4 +39,4 @@ class CompetitionScore extends BaseModel { return $this->belongsTo(ClassSection::class, 'class_section_id'); } -} \ No newline at end of file +} diff --git a/app/Models/CompetitionWinner.php b/app/Models/CompetitionWinner.php index 84c1126d..c9b47616 100644 --- a/app/Models/CompetitionWinner.php +++ b/app/Models/CompetitionWinner.php @@ -2,8 +2,6 @@ namespace App\Models; -use App\Models\BaseModel; - class CompetitionWinner extends BaseModel { protected $table = 'competition_winners'; @@ -22,13 +20,13 @@ class CompetitionWinner extends BaseModel ]; protected $casts = [ - 'competition_id' => 'integer', - 'student_id' => 'integer', + 'competition_id' => 'integer', + 'student_id' => 'integer', 'class_section_id' => 'integer', - 'rank' => 'integer', - 'score' => 'integer', // change to 'decimal:2' if needed - 'prize_amount' => 'decimal:2', // adjust precision if needed - 'created_at' => 'datetime', + 'rank' => 'integer', + 'score' => 'integer', // change to 'decimal:2' if needed + 'prize_amount' => 'decimal:2', // adjust precision if needed + 'created_at' => 'datetime', ]; /* Optional relationships */ @@ -46,4 +44,4 @@ class CompetitionWinner extends BaseModel { return $this->belongsTo(ClassSection::class, 'class_section_id'); } -} \ No newline at end of file +} diff --git a/app/Models/Configuration.php b/app/Models/Configuration.php index c8cc91ab..f5c433ef 100644 --- a/app/Models/Configuration.php +++ b/app/Models/Configuration.php @@ -2,8 +2,7 @@ namespace App\Models; -use App\Models\BaseModel; -use Illuminate\Support\Facades\DB; +use App\Services\SemesterRangeService; class Configuration extends BaseModel { @@ -58,11 +57,11 @@ class Configuration extends BaseModel } $row = static::query()->create([ - 'config_key' => $key, + 'config_key' => $key, 'config_value' => $value, ]); - return !empty($row->id); + return ! empty($row->id); } /** @@ -87,6 +86,7 @@ class Configuration extends BaseModel public static function addConfig(array $data): int { $row = static::query()->create($data); + return (int) $row->id; } @@ -104,7 +104,7 @@ class Configuration extends BaseModel try { // If you have this service in Laravel, inject/resolve it from container. // This call assumes the service constructor can accept Configuration model (like legacy did). - $semester = app(\App\Services\SemesterRangeService::class)->getSemesterForDate(); + $semester = app(SemesterRangeService::class)->getSemesterForDate(); if (is_string($semester) && $semester !== '') { return $semester; } diff --git a/app/Models/ContactUs.php b/app/Models/ContactUs.php index e891a22f..0bfd8c0f 100644 --- a/app/Models/ContactUs.php +++ b/app/Models/ContactUs.php @@ -2,8 +2,6 @@ namespace App\Models; -use App\Models\BaseModel; - class ContactUs extends BaseModel { protected $table = 'contactus'; @@ -23,7 +21,7 @@ class ContactUs extends BaseModel ]; protected $casts = [ - 'sender_id' => 'integer', + 'sender_id' => 'integer', 'reciever_id' => 'integer', ]; @@ -79,4 +77,4 @@ class ContactUs extends BaseModel { return static::query()->whereKey($id)->update($data) >= 0; } -} \ No newline at end of file +} diff --git a/app/Models/CurrentFlag.php b/app/Models/CurrentFlag.php index 1382c2df..7e9cf71d 100644 --- a/app/Models/CurrentFlag.php +++ b/app/Models/CurrentFlag.php @@ -2,8 +2,6 @@ namespace App\Models; -use App\Models\BaseModel; - class CurrentFlag extends BaseModel { protected $table = 'current_flag'; @@ -33,11 +31,11 @@ class CurrentFlag extends BaseModel ]; protected $casts = [ - 'student_id' => 'integer', - 'updated_by_open' => 'integer', - 'updated_by_closed' => 'integer', - 'updated_by_canceled'=> 'integer', - 'flag_datetime' => 'datetime', + 'student_id' => 'integer', + 'updated_by_open' => 'integer', + 'updated_by_closed' => 'integer', + 'updated_by_canceled' => 'integer', + 'flag_datetime' => 'datetime', ]; /* Optional relationships */ @@ -60,4 +58,4 @@ class CurrentFlag extends BaseModel { return $this->belongsTo(User::class, 'updated_by_canceled'); } -} \ No newline at end of file +} diff --git a/app/Models/CurrentIncident.php b/app/Models/CurrentIncident.php index 4cf08894..b106466a 100644 --- a/app/Models/CurrentIncident.php +++ b/app/Models/CurrentIncident.php @@ -2,11 +2,10 @@ namespace App\Models; -use App\Models\BaseModel; - class CurrentIncident extends BaseModel { protected $table = 'current_incident'; + public $timestamps = true; protected $fillable = [ diff --git a/app/Models/DiscountUsage.php b/app/Models/DiscountUsage.php index fc780085..b7620201 100644 --- a/app/Models/DiscountUsage.php +++ b/app/Models/DiscountUsage.php @@ -2,7 +2,6 @@ namespace App\Models; -use App\Models\BaseModel; use Illuminate\Support\Facades\DB; class DiscountUsage extends BaseModel @@ -24,12 +23,12 @@ class DiscountUsage extends BaseModel ]; protected $casts = [ - 'voucher_id' => 'integer', - 'invoice_id' => 'integer', - 'discount_amount' => 'decimal:2', // adjust precision if needed - 'updated_by' => 'integer', - 'parent_id' => 'integer', - 'used_at' => 'datetime', + 'voucher_id' => 'integer', + 'invoice_id' => 'integer', + 'discount_amount' => 'decimal:2', // adjust precision if needed + 'updated_by' => 'integer', + 'parent_id' => 'integer', + 'used_at' => 'datetime', ]; /* Optional relationships */ @@ -56,4 +55,4 @@ class DiscountUsage extends BaseModel return (float) $total; } -} \ No newline at end of file +} diff --git a/app/Models/DiscountVoucher.php b/app/Models/DiscountVoucher.php index 21a99b29..6defb93f 100644 --- a/app/Models/DiscountVoucher.php +++ b/app/Models/DiscountVoucher.php @@ -2,9 +2,7 @@ namespace App\Models; -use App\Models\BaseModel; use Illuminate\Support\Carbon; -use Illuminate\Support\Facades\DB; class DiscountVoucher extends BaseModel { @@ -31,11 +29,11 @@ class DiscountVoucher extends BaseModel protected $casts = [ 'discount_value' => 'decimal:2', - 'max_uses' => 'integer', - 'times_used' => 'integer', - 'is_active' => 'boolean', - 'valid_from' => 'date', - 'valid_until' => 'date', + 'max_uses' => 'integer', + 'times_used' => 'integer', + 'is_active' => 'boolean', + 'valid_from' => 'date', + 'valid_until' => 'date', ]; /* ========================= @@ -90,8 +88,8 @@ class DiscountVoucher extends BaseModel private function validateDatesOrder(): void { - if (!empty($this->valid_from) && !empty($this->valid_until)) { - $from = Carbon::parse($this->valid_from)->toDateString(); + if (! empty($this->valid_from) && ! empty($this->valid_until)) { + $from = Carbon::parse($this->valid_from)->toDateString(); $until = Carbon::parse($this->valid_until)->toDateString(); if ($from > $until) { // In Laravel, prefer validating in FormRequest. @@ -123,24 +121,24 @@ class DiscountVoucher extends BaseModel // Left join usages for this student only ->leftJoin('discount_usages as u', function ($join) use ($studentId) { $join->on('u.voucher_id', '=', 'v.id') - ->where('u.student_id', '=', (int) $studentId); + ->where('u.student_id', '=', (int) $studentId); }) ->where('v.code', $code) ->where('v.is_active', 1) // valid_from is null or <= today ->where(function ($w) use ($today) { $w->whereNull('v.valid_from') - ->orWhere('v.valid_from', '<=', $today); + ->orWhere('v.valid_from', '<=', $today); }) // valid_until is null or >= today ->where(function ($w) use ($today) { $w->whereNull('v.valid_until') - ->orWhere('v.valid_until', '>=', $today); + ->orWhere('v.valid_until', '>=', $today); }) // max_uses is null or times_used < max_uses ->where(function ($w) { $w->whereNull('v.max_uses') - ->orWhereColumn('v.times_used', '<', 'v.max_uses'); + ->orWhereColumn('v.times_used', '<', 'v.max_uses'); }) // Not previously used by this student (no usage row) ->whereNull('u.id') @@ -148,4 +146,4 @@ class DiscountVoucher extends BaseModel return $q->first(); } -} \ No newline at end of file +} diff --git a/app/Models/EarlyDismissalSignature.php b/app/Models/EarlyDismissalSignature.php index f9a703c4..63d0d7d5 100644 --- a/app/Models/EarlyDismissalSignature.php +++ b/app/Models/EarlyDismissalSignature.php @@ -2,8 +2,6 @@ namespace App\Models; -use App\Models\BaseModel; - class EarlyDismissalSignature extends BaseModel { protected $table = 'early_dismissal_signatures'; @@ -26,7 +24,7 @@ class EarlyDismissalSignature extends BaseModel // report_date is stored as Y-m-d in legacy validation; keep as date 'report_date' => 'date', - 'file_size' => 'integer', + 'file_size' => 'integer', 'uploaded_by' => 'integer', ]; @@ -35,4 +33,4 @@ class EarlyDismissalSignature extends BaseModel { return $this->belongsTo(User::class, 'uploaded_by'); } -} \ No newline at end of file +} diff --git a/app/Models/EmailTemplate.php b/app/Models/EmailTemplate.php index cf05bacf..b604b1b0 100644 --- a/app/Models/EmailTemplate.php +++ b/app/Models/EmailTemplate.php @@ -2,8 +2,6 @@ namespace App\Models; -use App\Models\BaseModel; - class EmailTemplate extends BaseModel { protected $table = 'email_templates'; @@ -31,9 +29,9 @@ class EmailTemplate extends BaseModel ->where('is_active', 1) ->where(function ($q) use ($variant) { $q->where('variant', $variant) - ->orWhere('variant', 'default'); + ->orWhere('variant', 'default'); }) - ->orderByRaw("CASE WHEN variant = ? THEN 0 ELSE 1 END", [$variant]) + ->orderByRaw('CASE WHEN variant = ? THEN 0 ELSE 1 END', [$variant]) ->first(); return $row; diff --git a/app/Models/EmergencyContact.php b/app/Models/EmergencyContact.php index 23d898df..555d1d53 100644 --- a/app/Models/EmergencyContact.php +++ b/app/Models/EmergencyContact.php @@ -2,8 +2,6 @@ namespace App\Models; -use App\Models\BaseModel; - class EmergencyContact extends BaseModel { protected $table = 'emergency_contacts'; @@ -24,8 +22,8 @@ class EmergencyContact extends BaseModel ]; protected $casts = [ - 'parent_id' => 'integer', - 'student_id' => 'integer', + 'parent_id' => 'integer', + 'student_id' => 'integer', 'authorized_user_id' => 'integer', ]; @@ -72,7 +70,7 @@ class EmergencyContact extends BaseModel return static::query() ->where(function ($q) use ($studentId) { $q->where('student_id', $studentId) - ->orWhere('parent_id', $studentId); + ->orWhere('parent_id', $studentId); }) ->get(); } diff --git a/app/Models/Enrollment.php b/app/Models/Enrollment.php index 5ff8e09f..605c3731 100644 --- a/app/Models/Enrollment.php +++ b/app/Models/Enrollment.php @@ -2,7 +2,6 @@ namespace App\Models; -use App\Models\BaseModel; use Illuminate\Support\Facades\DB; class Enrollment extends BaseModel @@ -28,14 +27,14 @@ class Enrollment extends BaseModel ]; protected $casts = [ - 'student_id' => 'integer', + 'student_id' => 'integer', 'class_section_id' => 'integer', - 'parent_id' => 'integer', - 'is_withdrawn' => 'boolean', + 'parent_id' => 'integer', + 'is_withdrawn' => 'boolean', // change to 'datetime' if your columns are DATETIME - 'enrollment_date' => 'date', - 'withdrawal_date' => 'date', + 'enrollment_date' => 'date', + 'withdrawal_date' => 'date', ]; /* Optional relationships */ @@ -69,11 +68,11 @@ class Enrollment extends BaseModel ->select('students.*') ->where('enrollments.parent_id', $parentId); - if (!empty($schoolYear)) { + if (! empty($schoolYear)) { $q->where('enrollments.school_year', $schoolYear); } - if (!empty($semester)) { + if (! empty($semester)) { $q->where('enrollments.semester', $semester); } @@ -92,10 +91,10 @@ class Enrollment extends BaseModel ->where('enrollments.parent_id', $parentId) ->where(function ($w) { $w->where('enrollments.enrollment_status', 'enrolled') - ->orWhere('enrollments.enrollment_status', 'payment pending'); + ->orWhere('enrollments.enrollment_status', 'payment pending'); }); - if (!empty($schoolYear)) { + if (! empty($schoolYear)) { $q->where('enrollments.school_year', $schoolYear); } @@ -118,4 +117,4 @@ class Enrollment extends BaseModel return $row?->enrollment_status; } -} \ No newline at end of file +} diff --git a/app/Models/Event.php b/app/Models/Event.php index 4eae8188..38bccfc2 100644 --- a/app/Models/Event.php +++ b/app/Models/Event.php @@ -2,8 +2,6 @@ namespace App\Models; -use App\Models\BaseModel; - class Event extends BaseModel { protected $table = 'events'; @@ -26,9 +24,9 @@ class Event extends BaseModel ]; protected $casts = [ - 'amount' => 'decimal:2', // adjust precision if needed + 'amount' => 'decimal:2', // adjust precision if needed 'expiration_date' => 'date', // change to 'datetime' if stored as DATETIME - 'created_by' => 'integer', + 'created_by' => 'integer', ]; /* Optional relationships */ @@ -54,7 +52,7 @@ class Event extends BaseModel $q = static::query() ->whereDate('expiration_date', '>=', now()->toDateString()); - if (!empty($schoolYear)) { + if (! empty($schoolYear)) { $q->where('school_year', $schoolYear); } @@ -67,9 +65,9 @@ class Event extends BaseModel public static function getEventsByName(string $name, ?string $schoolYear = null) { $q = static::query() - ->where('event_name', 'like', '%' . $name . '%'); + ->where('event_name', 'like', '%'.$name.'%'); - if (!empty($schoolYear)) { + if (! empty($schoolYear)) { $q->where('school_year', $schoolYear); } @@ -83,7 +81,7 @@ class Event extends BaseModel { $q = static::query(); - if (!empty($schoolYear)) { + if (! empty($schoolYear)) { $q->where('school_year', $schoolYear); } @@ -96,6 +94,7 @@ class Event extends BaseModel public static function addEvent(array $data): int { $row = static::create($data); + return (int) $row->id; } @@ -106,7 +105,7 @@ class Event extends BaseModel { $q = static::query()->whereDate('expiration_date', $date); - if (!empty($schoolYear)) { + if (! empty($schoolYear)) { $q->where('school_year', $schoolYear); } @@ -121,11 +120,11 @@ class Event extends BaseModel $q = static::query() ->whereDate('expiration_date', '>=', now()->toDateString()); - if (!empty($schoolYear)) { + if (! empty($schoolYear)) { $q->where('school_year', $schoolYear); } - if (!empty($semester)) { + if (! empty($semester)) { $q->where('semester', $semester); } @@ -139,7 +138,7 @@ class Event extends BaseModel { $q = static::query()->whereKey($id); - if (!empty($schoolYear)) { + if (! empty($schoolYear)) { $q->where('school_year', $schoolYear); } @@ -153,7 +152,7 @@ class Event extends BaseModel { $q = static::query()->whereKey($id); - if (!empty($schoolYear)) { + if (! empty($schoolYear)) { $q->where('school_year', $schoolYear); } @@ -167,10 +166,10 @@ class Event extends BaseModel { $q = static::query()->whereKey($id); - if (!empty($schoolYear)) { + if (! empty($schoolYear)) { $q->where('school_year', $schoolYear); } return (bool) $q->delete(); } -} \ No newline at end of file +} diff --git a/app/Models/EventCharge.php b/app/Models/EventCharge.php index 5f754a2e..7b6a7ebd 100644 --- a/app/Models/EventCharge.php +++ b/app/Models/EventCharge.php @@ -2,6 +2,4 @@ namespace App\Models; -class EventCharge extends EventCharges -{ -} +class EventCharge extends EventCharges {} diff --git a/app/Models/EventCharges.php b/app/Models/EventCharges.php index fd6b17e6..6eb27912 100644 --- a/app/Models/EventCharges.php +++ b/app/Models/EventCharges.php @@ -2,7 +2,6 @@ namespace App\Models; -use App\Models\BaseModel; use Illuminate\Support\Facades\DB; class EventCharges extends BaseModel @@ -41,12 +40,12 @@ class EventCharges extends BaseModel ]; protected $casts = [ - 'event_id' => 'integer', - 'parent_id' => 'integer', - 'student_id' => 'integer', - 'participation' => 'boolean', // change if stored as string/enum - 'charged' => 'boolean', // change if stored as string/enum - 'updated_by' => 'integer', + 'event_id' => 'integer', + 'parent_id' => 'integer', + 'student_id' => 'integer', + 'participation' => 'boolean', // change if stored as string/enum + 'charged' => 'boolean', // change if stored as string/enum + 'updated_by' => 'integer', ]; /* Optional relationships */ @@ -75,15 +74,15 @@ class EventCharges extends BaseModel ->leftJoin('events', 'events.id', '=', 'event_charges.event_id') ->select('event_charges.*', 'events.event_name', 'events.amount'); - if (!empty($parentId)) { + if (! empty($parentId)) { $q->where('event_charges.parent_id', $parentId); } - if (!empty($schoolYear)) { + if (! empty($schoolYear)) { $q->where('event_charges.school_year', $schoolYear); } - if (!empty($semester)) { + if (! empty($semester)) { $q->where('event_charges.semester', $semester); } diff --git a/app/Models/Exam.php b/app/Models/Exam.php index 2cf1ec1f..3bc386dc 100644 --- a/app/Models/Exam.php +++ b/app/Models/Exam.php @@ -2,8 +2,6 @@ namespace App\Models; -use App\Models\BaseModel; - class Exam extends BaseModel { protected $table = 'exams'; @@ -16,6 +14,7 @@ class Exam extends BaseModel * We'll keep auto created_at, no updated_at: */ public $timestamps = true; + const UPDATED_AT = null; // ✅ no updated_at column protected $fillable = [ @@ -27,8 +26,8 @@ class Exam extends BaseModel ]; protected $casts = [ - 'student_id' => 'integer', - 'school_id' => 'integer', + 'student_id' => 'integer', + 'school_id' => 'integer', 'class_section_id' => 'integer', ]; @@ -42,4 +41,4 @@ class Exam extends BaseModel { return $this->belongsTo(ClassSection::class, 'class_section_id'); } -} \ No newline at end of file +} diff --git a/app/Models/ExamDraft.php b/app/Models/ExamDraft.php index 50e69551..577af180 100644 --- a/app/Models/ExamDraft.php +++ b/app/Models/ExamDraft.php @@ -2,8 +2,6 @@ namespace App\Models; -use App\Models\BaseModel; - class ExamDraft extends BaseModel { protected $table = 'exam_drafts'; @@ -43,15 +41,15 @@ class ExamDraft extends BaseModel ]; protected $casts = [ - 'teacher_id' => 'integer', - 'class_section_id' => 'integer', - 'admin_id' => 'integer', - 'is_legacy' => 'boolean', + 'teacher_id' => 'integer', + 'class_section_id' => 'integer', + 'admin_id' => 'integer', + 'is_legacy' => 'boolean', - 'version' => 'integer', + 'version' => 'integer', 'previous_draft_id' => 'integer', - 'reviewed_at' => 'datetime', + 'reviewed_at' => 'datetime', ]; /** @@ -65,7 +63,7 @@ class ExamDraft extends BaseModel */ public function forceSave(array $attributes = []): bool { - if (!empty($attributes)) { + if (! empty($attributes)) { $this->fill($attributes); } diff --git a/app/Models/Expense.php b/app/Models/Expense.php index c7f841be..f5e98940 100644 --- a/app/Models/Expense.php +++ b/app/Models/Expense.php @@ -2,7 +2,6 @@ namespace App\Models; -use App\Models\BaseModel; use Illuminate\Support\Facades\DB; class Expense extends BaseModel @@ -31,14 +30,14 @@ class Expense extends BaseModel ]; protected $casts = [ - 'amount' => 'decimal:2', // adjust precision if needed - 'date_of_purchase'=> 'date', // change to 'datetime' if stored as DATETIME + 'amount' => 'decimal:2', // adjust precision if needed + 'date_of_purchase' => 'date', // change to 'datetime' if stored as DATETIME - 'purchased_by' => 'integer', - 'added_by' => 'integer', - 'reimbursement_id'=> 'integer', - 'approved_by' => 'integer', - 'updated_by' => 'integer', + 'purchased_by' => 'integer', + 'added_by' => 'integer', + 'reimbursement_id' => 'integer', + 'approved_by' => 'integer', + 'updated_by' => 'integer', ]; /* Optional relationships */ @@ -71,7 +70,7 @@ class Expense extends BaseModel public static function getReimbursedExpensesWithDetails(array $filters = []) { $q = DB::table('expenses') - ->selectRaw(" + ->selectRaw(' expenses.*, r.id AS reimb_id, r.amount AS reimb_amount, r.reimbursement_method, r.check_number, @@ -80,24 +79,24 @@ class Expense extends BaseModel r.reimbursed_to AS reimb_recipient_id, reimb.firstname AS reimb_firstname, reimb.lastname AS reimb_lastname, approver.firstname AS approver_firstname, approver.lastname AS approver_lastname - ") + ') ->join('reimbursements as r', 'r.expense_id', '=', 'expenses.id') ->leftJoin('users as reimb', 'reimb.id', '=', 'r.reimbursed_to') ->leftJoin('users as approver', 'approver.id', '=', 'r.approved_by'); - if (!empty($filters['school_year'])) { + if (! empty($filters['school_year'])) { $q->where('r.school_year', $filters['school_year']); } - if (!empty($filters['semester'])) { + if (! empty($filters['semester'])) { $q->where('r.semester', $filters['semester']); } - if (!empty($filters['status'])) { + if (! empty($filters['status'])) { $q->where('r.status', $filters['status']); } - if (!empty($filters['user_id'])) { + if (! empty($filters['user_id'])) { $q->where('r.reimbursed_to', $filters['user_id']); } return $q->orderByDesc('r.created_at'); } -} \ No newline at end of file +} diff --git a/app/Models/Family.php b/app/Models/Family.php index fdc35785..0b5aa216 100644 --- a/app/Models/Family.php +++ b/app/Models/Family.php @@ -2,8 +2,6 @@ namespace App\Models; -use App\Models\BaseModel; - class Family extends BaseModel { protected $table = 'families'; @@ -40,4 +38,4 @@ class Family extends BaseModel { return $this->hasMany(FamilyCommPref::class, 'family_id'); } -} \ No newline at end of file +} diff --git a/app/Models/FamilyCommPref.php b/app/Models/FamilyCommPref.php index 54133289..20211ac0 100644 --- a/app/Models/FamilyCommPref.php +++ b/app/Models/FamilyCommPref.php @@ -2,8 +2,6 @@ namespace App\Models; -use App\Models\BaseModel; - class FamilyCommPref extends BaseModel { protected $table = 'family_comm_prefs'; @@ -20,10 +18,10 @@ class FamilyCommPref extends BaseModel ]; protected $casts = [ - 'family_id' => 'integer', - 'via_email' => 'boolean', - 'via_sms' => 'boolean', - 'cc_all_guardians' => 'boolean', + 'family_id' => 'integer', + 'via_email' => 'boolean', + 'via_sms' => 'boolean', + 'cc_all_guardians' => 'boolean', ]; // Optional relationship @@ -31,4 +29,4 @@ class FamilyCommPref extends BaseModel { return $this->belongsTo(Family::class, 'family_id'); } -} \ No newline at end of file +} diff --git a/app/Models/FamilyGuardian.php b/app/Models/FamilyGuardian.php index b4d957ac..8bd3a5eb 100644 --- a/app/Models/FamilyGuardian.php +++ b/app/Models/FamilyGuardian.php @@ -2,8 +2,6 @@ namespace App\Models; -use App\Models\BaseModel; - class FamilyGuardian extends BaseModel { protected $table = 'family_guardians'; @@ -22,11 +20,11 @@ class FamilyGuardian extends BaseModel ]; protected $casts = [ - 'family_id' => 'integer', - 'user_id' => 'integer', - 'is_primary' => 'boolean', - 'receive_emails' => 'boolean', - 'receive_sms' => 'boolean', + 'family_id' => 'integer', + 'user_id' => 'integer', + 'is_primary' => 'boolean', + 'receive_emails' => 'boolean', + 'receive_sms' => 'boolean', ]; // Optional relationships @@ -39,4 +37,4 @@ class FamilyGuardian extends BaseModel { return $this->belongsTo(User::class, 'user_id'); } -} \ No newline at end of file +} diff --git a/app/Models/FamilyStudent.php b/app/Models/FamilyStudent.php index c472f3b1..80c3f880 100644 --- a/app/Models/FamilyStudent.php +++ b/app/Models/FamilyStudent.php @@ -2,7 +2,6 @@ namespace App\Models; -use App\Models\BaseModel; use Illuminate\Support\Facades\DB; class FamilyStudent extends BaseModel @@ -20,9 +19,9 @@ class FamilyStudent extends BaseModel ]; protected $casts = [ - 'family_id' => 'integer', - 'student_id' => 'integer', - 'is_primary_home' => 'boolean', + 'family_id' => 'integer', + 'student_id' => 'integer', + 'is_primary_home' => 'boolean', ]; /* Optional relationships */ @@ -53,4 +52,4 @@ class FamilyStudent extends BaseModel ->map(fn ($r) => (array) $r) ->all(); } -} \ No newline at end of file +} diff --git a/app/Models/FinalExam.php b/app/Models/FinalExam.php index 499d7571..23f8abb1 100644 --- a/app/Models/FinalExam.php +++ b/app/Models/FinalExam.php @@ -2,8 +2,6 @@ namespace App\Models; -use App\Models\BaseModel; - class FinalExam extends BaseModel { protected $table = 'final_exam'; @@ -33,19 +31,19 @@ class FinalExam extends BaseModel ]; protected $casts = [ - 'student_id' => 'integer', - 'school_id' => 'integer', + 'student_id' => 'integer', + 'school_id' => 'integer', 'class_section_id' => 'integer', - 'updated_by' => 'integer', + 'updated_by' => 'integer', // score is often numeric; adjust if yours is integer - 'score' => 'decimal:2', - 'max_points' => 'decimal:2', - 'locked_by' => 'integer', - 'locked_at' => 'datetime', + 'score' => 'decimal:2', + 'max_points' => 'decimal:2', + 'locked_by' => 'integer', + 'locked_at' => 'datetime', - 'created_at' => 'datetime', - 'updated_at' => 'datetime', + 'created_at' => 'datetime', + 'updated_at' => 'datetime', ]; /* Optional relationships */ @@ -82,4 +80,4 @@ class FinalExam extends BaseModel return $row?->score; } -} \ No newline at end of file +} diff --git a/app/Models/FinalScore.php b/app/Models/FinalScore.php index 88e4e46a..2b6ef597 100644 --- a/app/Models/FinalScore.php +++ b/app/Models/FinalScore.php @@ -2,8 +2,6 @@ namespace App\Models; -use App\Models\BaseModel; - class FinalScore extends BaseModel { protected $table = 'final_score'; @@ -27,11 +25,11 @@ class FinalScore extends BaseModel ]; protected $casts = [ - 'student_id' => 'integer', - 'school_id' => 'integer', + 'student_id' => 'integer', + 'school_id' => 'integer', 'class_section_id' => 'integer', - 'teacher_id' => 'integer', - 'score' => 'decimal:2', // change to 'integer' if score is whole number + 'teacher_id' => 'integer', + 'score' => 'decimal:2', // change to 'integer' if score is whole number ]; /* Optional relationships */ @@ -64,4 +62,4 @@ class FinalScore extends BaseModel return $row?->score; } -} \ No newline at end of file +} diff --git a/app/Models/FinanceBalanceCarryforward.php b/app/Models/FinanceBalanceCarryforward.php index 776b63b8..cee21143 100644 --- a/app/Models/FinanceBalanceCarryforward.php +++ b/app/Models/FinanceBalanceCarryforward.php @@ -5,6 +5,8 @@ namespace App\Models; class FinanceBalanceCarryforward extends BaseModel { protected $guarded = []; + protected $table = 'finance_balance_carryforwards'; + protected $casts = ['source_invoice_ids_json' => 'array', 'metadata_json' => 'array']; } diff --git a/app/Models/FinanceFollowUpNote.php b/app/Models/FinanceFollowUpNote.php index bd292657..34ecbdd5 100644 --- a/app/Models/FinanceFollowUpNote.php +++ b/app/Models/FinanceFollowUpNote.php @@ -5,5 +5,6 @@ namespace App\Models; class FinanceFollowUpNote extends BaseModel { protected $guarded = []; + protected $table = 'finance_follow_up_notes'; } diff --git a/app/Models/FinanceNotificationLog.php b/app/Models/FinanceNotificationLog.php index d4145137..7a9e58eb 100644 --- a/app/Models/FinanceNotificationLog.php +++ b/app/Models/FinanceNotificationLog.php @@ -5,5 +5,6 @@ namespace App\Models; class FinanceNotificationLog extends BaseModel { protected $guarded = []; + protected $table = 'finance_notification_logs'; } diff --git a/app/Models/FinanceNotificationPreference.php b/app/Models/FinanceNotificationPreference.php index 4a05d1a5..3544107d 100644 --- a/app/Models/FinanceNotificationPreference.php +++ b/app/Models/FinanceNotificationPreference.php @@ -5,5 +5,6 @@ namespace App\Models; class FinanceNotificationPreference extends BaseModel { protected $guarded = []; + protected $table = 'finance_notification_preferences'; } diff --git a/app/Models/FinanceReceiptSequence.php b/app/Models/FinanceReceiptSequence.php index 28a2d452..94494dcc 100644 --- a/app/Models/FinanceReceiptSequence.php +++ b/app/Models/FinanceReceiptSequence.php @@ -5,5 +5,6 @@ namespace App\Models; class FinanceReceiptSequence extends BaseModel { protected $guarded = []; + protected $table = 'finance_receipt_sequences'; } diff --git a/app/Models/GradingLock.php b/app/Models/GradingLock.php index 314135a9..24865d70 100644 --- a/app/Models/GradingLock.php +++ b/app/Models/GradingLock.php @@ -2,8 +2,6 @@ namespace App\Models; -use App\Models\BaseModel; - class GradingLock extends BaseModel { protected $table = 'grading_locks'; @@ -24,9 +22,9 @@ class GradingLock extends BaseModel protected $casts = [ 'class_section_id' => 'integer', - 'is_locked' => 'boolean', - 'locked_by' => 'integer', - 'locked_at' => 'datetime', + 'is_locked' => 'boolean', + 'locked_by' => 'integer', + 'locked_at' => 'datetime', ]; /* Optional relationships */ @@ -63,6 +61,7 @@ class GradingLock extends BaseModel public static function isLocked(int $classSectionId, string $semester, string $schoolYear): bool { $row = static::getLock($classSectionId, $semester, $schoolYear); + return (bool) ($row?->is_locked ?? false); } -} \ No newline at end of file +} diff --git a/app/Models/Homework.php b/app/Models/Homework.php index be7b8ecb..af827e8a 100644 --- a/app/Models/Homework.php +++ b/app/Models/Homework.php @@ -2,7 +2,6 @@ namespace App\Models; -use App\Models\BaseModel; use Illuminate\Support\Facades\DB; class Homework extends BaseModel @@ -35,20 +34,20 @@ class Homework extends BaseModel ]; protected $casts = [ - 'student_id' => 'integer', - 'school_id' => 'integer', + 'student_id' => 'integer', + 'school_id' => 'integer', 'class_section_id' => 'integer', - 'updated_by' => 'integer', - 'homework_index' => 'integer', + 'updated_by' => 'integer', + 'homework_index' => 'integer', // keep numeric; change to 'integer' if your scores are whole numbers - 'score' => 'decimal:2', - 'max_points' => 'decimal:2', - 'locked_by' => 'integer', - 'locked_at' => 'datetime', + 'score' => 'decimal:2', + 'max_points' => 'decimal:2', + 'locked_by' => 'integer', + 'locked_at' => 'datetime', - 'created_at' => 'datetime', - 'updated_at' => 'datetime', + 'created_at' => 'datetime', + 'updated_at' => 'datetime', ]; /* Optional relationships */ @@ -93,10 +92,18 @@ class Homework extends BaseModel foreach ($rows as $r) { $score = $r->score ?? null; - if ($score === null) continue; - if (is_string($score) && trim($score) === '') continue; - if ($score === '') continue; - if (!is_numeric($score)) continue; + if ($score === null) { + continue; + } + if (is_string($score) && trim($score) === '') { + continue; + } + if ($score === '') { + continue; + } + if (! is_numeric($score)) { + continue; + } $total += (float) $score; $count++; @@ -108,4 +115,4 @@ class Homework extends BaseModel return round($total / $count, 2); } -} \ No newline at end of file +} diff --git a/app/Models/Incident.php b/app/Models/Incident.php index 3ab7d922..5121d5d2 100644 --- a/app/Models/Incident.php +++ b/app/Models/Incident.php @@ -2,8 +2,6 @@ namespace App\Models; -use App\Models\BaseModel; - class Incident extends BaseModel { protected $table = 'incident'; @@ -32,11 +30,11 @@ class Incident extends BaseModel ]; protected $casts = [ - 'student_id' => 'integer', - 'updated_by_open' => 'integer', - 'updated_by_closed' => 'integer', + 'student_id' => 'integer', + 'updated_by_open' => 'integer', + 'updated_by_closed' => 'integer', 'updated_by_canceled' => 'integer', - 'incident_datetime' => 'datetime', + 'incident_datetime' => 'datetime', ]; /* Optional relationships */ diff --git a/app/Models/InventoryCategory.php b/app/Models/InventoryCategory.php index eb0cfbcb..c0901238 100644 --- a/app/Models/InventoryCategory.php +++ b/app/Models/InventoryCategory.php @@ -2,8 +2,6 @@ namespace App\Models; -use App\Models\BaseModel; - class InventoryCategory extends BaseModel { protected $table = 'inventory_categories'; @@ -39,4 +37,4 @@ class InventoryCategory extends BaseModel ->get() ->toArray(); } -} \ No newline at end of file +} diff --git a/app/Models/InventoryItem.php b/app/Models/InventoryItem.php index e04d239f..1d2fb24d 100644 --- a/app/Models/InventoryItem.php +++ b/app/Models/InventoryItem.php @@ -2,8 +2,6 @@ namespace App\Models; -use App\Models\BaseModel; - class InventoryItem extends BaseModel { protected $table = 'inventory_items'; @@ -44,21 +42,21 @@ class InventoryItem extends BaseModel ]; protected $casts = [ - 'category_id' => 'integer', - 'quantity' => 'integer', - 'updated_by' => 'integer', + 'category_id' => 'integer', + 'quantity' => 'integer', + 'updated_by' => 'integer', - 'good_qty' => 'integer', - 'needs_repair_qty' => 'integer', - 'need_replace_qty' => 'integer', - 'cannot_find_qty' => 'integer', + 'good_qty' => 'integer', + 'needs_repair_qty' => 'integer', + 'need_replace_qty' => 'integer', + 'cannot_find_qty' => 'integer', 'preferred_supplier_id' => 'integer', - 'last_purchase_price' => 'decimal:2', - 'average_unit_cost' => 'decimal:2', - 'reorder_point' => 'integer', - 'reorder_quantity' => 'integer', - 'lead_time_days' => 'integer', + 'last_purchase_price' => 'decimal:2', + 'average_unit_cost' => 'decimal:2', + 'reorder_point' => 'integer', + 'reorder_quantity' => 'integer', + 'lead_time_days' => 'integer', ]; /* Optional relationships */ @@ -87,4 +85,4 @@ class InventoryItem extends BaseModel { return $query->where('quantity', '<=', 0); } -} \ No newline at end of file +} diff --git a/app/Models/InventoryMovement.php b/app/Models/InventoryMovement.php index 3d3b0f8d..c4b0f952 100644 --- a/app/Models/InventoryMovement.php +++ b/app/Models/InventoryMovement.php @@ -2,8 +2,6 @@ namespace App\Models; -use App\Models\BaseModel; - class InventoryMovement extends BaseModel { protected $table = 'inventory_movements'; @@ -33,16 +31,16 @@ class InventoryMovement extends BaseModel ]; protected $casts = [ - 'item_id' => 'integer', - 'qty_change' => 'integer', - 'performed_by' => 'integer', - 'teacher_id' => 'integer', - 'student_id' => 'integer', - 'class_section_id'=> 'integer', - 'voided_by' => 'integer', + 'item_id' => 'integer', + 'qty_change' => 'integer', + 'performed_by' => 'integer', + 'teacher_id' => 'integer', + 'student_id' => 'integer', + 'class_section_id' => 'integer', + 'voided_by' => 'integer', 'corrects_movement_id' => 'integer', - 'source_id' => 'integer', - 'voided_at' => 'datetime', + 'source_id' => 'integer', + 'voided_at' => 'datetime', ]; /* Scopes */ @@ -96,4 +94,4 @@ class InventoryMovement extends BaseModel { return $this->hasMany(self::class, 'corrects_movement_id'); } -} \ No newline at end of file +} diff --git a/app/Models/Invoice.php b/app/Models/Invoice.php index f918522c..38a28c37 100644 --- a/app/Models/Invoice.php +++ b/app/Models/Invoice.php @@ -2,7 +2,6 @@ namespace App\Models; -use App\Models\BaseModel; use Illuminate\Support\Carbon; use Illuminate\Support\Facades\DB; @@ -35,19 +34,19 @@ class Invoice extends BaseModel ]; protected $casts = [ - 'parent_id' => 'integer', - 'updated_by' => 'integer', + 'parent_id' => 'integer', + 'updated_by' => 'integer', 'has_discount' => 'boolean', 'total_amount' => 'decimal:2', - 'balance' => 'decimal:2', - 'paid_amount' => 'decimal:2', + 'balance' => 'decimal:2', + 'paid_amount' => 'decimal:2', - 'issue_date' => 'datetime', - 'due_date' => 'datetime', + 'issue_date' => 'datetime', + 'due_date' => 'datetime', // created_at/updated_at exist but are DB-managed; you can still cast if you like: - 'created_at' => 'datetime', - 'updated_at' => 'datetime', + 'created_at' => 'datetime', + 'updated_at' => 'datetime', ]; /* Optional relationships */ @@ -84,13 +83,18 @@ class Invoice extends BaseModel private function normalizeDateTimes(): void { foreach (['issue_date', 'due_date', 'created_at', 'updated_at'] as $field) { - if (!$this->isDirty($field)) continue; + if (! $this->isDirty($field)) { + continue; + } $val = $this->{$field}; if ($val === null || $val === '' || $val === '0000-00-00' || $val === '0000-00-00 00:00:00') { // keep NULL for due_date; keep other values as-is (DB defaults/triggers) - if ($field === 'due_date') $this->{$field} = null; + if ($field === 'due_date') { + $this->{$field} = null; + } + continue; } @@ -108,11 +112,14 @@ class Invoice extends BaseModel } $s = trim((string) $raw); - if ($s === '') return null; + if ($s === '') { + return null; + } // strict datetime try { $dt = Carbon::createFromFormat('Y-m-d H:i:s', $s, 'UTC'); + return $dt->utc()->format('Y-m-d H:i:s'); } catch (\Throwable $e) { // ignore @@ -121,6 +128,7 @@ class Invoice extends BaseModel // date-only sneaks in => store at 12:00:00 try { $d = Carbon::createFromFormat('Y-m-d', $s, 'UTC')->setTime(12, 0, 0); + return $d->utc()->format('Y-m-d H:i:s'); } catch (\Throwable $e) { // ignore @@ -207,7 +215,7 @@ class Invoice extends BaseModel ->selectRaw('(SELECT COALESCE(SUM(du.discount_amount),0) FROM discount_usages du WHERE du.invoice_id = i.id) AS discount') ->where('i.parent_id', $parentId); - if (!empty($schoolYear)) { + if (! empty($schoolYear)) { $q->where('i.school_year', $schoolYear); } @@ -242,10 +250,12 @@ class Invoice extends BaseModel public static function getInvoicesByUserIds(array $parentIds, string $schoolYear): array { $parentIds = array_values(array_unique(array_map('intval', array_filter($parentIds)))); - if (empty($parentIds)) return []; + if (empty($parentIds)) { + return []; + } return DB::table('invoices as i') - ->select(['i.id','i.parent_id','i.invoice_number','i.status','i.balance','i.total_amount','i.issue_date','i.due_date']) + ->select(['i.id', 'i.parent_id', 'i.invoice_number', 'i.status', 'i.balance', 'i.total_amount', 'i.issue_date', 'i.due_date']) ->selectRaw('(SELECT COALESCE(SUM(du.discount_amount),0) FROM discount_usages du WHERE du.invoice_id = i.id) AS discount') ->whereIn('i.parent_id', $parentIds) ->where('i.school_year', $schoolYear) @@ -259,10 +269,12 @@ class Invoice extends BaseModel public static function getAllInvoicesByUserIds(array $parentIds, string $schoolYear): array { $parentIds = array_values(array_unique(array_map('intval', array_filter($parentIds)))); - if (empty($parentIds)) return []; + if (empty($parentIds)) { + return []; + } return DB::table('invoices as i') - ->select(['i.id','i.parent_id','i.invoice_number','i.status','i.balance','i.issue_date']) + ->select(['i.id', 'i.parent_id', 'i.invoice_number', 'i.status', 'i.balance', 'i.issue_date']) ->selectRaw('(SELECT COALESCE(SUM(du.discount_amount),0) FROM discount_usages du WHERE du.invoice_id = i.id) AS discount') ->whereIn('i.parent_id', $parentIds) ->where('i.school_year', $schoolYear) @@ -314,10 +326,10 @@ class Invoice extends BaseModel $affected = DB::table('invoices') ->where('id', $invoiceId) ->update([ - 'total_amount' => DB::raw('GREATEST(total_amount - ' . $amount . ', 0)'), - 'balance' => DB::raw('GREATEST(balance - ' . $amount . ', 0)'), + 'total_amount' => DB::raw('GREATEST(total_amount - '.$amount.', 0)'), + 'balance' => DB::raw('GREATEST(balance - '.$amount.', 0)'), ]); return $affected >= 0; } -} \ No newline at end of file +} diff --git a/app/Models/InvoiceEvent.php b/app/Models/InvoiceEvent.php index 8613000b..2ee0036a 100644 --- a/app/Models/InvoiceEvent.php +++ b/app/Models/InvoiceEvent.php @@ -2,8 +2,6 @@ namespace App\Models; -use App\Models\BaseModel; - class InvoiceEvent extends BaseModel { protected $table = 'invoice_event'; @@ -24,9 +22,9 @@ class InvoiceEvent extends BaseModel ]; protected $casts = [ - 'invoice_id' => 'integer', - 'amount' => 'decimal:2', // adjust precision if needed - 'updated_by' => 'integer', + 'invoice_id' => 'integer', + 'amount' => 'decimal:2', // adjust precision if needed + 'updated_by' => 'integer', ]; /* Optional relationships */ @@ -39,4 +37,4 @@ class InvoiceEvent extends BaseModel { return $this->belongsTo(User::class, 'updated_by'); } -} \ No newline at end of file +} diff --git a/app/Models/InvoiceInstallment.php b/app/Models/InvoiceInstallment.php index e9c0bb25..daec4dd9 100644 --- a/app/Models/InvoiceInstallment.php +++ b/app/Models/InvoiceInstallment.php @@ -5,5 +5,6 @@ namespace App\Models; class InvoiceInstallment extends BaseModel { protected $guarded = []; + protected $table = 'invoice_installments'; } diff --git a/app/Models/InvoiceInstallmentPlan.php b/app/Models/InvoiceInstallmentPlan.php index 610827a3..eadc6987 100644 --- a/app/Models/InvoiceInstallmentPlan.php +++ b/app/Models/InvoiceInstallmentPlan.php @@ -5,5 +5,6 @@ namespace App\Models; class InvoiceInstallmentPlan extends BaseModel { protected $guarded = []; + protected $table = 'invoice_installment_plans'; } diff --git a/app/Models/InvoiceStudentList.php b/app/Models/InvoiceStudentList.php index 256bd20b..0b2dab93 100644 --- a/app/Models/InvoiceStudentList.php +++ b/app/Models/InvoiceStudentList.php @@ -2,8 +2,6 @@ namespace App\Models; -use App\Models\BaseModel; - class InvoiceStudentList extends BaseModel { protected $table = 'invoice_students_list'; @@ -27,8 +25,8 @@ class InvoiceStudentList extends BaseModel protected $casts = [ 'invoice_id' => 'integer', 'student_id' => 'integer', - 'school_id' => 'integer', - 'enrolled' => 'boolean', + 'school_id' => 'integer', + 'enrolled' => 'boolean', ]; /* Optional relationships */ @@ -41,4 +39,4 @@ class InvoiceStudentList extends BaseModel { return $this->belongsTo(Student::class, 'student_id'); } -} \ No newline at end of file +} diff --git a/app/Models/IpAttempt.php b/app/Models/IpAttempt.php index b2a4175d..ca1e6d75 100644 --- a/app/Models/IpAttempt.php +++ b/app/Models/IpAttempt.php @@ -2,8 +2,6 @@ namespace App\Models; -use App\Models\BaseModel; - class IpAttempt extends BaseModel { protected $table = 'ip_attempts'; @@ -21,9 +19,9 @@ class IpAttempt extends BaseModel ]; protected $casts = [ - 'attempts' => 'integer', + 'attempts' => 'integer', 'last_attempt_at' => 'datetime', - 'blocked_until' => 'datetime', + 'blocked_until' => 'datetime', ]; /** @@ -53,6 +51,7 @@ class IpAttempt extends BaseModel public static function insertIpAttempt(array $data): int { $row = static::query()->create($data); + return (int) $row->id; } -} \ No newline at end of file +} diff --git a/app/Models/LateSlipLog.php b/app/Models/LateSlipLog.php index c8103abd..85be8c58 100644 --- a/app/Models/LateSlipLog.php +++ b/app/Models/LateSlipLog.php @@ -2,7 +2,6 @@ namespace App\Models; -use App\Models\BaseModel; use Illuminate\Database\QueryException; use Illuminate\Support\Facades\Log; @@ -28,10 +27,10 @@ class LateSlipLog extends BaseModel protected $casts = [ 'printed_by' => 'integer', - 'slip_date' => 'date', // slip_date is Y-m-d + 'slip_date' => 'date', // slip_date is Y-m-d 'printed_at' => 'datetime', // time_in is often stored as TIME or string; keep as string unless you store as DATETIME - 'time_in' => 'string', + 'time_in' => 'string', ]; /** @@ -41,27 +40,30 @@ class LateSlipLog extends BaseModel public static function logSlip(array $data, ?int $printedBy): bool { $row = [ - 'school_year' => (string)($data['school_year'] ?? ''), - 'semester' => (string)($data['semester'] ?? ''), - 'student_name' => (string)($data['student_name'] ?? ''), - 'slip_date' => $data['slip_date'] ?? null, // Y-m-d - 'time_in' => $data['time_in'] ?? null, // H:i:s - 'grade' => (string)($data['grade'] ?? ''), - 'reason' => (string)($data['reason'] ?? ''), - 'admin_name' => (string)($data['admin_name'] ?? ''), - 'printed_by' => $printedBy, - 'printed_at' => now(), // use UTC if your app timezone is UTC + 'school_year' => (string) ($data['school_year'] ?? ''), + 'semester' => (string) ($data['semester'] ?? ''), + 'student_name' => (string) ($data['student_name'] ?? ''), + 'slip_date' => $data['slip_date'] ?? null, // Y-m-d + 'time_in' => $data['time_in'] ?? null, // H:i:s + 'grade' => (string) ($data['grade'] ?? ''), + 'reason' => (string) ($data['reason'] ?? ''), + 'admin_name' => (string) ($data['admin_name'] ?? ''), + 'printed_by' => $printedBy, + 'printed_at' => now(), // use UTC if your app timezone is UTC ]; try { // create() returns model; treat as success if created $created = static::query()->create($row); - return !empty($created->id); + + return ! empty($created->id); } catch (QueryException $e) { - Log::error('LateSlipLog::logSlip failed: ' . $e->getMessage()); + Log::error('LateSlipLog::logSlip failed: '.$e->getMessage()); + return false; } catch (\Throwable $e) { - Log::error('LateSlipLog::logSlip failed: ' . $e->getMessage()); + Log::error('LateSlipLog::logSlip failed: '.$e->getMessage()); + return false; } } @@ -71,4 +73,4 @@ class LateSlipLog extends BaseModel { return $this->belongsTo(User::class, 'printed_by'); } -} \ No newline at end of file +} diff --git a/app/Models/LoginActivity.php b/app/Models/LoginActivity.php index a2995895..df5cb17e 100644 --- a/app/Models/LoginActivity.php +++ b/app/Models/LoginActivity.php @@ -2,8 +2,6 @@ namespace App\Models; -use App\Models\BaseModel; - class LoginActivity extends BaseModel { protected $table = 'login_activity'; @@ -25,8 +23,8 @@ class LoginActivity extends BaseModel ]; protected $casts = [ - 'user_id' => 'integer', - 'login_time' => 'datetime', + 'user_id' => 'integer', + 'login_time' => 'datetime', 'logout_time' => 'datetime', ]; @@ -46,4 +44,4 @@ class LoginActivity extends BaseModel ->limit(max(1, $limit)) ->get(); } -} \ No newline at end of file +} diff --git a/app/Models/ManualPayment.php b/app/Models/ManualPayment.php index a5edf9e9..bece370d 100644 --- a/app/Models/ManualPayment.php +++ b/app/Models/ManualPayment.php @@ -2,8 +2,6 @@ namespace App\Models; -use App\Models\BaseModel; - class ManualPayment extends BaseModel { protected $table = 'manual_payments'; @@ -23,7 +21,7 @@ class ManualPayment extends BaseModel ]; protected $casts = [ - 'amount' => 'decimal:2', // adjust precision if needed + 'amount' => 'decimal:2', // adjust precision if needed 'created_at' => 'datetime', ]; @@ -32,4 +30,4 @@ class ManualPayment extends BaseModel { return $this->belongsTo(Invoice::class, 'invoice_number', 'invoice_number'); } -} \ No newline at end of file +} diff --git a/app/Models/Message.php b/app/Models/Message.php index ed7dcbdc..951a1c66 100644 --- a/app/Models/Message.php +++ b/app/Models/Message.php @@ -2,8 +2,6 @@ namespace App\Models; -use App\Models\BaseModel; - class Message extends BaseModel { protected $table = 'messages'; @@ -28,13 +26,13 @@ class Message extends BaseModel ]; protected $casts = [ - 'sender_id' => 'integer', - 'recipient_id' => 'integer', - 'read_status' => 'boolean', + 'sender_id' => 'integer', + 'recipient_id' => 'integer', + 'read_status' => 'boolean', 'message_number' => 'integer', - 'sent_datetime' => 'datetime', - 'read_datetime' => 'datetime', + 'sent_datetime' => 'datetime', + 'read_datetime' => 'datetime', ]; /* Optional relationships */ @@ -66,7 +64,7 @@ class Message extends BaseModel $affected = static::query() ->whereKey($messageId) ->update([ - 'read_status' => 1, + 'read_status' => 1, 'read_datetime' => now(), // set app timezone (configure to UTC if desired) ]); @@ -121,7 +119,7 @@ class Message extends BaseModel return static::query() ->where(function ($q) use ($userId) { $q->where('sender_id', $userId) - ->orWhere('recipient_id', $userId); + ->orWhere('recipient_id', $userId); }) ->where('status', 'trashed') ->orderByDesc('sent_datetime') @@ -132,10 +130,10 @@ class Message extends BaseModel { $q = static::query()->where('semester', $semester); - if (!empty($schoolYear)) { + if (! empty($schoolYear)) { $q->where('school_year', $schoolYear); } return $q->orderByDesc('sent_datetime')->get(); } -} \ No newline at end of file +} diff --git a/app/Models/MidtermExam.php b/app/Models/MidtermExam.php index 9ca5eace..5feaeb8d 100644 --- a/app/Models/MidtermExam.php +++ b/app/Models/MidtermExam.php @@ -2,8 +2,6 @@ namespace App\Models; -use App\Models\BaseModel; - class MidtermExam extends BaseModel { protected $table = 'midterm_exam'; @@ -33,19 +31,19 @@ class MidtermExam extends BaseModel ]; protected $casts = [ - 'student_id' => 'integer', - 'school_id' => 'integer', + 'student_id' => 'integer', + 'school_id' => 'integer', 'class_section_id' => 'integer', - 'updated_by' => 'integer', + 'updated_by' => 'integer', // score can be decimal; change to 'integer' if it's whole number - 'score' => 'decimal:2', - 'max_points' => 'decimal:2', - 'locked_by' => 'integer', - 'locked_at' => 'datetime', + 'score' => 'decimal:2', + 'max_points' => 'decimal:2', + 'locked_by' => 'integer', + 'locked_at' => 'datetime', - 'created_at' => 'datetime', - 'updated_at' => 'datetime', + 'created_at' => 'datetime', + 'updated_at' => 'datetime', ]; /* Optional relationships */ @@ -85,4 +83,4 @@ class MidtermExam extends BaseModel return $row?->score; } -} \ No newline at end of file +} diff --git a/app/Models/MissingScoreOverride.php b/app/Models/MissingScoreOverride.php index cc0bf55e..0831fe47 100644 --- a/app/Models/MissingScoreOverride.php +++ b/app/Models/MissingScoreOverride.php @@ -2,7 +2,6 @@ namespace App\Models; -use App\Models\BaseModel; use Illuminate\Support\Facades\DB; class MissingScoreOverride extends BaseModel @@ -26,11 +25,11 @@ class MissingScoreOverride extends BaseModel ]; protected $casts = [ - 'student_id' => 'integer', + 'student_id' => 'integer', 'class_section_id' => 'integer', - 'item_index' => 'integer', - 'is_allowed' => 'boolean', - 'updated_by' => 'integer', + 'item_index' => 'integer', + 'is_allowed' => 'boolean', + 'updated_by' => 'integer', ]; /** @@ -101,12 +100,12 @@ class MissingScoreOverride extends BaseModel ->where('school_year', $schoolYear) ->where('item_type', $itemType); - if (!empty($studentIds)) { + if (! empty($studentIds)) { $q->whereIn('student_id', array_map('intval', $studentIds)); } if ($itemIndexes !== null) { - if (!empty($itemIndexes)) { + if (! empty($itemIndexes)) { $q->whereIn('item_index', array_map('intval', $itemIndexes)); } else { // legacy behavior: if itemIndexes is provided but empty, do nothing. @@ -128,25 +127,27 @@ class MissingScoreOverride extends BaseModel foreach ($checkedItems as $item) { $sid = (int) ($item['student_id'] ?? 0); - if ($sid <= 0) continue; + if ($sid <= 0) { + continue; + } $rows[] = [ - 'student_id' => $sid, + 'student_id' => $sid, 'class_section_id' => $classSectionId, - 'semester' => $semester, - 'school_year' => $schoolYear, - 'item_type' => $itemType, - 'item_index' => array_key_exists('item_index', $item) ? $item['item_index'] : null, - 'is_allowed' => 1, - 'updated_by' => $updatedBy, - 'created_at' => $now, - 'updated_at' => $now, + 'semester' => $semester, + 'school_year' => $schoolYear, + 'item_type' => $itemType, + 'item_index' => array_key_exists('item_index', $item) ? $item['item_index'] : null, + 'is_allowed' => 1, + 'updated_by' => $updatedBy, + 'created_at' => $now, + 'updated_at' => $now, ]; } - if (!empty($rows)) { + if (! empty($rows)) { DB::table((new static)->getTable())->insert($rows); } }); } -} \ No newline at end of file +} diff --git a/app/Models/NavItem.php b/app/Models/NavItem.php index deb66f32..d425fc15 100644 --- a/app/Models/NavItem.php +++ b/app/Models/NavItem.php @@ -2,7 +2,6 @@ namespace App\Models; -use App\Models\BaseModel; use Illuminate\Database\Eloquent\SoftDeletes; class NavItem extends BaseModel @@ -11,6 +10,7 @@ class NavItem extends BaseModel // ✅ legacy: timestamps + soft deletes use SoftDeletes; + public $timestamps = true; protected $fillable = [ @@ -25,8 +25,8 @@ class NavItem extends BaseModel protected $casts = [ 'menu_parent_id' => 'integer', - 'sort_order' => 'integer', - 'is_enabled' => 'boolean', + 'sort_order' => 'integer', + 'is_enabled' => 'boolean', ]; /* Optional relationships */ @@ -55,4 +55,4 @@ class NavItem extends BaseModel ->get() ->toArray(); } -} \ No newline at end of file +} diff --git a/app/Models/Notification.php b/app/Models/Notification.php index 94b895b9..056855ef 100644 --- a/app/Models/Notification.php +++ b/app/Models/Notification.php @@ -2,7 +2,6 @@ namespace App\Models; -use App\Models\BaseModel; use Illuminate\Database\Eloquent\Casts\Attribute; use Illuminate\Database\Eloquent\SoftDeletes; @@ -12,6 +11,7 @@ class Notification extends BaseModel // ✅ legacy: soft deletes + timestamps use SoftDeletes; + public $timestamps = true; protected $fillable = [ @@ -73,10 +73,10 @@ class Notification extends BaseModel ->where('scheduled_at', '<=', now()) ->where(function ($w) { $w->whereNull('expires_at') - ->orWhere('expires_at', '>', now()); + ->orWhere('expires_at', '>', now()); }); - if (!empty($targetGroup)) { + if (! empty($targetGroup)) { $q->where('target_group', $targetGroup); } @@ -114,7 +114,10 @@ class Notification extends BaseModel public static function restoreNotification(int $id): bool { $row = static::withTrashed()->find($id); - if (!$row) return false; + if (! $row) { + return false; + } + return (bool) $row->restore(); } @@ -129,4 +132,4 @@ class Notification extends BaseModel ->where('expires_at', '<', now()) ->delete(); // soft delete } -} \ No newline at end of file +} diff --git a/app/Models/ParentAttendanceReport.php b/app/Models/ParentAttendanceReport.php index d559d052..d4ae0341 100644 --- a/app/Models/ParentAttendanceReport.php +++ b/app/Models/ParentAttendanceReport.php @@ -2,7 +2,6 @@ namespace App\Models; -use App\Models\BaseModel; use Illuminate\Support\Facades\DB; class ParentAttendanceReport extends BaseModel @@ -27,10 +26,10 @@ class ParentAttendanceReport extends BaseModel ]; protected $casts = [ - 'parent_id' => 'integer', - 'student_id' => 'integer', - 'class_section_id'=> 'integer', - 'report_date' => 'date', + 'parent_id' => 'integer', + 'student_id' => 'integer', + 'class_section_id' => 'integer', + 'report_date' => 'date', ]; /* Optional relationships */ @@ -68,10 +67,10 @@ class ParentAttendanceReport extends BaseModel $q->where('par.parent_id', $parentId); } - if (!empty($startDate)) { + if (! empty($startDate)) { $q->where('par.report_date', '>=', $startDate); } - if (!empty($endDate)) { + if (! empty($endDate)) { $q->where('par.report_date', '<=', $endDate); } if (is_string($schoolYear) && $schoolYear !== '') { @@ -82,12 +81,12 @@ class ParentAttendanceReport extends BaseModel } $q->leftJoin('students as s', 's.id', '=', 'par.student_id') - ->leftJoin('classSection as cs', 'cs.class_section_id', '=', 'par.class_section_id') - ->select('par.*', 's.firstname', 's.lastname', 'cs.class_section_name') - ->orderBy('par.report_date', 'asc') - ->orderBy('par.class_section_id', 'asc') - ->orderBy('par.student_id', 'asc'); + ->leftJoin('classSection as cs', 'cs.class_section_id', '=', 'par.class_section_id') + ->select('par.*', 's.firstname', 's.lastname', 'cs.class_section_name') + ->orderBy('par.report_date', 'asc') + ->orderBy('par.class_section_id', 'asc') + ->orderBy('par.student_id', 'asc'); return $q->get()->map(fn ($r) => (array) $r)->all(); } -} \ No newline at end of file +} diff --git a/app/Models/ParentMeetingSchedule.php b/app/Models/ParentMeetingSchedule.php index b0d68924..6213d9e8 100644 --- a/app/Models/ParentMeetingSchedule.php +++ b/app/Models/ParentMeetingSchedule.php @@ -2,8 +2,6 @@ namespace App\Models; -use App\Models\BaseModel; - class ParentMeetingSchedule extends BaseModel { protected $table = 'parent_meeting_schedules'; @@ -27,14 +25,14 @@ class ParentMeetingSchedule extends BaseModel ]; protected $casts = [ - 'student_id' => 'integer', + 'student_id' => 'integer', 'parent_user_id' => 'integer', - 'created_by' => 'integer', + 'created_by' => 'integer', // if these are DATE/TIME columns: - 'date' => 'date', + 'date' => 'date', // time is typically stored as TIME or string; keep as string unless DATETIME - 'time' => 'string', + 'time' => 'string', ]; /* Optional relationships */ @@ -52,4 +50,4 @@ class ParentMeetingSchedule extends BaseModel { return $this->belongsTo(User::class, 'created_by'); } -} \ No newline at end of file +} diff --git a/app/Models/ParentModel.php b/app/Models/ParentModel.php index 1313ffad..dbaba3fc 100644 --- a/app/Models/ParentModel.php +++ b/app/Models/ParentModel.php @@ -2,8 +2,6 @@ namespace App\Models; -use App\Models\BaseModel; - class ParentModel extends BaseModel { protected $table = 'parents'; @@ -31,4 +29,4 @@ class ParentModel extends BaseModel { return $this->belongsTo(User::class, 'firstparent_id'); } -} \ No newline at end of file +} diff --git a/app/Models/ParentNotification.php b/app/Models/ParentNotification.php index d09c6b94..2a41cc83 100644 --- a/app/Models/ParentNotification.php +++ b/app/Models/ParentNotification.php @@ -2,8 +2,6 @@ namespace App\Models; -use App\Models\BaseModel; - class ParentNotification extends BaseModel { protected $table = 'parent_notifications'; @@ -25,7 +23,7 @@ class ParentNotification extends BaseModel ]; protected $casts = [ - 'student_id' => 'integer', + 'student_id' => 'integer', // incident_date is stored as Y-m-d in legacy usage 'incident_date' => 'date', ]; @@ -52,7 +50,7 @@ class ParentNotification extends BaseModel ->whereDate('incident_date', $incidentYmd) ->where('channel', $channel); - if (!empty($to)) { + if (! empty($to)) { $q->where('to_address', $to); } @@ -60,4 +58,4 @@ class ParentNotification extends BaseModel return $row && (($row->status ?? '') === 'sent'); } -} \ No newline at end of file +} diff --git a/app/Models/Participation.php b/app/Models/Participation.php index 4abe7d62..241d278f 100644 --- a/app/Models/Participation.php +++ b/app/Models/Participation.php @@ -2,8 +2,6 @@ namespace App\Models; -use App\Models\BaseModel; - class Participation extends BaseModel { protected $table = 'participation'; @@ -33,19 +31,19 @@ class Participation extends BaseModel ]; protected $casts = [ - 'student_id' => 'integer', - 'school_id' => 'integer', + 'student_id' => 'integer', + 'school_id' => 'integer', 'class_section_id' => 'integer', - 'updated_by' => 'integer', + 'updated_by' => 'integer', // score is numeric; change to 'integer' if it is whole number - 'score' => 'decimal:2', - 'max_points' => 'decimal:2', - 'locked_by' => 'integer', - 'locked_at' => 'datetime', + 'score' => 'decimal:2', + 'max_points' => 'decimal:2', + 'locked_by' => 'integer', + 'locked_at' => 'datetime', - 'created_at' => 'datetime', - 'updated_at' => 'datetime', + 'created_at' => 'datetime', + 'updated_at' => 'datetime', ]; /* Optional relationships */ @@ -80,15 +78,25 @@ class Participation extends BaseModel } $row = $q->first(); - if (!$row) return null; + if (! $row) { + return null; + } $score = $row->score; - if ($score === null) return null; - if (is_string($score) && trim($score) === '') return null; - if ($score === '') return null; - if (!is_numeric($score)) return null; + if ($score === null) { + return null; + } + if (is_string($score) && trim($score) === '') { + return null; + } + if ($score === '') { + return null; + } + if (! is_numeric($score)) { + return null; + } return (float) $score; } -} \ No newline at end of file +} diff --git a/app/Models/PasswordReset.php b/app/Models/PasswordReset.php index 7857f199..ab6f1330 100644 --- a/app/Models/PasswordReset.php +++ b/app/Models/PasswordReset.php @@ -2,8 +2,6 @@ namespace App\Models; -use App\Models\BaseModel; - class PasswordReset extends BaseModel { protected $table = 'password_resets'; @@ -23,6 +21,7 @@ class PasswordReset extends BaseModel public $timestamps = true; const CREATED_AT = 'created_at'; + const UPDATED_AT = 'expires_at'; protected $fillable = [ @@ -45,4 +44,4 @@ class PasswordReset extends BaseModel // protected $primaryKey = 'id'; // public $incrementing = true; // protected $keyType = 'int'; -} \ No newline at end of file +} diff --git a/app/Models/PasswordResetRequest.php b/app/Models/PasswordResetRequest.php index f424b1a1..b136b6f9 100644 --- a/app/Models/PasswordResetRequest.php +++ b/app/Models/PasswordResetRequest.php @@ -2,8 +2,6 @@ namespace App\Models; -use App\Models\BaseModel; - class PasswordResetRequest extends BaseModel { protected $table = 'password_reset_requests'; @@ -19,4 +17,4 @@ class PasswordResetRequest extends BaseModel protected $casts = [ 'requested_at' => 'datetime', ]; -} \ No newline at end of file +} diff --git a/app/Models/PayPalPayment.php b/app/Models/PayPalPayment.php index 25586958..1c7e56c0 100644 --- a/app/Models/PayPalPayment.php +++ b/app/Models/PayPalPayment.php @@ -2,8 +2,6 @@ namespace App\Models; -use App\Models\BaseModel; - class PayPalPayment extends BaseModel { protected $table = 'paypal_payments'; @@ -13,6 +11,7 @@ class PayPalPayment extends BaseModel * In Laravel: enable timestamps but disable UPDATED_AT. */ public $timestamps = true; + const UPDATED_AT = null; protected $fillable = [ @@ -38,10 +37,10 @@ class PayPalPayment extends BaseModel protected $casts = [ 'parent_school_id' => 'integer', - 'amount' => 'decimal:2', - 'paypal_fee' => 'decimal:2', - 'net_amount' => 'decimal:2', - 'synced' => 'boolean', - 'sync_attempts' => 'integer', + 'amount' => 'decimal:2', + 'paypal_fee' => 'decimal:2', + 'net_amount' => 'decimal:2', + 'synced' => 'boolean', + 'sync_attempts' => 'integer', ]; -} \ No newline at end of file +} diff --git a/app/Models/Payment.php b/app/Models/Payment.php index e57892a2..a06bb29b 100644 --- a/app/Models/Payment.php +++ b/app/Models/Payment.php @@ -2,7 +2,6 @@ namespace App\Models; -use App\Models\BaseModel; use Illuminate\Support\Facades\DB; class Payment extends BaseModel @@ -34,16 +33,16 @@ class Payment extends BaseModel ]; protected $casts = [ - 'parent_id' => 'integer', - 'invoice_id' => 'integer', - 'number_of_installments' => 'integer', - 'updated_by' => 'integer', + 'parent_id' => 'integer', + 'invoice_id' => 'integer', + 'number_of_installments' => 'integer', + 'updated_by' => 'integer', - 'total_amount' => 'decimal:2', - 'paid_amount' => 'decimal:2', - 'balance' => 'decimal:2', + 'total_amount' => 'decimal:2', + 'paid_amount' => 'decimal:2', + 'balance' => 'decimal:2', - 'payment_date' => 'datetime', + 'payment_date' => 'datetime', ]; /* Optional relationships */ @@ -84,18 +83,20 @@ class Payment extends BaseModel { /** @var self|null $payment */ $payment = static::query()->find($paymentId); - if (!$payment) return false; + if (! $payment) { + return false; + } - $newBalance = (float)$payment->balance - (float)$amountPaid; + $newBalance = (float) $payment->balance - (float) $amountPaid; // keep precision stable - $paidAmount = (float)$payment->paid_amount + (float)$amountPaid; + $paidAmount = (float) $payment->paid_amount + (float) $amountPaid; $affected = static::query() ->whereKey($paymentId) ->update([ 'paid_amount' => $paidAmount, - 'balance' => $newBalance, + 'balance' => $newBalance, ]); return $affected > 0; @@ -116,21 +117,21 @@ class Payment extends BaseModel public static function generateNewTransactionId(): string { $currentYear = date('Y'); - $prefix = $currentYear . '-'; + $prefix = $currentYear.'-'; $latest = static::query() - ->where('transaction_id', 'like', $prefix . '%') + ->where('transaction_id', 'like', $prefix.'%') ->orderByDesc('transaction_id') ->first(); - if ($latest && !empty($latest->transaction_id)) { - $lastNumber = (int) str_replace($prefix, '', (string)$latest->transaction_id); + if ($latest && ! empty($latest->transaction_id)) { + $lastNumber = (int) str_replace($prefix, '', (string) $latest->transaction_id); $newNumber = $lastNumber + 1; } else { $newNumber = 1; } - return $prefix . str_pad((string)$newNumber, 6, '0', STR_PAD_LEFT); + return $prefix.str_pad((string) $newNumber, 6, '0', STR_PAD_LEFT); } /** @@ -139,11 +140,13 @@ class Payment extends BaseModel */ public static function getPaymentsByInvoice($invoiceIds): array { - if (!is_array($invoiceIds)) { + if (! is_array($invoiceIds)) { $invoiceIds = [$invoiceIds]; } $invoiceIds = array_values(array_unique(array_map('intval', array_filter($invoiceIds)))); - if (empty($invoiceIds)) return []; + if (empty($invoiceIds)) { + return []; + } // Subquery: latest payment_date per invoice (Full/Partial only) $latestSub = DB::table('payments') @@ -155,7 +158,7 @@ class Payment extends BaseModel $rows = DB::table('payments as p') ->joinSub($latestSub, 'latest', function ($join) { $join->on('latest.invoice_id', '=', 'p.invoice_id') - ->on('latest.max_date', '=', 'p.payment_date'); + ->on('latest.max_date', '=', 'p.payment_date'); }) ->whereIn('p.invoice_id', $invoiceIds) ->whereIn('p.status', ['Full', 'Paid', 'Partially Paid']) @@ -169,4 +172,4 @@ class Payment extends BaseModel return $rows->map(fn ($r) => (array) $r)->all(); } -} \ No newline at end of file +} diff --git a/app/Models/PaymentCarryforwardAllocation.php b/app/Models/PaymentCarryforwardAllocation.php index 0c2118f8..dd01f8cb 100644 --- a/app/Models/PaymentCarryforwardAllocation.php +++ b/app/Models/PaymentCarryforwardAllocation.php @@ -5,5 +5,6 @@ namespace App\Models; class PaymentCarryforwardAllocation extends BaseModel { protected $guarded = []; + protected $table = 'payment_carryforward_allocations'; } diff --git a/app/Models/PaymentError.php b/app/Models/PaymentError.php index 753b7a34..ed41bbd3 100644 --- a/app/Models/PaymentError.php +++ b/app/Models/PaymentError.php @@ -2,8 +2,6 @@ namespace App\Models; -use App\Models\BaseModel; - class PaymentError extends BaseModel { protected $table = 'payment_error'; @@ -26,12 +24,12 @@ class PaymentError extends BaseModel ]; protected $casts = [ - 'payment_id' => 'integer', - 'invoice_id' => 'integer', - 'parent_id' => 'integer', - 'wrong_paid_amount' => 'decimal:2', // adjust precision if needed - 'logged_by' => 'integer', - 'logged_at' => 'datetime', + 'payment_id' => 'integer', + 'invoice_id' => 'integer', + 'parent_id' => 'integer', + 'wrong_paid_amount' => 'decimal:2', // adjust precision if needed + 'logged_by' => 'integer', + 'logged_at' => 'datetime', ]; /* Optional relationships */ @@ -49,4 +47,4 @@ class PaymentError extends BaseModel { return $this->belongsTo(User::class, 'logged_by'); } -} \ No newline at end of file +} diff --git a/app/Models/PaymentInstallmentAllocation.php b/app/Models/PaymentInstallmentAllocation.php index 2ea9896c..921913eb 100644 --- a/app/Models/PaymentInstallmentAllocation.php +++ b/app/Models/PaymentInstallmentAllocation.php @@ -5,5 +5,6 @@ namespace App\Models; class PaymentInstallmentAllocation extends BaseModel { protected $guarded = []; + protected $table = 'payment_installment_allocations'; } diff --git a/app/Models/PaymentNotificationLog.php b/app/Models/PaymentNotificationLog.php index 128d0738..c8781282 100644 --- a/app/Models/PaymentNotificationLog.php +++ b/app/Models/PaymentNotificationLog.php @@ -2,8 +2,6 @@ namespace App\Models; -use App\Models\BaseModel; - class PaymentNotificationLog extends BaseModel { protected $table = 'payment_notification_logs'; @@ -31,14 +29,14 @@ class PaymentNotificationLog extends BaseModel ]; protected $casts = [ - 'parent_id' => 'integer', - 'invoice_id' => 'integer', - 'period_year' => 'integer', - 'period_month' => 'integer', - 'head_fa_notified' => 'boolean', - 'balance_snapshot' => 'decimal:2', // adjust precision if needed - 'created_at' => 'datetime', - 'sent_at' => 'datetime', + 'parent_id' => 'integer', + 'invoice_id' => 'integer', + 'period_year' => 'integer', + 'period_month' => 'integer', + 'head_fa_notified' => 'boolean', + 'balance_snapshot' => 'decimal:2', // adjust precision if needed + 'created_at' => 'datetime', + 'sent_at' => 'datetime', ]; /* Optional relationships */ @@ -73,16 +71,16 @@ class PaymentNotificationLog extends BaseModel { $q = static::query()->orderByDesc('sent_at'); - if (!empty($from)) { + if (! empty($from)) { $q->where('sent_at', '>=', $from); } - if (!empty($to)) { + if (! empty($to)) { $q->where('sent_at', '<=', $to); } - if (!empty($type)) { + if (! empty($type)) { $q->where('type', $type); } return $q->get()->toArray(); } -} \ No newline at end of file +} diff --git a/app/Models/PaymentTransaction.php b/app/Models/PaymentTransaction.php index 506323b2..a765d81e 100644 --- a/app/Models/PaymentTransaction.php +++ b/app/Models/PaymentTransaction.php @@ -2,9 +2,6 @@ namespace App\Models; -use App\Models\BaseModel; -use Illuminate\Support\Facades\DB; - class PaymentTransaction extends BaseModel { protected $table = 'payment_transactions'; @@ -29,11 +26,11 @@ class PaymentTransaction extends BaseModel ]; protected $casts = [ - 'payment_id' => 'integer', - 'amount' => 'decimal:2', // adjust precision if needed - 'transaction_fee' => 'decimal:2', // adjust precision if needed - 'transaction_date' => 'datetime', - 'is_full_payment' => 'boolean', + 'payment_id' => 'integer', + 'amount' => 'decimal:2', // adjust precision if needed + 'transaction_fee' => 'decimal:2', // adjust precision if needed + 'transaction_date' => 'datetime', + 'is_full_payment' => 'boolean', ]; /* Optional relationship */ diff --git a/app/Models/PaypalTransaction.php b/app/Models/PaypalTransaction.php index 1e4b2138..375f6de5 100644 --- a/app/Models/PaypalTransaction.php +++ b/app/Models/PaypalTransaction.php @@ -2,8 +2,6 @@ namespace App\Models; -use App\Models\BaseModel; - class PaypalTransaction extends BaseModel { protected $table = 'paypal_transactions'; @@ -29,11 +27,11 @@ class PaypalTransaction extends BaseModel ]; protected $casts = [ - 'payment_id' => 'integer', - 'amount' => 'decimal:2', // adjust precision if needed - 'transaction_fee' => 'decimal:2', // adjust precision if needed + 'payment_id' => 'integer', + 'amount' => 'decimal:2', // adjust precision if needed + 'transaction_fee' => 'decimal:2', // adjust precision if needed // raw_data is often JSON; if it's JSON in DB, cast to array - 'raw_data' => 'array', + 'raw_data' => 'array', ]; /* Optional relationship */ @@ -41,4 +39,4 @@ class PaypalTransaction extends BaseModel { return $this->belongsTo(Payment::class, 'payment_id'); } -} \ No newline at end of file +} diff --git a/app/Models/Permission.php b/app/Models/Permission.php index 51c6ff03..c92367ea 100644 --- a/app/Models/Permission.php +++ b/app/Models/Permission.php @@ -2,7 +2,6 @@ namespace App\Models; -use App\Models\BaseModel; use Illuminate\Support\Facades\DB; use Illuminate\Support\Facades\Log; @@ -32,7 +31,7 @@ class Permission extends BaseModel try { return static::query()->get(); } catch (\Throwable $e) { - Log::error('Error fetching permissions: ' . $e->getMessage()); + Log::error('Error fetching permissions: '.$e->getMessage()); throw $e; } } @@ -53,12 +52,12 @@ class Permission extends BaseModel ->map(fn ($r) => (array) $r) ->all(); - Log::info('Role permissions fetched: ' . print_r($rows, true)); + Log::info('Role permissions fetched: '.print_r($rows, true)); return $rows; } catch (\Throwable $e) { - Log::error("Error fetching role permissions for role ID {$roleId}: " . $e->getMessage()); + Log::error("Error fetching role permissions for role ID {$roleId}: ".$e->getMessage()); throw $e; } } -} \ No newline at end of file +} diff --git a/app/Models/PlacementBatch.php b/app/Models/PlacementBatch.php index 1cdc517c..1fd0855c 100644 --- a/app/Models/PlacementBatch.php +++ b/app/Models/PlacementBatch.php @@ -2,8 +2,6 @@ namespace App\Models; -use App\Models\BaseModel; - class PlacementBatch extends BaseModel { protected $table = 'placement_batches'; @@ -35,4 +33,4 @@ class PlacementBatch extends BaseModel { return $this->belongsTo(User::class, 'updated_by'); } -} \ No newline at end of file +} diff --git a/app/Models/PlacementLevel.php b/app/Models/PlacementLevel.php index 17155cec..a1b251bd 100644 --- a/app/Models/PlacementLevel.php +++ b/app/Models/PlacementLevel.php @@ -2,8 +2,6 @@ namespace App\Models; -use App\Models\BaseModel; - class PlacementLevel extends BaseModel { protected $table = 'placement_levels'; @@ -42,4 +40,4 @@ class PlacementLevel extends BaseModel { return $this->belongsTo(User::class, 'updated_by'); } -} \ No newline at end of file +} diff --git a/app/Models/PlacementScore.php b/app/Models/PlacementScore.php index 21f41b3f..0cb61c10 100644 --- a/app/Models/PlacementScore.php +++ b/app/Models/PlacementScore.php @@ -2,8 +2,6 @@ namespace App\Models; -use App\Models\BaseModel; - class PlacementScore extends BaseModel { protected $table = 'placement_scores'; @@ -22,9 +20,9 @@ class PlacementScore extends BaseModel ]; protected $casts = [ - 'batch_id' => 'integer', + 'batch_id' => 'integer', 'student_id' => 'integer', - 'score' => 'decimal:2', // change to 'integer' if score is whole number + 'score' => 'decimal:2', // change to 'integer' if score is whole number 'created_by' => 'integer', 'updated_by' => 'integer', ]; @@ -49,4 +47,4 @@ class PlacementScore extends BaseModel { return $this->belongsTo(User::class, 'updated_by'); } -} \ No newline at end of file +} diff --git a/app/Models/Preferences.php b/app/Models/Preferences.php index 4b202b82..e0434c00 100644 --- a/app/Models/Preferences.php +++ b/app/Models/Preferences.php @@ -2,8 +2,6 @@ namespace App\Models; -use App\Models\BaseModel; - class Preferences extends BaseModel { protected $table = 'user_preferences'; @@ -34,15 +32,15 @@ class Preferences extends BaseModel ]; protected $casts = [ - 'user_id' => 'integer', + 'user_id' => 'integer', 'receive_email_notifications' => 'boolean', - 'receive_sms_notifications' => 'boolean', - 'receive_push_notifications' => 'boolean', - 'daily_summary_email' => 'boolean', - 'privacy_mode' => 'boolean', - 'marketing_emails' => 'boolean', - 'account_activity_alerts' => 'boolean', + 'receive_sms_notifications' => 'boolean', + 'receive_push_notifications' => 'boolean', + 'daily_summary_email' => 'boolean', + 'privacy_mode' => 'boolean', + 'marketing_emails' => 'boolean', + 'account_activity_alerts' => 'boolean', ]; /* Optional relationship */ diff --git a/app/Models/PrintRequest.php b/app/Models/PrintRequest.php index ef860363..afddbdca 100644 --- a/app/Models/PrintRequest.php +++ b/app/Models/PrintRequest.php @@ -2,8 +2,6 @@ namespace App\Models; -use App\Models\BaseModel; - class PrintRequest extends BaseModel { protected $table = 'print_requests'; @@ -24,11 +22,11 @@ class PrintRequest extends BaseModel ]; protected $casts = [ - 'teacher_id' => 'integer', - 'admin_id' => 'integer', - 'class_id' => 'integer', - 'num_copies' => 'integer', - 'required_by' => 'datetime', // if stored as DATETIME + 'teacher_id' => 'integer', + 'admin_id' => 'integer', + 'class_id' => 'integer', + 'num_copies' => 'integer', + 'required_by' => 'datetime', // if stored as DATETIME ]; /* Optional relationships */ @@ -46,4 +44,4 @@ class PrintRequest extends BaseModel { return $this->belongsTo(SchoolClass::class, 'class_id'); } -} \ No newline at end of file +} diff --git a/app/Models/Project.php b/app/Models/Project.php index 7041a6bb..c0e57a25 100644 --- a/app/Models/Project.php +++ b/app/Models/Project.php @@ -3,7 +3,6 @@ namespace App\Models; use Illuminate\Database\Eloquent\Builder; -use App\Models\BaseModel; use Illuminate\Database\Eloquent\Relations\BelongsTo; class Project extends BaseModel @@ -45,17 +44,17 @@ class Project extends BaseModel * Helpful casts (adjust types if your DB columns differ). */ protected $casts = [ - 'student_id' => 'integer', - 'school_id' => 'integer', - 'class_section_id' => 'integer', - 'updated_by' => 'integer', - 'project_index' => 'integer', - 'score' => 'decimal:2', - 'max_points' => 'decimal:2', - 'locked_by' => 'integer', - 'locked_at' => 'datetime', // or 'float' if you prefer - 'created_at' => 'datetime', - 'updated_at' => 'datetime', + 'student_id' => 'integer', + 'school_id' => 'integer', + 'class_section_id' => 'integer', + 'updated_by' => 'integer', + 'project_index' => 'integer', + 'score' => 'decimal:2', + 'max_points' => 'decimal:2', + 'locked_by' => 'integer', + 'locked_at' => 'datetime', // or 'float' if you prefer + 'created_at' => 'datetime', + 'updated_at' => 'datetime', ]; /* ============================================================ @@ -91,7 +90,7 @@ class Project extends BaseModel public function scopeForTerm(Builder $q, string $semester, string $schoolYear): Builder { return $q->where('semester', $semester) - ->where('school_year', $schoolYear); + ->where('school_year', $schoolYear); } public function scopeForClassSection(Builder $q, ?int $classSectionId): Builder @@ -161,4 +160,4 @@ class Project extends BaseModel // Safe default if you're unsure. Set to false if score is numeric. return true; } -} \ No newline at end of file +} diff --git a/app/Models/PromotionAuditLog.php b/app/Models/PromotionAuditLog.php index 7d6b1423..f16d4bfa 100644 --- a/app/Models/PromotionAuditLog.php +++ b/app/Models/PromotionAuditLog.php @@ -16,16 +16,27 @@ class PromotionAuditLog extends BaseModel public $timestamps = true; public const ACTION_RECORD_CREATED = 'record_created'; + public const ACTION_STATUS_CHANGED = 'status_changed'; + public const ACTION_ELIGIBILITY_EVALUATED = 'eligibility_evaluated'; + public const ACTION_PARENT_NOTIFIED = 'parent_notified'; + public const ACTION_ENROLLMENT_STARTED = 'enrollment_started'; + public const ACTION_ENROLLMENT_STEP_COMPLETED = 'enrollment_step_completed'; + public const ACTION_ENROLLMENT_COMPLETED = 'enrollment_completed'; + public const ACTION_PROMOTION_FINALIZED = 'promotion_finalized'; + public const ACTION_DEADLINE_SET = 'deadline_set'; + public const ACTION_REMINDER_SENT = 'reminder_sent'; + public const ACTION_DEADLINE_EXPIRED = 'deadline_expired'; + public const ACTION_MANUAL_OVERRIDE = 'manual_override'; protected $fillable = [ diff --git a/app/Models/PromotionQueue.php b/app/Models/PromotionQueue.php index 578799ee..c913d34e 100644 --- a/app/Models/PromotionQueue.php +++ b/app/Models/PromotionQueue.php @@ -3,7 +3,6 @@ namespace App\Models; use Illuminate\Database\Eloquent\Builder; -use App\Models\BaseModel; use Illuminate\Database\Eloquent\Relations\BelongsTo; class PromotionQueue extends BaseModel @@ -22,16 +21,17 @@ class PromotionQueue extends BaseModel 'updated_at', 'updated_by', ]; + public $timestamps = true; protected $casts = [ - 'student_id' => 'integer', - 'from_class_section_id' => 'integer', - 'to_class_id' => 'integer', - 'to_class_section_id' => 'integer', - 'updated_by' => 'integer', - 'created_at' => 'datetime', - 'updated_at' => 'datetime', + 'student_id' => 'integer', + 'from_class_section_id' => 'integer', + 'to_class_id' => 'integer', + 'to_class_section_id' => 'integer', + 'updated_by' => 'integer', + 'created_at' => 'datetime', + 'updated_at' => 'datetime', ]; /* ============================================================ @@ -92,9 +92,9 @@ class PromotionQueue extends BaseModel public static function upsertQueue(array $data): ?self { $studentId = isset($data['student_id']) ? (int) $data['student_id'] : null; - $yearTo = isset($data['school_year_to']) ? (string) $data['school_year_to'] : null; + $yearTo = isset($data['school_year_to']) ? (string) $data['school_year_to'] : null; - if (!$studentId || !$yearTo) { + if (! $studentId || ! $yearTo) { return null; } @@ -114,4 +114,4 @@ class PromotionQueue extends BaseModel { return (bool) static::upsertQueue($data); } -} \ No newline at end of file +} diff --git a/app/Models/PromotionReminderLog.php b/app/Models/PromotionReminderLog.php index 88973cf1..cbfd0c58 100644 --- a/app/Models/PromotionReminderLog.php +++ b/app/Models/PromotionReminderLog.php @@ -16,9 +16,13 @@ class PromotionReminderLog extends BaseModel public $timestamps = true; public const TYPE_FIRST = 'first'; + public const TYPE_HALFWAY = 'halfway'; + public const TYPE_FINAL = 'final'; + public const TYPE_EXPIRATION = 'expiration'; + public const TYPE_MANUAL = 'manual'; public const ALLOWED_TYPES = [ diff --git a/app/Models/PurchaseOrder.php b/app/Models/PurchaseOrder.php index fbca9813..ef898059 100644 --- a/app/Models/PurchaseOrder.php +++ b/app/Models/PurchaseOrder.php @@ -3,10 +3,9 @@ namespace App\Models; use Illuminate\Database\Eloquent\Builder; -use App\Models\BaseModel; -use Illuminate\Database\Eloquent\SoftDeletes; use Illuminate\Database\Eloquent\Relations\BelongsTo; use Illuminate\Database\Eloquent\Relations\HasMany; +use Illuminate\Database\Eloquent\SoftDeletes; class PurchaseOrder extends BaseModel { @@ -25,18 +24,19 @@ class PurchaseOrder extends BaseModel 'total', 'notes', ]; + public $timestamps = true; protected $casts = [ - 'supplier_id' => 'integer', - 'order_date' => 'date', // or 'datetime' if your DB stores time too - 'expected_date' => 'date', - 'subtotal' => 'decimal:2', - 'tax' => 'decimal:2', - 'total' => 'decimal:2', - 'created_at' => 'datetime', - 'updated_at' => 'datetime', - 'deleted_at' => 'datetime', + 'supplier_id' => 'integer', + 'order_date' => 'date', // or 'datetime' if your DB stores time too + 'expected_date' => 'date', + 'subtotal' => 'decimal:2', + 'tax' => 'decimal:2', + 'total' => 'decimal:2', + 'created_at' => 'datetime', + 'updated_at' => 'datetime', + 'deleted_at' => 'datetime', ]; public function getFillable(): array @@ -49,9 +49,12 @@ class PurchaseOrder extends BaseModel * ============================================================ */ - public const STATUS_DRAFT = 'draft'; - public const STATUS_ORDERED = 'ordered'; + public const STATUS_DRAFT = 'draft'; + + public const STATUS_ORDERED = 'ordered'; + public const STATUS_RECEIVED = 'received'; + public const STATUS_CANCELED = 'canceled'; public static function allowedStatuses(): array @@ -116,8 +119,8 @@ class PurchaseOrder extends BaseModel $total = $subtotal + $tax; $this->subtotal = $subtotal; - $this->tax = $tax; - $this->total = $total; + $this->tax = $tax; + $this->total = $total; return $this; } @@ -125,18 +128,21 @@ class PurchaseOrder extends BaseModel public function markOrdered(): self { $this->status = self::STATUS_ORDERED; + return $this; } public function markReceived(): self { $this->status = self::STATUS_RECEIVED; + return $this; } public function markCanceled(): self { $this->status = self::STATUS_CANCELED; + return $this; } @@ -149,16 +155,16 @@ class PurchaseOrder extends BaseModel { // Mirrors legacy: unique purchase_orders.po_number except current id. return [ - 'po_number' => ['required', 'string', 'max:60', 'unique:purchase_orders,po_number,' . ($id ?? 'NULL') . ',id'], + 'po_number' => ['required', 'string', 'max:60', 'unique:purchase_orders,po_number,'.($id ?? 'NULL').',id'], 'supplier_id' => ['required', 'integer', 'min:1', 'exists:suppliers,id'], - 'status' => ['nullable', 'string', 'in:' . implode(',', self::allowedStatuses())], + 'status' => ['nullable', 'string', 'in:'.implode(',', self::allowedStatuses())], - 'order_date' => ['nullable', 'date'], - 'expected_date' => ['nullable', 'date', 'after_or_equal:order_date'], - 'subtotal' => ['nullable', 'numeric', 'min:0'], - 'tax' => ['nullable', 'numeric', 'min:0'], - 'total' => ['nullable', 'numeric', 'min:0'], - 'notes' => ['nullable', 'string', 'max:5000'], + 'order_date' => ['nullable', 'date'], + 'expected_date' => ['nullable', 'date', 'after_or_equal:order_date'], + 'subtotal' => ['nullable', 'numeric', 'min:0'], + 'tax' => ['nullable', 'numeric', 'min:0'], + 'total' => ['nullable', 'numeric', 'min:0'], + 'notes' => ['nullable', 'string', 'max:5000'], ]; } } diff --git a/app/Models/PurchaseOrderItem.php b/app/Models/PurchaseOrderItem.php index 276a3043..ff9a26e8 100644 --- a/app/Models/PurchaseOrderItem.php +++ b/app/Models/PurchaseOrderItem.php @@ -2,7 +2,6 @@ namespace App\Models; -use App\Models\BaseModel; use Illuminate\Database\Eloquent\Relations\BelongsTo; class PurchaseOrderItem extends BaseModel @@ -18,16 +17,17 @@ class PurchaseOrderItem extends BaseModel 'received_qty', 'unit_cost', ]; + public $timestamps = true; protected $casts = [ 'purchase_order_id' => 'integer', - 'supply_id' => 'integer', - 'quantity' => 'integer', - 'received_qty' => 'integer', - 'unit_cost' => 'decimal:2', // adjust scale to match DB (e.g., decimal:4) - 'created_at' => 'datetime', - 'updated_at' => 'datetime', + 'supply_id' => 'integer', + 'quantity' => 'integer', + 'received_qty' => 'integer', + 'unit_cost' => 'decimal:2', // adjust scale to match DB (e.g., decimal:4) + 'created_at' => 'datetime', + 'updated_at' => 'datetime', ]; public function getFillable(): array @@ -58,16 +58,18 @@ class PurchaseOrderItem extends BaseModel // total ordered cost public function getLineTotalAttribute(): float { - $qty = (int) ($this->quantity ?? 0); + $qty = (int) ($this->quantity ?? 0); $cost = (float) ($this->unit_cost ?? 0); + return $qty * $cost; } // total received cost public function getReceivedLineTotalAttribute(): float { - $qty = (int) ($this->received_qty ?? 0); + $qty = (int) ($this->received_qty ?? 0); $cost = (float) ($this->unit_cost ?? 0); + return $qty * $cost; } @@ -82,11 +84,11 @@ class PurchaseOrderItem extends BaseModel // keep strict by default to match legacy behavior return [ 'purchase_order_id' => [$updating ? 'sometimes' : 'required', 'integer', 'min:1', 'exists:purchase_orders,id'], - 'supply_id' => [$updating ? 'sometimes' : 'required', 'integer', 'min:1', 'exists:supplies,id'], - 'description' => ['nullable', 'string', 'max:1000'], - 'quantity' => [$updating ? 'sometimes' : 'required', 'integer', 'min:1'], - 'received_qty' => ['nullable', 'integer', 'min:0', 'lte:quantity'], - 'unit_cost' => [$updating ? 'sometimes' : 'required', 'numeric', 'min:0'], + 'supply_id' => [$updating ? 'sometimes' : 'required', 'integer', 'min:1', 'exists:supplies,id'], + 'description' => ['nullable', 'string', 'max:1000'], + 'quantity' => [$updating ? 'sometimes' : 'required', 'integer', 'min:1'], + 'received_qty' => ['nullable', 'integer', 'min:0', 'lte:quantity'], + 'unit_cost' => [$updating ? 'sometimes' : 'required', 'numeric', 'min:0'], ]; } } diff --git a/app/Models/Quiz.php b/app/Models/Quiz.php index 6663fd33..c131eb4c 100644 --- a/app/Models/Quiz.php +++ b/app/Models/Quiz.php @@ -2,12 +2,12 @@ namespace App\Models; -use App\Models\BaseModel; use Illuminate\Database\Eloquent\Relations\BelongsTo; class Quiz extends BaseModel { protected $table = 'quiz'; + public $timestamps = false; protected $fillable = [ @@ -30,17 +30,17 @@ class Quiz extends BaseModel ]; protected $casts = [ - 'student_id' => 'integer', - 'school_id' => 'integer', + 'student_id' => 'integer', + 'school_id' => 'integer', 'class_section_id' => 'integer', - 'updated_by' => 'integer', - 'quiz_index' => 'integer', - 'score' => 'decimal:2', - 'max_points' => 'decimal:2', - 'locked_by' => 'integer', - 'locked_at' => 'datetime', - 'created_at' => 'datetime', - 'updated_at' => 'datetime', + 'updated_by' => 'integer', + 'quiz_index' => 'integer', + 'score' => 'decimal:2', + 'max_points' => 'decimal:2', + 'locked_by' => 'integer', + 'locked_at' => 'datetime', + 'created_at' => 'datetime', + 'updated_at' => 'datetime', ]; public function student(): BelongsTo diff --git a/app/Models/Refund.php b/app/Models/Refund.php index 0755eebe..536b5010 100644 --- a/app/Models/Refund.php +++ b/app/Models/Refund.php @@ -3,7 +3,6 @@ namespace App\Models; use Illuminate\Database\Eloquent\Builder; -use App\Models\BaseModel; use Illuminate\Database\Eloquent\Relations\BelongsTo; class Refund extends BaseModel @@ -31,23 +30,24 @@ class Refund extends BaseModel 'check_nbr', 'check_file', ]; + public $timestamps = true; protected $casts = [ - 'parent_id' => 'integer', - 'invoice_id' => 'integer', - 'approved_by' => 'integer', - 'updated_by' => 'integer', + 'parent_id' => 'integer', + 'invoice_id' => 'integer', + 'approved_by' => 'integer', + 'updated_by' => 'integer', - 'refund_amount' => 'decimal:2', + 'refund_amount' => 'decimal:2', 'refund_paid_amount' => 'decimal:2', - 'requested_at' => 'datetime', - 'approved_at' => 'datetime', - 'refunded_at' => 'datetime', + 'requested_at' => 'datetime', + 'approved_at' => 'datetime', + 'refunded_at' => 'datetime', - 'created_at' => 'datetime', - 'updated_at' => 'datetime', + 'created_at' => 'datetime', + 'updated_at' => 'datetime', ]; /* ============================================================ @@ -56,10 +56,15 @@ class Refund extends BaseModel */ public const STATUS_PENDING = 'Pending'; + public const STATUS_APPROVED = 'Approved'; + public const STATUS_PARTIAL = 'Partial'; + public const STATUS_PAID = 'Paid'; + public const STATUS_REJECTED = 'Rejected'; + public const STATUS_CANCELED = 'Canceled'; public static function paidOutStatuses(): array @@ -139,18 +144,18 @@ class Refund extends BaseModel public static function rules(?int $id = null): array { return [ - 'parent_id' => ['required', 'integer', 'min:1', 'exists:parents,id'], // adjust table name - 'school_year' => ['required', 'string', 'max:20'], - 'invoice_id' => ['nullable', 'integer', 'exists:invoices,id'], + 'parent_id' => ['required', 'integer', 'min:1', 'exists:parents,id'], // adjust table name + 'school_year' => ['required', 'string', 'max:20'], + 'invoice_id' => ['nullable', 'integer', 'exists:invoices,id'], - 'refund_amount' => ['required', 'numeric', 'min:0'], + 'refund_amount' => ['required', 'numeric', 'min:0'], 'refund_paid_amount' => ['nullable', 'numeric', 'min:0'], - 'requested_at' => ['nullable', 'date'], - 'approved_at' => ['nullable', 'date', 'after_or_equal:requested_at'], - 'refunded_at' => ['nullable', 'date', 'after_or_equal:approved_at'], + 'requested_at' => ['nullable', 'date'], + 'approved_at' => ['nullable', 'date', 'after_or_equal:requested_at'], + 'refunded_at' => ['nullable', 'date', 'after_or_equal:approved_at'], - 'status' => ['required', 'string', 'in:' . implode(',', [ + 'status' => ['required', 'string', 'in:'.implode(',', [ self::STATUS_PENDING, self::STATUS_APPROVED, self::STATUS_PARTIAL, @@ -159,17 +164,17 @@ class Refund extends BaseModel self::STATUS_CANCELED, ])], - 'reason' => ['nullable', 'string', 'max:2000'], - 'request' => ['nullable', 'string'], - 'note' => ['nullable', 'string', 'max:5000'], - 'semester' => ['nullable', 'string', 'max:20'], + 'reason' => ['nullable', 'string', 'max:2000'], + 'request' => ['nullable', 'string'], + 'note' => ['nullable', 'string', 'max:5000'], + 'semester' => ['nullable', 'string', 'max:20'], - 'approved_by' => ['nullable', 'integer'], - 'updated_by' => ['nullable', 'integer'], + 'approved_by' => ['nullable', 'integer'], + 'updated_by' => ['nullable', 'integer'], 'refund_method' => ['nullable', 'string', 'max:50'], // cash/check/cc/etc. - 'check_nbr' => ['nullable', 'string', 'max:80'], - 'check_file' => ['nullable', 'string', 'max:255'], + 'check_nbr' => ['nullable', 'string', 'max:80'], + 'check_file' => ['nullable', 'string', 'max:255'], ]; } -} \ No newline at end of file +} diff --git a/app/Models/Reimbursement.php b/app/Models/Reimbursement.php index d69e6a5c..14a07d73 100644 --- a/app/Models/Reimbursement.php +++ b/app/Models/Reimbursement.php @@ -3,7 +3,6 @@ namespace App\Models; use Illuminate\Database\Eloquent\Builder; -use App\Models\BaseModel; use Illuminate\Database\Eloquent\Relations\BelongsTo; class Reimbursement extends BaseModel @@ -33,13 +32,13 @@ class Reimbursement extends BaseModel public $timestamps = true; protected $casts = [ - 'amount' => 'decimal:2', - 'expense_id' => 'integer', - 'approved_by' => 'integer', - 'added_by' => 'integer', - 'batch_number' => 'integer', - 'created_at' => 'datetime', - 'updated_at' => 'datetime', + 'amount' => 'decimal:2', + 'expense_id' => 'integer', + 'approved_by' => 'integer', + 'added_by' => 'integer', + 'batch_number' => 'integer', + 'created_at' => 'datetime', + 'updated_at' => 'datetime', ]; /* ============================================================ @@ -47,11 +46,15 @@ class Reimbursement extends BaseModel * ============================================================ */ - public const STATUS_DRAFT = 'draft'; - public const STATUS_PENDING = 'pending'; - public const STATUS_APPROVED = 'approved'; + public const STATUS_DRAFT = 'draft'; + + public const STATUS_PENDING = 'pending'; + + public const STATUS_APPROVED = 'approved'; + public const STATUS_REIMBURSED = 'reimbursed'; - public const STATUS_REJECTED = 'rejected'; + + public const STATUS_REJECTED = 'rejected'; public static function allowedStatuses(): array { @@ -149,22 +152,22 @@ class Reimbursement extends BaseModel public static function rules(bool $updating = false): array { return [ - 'amount' => [$updating ? 'sometimes' : 'required', 'numeric', 'min:0'], - 'reimbursed_to' => ['nullable', 'string', 'max:255'], - 'expense_id' => ['nullable', 'integer', 'exists:expenses,id'], - 'description' => ['nullable', 'string', 'max:5000'], - 'status' => ['nullable', 'string', 'in:' . implode(',', self::allowedStatuses())], - 'receipt_path' => ['nullable', 'string', 'max:255'], + 'amount' => [$updating ? 'sometimes' : 'required', 'numeric', 'min:0'], + 'reimbursed_to' => ['nullable', 'string', 'max:255'], + 'expense_id' => ['nullable', 'integer', 'exists:expenses,id'], + 'description' => ['nullable', 'string', 'max:5000'], + 'status' => ['nullable', 'string', 'in:'.implode(',', self::allowedStatuses())], + 'receipt_path' => ['nullable', 'string', 'max:255'], - 'approved_by' => ['nullable', 'integer'], - 'added_by' => ['nullable', 'integer'], + 'approved_by' => ['nullable', 'integer'], + 'added_by' => ['nullable', 'integer'], - 'school_year' => ['nullable', 'string', 'max:20'], - 'semester' => ['nullable', 'string', 'max:20'], + 'school_year' => ['nullable', 'string', 'max:20'], + 'semester' => ['nullable', 'string', 'max:20'], - 'check_number' => ['nullable', 'string', 'max:80'], + 'check_number' => ['nullable', 'string', 'max:80'], 'reimbursement_method' => ['nullable', 'string', 'max:50'], - 'batch_number' => ['nullable', 'integer', 'min:1'], + 'batch_number' => ['nullable', 'integer', 'min:1'], ]; } -} \ No newline at end of file +} diff --git a/app/Models/ReimbursementBatch.php b/app/Models/ReimbursementBatch.php index 02796a29..8f866a52 100644 --- a/app/Models/ReimbursementBatch.php +++ b/app/Models/ReimbursementBatch.php @@ -3,7 +3,6 @@ namespace App\Models; use Illuminate\Database\Eloquent\Builder; -use App\Models\BaseModel; use Illuminate\Database\Eloquent\Relations\BelongsTo; use Illuminate\Database\Eloquent\Relations\HasMany; @@ -30,11 +29,11 @@ class ReimbursementBatch extends BaseModel ]; protected $casts = [ - 'created_by' => 'integer', - 'closed_by' => 'integer', + 'created_by' => 'integer', + 'closed_by' => 'integer', 'yearly_batch_number' => 'integer', - 'opened_at' => 'datetime', - 'closed_at' => 'datetime', + 'opened_at' => 'datetime', + 'closed_at' => 'datetime', ]; /* ============================================================ @@ -42,9 +41,11 @@ class ReimbursementBatch extends BaseModel * ============================================================ */ - public const STATUS_OPEN = 'open'; + public const STATUS_OPEN = 'open'; + public const STATUS_CLOSED = 'closed'; - public const STATUS_DRAFT = 'draft'; + + public const STATUS_DRAFT = 'draft'; public static function allowedStatuses(): array { @@ -155,16 +156,16 @@ class ReimbursementBatch extends BaseModel public static function rules(?int $id = null): array { return [ - 'title' => ['required', 'string', 'max:255'], - 'status' => ['required', 'string', 'in:' . implode(',', self::allowedStatuses())], - 'created_by' => ['nullable', 'integer'], - 'closed_by' => ['nullable', 'integer'], - 'opened_at' => ['nullable', 'date'], - 'closed_at' => ['nullable', 'date', 'after_or_equal:opened_at'], - 'notes' => ['nullable', 'string', 'max:5000'], - 'school_year' => ['nullable', 'string', 'max:20'], - 'semester' => ['nullable', 'string', 'max:20'], + 'title' => ['required', 'string', 'max:255'], + 'status' => ['required', 'string', 'in:'.implode(',', self::allowedStatuses())], + 'created_by' => ['nullable', 'integer'], + 'closed_by' => ['nullable', 'integer'], + 'opened_at' => ['nullable', 'date'], + 'closed_at' => ['nullable', 'date', 'after_or_equal:opened_at'], + 'notes' => ['nullable', 'string', 'max:5000'], + 'school_year' => ['nullable', 'string', 'max:20'], + 'semester' => ['nullable', 'string', 'max:20'], 'yearly_batch_number' => ['nullable', 'integer', 'min:1'], ]; } -} \ No newline at end of file +} diff --git a/app/Models/ReimbursementBatchAdminFile.php b/app/Models/ReimbursementBatchAdminFile.php index f385d95d..757fe6d2 100644 --- a/app/Models/ReimbursementBatchAdminFile.php +++ b/app/Models/ReimbursementBatchAdminFile.php @@ -2,7 +2,6 @@ namespace App\Models; -use App\Models\BaseModel; use Illuminate\Database\Eloquent\Relations\BelongsTo; class ReimbursementBatchAdminFile extends BaseModel @@ -24,10 +23,10 @@ class ReimbursementBatchAdminFile extends BaseModel ]; protected $casts = [ - 'batch_id' => 'integer', - 'admin_id' => 'integer', - 'uploaded_by' => 'integer', - 'uploaded_at' => 'datetime', + 'batch_id' => 'integer', + 'admin_id' => 'integer', + 'uploaded_by' => 'integer', + 'uploaded_at' => 'datetime', ]; /* ============================================================ @@ -75,12 +74,12 @@ class ReimbursementBatchAdminFile extends BaseModel public static function rules(bool $updating = false): array { return [ - 'batch_id' => [$updating ? 'sometimes' : 'required', 'integer', 'min:1', 'exists:reimbursement_batches,id'], - 'admin_id' => [$updating ? 'sometimes' : 'required', 'integer', 'min:1', 'exists:admins,id'], // adjust table - 'filename' => [$updating ? 'sometimes' : 'required', 'string', 'max:255'], - 'original_filename' => ['nullable', 'string', 'max:255'], - 'uploaded_at' => ['nullable', 'date'], - 'uploaded_by' => ['nullable', 'integer'], + 'batch_id' => [$updating ? 'sometimes' : 'required', 'integer', 'min:1', 'exists:reimbursement_batches,id'], + 'admin_id' => [$updating ? 'sometimes' : 'required', 'integer', 'min:1', 'exists:admins,id'], // adjust table + 'filename' => [$updating ? 'sometimes' : 'required', 'string', 'max:255'], + 'original_filename' => ['nullable', 'string', 'max:255'], + 'uploaded_at' => ['nullable', 'date'], + 'uploaded_by' => ['nullable', 'integer'], ]; } -} \ No newline at end of file +} diff --git a/app/Models/ReimbursementBatchItem.php b/app/Models/ReimbursementBatchItem.php index daff3c56..d10a80b3 100644 --- a/app/Models/ReimbursementBatchItem.php +++ b/app/Models/ReimbursementBatchItem.php @@ -3,7 +3,6 @@ namespace App\Models; use Illuminate\Database\Eloquent\Builder; -use App\Models\BaseModel; use Illuminate\Database\Eloquent\Relations\BelongsTo; class ReimbursementBatchItem extends BaseModel @@ -28,12 +27,12 @@ class ReimbursementBatchItem extends BaseModel ]; protected $casts = [ - 'batch_id' => 'integer', - 'expense_id' => 'integer', + 'batch_id' => 'integer', + 'expense_id' => 'integer', 'reimbursement_id' => 'integer', - 'admin_id' => 'integer', - 'assigned_at' => 'datetime', - 'unassigned_at' => 'datetime', + 'admin_id' => 'integer', + 'assigned_at' => 'datetime', + 'unassigned_at' => 'datetime', ]; /* ============================================================ @@ -85,14 +84,14 @@ class ReimbursementBatchItem extends BaseModel public function scopeAssigned(Builder $q): Builder { return $q->whereNotNull('admin_id') - ->whereNotNull('assigned_at') - ->whereNull('unassigned_at'); + ->whereNotNull('assigned_at') + ->whereNull('unassigned_at'); } public function scopeUnassigned(Builder $q): Builder { return $q->whereNull('admin_id') - ->whereNull('assigned_at'); + ->whereNull('assigned_at'); } /* ============================================================ @@ -137,15 +136,15 @@ class ReimbursementBatchItem extends BaseModel public static function rules(bool $updating = false): array { return [ - 'batch_id' => [$updating ? 'sometimes' : 'required', 'integer', 'min:1', 'exists:reimbursement_batches,id'], - 'expense_id' => ['nullable', 'integer', 'exists:expenses,id'], + 'batch_id' => [$updating ? 'sometimes' : 'required', 'integer', 'min:1', 'exists:reimbursement_batches,id'], + 'expense_id' => ['nullable', 'integer', 'exists:expenses,id'], 'reimbursement_id' => ['nullable', 'integer', 'exists:reimbursements,id'], - 'admin_id' => ['nullable', 'integer'], // add exists rule if you know the table - 'assigned_at' => ['nullable', 'date'], - 'unassigned_at' => ['nullable', 'date', 'after_or_equal:assigned_at'], - 'notes' => ['nullable', 'string', 'max:5000'], - 'school_year' => ['nullable', 'string', 'max:20'], - 'semester' => ['nullable', 'string', 'max:20'], + 'admin_id' => ['nullable', 'integer'], // add exists rule if you know the table + 'assigned_at' => ['nullable', 'date'], + 'unassigned_at' => ['nullable', 'date', 'after_or_equal:assigned_at'], + 'notes' => ['nullable', 'string', 'max:5000'], + 'school_year' => ['nullable', 'string', 'max:20'], + 'semester' => ['nullable', 'string', 'max:20'], ]; } -} \ No newline at end of file +} diff --git a/app/Models/ReportCardAcknowledgement.php b/app/Models/ReportCardAcknowledgement.php index d7f3ad90..0755198c 100644 --- a/app/Models/ReportCardAcknowledgement.php +++ b/app/Models/ReportCardAcknowledgement.php @@ -3,7 +3,6 @@ namespace App\Models; use Illuminate\Database\Eloquent\Builder; -use App\Models\BaseModel; use Illuminate\Database\Eloquent\Relations\BelongsTo; class ReportCardAcknowledgement extends BaseModel @@ -22,15 +21,16 @@ class ReportCardAcknowledgement extends BaseModel 'created_at', 'updated_at', ]; + public $timestamps = true; protected $casts = [ - 'parent_id' => 'integer', - 'student_id' => 'integer', - 'viewed_at' => 'datetime', - 'signed_at' => 'datetime', - 'created_at' => 'datetime', - 'updated_at' => 'datetime', + 'parent_id' => 'integer', + 'student_id' => 'integer', + 'viewed_at' => 'datetime', + 'signed_at' => 'datetime', + 'created_at' => 'datetime', + 'updated_at' => 'datetime', ]; /* ============================================================ @@ -67,7 +67,7 @@ class ReportCardAcknowledgement extends BaseModel public function scopeForTerm(Builder $q, string $schoolYear, string $semester): Builder { return $q->where('school_year', $schoolYear) - ->where('semester', $semester); + ->where('semester', $semester); } public function scopeSigned(Builder $q): Builder @@ -137,15 +137,15 @@ class ReportCardAcknowledgement extends BaseModel public static function rules(bool $updating = false): array { return [ - 'parent_id' => [$updating ? 'sometimes' : 'required', 'integer', 'min:1'], - 'student_id' => [$updating ? 'sometimes' : 'required', 'integer', 'min:1'], - 'school_year' => [$updating ? 'sometimes' : 'required', 'string', 'max:20'], - 'semester' => [$updating ? 'sometimes' : 'required', 'string', 'max:20'], + 'parent_id' => [$updating ? 'sometimes' : 'required', 'integer', 'min:1'], + 'student_id' => [$updating ? 'sometimes' : 'required', 'integer', 'min:1'], + 'school_year' => [$updating ? 'sometimes' : 'required', 'string', 'max:20'], + 'semester' => [$updating ? 'sometimes' : 'required', 'string', 'max:20'], - 'viewed_at' => ['nullable', 'date'], - 'signed_at' => ['nullable', 'date', 'after_or_equal:viewed_at'], - 'signed_name' => ['nullable', 'string', 'max:255'], - 'signer_ip' => ['nullable', 'string', 'max:45'], // IPv4/IPv6 + 'viewed_at' => ['nullable', 'date'], + 'signed_at' => ['nullable', 'date', 'after_or_equal:viewed_at'], + 'signed_name' => ['nullable', 'string', 'max:255'], + 'signer_ip' => ['nullable', 'string', 'max:45'], // IPv4/IPv6 ]; } -} \ No newline at end of file +} diff --git a/app/Models/Role.php b/app/Models/Role.php index 979b0cc3..e129b82e 100644 --- a/app/Models/Role.php +++ b/app/Models/Role.php @@ -3,7 +3,6 @@ namespace App\Models; use Illuminate\Database\Eloquent\Builder; -use App\Models\BaseModel; class Role extends BaseModel { @@ -19,11 +18,12 @@ class Role extends BaseModel 'created_at', 'updated_at', ]; + public $timestamps = true; protected $casts = [ - 'priority' => 'integer', - 'is_active' => 'boolean', + 'priority' => 'integer', + 'is_active' => 'boolean', 'created_at' => 'datetime', 'updated_at' => 'datetime', ]; @@ -52,43 +52,47 @@ class Role extends BaseModel * Find roles by names or slugs (case-insensitive), active only, ordered by priority ASC. */ -// Put these INSIDE the Role model, replacing the current implementations + // Put these INSIDE the Role model, replacing the current implementations -public static function findByNamesOrSlugs(array $keys): array -{ - $keys = array_values(array_filter(array_map('strval', $keys))); - if (empty($keys)) return []; + public static function findByNamesOrSlugs(array $keys): array + { + $keys = array_values(array_filter(array_map('strval', $keys))); + if (empty($keys)) { + return []; + } - $lower = array_map('mb_strtolower', $keys); - $placeholders = implode(',', array_fill(0, count($lower), '?')); + $lower = array_map('mb_strtolower', $keys); + $placeholders = implode(',', array_fill(0, count($lower), '?')); - return static::query() - ->active() - ->where(function (Builder $q) use ($lower, $placeholders) { - $q->whereRaw("LOWER(name) IN ($placeholders)", $lower) - ->orWhereRaw("LOWER(slug) IN ($placeholders)", $lower); - }) - ->orderBy('priority', 'asc') - ->get() - ->all(); -} + return static::query() + ->active() + ->where(function (Builder $q) use ($lower, $placeholders) { + $q->whereRaw("LOWER(name) IN ($placeholders)", $lower) + ->orWhereRaw("LOWER(slug) IN ($placeholders)", $lower); + }) + ->orderBy('priority', 'asc') + ->get() + ->all(); + } -public static function getIdsByNames(array $names): array -{ - $names = array_values(array_filter(array_map('strval', $names))); - if (empty($names)) return []; + public static function getIdsByNames(array $names): array + { + $names = array_values(array_filter(array_map('strval', $names))); + if (empty($names)) { + return []; + } - $lower = array_map('mb_strtolower', $names); - $placeholders = implode(',', array_fill(0, count($lower), '?')); + $lower = array_map('mb_strtolower', $names); + $placeholders = implode(',', array_fill(0, count($lower), '?')); - $ids = static::query() - ->select('id') - ->whereRaw("LOWER(name) IN ($placeholders)", $lower) - ->pluck('id') - ->all(); + $ids = static::query() + ->select('id') + ->whereRaw("LOWER(name) IN ($placeholders)", $lower) + ->pluck('id') + ->all(); - return array_map('intval', $ids); -} + return array_map('intval', $ids); + } /** * Get dashboard_route by name or slug (case-insensitive), active only. @@ -96,7 +100,9 @@ public static function getIdsByNames(array $names): array public static function getRouteByNameOrSlug(string $key): ?string { $key = trim((string) $key); - if ($key === '') return null; + if ($key === '') { + return null; + } $lower = mb_strtolower($key); @@ -104,7 +110,7 @@ public static function getIdsByNames(array $names): array ->active() ->where(function (Builder $q) use ($lower) { $q->whereRaw('LOWER(name) = ?', [$lower]) - ->orWhereRaw('LOWER(slug) = ?', [$lower]); + ->orWhereRaw('LOWER(slug) = ?', [$lower]); }) ->first(); @@ -141,12 +147,12 @@ public static function getIdsByNames(array $names): array public static function rules(?int $id = null): array { return [ - 'name' => ['required', 'string', 'max:255'], - 'slug' => ['required', 'string', 'max:255', 'unique:roles,slug,' . ($id ?? 'NULL') . ',id'], - 'description' => ['nullable', 'string', 'max:2000'], + 'name' => ['required', 'string', 'max:255'], + 'slug' => ['required', 'string', 'max:255', 'unique:roles,slug,'.($id ?? 'NULL').',id'], + 'description' => ['nullable', 'string', 'max:2000'], 'dashboard_route' => ['nullable', 'string', 'max:255'], - 'priority' => ['nullable', 'integer', 'min:0'], - 'is_active' => ['nullable', 'boolean'], + 'priority' => ['nullable', 'integer', 'min:0'], + 'is_active' => ['nullable', 'boolean'], ]; } -} \ No newline at end of file +} diff --git a/app/Models/RoleNavItem.php b/app/Models/RoleNavItem.php index 41f16cff..8b1968b4 100644 --- a/app/Models/RoleNavItem.php +++ b/app/Models/RoleNavItem.php @@ -3,12 +3,12 @@ namespace App\Models; use Illuminate\Database\Eloquent\Builder; -use App\Models\BaseModel; use Illuminate\Database\Eloquent\Relations\BelongsTo; class RoleNavItem extends BaseModel { protected $table = 'role_nav_items'; + public $timestamps = true; protected $fillable = [ @@ -17,10 +17,10 @@ class RoleNavItem extends BaseModel ]; protected $casts = [ - 'role_id' => 'integer', + 'role_id' => 'integer', 'nav_item_id' => 'integer', - 'created_at' => 'datetime', - 'updated_at' => 'datetime', + 'created_at' => 'datetime', + 'updated_at' => 'datetime', ]; /* ============================================================ @@ -46,6 +46,7 @@ class RoleNavItem extends BaseModel public function scopeForRoles(Builder $q, array $roleIds): Builder { $roleIds = array_values(array_unique(array_filter(array_map('intval', $roleIds)))); + return empty($roleIds) ? $q->whereRaw('1=0') : $q->whereIn('role_id', $roleIds); } @@ -60,11 +61,15 @@ class RoleNavItem extends BaseModel public static function getNavItemIdsForRoles(array $roleNames): array { $roleNames = array_values(array_filter(array_map('strval', $roleNames))); - if (empty($roleNames)) return []; + if (empty($roleNames)) { + return []; + } // Uses the Role model you converted earlier $roleIds = Role::getIdsByNames($roleNames); - if (empty($roleIds)) return []; + if (empty($roleIds)) { + return []; + } // pluck nav_item_id, unique, sorted (optional) return static::query() @@ -85,8 +90,8 @@ class RoleNavItem extends BaseModel public static function rules(bool $updating = false): array { return [ - 'role_id' => [$updating ? 'sometimes' : 'required', 'integer', 'min:1', 'exists:roles,id'], + 'role_id' => [$updating ? 'sometimes' : 'required', 'integer', 'min:1', 'exists:roles,id'], 'nav_item_id' => [$updating ? 'sometimes' : 'required', 'integer', 'min:1', 'exists:nav_items,id'], ]; } -} \ No newline at end of file +} diff --git a/app/Models/RolePermission.php b/app/Models/RolePermission.php index b39760ab..2cf521f0 100644 --- a/app/Models/RolePermission.php +++ b/app/Models/RolePermission.php @@ -3,7 +3,6 @@ namespace App\Models; use Illuminate\Database\Eloquent\Builder; -use App\Models\BaseModel; use Illuminate\Database\Eloquent\Relations\BelongsTo; use Illuminate\Support\Facades\Log; @@ -22,18 +21,19 @@ class RolePermission extends BaseModel 'created_at', 'updated_at', ]; + public $timestamps = true; protected $casts = [ - 'role_id' => 'integer', - 'permission_id' => 'integer', - 'can_create' => 'boolean', - 'can_read' => 'boolean', - 'can_update' => 'boolean', - 'can_delete' => 'boolean', - 'can_manage' => 'boolean', - 'created_at' => 'datetime', - 'updated_at' => 'datetime', + 'role_id' => 'integer', + 'permission_id' => 'integer', + 'can_create' => 'boolean', + 'can_read' => 'boolean', + 'can_update' => 'boolean', + 'can_delete' => 'boolean', + 'can_manage' => 'boolean', + 'created_at' => 'datetime', + 'updated_at' => 'datetime', ]; /* ============================================================ @@ -117,7 +117,7 @@ class RolePermission extends BaseModel return [ 'can_create' => $manage ? true : (bool) $this->can_create, - 'can_read' => $manage ? true : (bool) $this->can_read, + 'can_read' => $manage ? true : (bool) $this->can_read, 'can_update' => $manage ? true : (bool) $this->can_update, 'can_delete' => $manage ? true : (bool) $this->can_delete, 'can_manage' => $manage, @@ -130,10 +130,11 @@ class RolePermission extends BaseModel public function setCrud(bool $create, bool $read, bool $update, bool $delete, bool $manage = false): self { $this->can_create = $create; - $this->can_read = $read; + $this->can_read = $read; $this->can_update = $update; $this->can_delete = $delete; $this->can_manage = $manage; + return $this; } @@ -145,14 +146,14 @@ class RolePermission extends BaseModel public static function rules(bool $updating = false): array { return [ - 'role_id' => [$updating ? 'sometimes' : 'required', 'integer', 'min:1', 'exists:roles,id'], + 'role_id' => [$updating ? 'sometimes' : 'required', 'integer', 'min:1', 'exists:roles,id'], 'permission_id' => [$updating ? 'sometimes' : 'required', 'integer', 'min:1', 'exists:permissions,id'], - 'can_create' => ['nullable', 'boolean'], - 'can_read' => ['nullable', 'boolean'], - 'can_update' => ['nullable', 'boolean'], - 'can_delete' => ['nullable', 'boolean'], - 'can_manage' => ['nullable', 'boolean'], + 'can_create' => ['nullable', 'boolean'], + 'can_read' => ['nullable', 'boolean'], + 'can_update' => ['nullable', 'boolean'], + 'can_delete' => ['nullable', 'boolean'], + 'can_manage' => ['nullable', 'boolean'], ]; } -} \ No newline at end of file +} diff --git a/app/Models/SchoolClass.php b/app/Models/SchoolClass.php index e5ee65e4..f42a4ca1 100644 --- a/app/Models/SchoolClass.php +++ b/app/Models/SchoolClass.php @@ -2,8 +2,6 @@ namespace App\Models; -use App\Models\BaseModel; - class SchoolClass extends BaseModel { protected $table = 'classes'; @@ -40,6 +38,7 @@ class SchoolClass extends BaseModel public static function createClass(array $data): int { $row = static::create($data); + return (int) $row->id; } @@ -51,7 +50,10 @@ class SchoolClass extends BaseModel public static function deleteClass(int $id): bool { $row = static::query()->find($id); - if (!$row) return false; + if (! $row) { + return false; + } + return (bool) $row->delete(); } -} \ No newline at end of file +} diff --git a/app/Models/SchoolYear.php b/app/Models/SchoolYear.php index 385e4de4..1170546f 100644 --- a/app/Models/SchoolYear.php +++ b/app/Models/SchoolYear.php @@ -5,8 +5,11 @@ namespace App\Models; class SchoolYear extends BaseModel { public const STATUS_DRAFT = 'draft'; + public const STATUS_ACTIVE = 'active'; + public const STATUS_CLOSED = 'closed'; + public const STATUS_ARCHIVED = 'archived'; protected $table = 'school_years'; diff --git a/app/Models/ScoreComment.php b/app/Models/ScoreComment.php index a4a19d12..9045017a 100644 --- a/app/Models/ScoreComment.php +++ b/app/Models/ScoreComment.php @@ -3,7 +3,6 @@ namespace App\Models; use Illuminate\Database\Eloquent\Builder; -use App\Models\BaseModel; use Illuminate\Database\Eloquent\Relations\BelongsTo; class ScoreComment extends BaseModel @@ -34,14 +33,14 @@ class ScoreComment extends BaseModel ]; protected $casts = [ - 'student_id' => 'integer', + 'student_id' => 'integer', 'class_section_id' => 'integer', - 'reviewed_by' => 'integer', - 'commented_by' => 'integer', - 'created_at' => 'datetime', + 'reviewed_by' => 'integer', + 'commented_by' => 'integer', + 'created_at' => 'datetime', // If comment_review is a tinyint(1) flag in DB, boolean cast helps - 'comment_review' => 'boolean', + 'comment_review' => 'boolean', ]; /* ============================================================ @@ -89,7 +88,7 @@ class ScoreComment extends BaseModel public function scopeForTerm(Builder $q, string $schoolYear, string $semester): Builder { return $q->where('school_year', $schoolYear) - ->where('semester', $semester); + ->where('semester', $semester); } public function scopeForScoreType(Builder $q, string $scoreType): Builder @@ -102,7 +101,7 @@ class ScoreComment extends BaseModel // supports either boolean flag or reviewed_by presence return $q->where(function (Builder $w) { $w->where('comment_review', 1) - ->orWhereNotNull('reviewed_by'); + ->orWhereNotNull('reviewed_by'); }); } @@ -157,19 +156,19 @@ class ScoreComment extends BaseModel public static function rules(bool $updating = false): array { return [ - 'student_id' => [$updating ? 'sometimes' : 'required', 'integer', 'min:1'], + 'student_id' => [$updating ? 'sometimes' : 'required', 'integer', 'min:1'], 'class_section_id' => [$updating ? 'sometimes' : 'required', 'integer', 'min:1'], - 'score_type' => [$updating ? 'sometimes' : 'required', 'string', 'max:50'], - 'school_year' => [$updating ? 'sometimes' : 'required', 'string', 'max:20'], - 'semester' => [$updating ? 'sometimes' : 'required', 'string', 'max:20'], + 'score_type' => [$updating ? 'sometimes' : 'required', 'string', 'max:50'], + 'school_year' => [$updating ? 'sometimes' : 'required', 'string', 'max:20'], + 'semester' => [$updating ? 'sometimes' : 'required', 'string', 'max:20'], - 'comment' => ['nullable', 'string', 'max:5000'], - 'comment_review' => ['nullable', 'boolean'], - 'reviewed_by' => ['nullable', 'integer'], - 'commented_by' => ['nullable', 'integer'], + 'comment' => ['nullable', 'string', 'max:5000'], + 'comment_review' => ['nullable', 'boolean'], + 'reviewed_by' => ['nullable', 'integer'], + 'commented_by' => ['nullable', 'integer'], // created_at is DB default; allow client to omit it - 'created_at' => ['nullable', 'date'], + 'created_at' => ['nullable', 'date'], ]; } -} \ No newline at end of file +} diff --git a/app/Models/Section.php b/app/Models/Section.php index ce3a37cc..66992a5d 100644 --- a/app/Models/Section.php +++ b/app/Models/Section.php @@ -3,7 +3,6 @@ namespace App\Models; use Illuminate\Database\Eloquent\Builder; -use App\Models\BaseModel; use Illuminate\Database\Eloquent\Relations\BelongsTo; class Section extends BaseModel @@ -17,6 +16,7 @@ class Section extends BaseModel 'updated_at', 'updated_by', ]; + public $timestamps = true; protected $casts = [ @@ -49,11 +49,13 @@ class Section extends BaseModel public function scopeSearch(Builder $q, string $term): Builder { $term = trim($term); - if ($term === '') return $q; + if ($term === '') { + return $q; + } return $q->where(function (Builder $w) use ($term) { $w->where('section_name', 'like', "%{$term}%") - ->orWhere('description', 'like', "%{$term}%"); + ->orWhere('description', 'like', "%{$term}%"); }); } @@ -78,7 +80,9 @@ class Section extends BaseModel public static function updateSection(int $sectionId, array $data): bool { $section = static::query()->find($sectionId); - if (!$section) return false; + if (! $section) { + return false; + } // Laravel will set updated_at automatically when saving return $section->fill($data)->save(); @@ -91,7 +95,9 @@ class Section extends BaseModel public static function deleteSection(int $sectionId): bool { $section = static::query()->find($sectionId); - if (!$section) return false; + if (! $section) { + return false; + } return (bool) $section->delete(); } @@ -104,9 +110,9 @@ class Section extends BaseModel public static function rules(?int $id = null): array { return [ - 'section_name' => ['required', 'string', 'max:255', 'unique:sections,section_name,' . ($id ?? 'NULL') . ',id'], - 'description' => ['nullable', 'string', 'max:2000'], - 'updated_by' => ['nullable', 'integer'], + 'section_name' => ['required', 'string', 'max:255', 'unique:sections,section_name,'.($id ?? 'NULL').',id'], + 'description' => ['nullable', 'string', 'max:2000'], + 'updated_by' => ['nullable', 'integer'], ]; } -} \ No newline at end of file +} diff --git a/app/Models/SectionPlacementBatch.php b/app/Models/SectionPlacementBatch.php index c4a22c14..f9c585e1 100644 --- a/app/Models/SectionPlacementBatch.php +++ b/app/Models/SectionPlacementBatch.php @@ -9,8 +9,11 @@ class SectionPlacementBatch extends BaseModel protected $table = 'section_placement_batches'; public const STATUS_DRAFT = 'draft'; + public const STATUS_FINALIZED = 'finalized'; + public const STATUS_CANCELLED = 'cancelled'; + public const STATUS_SUPERSEDED = 'superseded'; protected $fillable = [ diff --git a/app/Models/SemesterScore.php b/app/Models/SemesterScore.php index d232d307..f2f46aba 100644 --- a/app/Models/SemesterScore.php +++ b/app/Models/SemesterScore.php @@ -3,7 +3,6 @@ namespace App\Models; use Illuminate\Database\Eloquent\Builder; -use App\Models\BaseModel; use Illuminate\Database\Eloquent\Relations\BelongsTo; class SemesterScore extends BaseModel @@ -39,26 +38,26 @@ class SemesterScore extends BaseModel public $timestamps = true; protected $casts = [ - 'student_id' => 'integer', - 'school_id' => 'integer', - 'class_section_id' => 'integer', - 'updated_by' => 'integer', + 'student_id' => 'integer', + 'school_id' => 'integer', + 'class_section_id' => 'integer', + 'updated_by' => 'integer', // averages/scores: keep decimal to avoid float drift - 'homework_avg' => 'decimal:2', - 'quiz_avg' => 'decimal:2', - 'project_avg' => 'decimal:2', - 'midterm_exam_score' => 'decimal:2', - 'final_exam_score' => 'decimal:2', - 'attendance_score' => 'decimal:2', + 'homework_avg' => 'decimal:2', + 'quiz_avg' => 'decimal:2', + 'project_avg' => 'decimal:2', + 'midterm_exam_score' => 'decimal:2', + 'final_exam_score' => 'decimal:2', + 'attendance_score' => 'decimal:2', 'participation_score' => 'decimal:2', - 'ptap_score' => 'decimal:2', - 'test_avg' => 'decimal:2', - 'semester_score' => 'decimal:2', - 'snapshot_id' => 'integer', + 'ptap_score' => 'decimal:2', + 'test_avg' => 'decimal:2', + 'semester_score' => 'decimal:2', + 'snapshot_id' => 'integer', - 'created_at' => 'datetime', - 'updated_at' => 'datetime', + 'created_at' => 'datetime', + 'updated_at' => 'datetime', ]; /* ============================================================ @@ -100,7 +99,7 @@ class SemesterScore extends BaseModel public function scopeForTerm(Builder $q, string $semester, string $schoolYear): Builder { return $q->where('semester', $semester) - ->where('school_year', $schoolYear); + ->where('school_year', $schoolYear); } public function scopeForClassSection(Builder $q, ?int $classSectionId): Builder @@ -126,8 +125,8 @@ class SemesterScore extends BaseModel } $keys = [ - 'student_id' => (int) $data['student_id'], - 'semester' => (string) $data['semester'], + 'student_id' => (int) $data['student_id'], + 'semester' => (string) $data['semester'], 'school_year' => (string) $data['school_year'], ]; @@ -155,27 +154,27 @@ class SemesterScore extends BaseModel $required = $updating ? 'sometimes' : 'required'; return [ - 'student_id' => [$required, 'integer', 'min:1'], - 'semester' => [$required, 'string', 'max:20'], - 'school_year' => [$required, 'string', 'max:20'], + 'student_id' => [$required, 'integer', 'min:1'], + 'semester' => [$required, 'string', 'max:20'], + 'school_year' => [$required, 'string', 'max:20'], - 'school_id' => ['nullable', 'integer', 'min:1'], + 'school_id' => ['nullable', 'integer', 'min:1'], 'class_section_id' => ['nullable', 'integer', 'min:1'], - 'updated_by' => ['nullable', 'integer', 'min:1'], + 'updated_by' => ['nullable', 'integer', 'min:1'], - 'homework_avg' => ['nullable', 'numeric', 'min:0'], - 'quiz_avg' => ['nullable', 'numeric', 'min:0'], - 'project_avg' => ['nullable', 'numeric', 'min:0'], - 'midterm_exam_score' => ['nullable', 'numeric', 'min:0'], - 'final_exam_score' => ['nullable', 'numeric', 'min:0'], - 'attendance_score' => ['nullable', 'numeric', 'min:0'], + 'homework_avg' => ['nullable', 'numeric', 'min:0'], + 'quiz_avg' => ['nullable', 'numeric', 'min:0'], + 'project_avg' => ['nullable', 'numeric', 'min:0'], + 'midterm_exam_score' => ['nullable', 'numeric', 'min:0'], + 'final_exam_score' => ['nullable', 'numeric', 'min:0'], + 'attendance_score' => ['nullable', 'numeric', 'min:0'], 'participation_score' => ['nullable', 'numeric', 'min:0'], - 'ptap_score' => ['nullable', 'numeric', 'min:0'], - 'test_avg' => ['nullable', 'numeric', 'min:0'], - 'semester_score' => ['nullable', 'numeric', 'min:0'], - 'calculation_mode' => ['nullable', 'string', 'in:legacy,strong'], + 'ptap_score' => ['nullable', 'numeric', 'min:0'], + 'test_avg' => ['nullable', 'numeric', 'min:0'], + 'semester_score' => ['nullable', 'numeric', 'min:0'], + 'calculation_mode' => ['nullable', 'string', 'in:legacy,strong'], 'calculation_policy_version' => ['nullable', 'string', 'max:50'], - 'snapshot_id' => ['nullable', 'integer', 'min:1'], + 'snapshot_id' => ['nullable', 'integer', 'min:1'], ]; } -} \ No newline at end of file +} diff --git a/app/Models/SemesterScoreSnapshot.php b/app/Models/SemesterScoreSnapshot.php index 46cc07f0..317c95a6 100644 --- a/app/Models/SemesterScoreSnapshot.php +++ b/app/Models/SemesterScoreSnapshot.php @@ -2,7 +2,6 @@ namespace App\Models; -use App\Models\BaseModel; use Illuminate\Database\Eloquent\Relations\BelongsTo; class SemesterScoreSnapshot extends BaseModel diff --git a/app/Models/Setting.php b/app/Models/Setting.php index 49c2ac72..0815035d 100644 --- a/app/Models/Setting.php +++ b/app/Models/Setting.php @@ -2,7 +2,6 @@ namespace App\Models; -use App\Models\BaseModel; use Illuminate\Database\Eloquent\Relations\BelongsTo; use Illuminate\Support\Facades\Cache; @@ -17,6 +16,7 @@ class Setting extends BaseModel 'created_at', 'updated_at', ]; + public $timestamps = true; protected $casts = [ @@ -49,7 +49,7 @@ class Setting extends BaseModel { return static::query()->first() ?? static::query()->create([ // set safe defaults if you want - 'name' => 'App', + 'name' => 'App', 'timezone' => config('app.timezone', 'UTC'), ]); } @@ -70,6 +70,7 @@ class Setting extends BaseModel public static function updateSettings(array $data): bool { $settings = static::singleton(); + return $settings->fill($data)->save(); } @@ -104,9 +105,9 @@ class Setting extends BaseModel public static function rules(bool $updating = false): array { return [ - 'name' => ['nullable', 'string', 'max:255'], - 'timezone' => ['nullable', 'string', 'max:80'], // validate against a list if you want - 'updated_by'=> ['nullable', 'integer'], + 'name' => ['nullable', 'string', 'max:255'], + 'timezone' => ['nullable', 'string', 'max:80'], // validate against a list if you want + 'updated_by' => ['nullable', 'integer'], ]; } -} \ No newline at end of file +} diff --git a/app/Models/Staff.php b/app/Models/Staff.php index 49ed10db..a346562d 100644 --- a/app/Models/Staff.php +++ b/app/Models/Staff.php @@ -3,7 +3,6 @@ namespace App\Models; use Illuminate\Database\Eloquent\Builder; -use App\Models\BaseModel; use Illuminate\Database\Eloquent\Relations\BelongsTo; class Staff extends BaseModel @@ -30,9 +29,9 @@ class Staff extends BaseModel ]; protected $casts = [ - 'user_id' => 'integer', - 'created_at' => 'datetime', - 'updated_at' => 'datetime', + 'user_id' => 'integer', + 'created_at' => 'datetime', + 'updated_at' => 'datetime', ]; /* ============================================================ @@ -137,7 +136,7 @@ class Staff extends BaseModel // generated email. If an exact school-year row is missing but the user // already has any staff row, update that existing row instead of trying // to insert a duplicate-email record. - if (!$existing) { + if (! $existing) { $existing = static::query() ->where('user_id', $userId) ->orderByDesc('id') @@ -151,11 +150,11 @@ class Staff extends BaseModel if ($existing) { // Preserve original created_at unless explicitly provided - if (!array_key_exists('created_at', $payload) && $existing->created_at) { + if (! array_key_exists('created_at', $payload) && $existing->created_at) { $payload['created_at'] = $existing->created_at; } // Ensure updated_at exists - if (!array_key_exists('updated_at', $payload)) { + if (! array_key_exists('updated_at', $payload)) { $payload['updated_at'] = $now; } @@ -166,10 +165,10 @@ class Staff extends BaseModel } // Insert new row — set created_at/updated_at if not provided - if (!array_key_exists('created_at', $payload)) { + if (! array_key_exists('created_at', $payload)) { $payload['created_at'] = $now; } - if (!array_key_exists('updated_at', $payload)) { + if (! array_key_exists('updated_at', $payload)) { $payload['updated_at'] = $now; } @@ -194,17 +193,17 @@ class Staff extends BaseModel $req = $updating ? 'sometimes' : 'required'; return [ - 'user_id' => [$req, 'integer', 'min:1', 'exists:users,id'], - 'firstname' => ['nullable', 'string', 'max:120'], - 'lastname' => ['nullable', 'string', 'max:120'], - 'email' => ['nullable', 'email', 'max:255'], - 'phone' => ['nullable', 'string', 'max:40'], - 'role_name' => ['nullable', 'string', 'max:80'], - 'active_role' => ['nullable', 'string', 'max:80'], // includes 'inactive' - 'status' => ['nullable', 'string', 'max:40'], - 'school_year' => ['nullable', 'string', 'max:20'], - 'created_at' => ['nullable', 'date'], - 'updated_at' => ['nullable', 'date'], + 'user_id' => [$req, 'integer', 'min:1', 'exists:users,id'], + 'firstname' => ['nullable', 'string', 'max:120'], + 'lastname' => ['nullable', 'string', 'max:120'], + 'email' => ['nullable', 'email', 'max:255'], + 'phone' => ['nullable', 'string', 'max:40'], + 'role_name' => ['nullable', 'string', 'max:80'], + 'active_role' => ['nullable', 'string', 'max:80'], // includes 'inactive' + 'status' => ['nullable', 'string', 'max:40'], + 'school_year' => ['nullable', 'string', 'max:20'], + 'created_at' => ['nullable', 'date'], + 'updated_at' => ['nullable', 'date'], ]; } } diff --git a/app/Models/StaffAttendance.php b/app/Models/StaffAttendance.php index c44d94c9..125d8fbd 100644 --- a/app/Models/StaffAttendance.php +++ b/app/Models/StaffAttendance.php @@ -3,13 +3,13 @@ namespace App\Models; use Illuminate\Database\Eloquent\Builder; -use App\Models\BaseModel; use Illuminate\Database\Eloquent\Relations\BelongsTo; use Illuminate\Support\Facades\DB; class StaffAttendance extends BaseModel { protected $table = 'staff_attendance'; + public $timestamps = true; /** @@ -38,13 +38,13 @@ class StaffAttendance extends BaseModel ]; protected $casts = [ - 'user_id' => 'integer', + 'user_id' => 'integer', 'class_section_id' => 'integer', - 'created_by' => 'integer', - 'updated_by' => 'integer', - 'date' => 'date:Y-m-d', - 'created_at' => 'datetime', - 'updated_at' => 'datetime', + 'created_by' => 'integer', + 'updated_by' => 'integer', + 'date' => 'date:Y-m-d', + 'created_at' => 'datetime', + 'updated_at' => 'datetime', ]; /* ============================================================ @@ -52,11 +52,14 @@ class StaffAttendance extends BaseModel * ============================================================ */ public const STATUS_PRESENT = 'present'; - public const STATUS_ABSENT = 'absent'; - public const STATUS_LATE = 'late'; + + public const STATUS_ABSENT = 'absent'; + + public const STATUS_LATE = 'late'; public const POS_MAIN = 'main'; - public const POS_TA = 'ta'; + + public const POS_TA = 'ta'; public static function allowedStatuses(): array { @@ -118,12 +121,14 @@ class StaffAttendance extends BaseModel protected static function normStatus(?string $status): ?string { $s = strtolower(trim((string) $status)); + return in_array($s, self::allowedStatuses(), true) ? $s : null; } protected static function isValidDate(string $d): bool { $dt = date_create_from_format('Y-m-d', $d); + return $dt && $dt->format('Y-m-d') === $d; } @@ -146,7 +151,7 @@ class StaffAttendance extends BaseModel ?string $reason, ?int $editorId = null ): ?self { - if ($userId <= 0 || !self::isValidDate($date) || $semester === '' || $schoolYear === '') { + if ($userId <= 0 || ! self::isValidDate($date) || $semester === '' || $schoolYear === '') { return null; } @@ -158,17 +163,17 @@ class StaffAttendance extends BaseModel $editorId = $editorId ?: (auth()->id() ?? null); $keys = [ - 'user_id' => $userId, - 'date' => $date, - 'semester' => $semester, + 'user_id' => $userId, + 'date' => $date, + 'semester' => $semester, 'school_year' => $schoolYear, ]; $payload = [ - 'role_name' => $roleName, - 'status' => $norm, - 'reason' => $reason ?: null, - 'updated_by' => $editorId, + 'role_name' => $roleName, + 'status' => $norm, + 'reason' => $reason ?: null, + 'updated_by' => $editorId, ]; // Ensure created_by is set on create @@ -194,7 +199,7 @@ class StaffAttendance extends BaseModel ?string $reason = null, ?int $editorId = null ): bool { - if ($userId <= 0 || !self::isValidDate($date) || $semester === '' || $schoolYear === '') { + if ($userId <= 0 || ! self::isValidDate($date) || $semester === '' || $schoolYear === '') { return false; } @@ -212,26 +217,28 @@ class StaffAttendance extends BaseModel if ($existing) { return (bool) $existing->delete(); } + return true; } $keys = [ - 'user_id' => $userId, - 'date' => $date, - 'semester' => $semester, + 'user_id' => $userId, + 'date' => $date, + 'semester' => $semester, 'school_year' => $schoolYear, ]; $payload = [ - 'role_name' => $roleName ?? ($existing->role_name ?? null), + 'role_name' => $roleName ?? ($existing->role_name ?? null), 'class_section_id' => $classSectionId, - 'position' => $position, - 'status' => $norm, - 'reason' => $reason, - 'updated_by' => $editorId, + 'position' => $position, + 'status' => $norm, + 'reason' => $reason, + 'updated_by' => $editorId, ]; static::query()->updateOrCreate($keys, $payload + ['created_by' => $editorId]); + return true; } @@ -243,6 +250,7 @@ class StaffAttendance extends BaseModel public static function deleteOne(int $userId, string $date): bool { $row = static::getOne($userId, $date); + return $row ? (bool) $row->delete() : true; } @@ -276,27 +284,27 @@ class StaffAttendance extends BaseModel ->leftJoinSub($roleSub, 'rs', fn ($j) => $j->on('rs.user_id', '=', 'u.id')) ->leftJoin('staff_attendance as sa', function ($j) use ($date, $semester, $schoolYear) { $j->on('sa.user_id', '=', 'u.id') - ->whereDate('sa.date', '=', $date) - ->where('sa.semester', '=', $semester) - ->where('sa.school_year', '=', $schoolYear); + ->whereDate('sa.date', '=', $date) + ->where('sa.semester', '=', $semester) + ->where('sa.school_year', '=', $schoolYear); }) ->whereNotIn(DB::raw('LOWER(COALESCE(rs.role_name,""))'), ['parent', 'guest']) ->orderBy('u.firstname') ->orderBy('u.lastname'); - if (!empty($excludeUserIds)) { + if (! empty($excludeUserIds)) { $q->whereNotIn('u.id', array_values(array_map('intval', $excludeUserIds))); } return $q->get()->map(function ($r) { return [ - 'user_id' => (int) $r->user_id, - 'firstname' => (string) ($r->firstname ?? ''), - 'lastname' => (string) ($r->lastname ?? ''), - 'role_name' => (string) ($r->role_name ?? ''), - 'satt_id' => $r->satt_id ? (int) $r->satt_id : null, - 'status' => $r->status ? (string) $r->status : null, - 'reason' => $r->reason, + 'user_id' => (int) $r->user_id, + 'firstname' => (string) ($r->firstname ?? ''), + 'lastname' => (string) ($r->lastname ?? ''), + 'role_name' => (string) ($r->role_name ?? ''), + 'satt_id' => $r->satt_id ? (int) $r->satt_id : null, + 'status' => $r->status ? (string) $r->status : null, + 'reason' => $r->reason, ]; })->all(); } @@ -318,10 +326,10 @@ class StaffAttendance extends BaseModel ->get() ->map(fn ($r) => [ 'class_section_id' => (int) $r->class_section_id, - 'teacher_id' => (int) $r->teacher_id, - 'position' => (string) $r->position, - 'firstname' => (string) ($r->firstname ?? ''), - 'lastname' => (string) ($r->lastname ?? ''), + 'teacher_id' => (int) $r->teacher_id, + 'position' => (string) $r->position, + 'firstname' => (string) ($r->firstname ?? ''), + 'lastname' => (string) ($r->lastname ?? ''), ])->all(); } @@ -350,20 +358,20 @@ class StaffAttendance extends BaseModel string $schoolYear ): array { return DB::table('teacher_class as tc') - ->selectRaw(" + ->selectRaw(' tc.class_section_id, tc.teacher_id as teacher_id, tc.position, u.firstname, u.lastname, t.id as satt_id, t.status, t.reason - ") + ') ->join('users as u', 'u.id', '=', 'tc.teacher_id') ->leftJoin('staff_attendance as t', function ($j) use ($date, $semester, $schoolYear) { $j->on('t.user_id', '=', 'tc.teacher_id') - ->on('t.class_section_id', '=', 'tc.class_section_id') - ->whereDate('t.date', '=', $date) - ->where('t.semester', '=', $semester) - ->where('t.school_year', '=', $schoolYear); + ->on('t.class_section_id', '=', 'tc.class_section_id') + ->whereDate('t.date', '=', $date) + ->where('t.semester', '=', $semester) + ->where('t.school_year', '=', $schoolYear); }) ->where('tc.class_section_id', $classSectionId) ->where('tc.school_year', $schoolYear) @@ -372,13 +380,13 @@ class StaffAttendance extends BaseModel ->get() ->map(fn ($r) => [ 'class_section_id' => (int) $r->class_section_id, - 'teacher_id' => (int) $r->teacher_id, - 'position' => (string) $r->position, - 'firstname' => (string) ($r->firstname ?? ''), - 'lastname' => (string) ($r->lastname ?? ''), - 'satt_id' => $r->satt_id ? (int) $r->satt_id : null, - 'status' => $r->status ? strtolower((string) $r->status) : null, - 'reason' => $r->reason, + 'teacher_id' => (int) $r->teacher_id, + 'position' => (string) $r->position, + 'firstname' => (string) ($r->firstname ?? ''), + 'lastname' => (string) ($r->lastname ?? ''), + 'satt_id' => $r->satt_id ? (int) $r->satt_id : null, + 'status' => $r->status ? strtolower((string) $r->status) : null, + 'reason' => $r->reason, ])->all(); } @@ -393,7 +401,9 @@ class StaffAttendance extends BaseModel ->where('school_year', $schoolYear) ->count(); - if ($assigned <= 0) return true; + if ($assigned <= 0) { + return true; + } $present = (int) DB::table('staff_attendance') ->where('class_section_id', $classSectionId) @@ -418,7 +428,9 @@ class StaffAttendance extends BaseModel string $schoolYear ): array { $codes = array_values(array_unique(array_filter(array_map('intval', $sectionCodes)))); - if (empty($codes)) return []; + if (empty($codes)) { + return []; + } $rows = DB::table('staff_attendance') ->selectRaw('class_section_id, user_id, DATE_FORMAT(date,"%Y-%m-%d") as d, status, reason') @@ -440,6 +452,7 @@ class StaffAttendance extends BaseModel 'reason' => $r->reason, ]; } + return $map; } @@ -452,20 +465,20 @@ class StaffAttendance extends BaseModel $req = $updating ? 'sometimes' : 'required'; return [ - 'user_id' => [$req, 'integer', 'min:1', 'exists:users,id'], - 'date' => [$req, 'date_format:Y-m-d'], - 'semester' => [$req, 'string', 'max:20'], + 'user_id' => [$req, 'integer', 'min:1', 'exists:users,id'], + 'date' => [$req, 'date_format:Y-m-d'], + 'semester' => [$req, 'string', 'max:20'], 'school_year' => [$req, 'string', 'max:20'], - 'role_name' => ['nullable', 'string', 'max:80'], - 'status' => ['nullable', 'string', 'in:' . implode(',', self::allowedStatuses())], - 'reason' => ['nullable', 'string', 'max:2000'], + 'role_name' => ['nullable', 'string', 'max:80'], + 'status' => ['nullable', 'string', 'in:'.implode(',', self::allowedStatuses())], + 'reason' => ['nullable', 'string', 'max:2000'], 'class_section_id' => ['nullable', 'integer'], - 'position' => ['nullable', 'string', 'in:' . implode(',', [self::POS_MAIN, self::POS_TA])], + 'position' => ['nullable', 'string', 'in:'.implode(',', [self::POS_MAIN, self::POS_TA])], - 'created_by' => ['nullable', 'integer'], - 'updated_by' => ['nullable', 'integer'], + 'created_by' => ['nullable', 'integer'], + 'updated_by' => ['nullable', 'integer'], ]; } -} \ No newline at end of file +} diff --git a/app/Models/Stats.php b/app/Models/Stats.php index c8e3bf07..a260abe5 100644 --- a/app/Models/Stats.php +++ b/app/Models/Stats.php @@ -2,8 +2,6 @@ namespace App\Models; -use App\Models\BaseModel; - class Stats extends BaseModel { protected $table = 'stats'; @@ -24,8 +22,8 @@ class Stats extends BaseModel protected $casts = [ 'students' => 'integer', 'teachers' => 'integer', - 'admins' => 'integer', - 'users' => 'integer', + 'admins' => 'integer', + 'users' => 'integer', ]; public function getFillable(): array @@ -51,8 +49,8 @@ class Stats extends BaseModel return static::query()->first() ?? static::query()->create([ 'students' => 0, 'teachers' => 0, - 'admins' => 0, - 'users' => 0, + 'admins' => 0, + 'users' => 0, ]); } @@ -64,6 +62,7 @@ class Stats extends BaseModel $row = static::singleton(); $row->fill($data); $row->save(); + return $row; } } diff --git a/app/Models/Student.php b/app/Models/Student.php index a3fba3e7..bd639d8a 100644 --- a/app/Models/Student.php +++ b/app/Models/Student.php @@ -3,7 +3,6 @@ namespace App\Models; use Illuminate\Database\Eloquent\Builder; -use App\Models\BaseModel; use Illuminate\Database\Eloquent\Factories\HasFactory; use Illuminate\Database\Eloquent\Relations\BelongsTo; use Illuminate\Database\Eloquent\Relations\HasMany; @@ -12,6 +11,7 @@ use Illuminate\Support\Facades\DB; class Student extends BaseModel { use HasFactory; + protected $table = 'students'; /** @@ -40,15 +40,15 @@ class Student extends BaseModel ]; protected $casts = [ - 'school_id' => 'string', - 'age' => 'integer', - 'photo_consent' => 'boolean', - 'is_new' => 'boolean', - 'parent_id' => 'integer', - 'tuition_paid' => 'boolean', - 'is_active' => 'boolean', - 'dob' => 'date', - 'registration_date' => 'date', + 'school_id' => 'string', + 'age' => 'integer', + 'photo_consent' => 'boolean', + 'is_new' => 'boolean', + 'parent_id' => 'integer', + 'tuition_paid' => 'boolean', + 'is_active' => 'boolean', + 'dob' => 'date', + 'registration_date' => 'date', 'year_of_registration' => 'integer', ]; @@ -98,6 +98,7 @@ class Student extends BaseModel if ($schoolYear === null || strtolower(trim($schoolYear)) === 'all' || trim($schoolYear) === '') { return $q; } + return $q->where('school_year', $schoolYear); } @@ -214,6 +215,7 @@ class Student extends BaseModel public static function getStudentSchoolIdByStudentId(int $studentId): ?string { $v = static::query()->where('id', $studentId)->value('school_id'); + return $v !== null ? (string) $v : null; } @@ -233,12 +235,16 @@ class Student extends BaseModel public static function getFullNameById($studentId): ?array { $student = static::query()->find($studentId); - if (!$student) return null; + if (! $student) { + return null; + } $first = (string) ($student->firstname ?? ''); - $last = (string) ($student->lastname ?? ''); + $last = (string) ($student->lastname ?? ''); - if ($first === '' && $last === '') return null; + if ($first === '' && $last === '') { + return null; + } return ['firstname' => $first, 'lastname' => $last]; } @@ -246,7 +252,7 @@ class Student extends BaseModel /** legacy: getFullName($student array) */ public static function getFullName(array $student): string { - return trim(($student['firstname'] ?? '') . ' ' . ($student['lastname'] ?? '')); + return trim(($student['firstname'] ?? '').' '.($student['lastname'] ?? '')); } /** legacy: getLastRegistrationDate($ParentId) */ @@ -273,7 +279,9 @@ class Student extends BaseModel ->pluck('student_id') ->all(); - if (empty($studentIds)) return []; + if (empty($studentIds)) { + return []; + } return static::query() ->whereIn('id', $studentIds) @@ -341,7 +349,9 @@ class Student extends BaseModel */ public static function getStudentInfoByClassSectionId(int $classSectionId, ?string $semester = null, ?string $schoolYear = null, ?int $actorId = null): array { - if ($classSectionId <= 0) return []; + if ($classSectionId <= 0) { + return []; + } $q = DB::table('students') ->selectRaw('students.id AS student_id, students.school_id, students.firstname, students.lastname, sc.class_section_id') @@ -376,22 +386,22 @@ class Student extends BaseModel $req = $updating ? 'sometimes' : 'required'; return [ - 'school_id' => ['nullable', 'integer', 'min:1'], - 'firstname' => [$req, 'string', 'max:120'], - 'lastname' => [$req, 'string', 'max:120'], - 'dob' => ['nullable', 'date'], - 'age' => ['nullable', 'integer', 'min:0'], - 'gender' => ['nullable', 'string', 'max:20'], - 'registration_grade' => ['nullable', 'string', 'max:50'], - 'photo_consent' => ['nullable', 'boolean'], - 'is_new' => ['nullable', 'boolean'], - 'parent_id' => ['nullable', 'integer'], - 'school_year' => ['nullable', 'string', 'max:20'], - 'registration_date' => ['nullable', 'date'], - 'tuition_paid' => ['nullable', 'boolean'], - 'year_of_registration'=> ['nullable', 'integer', 'min:0'], - 'rfid_tag' => ['nullable', 'string', 'max:120'], - 'is_active' => ['nullable', 'boolean'], + 'school_id' => ['nullable', 'integer', 'min:1'], + 'firstname' => [$req, 'string', 'max:120'], + 'lastname' => [$req, 'string', 'max:120'], + 'dob' => ['nullable', 'date'], + 'age' => ['nullable', 'integer', 'min:0'], + 'gender' => ['nullable', 'string', 'max:20'], + 'registration_grade' => ['nullable', 'string', 'max:50'], + 'photo_consent' => ['nullable', 'boolean'], + 'is_new' => ['nullable', 'boolean'], + 'parent_id' => ['nullable', 'integer'], + 'school_year' => ['nullable', 'string', 'max:20'], + 'registration_date' => ['nullable', 'date'], + 'tuition_paid' => ['nullable', 'boolean'], + 'year_of_registration' => ['nullable', 'integer', 'min:0'], + 'rfid_tag' => ['nullable', 'string', 'max:120'], + 'is_active' => ['nullable', 'boolean'], ]; } } diff --git a/app/Models/StudentAllergy.php b/app/Models/StudentAllergy.php index 83c1ea7b..11d7fc2b 100644 --- a/app/Models/StudentAllergy.php +++ b/app/Models/StudentAllergy.php @@ -3,7 +3,6 @@ namespace App\Models; use Illuminate\Database\Eloquent\Builder; -use App\Models\BaseModel; use Illuminate\Database\Eloquent\Relations\BelongsTo; class StudentAllergy extends BaseModel @@ -47,6 +46,7 @@ class StudentAllergy extends BaseModel public function scopeForStudents(Builder $q, array $studentIds): Builder { $ids = array_values(array_unique(array_filter(array_map('intval', $studentIds)))); + return empty($ids) ? $q->whereRaw('1=0') : $q->whereIn('student_id', $ids); } @@ -103,7 +103,7 @@ class StudentAllergy extends BaseModel return [ 'student_id' => [$req, 'integer', 'min:1', 'exists:students,id'], - 'allergy' => [$req, 'string', 'max:100'], + 'allergy' => [$req, 'string', 'max:100'], ]; } @@ -111,9 +111,9 @@ class StudentAllergy extends BaseModel { return [ 'student_id.required' => 'Student ID is required.', - 'student_id.min' => 'Invalid Student ID.', - 'allergy.required' => 'Allergy is required.', - 'allergy.max' => 'Allergy must be 100 characters or fewer.', + 'student_id.min' => 'Invalid Student ID.', + 'allergy.required' => 'Allergy is required.', + 'allergy.max' => 'Allergy must be 100 characters or fewer.', ]; } -} \ No newline at end of file +} diff --git a/app/Models/StudentClass.php b/app/Models/StudentClass.php index 70bd4d2d..47d3584b 100644 --- a/app/Models/StudentClass.php +++ b/app/Models/StudentClass.php @@ -3,7 +3,6 @@ namespace App\Models; use Illuminate\Database\Eloquent\Builder; -use App\Models\BaseModel; use Illuminate\Database\Eloquent\Relations\BelongsTo; use Illuminate\Support\Facades\DB; @@ -23,16 +22,17 @@ class StudentClass extends BaseModel 'updated_at', 'created_at', ]; + public $timestamps = true; protected $casts = [ - 'student_id' => 'integer', - 'school_id' => 'integer', + 'student_id' => 'integer', + 'school_id' => 'integer', 'class_section_id' => 'integer', - 'is_event_only' => 'boolean', - 'updated_by' => 'integer', - 'created_at' => 'datetime', - 'updated_at' => 'datetime', + 'is_event_only' => 'boolean', + 'updated_by' => 'integer', + 'created_at' => 'datetime', + 'updated_at' => 'datetime', ]; /* ============================================================ @@ -84,7 +84,10 @@ class StudentClass extends BaseModel public function scopeForYear(Builder $q, ?string $schoolYear): Builder { - if ($schoolYear === null || trim($schoolYear) === '') return $q; + if ($schoolYear === null || trim($schoolYear) === '') { + return $q; + } + return $q->where('student_class.school_year', $schoolYear); } @@ -134,12 +137,12 @@ class StudentClass extends BaseModel ->where('sc.school_year', $schoolYear) ->orderBy('cs.class_section_name', 'asc') ->pluck('cs.class_section_name') - ->filter(fn ($v) => $v !== null && trim((string)$v) !== '') + ->filter(fn ($v) => $v !== null && trim((string) $v) !== '') ->unique() ->values() ->all(); - return $asArray ? $names : (!empty($names) ? implode(', ', $names) : ''); + return $asArray ? $names : (! empty($names) ? implode(', ', $names) : ''); } /** @@ -162,15 +165,17 @@ class StudentClass extends BaseModel $names = []; foreach ($rows as $r) { $name = $r->class_section_name ?? null; - if (!$name) continue; + if (! $name) { + continue; + } - $isEvent = (int)($r->is_event_only ?? 0) === 1; - $names[] = $isEvent ? ($name . ' (Event)') : $name; + $isEvent = (int) ($r->is_event_only ?? 0) === 1; + $names[] = $isEvent ? ($name.' (Event)') : $name; } $names = array_values(array_unique(array_filter($names))); - return $asArray ? $names : (!empty($names) ? implode(', ', $names) : ''); + return $asArray ? $names : (! empty($names) ? implode(', ', $names) : ''); } /** @@ -200,7 +205,9 @@ class StudentClass extends BaseModel public static function getStudentsByClassSectionIds(array $classSectionIds): array { $ids = array_values(array_unique(array_filter(array_map('intval', $classSectionIds)))); - if (empty($ids)) return []; + if (empty($ids)) { + return []; + } return DB::table('student_class as sc') ->selectRaw(' @@ -234,7 +241,7 @@ class StudentClass extends BaseModel ->orderByDesc('created_at') ->first(); - if (!$sc || !$sc->class_section_id) { + if (! $sc || ! $sc->class_section_id) { return 'N/A'; } @@ -282,7 +289,9 @@ class StudentClass extends BaseModel $out = []; foreach ($rows as $r) { $cid = $r->class_section_id; - if ($cid === null || $cid === '') continue; + if ($cid === null || $cid === '') { + continue; + } $out[$cid] = (int) ($r->total ?? 0); } @@ -298,13 +307,13 @@ class StudentClass extends BaseModel $req = $updating ? 'sometimes' : 'required'; return [ - 'student_id' => [$req, 'integer', 'min:1', 'exists:students,id'], - 'school_id' => ['nullable', 'integer', 'min:1'], + 'student_id' => [$req, 'integer', 'min:1', 'exists:students,id'], + 'school_id' => ['nullable', 'integer', 'min:1'], 'class_section_id' => ['nullable', 'integer'], - 'school_year' => [$req, 'string', 'max:20'], - 'is_event_only' => ['nullable', 'boolean'], - 'description' => ['nullable', 'string', 'max:2000'], - 'updated_by' => ['nullable', 'integer'], + 'school_year' => [$req, 'string', 'max:20'], + 'is_event_only' => ['nullable', 'boolean'], + 'description' => ['nullable', 'string', 'max:2000'], + 'updated_by' => ['nullable', 'integer'], ]; } -} \ No newline at end of file +} diff --git a/app/Models/StudentMedicalCondition.php b/app/Models/StudentMedicalCondition.php index 2f1209ff..c89aefbb 100644 --- a/app/Models/StudentMedicalCondition.php +++ b/app/Models/StudentMedicalCondition.php @@ -3,7 +3,6 @@ namespace App\Models; use Illuminate\Database\Eloquent\Builder; -use App\Models\BaseModel; use Illuminate\Database\Eloquent\Relations\BelongsTo; class StudentMedicalCondition extends BaseModel @@ -47,6 +46,7 @@ class StudentMedicalCondition extends BaseModel public function scopeForStudents(Builder $q, array $studentIds): Builder { $ids = array_values(array_unique(array_filter(array_map('intval', $studentIds)))); + return empty($ids) ? $q->whereRaw('1=0') : $q->whereIn('student_id', $ids); } @@ -100,7 +100,7 @@ class StudentMedicalCondition extends BaseModel $req = $updating ? 'sometimes' : 'required'; return [ - 'student_id' => [$req, 'integer', 'min:1', 'exists:students,id'], + 'student_id' => [$req, 'integer', 'min:1', 'exists:students,id'], 'condition_name' => [$req, 'string', 'max:100'], ]; } @@ -108,10 +108,10 @@ class StudentMedicalCondition extends BaseModel public static function messages(): array { return [ - 'student_id.required' => 'Student ID is required.', - 'student_id.min' => 'Invalid Student ID.', + 'student_id.required' => 'Student ID is required.', + 'student_id.min' => 'Invalid Student ID.', 'condition_name.required' => 'Condition name is required.', - 'condition_name.max' => 'Condition name must be 100 characters or fewer.', + 'condition_name.max' => 'Condition name must be 100 characters or fewer.', ]; } -} \ No newline at end of file +} diff --git a/app/Models/StudentPromotionRecord.php b/app/Models/StudentPromotionRecord.php index 1611f7ae..a978301a 100644 --- a/app/Models/StudentPromotionRecord.php +++ b/app/Models/StudentPromotionRecord.php @@ -16,25 +16,39 @@ use Illuminate\Database\Eloquent\Relations\BelongsTo; class StudentPromotionRecord extends BaseModel { protected $table = 'student_promotion_records'; + protected $primaryKey = 'promotion_id'; public $timestamps = true; public const STATUS_NOT_REVIEWED = 'not_reviewed'; + public const STATUS_ELIGIBLE = 'eligible_for_promotion'; + public const STATUS_AWAITING_PARENT = 'awaiting_parent_enrollment'; + public const STATUS_ENROLLMENT_STARTED = 'enrollment_started'; + public const STATUS_PROMOTED_AND_ENROLLED = 'promoted_and_enrolled'; + public const STATUS_CONDITIONAL = 'conditional_promotion'; + public const STATUS_REPEATED = 'repeated_level'; + public const STATUS_ON_HOLD = 'on_hold'; + public const STATUS_WITHDRAWN = 'withdrawn'; + public const STATUS_GRADUATED = 'graduated'; + public const STATUS_NOT_ENROLLED = 'not_enrolled_for_next_year'; public const ENROLLMENT_NOT_STARTED = 'not_started'; + public const ENROLLMENT_IN_PROGRESS = 'in_progress'; + public const ENROLLMENT_COMPLETED = 'completed'; + public const ENROLLMENT_EXPIRED = 'expired'; public const ALL_STATUSES = [ diff --git a/app/Models/SubjectCurriculum.php b/app/Models/SubjectCurriculum.php index c0196437..8652bda2 100644 --- a/app/Models/SubjectCurriculum.php +++ b/app/Models/SubjectCurriculum.php @@ -3,7 +3,6 @@ namespace App\Models; use Illuminate\Database\Eloquent\Builder; -use App\Models\BaseModel; use Illuminate\Database\Eloquent\Relations\BelongsTo; use Illuminate\Support\Facades\Schema; @@ -21,13 +20,14 @@ class SubjectCurriculum extends BaseModel 'created_at', 'updated_at', ]; + public $timestamps = true; protected $casts = [ - 'class_id' => 'integer', - 'unit_number' => 'integer', - 'created_at' => 'datetime', - 'updated_at' => 'datetime', + 'class_id' => 'integer', + 'unit_number' => 'integer', + 'created_at' => 'datetime', + 'updated_at' => 'datetime', ]; /* ============================================================ @@ -59,7 +59,7 @@ class SubjectCurriculum extends BaseModel public function scopeForSchoolYear(Builder $q, ?string $schoolYear): Builder { $schoolYear = trim((string) $schoolYear); - if ($schoolYear === '' || !Schema::hasColumn($this->getTable(), 'school_year')) { + if ($schoolYear === '' || ! Schema::hasColumn($this->getTable(), 'school_year')) { return $q; } @@ -69,7 +69,7 @@ class SubjectCurriculum extends BaseModel public function scopeOrdered(Builder $q): Builder { return $q->orderBy('unit_number', 'asc') - ->orderBy('chapter_name', 'asc'); + ->orderBy('chapter_name', 'asc'); } /* ============================================================ @@ -112,7 +112,7 @@ class SubjectCurriculum extends BaseModel ->get() ->map(fn ($r) => [ 'unit_number' => (int) $r->unit_number, - 'unit_title' => (string) ($r->unit_title ?? ''), + 'unit_title' => (string) ($r->unit_title ?? ''), ]) ->all(); } @@ -131,7 +131,7 @@ class SubjectCurriculum extends BaseModel ->orderBy('chapter_name', 'asc') ->distinct() ->pluck('chapter_name') - ->filter(fn ($v) => $v !== null && trim((string)$v) !== '') + ->filter(fn ($v) => $v !== null && trim((string) $v) !== '') ->values() ->all(); } @@ -146,11 +146,11 @@ class SubjectCurriculum extends BaseModel $req = $updating ? 'sometimes' : 'required'; return [ - 'class_id' => [$req, 'integer', 'min:1'], - 'subject' => [$req, 'string', 'max:120'], - 'unit_number' => ['nullable', 'integer', 'min:0'], - 'unit_title' => ['nullable', 'string', 'max:255'], - 'chapter_name' => ['nullable', 'string', 'max:255'], + 'class_id' => [$req, 'integer', 'min:1'], + 'subject' => [$req, 'string', 'max:120'], + 'unit_number' => ['nullable', 'integer', 'min:0'], + 'unit_title' => ['nullable', 'string', 'max:255'], + 'chapter_name' => ['nullable', 'string', 'max:255'], ]; } } diff --git a/app/Models/Supplier.php b/app/Models/Supplier.php index 47980b7e..bd9c79c7 100644 --- a/app/Models/Supplier.php +++ b/app/Models/Supplier.php @@ -2,11 +2,10 @@ namespace App\Models; -use App\Models\BaseModel; - class Supplier extends BaseModel { protected $table = 'suppliers'; + public $timestamps = true; protected $fillable = [ diff --git a/app/Models/Supply.php b/app/Models/Supply.php index 162cf2e6..cc0201f0 100644 --- a/app/Models/Supply.php +++ b/app/Models/Supply.php @@ -2,8 +2,6 @@ namespace App\Models; -use App\Models\BaseModel; - class Supply extends BaseModel { protected $table = 'supplies'; diff --git a/app/Models/SupplyCategory.php b/app/Models/SupplyCategory.php index 8f422b09..00d58c46 100644 --- a/app/Models/SupplyCategory.php +++ b/app/Models/SupplyCategory.php @@ -2,11 +2,10 @@ namespace App\Models; -use App\Models\BaseModel; - class SupplyCategory extends BaseModel { protected $table = 'supply_categories'; + public $timestamps = true; protected $fillable = [ diff --git a/app/Models/SupplyTransaction.php b/app/Models/SupplyTransaction.php index 9367af3e..1d566b84 100644 --- a/app/Models/SupplyTransaction.php +++ b/app/Models/SupplyTransaction.php @@ -2,8 +2,6 @@ namespace App\Models; -use App\Models\BaseModel; - class SupplyTransaction extends BaseModel { protected $table = 'supply_transactions'; diff --git a/app/Models/SupportRequest.php b/app/Models/SupportRequest.php index 7c4bb6c6..78234c41 100644 --- a/app/Models/SupportRequest.php +++ b/app/Models/SupportRequest.php @@ -2,11 +2,10 @@ namespace App\Models; -use App\Models\BaseModel; - class SupportRequest extends BaseModel { protected $table = 'support_requests'; + public $timestamps = true; protected $fillable = [ diff --git a/app/Models/Teacher.php b/app/Models/Teacher.php index 826cac19..dd51c42c 100644 --- a/app/Models/Teacher.php +++ b/app/Models/Teacher.php @@ -2,6 +2,4 @@ namespace App\Models; -class Teacher extends User -{ -} +class Teacher extends User {} diff --git a/app/Models/TeacherClass.php b/app/Models/TeacherClass.php index 051f954c..2471096d 100644 --- a/app/Models/TeacherClass.php +++ b/app/Models/TeacherClass.php @@ -3,7 +3,6 @@ namespace App\Models; use Illuminate\Database\Eloquent\Builder; -use App\Models\BaseModel; use Illuminate\Database\Eloquent\Relations\BelongsTo; use Illuminate\Support\Facades\DB; @@ -21,30 +20,30 @@ class TeacherClass extends BaseModel 'updated_at', 'created_at', ]; + public $timestamps = true; protected $casts = [ - 'teacher_id' => 'integer', + 'teacher_id' => 'integer', 'class_section_id' => 'integer', - 'updated_by' => 'integer', - 'created_at' => 'datetime', - 'updated_at' => 'datetime', + 'updated_by' => 'integer', + 'created_at' => 'datetime', + 'updated_at' => 'datetime', ]; - /* ============================================================ * Constants * ============================================================ */ public const POS_MAIN = 'main'; - public const POS_TA = 'ta'; + + public const POS_TA = 'ta'; public static function allowedPositions(): array { return [self::POS_MAIN, self::POS_TA]; } - /* ============================================================ * Relationships (optional) * ============================================================ @@ -138,15 +137,15 @@ class TeacherClass extends BaseModel return $rows->map(function ($r) use ($semester) { return [ - 'class_section_pk' => isset($r->class_section_pk) ? (int) $r->class_section_pk : null, - 'class_section_id' => (int) $r->class_section_id, + 'class_section_pk' => isset($r->class_section_pk) ? (int) $r->class_section_pk : null, + 'class_section_id' => (int) $r->class_section_id, 'class_section_name' => (string) $r->class_section_name, - 'class_id' => isset($r->class_id) ? (int) $r->class_id : null, - 'class_name' => $r->class_name ?? null, - 'teacher_id' => (int) $r->teacher_id, - 'teacher_role' => ucfirst((string) $r->position), // Main / Ta - 'school_year' => (string) $r->school_year, - 'semester' => $semester, + 'class_id' => isset($r->class_id) ? (int) $r->class_id : null, + 'class_name' => $r->class_name ?? null, + 'teacher_id' => (int) $r->teacher_id, + 'teacher_role' => ucfirst((string) $r->position), // Main / Ta + 'school_year' => (string) $r->school_year, + 'semester' => $semester, ]; })->all(); } @@ -214,15 +213,21 @@ class TeacherClass extends BaseModel $classSectionName = trim($classSectionName); if (str_contains($classSectionName, ',')) { $parts = array_values(array_filter(array_map('trim', explode(',', $classSectionName)))); - if (!empty($parts)) $classSectionName = $parts[0]; + if (! empty($parts)) { + $classSectionName = $parts[0]; + } + } + if ($classSectionName === '') { + return null; } - if ($classSectionName === '') return null; $sectionCode = DB::table('classSection') ->where('class_section_name', $classSectionName) ->value('class_section_id'); - if (!$sectionCode) return null; + if (! $sectionCode) { + return null; + } $teacherId = DB::table('teacher_class') ->where('class_section_id', $sectionCode) @@ -264,7 +269,7 @@ class TeacherClass extends BaseModel ?int $updatedBy = null ): bool { $position = strtolower(trim($position)); - if (!in_array($position, self::allowedPositions(), true)) { + if (! in_array($position, self::allowedPositions(), true)) { throw new \InvalidArgumentException('Position must be either "main" or "ta".'); } @@ -272,10 +277,10 @@ class TeacherClass extends BaseModel static::query()->updateOrCreate( [ - 'teacher_id' => $teacherId, + 'teacher_id' => $teacherId, 'class_section_id' => $sectionCode, - 'school_year' => $schoolYear, - 'position' => $position, + 'school_year' => $schoolYear, + 'position' => $position, ], [ 'updated_by' => $updatedBy, @@ -309,7 +314,7 @@ class TeacherClass extends BaseModel * Returns rows: class_section_id, teacher_id, position, firstname, lastname * * @param int|null $alsoSectionCode When teacher_class rows use the section PK while $sectionCode - * is the business code (or the reverse), pass the other id. + * is the business code (or the reverse), pass the other id. * @param bool $requireSemester When false, ignore semester (legacy DBs with blank/wrong tc.semester). */ public static function assignedForSectionTerm( @@ -360,6 +365,7 @@ class TeacherClass extends BaseModel foreach (static::assignedForSectionTerm($sectionCode, $semester, $schoolYear, $alsoSectionCode, $requireSemester) as $r) { $out[(int) $r['teacher_id']] = $r; } + return $out; } @@ -377,9 +383,11 @@ class TeacherClass extends BaseModel ->orderByRaw("CASE tc.position WHEN 'main' THEN 0 WHEN 'ta' THEN 1 ELSE 2 END") ->orderBy('u.firstname', 'asc'); - if (!empty($onlySectionCodes)) { + if (! empty($onlySectionCodes)) { $ids = array_values(array_unique(array_filter(array_map('intval', $onlySectionCodes)))); - if (!empty($ids)) $q->whereIn('tc.class_section_id', $ids); + if (! empty($ids)) { + $q->whereIn('tc.class_section_id', $ids); + } } $rows = $q->get()->map(fn ($r) => (array) $r)->all(); @@ -387,7 +395,9 @@ class TeacherClass extends BaseModel $out = []; foreach ($rows as $r) { $code = (int) ($r['class_section_id'] ?? 0); - if ($code <= 0) continue; + if ($code <= 0) { + continue; + } $out[$code][] = $r; } @@ -403,12 +413,12 @@ class TeacherClass extends BaseModel $req = $updating ? 'sometimes' : 'required'; return [ - 'teacher_id' => [$req, 'integer', 'min:1', 'exists:users,id'], + 'teacher_id' => [$req, 'integer', 'min:1', 'exists:users,id'], 'class_section_id' => [$req, 'integer', 'min:1'], - 'semester' => [$req, 'string', 'max:25'], - 'school_year' => [$req, 'string', 'max:9'], - 'position' => [$req, 'string', 'in:' . implode(',', self::allowedPositions())], - 'updated_by' => ['nullable', 'integer'], + 'semester' => [$req, 'string', 'max:25'], + 'school_year' => [$req, 'string', 'max:9'], + 'position' => [$req, 'string', 'in:'.implode(',', self::allowedPositions())], + 'updated_by' => ['nullable', 'integer'], ]; } } diff --git a/app/Models/TeacherSubmissionNotificationHistory.php b/app/Models/TeacherSubmissionNotificationHistory.php index 294ac2fe..1bfe8cd6 100644 --- a/app/Models/TeacherSubmissionNotificationHistory.php +++ b/app/Models/TeacherSubmissionNotificationHistory.php @@ -3,7 +3,6 @@ namespace App\Models; use Illuminate\Database\Eloquent\Builder; -use App\Models\BaseModel; use Illuminate\Database\Eloquent\Relations\BelongsTo; class TeacherSubmissionNotificationHistory extends BaseModel @@ -28,10 +27,10 @@ class TeacherSubmissionNotificationHistory extends BaseModel ]; protected $casts = [ - 'teacher_id' => 'integer', + 'teacher_id' => 'integer', 'class_section_id' => 'integer', - 'admin_id' => 'integer', - 'sent_at' => 'datetime', + 'admin_id' => 'integer', + 'sent_at' => 'datetime', ]; /* ============================================================ @@ -75,7 +74,7 @@ class TeacherSubmissionNotificationHistory extends BaseModel public function scopeForTerm(Builder $q, string $schoolYear, string $semester): Builder { return $q->where('school_year', $schoolYear) - ->where('semester', $semester); + ->where('semester', $semester); } public function scopeCategory(Builder $q, string $category): Builder @@ -113,15 +112,15 @@ class TeacherSubmissionNotificationHistory extends BaseModel $req = $updating ? 'sometimes' : 'required'; return [ - 'teacher_id' => [$req, 'integer', 'min:1', 'exists:users,id'], - 'class_section_id' => ['nullable', 'integer'], - 'admin_id' => ['nullable', 'integer', 'exists:users,id'], - 'notification_category' => [$req, 'string', 'max:120'], - 'message' => [$req, 'string', 'max:5000'], - 'status' => ['nullable', 'string', 'max:50'], - 'school_year' => ['nullable', 'string', 'max:20'], - 'semester' => ['nullable', 'string', 'max:20'], - 'sent_at' => ['nullable', 'date'], + 'teacher_id' => [$req, 'integer', 'min:1', 'exists:users,id'], + 'class_section_id' => ['nullable', 'integer'], + 'admin_id' => ['nullable', 'integer', 'exists:users,id'], + 'notification_category' => [$req, 'string', 'max:120'], + 'message' => [$req, 'string', 'max:5000'], + 'status' => ['nullable', 'string', 'max:50'], + 'school_year' => ['nullable', 'string', 'max:20'], + 'semester' => ['nullable', 'string', 'max:20'], + 'sent_at' => ['nullable', 'date'], ]; } -} \ No newline at end of file +} diff --git a/app/Models/User.php b/app/Models/User.php index 68ef9be4..12ff20ad 100644 --- a/app/Models/User.php +++ b/app/Models/User.php @@ -4,9 +4,9 @@ namespace App\Models; use Illuminate\Database\Eloquent\Builder; use Illuminate\Database\Eloquent\Factories\HasFactory; -use Illuminate\Foundation\Auth\User as Authenticatable; use Illuminate\Database\Eloquent\Relations\BelongsToMany; use Illuminate\Database\Eloquent\Relations\HasMany; +use Illuminate\Foundation\Auth\User as Authenticatable; use Illuminate\Support\Facades\DB; use Illuminate\Support\Facades\Hash; use Illuminate\Support\Facades\Schema; @@ -58,14 +58,14 @@ class User extends Authenticatable implements JWTSubject ]; protected $casts = [ - 'school_id' => 'integer', - 'failed_attempts' => 'integer', - 'last_failed_at' => 'datetime', - 'accept_school_policy' => 'boolean', - 'is_suspended' => 'boolean', - 'is_verified' => 'boolean', - 'created_at' => 'datetime', - 'updated_at' => 'datetime', + 'school_id' => 'integer', + 'failed_attempts' => 'integer', + 'last_failed_at' => 'datetime', + 'accept_school_policy' => 'boolean', + 'is_suspended' => 'boolean', + 'is_verified' => 'boolean', + 'created_at' => 'datetime', + 'updated_at' => 'datetime', ]; public function getJWTIdentifier() @@ -176,7 +176,7 @@ class User extends Authenticatable implements JWTSubject $pivotColumns[] = 'school_year'; } - if (!empty($pivotColumns)) { + if (! empty($pivotColumns)) { $relation->withPivot($pivotColumns); } @@ -218,7 +218,7 @@ class User extends Authenticatable implements JWTSubject public function scopeHasAnyRoleNames(Builder $q, array $roleNames): Builder { $names = array_values(array_unique(array_filter(array_map( - fn($v) => strtolower(trim((string)$v)), + fn ($v) => strtolower(trim((string) $v)), $roleNames )))); @@ -267,8 +267,10 @@ class User extends Authenticatable implements JWTSubject $order = strtolower($order) === 'desc' ? 'desc' : 'asc'; // whitelist to prevent SQL injection - $allowedSort = ['users.id','users.firstname','users.lastname','users.email','role']; - if (!in_array($sort, $allowedSort, true)) $sort = 'users.id'; + $allowedSort = ['users.id', 'users.firstname', 'users.lastname', 'users.email', 'role']; + if (! in_array($sort, $allowedSort, true)) { + $sort = 'users.id'; + } $q = DB::table('users') ->select('users.id', 'users.firstname', 'users.lastname', 'users.email', DB::raw('roles.name as role')) @@ -277,12 +279,12 @@ class User extends Authenticatable implements JWTSubject // sorting "role" alias requires orderByRaw if ($sort === 'role') { - $q->orderByRaw('role ' . strtoupper($order)); + $q->orderByRaw('role '.strtoupper($order)); } else { $q->orderBy($sort, $order); } - return $q->get()->map(fn($r) => (array) $r)->all(); + return $q->get()->map(fn ($r) => (array) $r)->all(); } /** @@ -312,14 +314,16 @@ class User extends Authenticatable implements JWTSubject // whitelist sort fields $allowedSort = [ - 'users.id','users.firstname','users.lastname','users.email','users.cellphone', - 'users.school_year','users.status','users.created_at','users.updated_at' + 'users.id', 'users.firstname', 'users.lastname', 'users.email', 'users.cellphone', + 'users.school_year', 'users.status', 'users.created_at', 'users.updated_at', ]; - if (!in_array($sort, $allowedSort, true)) $sort = 'users.id'; + if (! in_array($sort, $allowedSort, true)) { + $sort = 'users.id'; + } $q = static::query(); - if (!empty($conditions)) { + if (! empty($conditions)) { $q->where($conditions); } @@ -329,7 +333,7 @@ class User extends Authenticatable implements JWTSubject $q->skip($offset)->take($limit); } - return $q->get()->map(fn($u) => $u->toArray())->all(); + return $q->get()->map(fn ($u) => $u->toArray())->all(); } /** @@ -353,6 +357,7 @@ class User extends Authenticatable implements JWTSubject public static function getAllUsers(string $sort = 'users.id', string $order = 'asc') { $order = strtolower($order) === 'desc' ? 'desc' : 'asc'; + return static::query()->orderBy($sort, $order)->get(); } @@ -364,15 +369,17 @@ class User extends Authenticatable implements JWTSubject { $user = static::query() ->select([ - 'id','account_id','lastname','firstname','gender','cellphone','email', - 'address_street','apt','city','state','zip','accept_school_policy', - 'user_type','rfid_tag','school_id','failed_attempts','last_failed_at', - 'semester','school_year','status','is_suspended','created_at','updated_at', + 'id', 'account_id', 'lastname', 'firstname', 'gender', 'cellphone', 'email', + 'address_street', 'apt', 'city', 'state', 'zip', 'accept_school_policy', + 'user_type', 'rfid_tag', 'school_id', 'failed_attempts', 'last_failed_at', + 'semester', 'school_year', 'status', 'is_suspended', 'created_at', 'updated_at', ]) ->where('id', $userId) ->first(); - if (!$user) return null; + if (! $user) { + return null; + } $roles = DB::table('user_roles as ur') ->select('r.id', 'r.name', 'r.slug') @@ -380,7 +387,7 @@ class User extends Authenticatable implements JWTSubject ->where('ur.user_id', $userId) ->whereNull('ur.deleted_at') ->get() - ->map(fn($r) => (array) $r) + ->map(fn ($r) => (array) $r) ->all(); $arr = $user->toArray(); @@ -401,7 +408,7 @@ class User extends Authenticatable implements JWTSubject ->where('roles.name', $roleName) ->whereNull('user_roles.deleted_at') ->get() - ->map(fn($r) => (array) $r) + ->map(fn ($r) => (array) $r) ->all(); } @@ -418,7 +425,7 @@ class User extends Authenticatable implements JWTSubject ->where('users.school_year', $schoolYear) ->whereNull('user_roles.deleted_at') ->get() - ->map(fn($r) => (array) $r) + ->map(fn ($r) => (array) $r) ->all(); } @@ -445,6 +452,7 @@ class User extends Authenticatable implements JWTSubject public static function getSchoolIdByUserId(int $userId): ?int { $v = static::query()->where('id', $userId)->value('school_id'); + return $v === null ? null : (int) $v; } @@ -458,7 +466,9 @@ class User extends Authenticatable implements JWTSubject ->whereRaw('LOWER(name) = ?', ['parent']) ->value('id'); - if (!$parentRoleId) return []; + if (! $parentRoleId) { + return []; + } return DB::table('users') ->select('users.id', 'users.firstname', 'users.lastname', 'users.email', 'users.school_id') @@ -467,7 +477,7 @@ class User extends Authenticatable implements JWTSubject ->whereNull('user_roles.deleted_at') ->groupBy('users.id', 'users.firstname', 'users.lastname', 'users.email', 'users.school_id') ->get() - ->map(fn($r) => (array) $r) + ->map(fn ($r) => (array) $r) ->all(); } @@ -511,7 +521,9 @@ class User extends Authenticatable implements JWTSubject // whitelist sort fields $allowedSort = ['users.id', 'users.firstname', 'users.lastname', 'users.email', 'roles']; - if (!in_array($sort, $allowedSort, true)) $sort = 'users.lastname'; + if (! in_array($sort, $allowedSort, true)) { + $sort = 'users.lastname'; + } $q = DB::table('users') ->selectRaw(" @@ -526,12 +538,14 @@ class User extends Authenticatable implements JWTSubject ->whereNull('ur.deleted_at') ->whereNotIn(DB::raw('LOWER(r.name)'), ['parent', 'guest']); // drop parent/guest - if (!empty($selectedUserIds)) { + if (! empty($selectedUserIds)) { $ids = array_values(array_unique(array_filter(array_map('intval', $selectedUserIds)))); - if (!empty($ids)) $q->whereIn('users.id', $ids); + if (! empty($ids)) { + $q->whereIn('users.id', $ids); + } } - if (!empty($schoolYear)) { + if (! empty($schoolYear)) { // Prefer ur.school_year if it exists (role is year-scoped) $hasUrSchoolYear = false; try { @@ -581,7 +595,7 @@ class User extends Authenticatable implements JWTSubject $q->orderBy($sort, strtolower($order)); } - return $q->get()->map(fn($r) => (array) $r)->all(); + return $q->get()->map(fn ($r) => (array) $r)->all(); } /* ============================================================ @@ -601,7 +615,7 @@ class User extends Authenticatable implements JWTSubject ->orderBy('users.lastname', 'asc') ->orderBy('users.firstname', 'asc') ->get() - ->map(fn($u) => $u->toArray()) + ->map(fn ($u) => $u->toArray()) ->all(); } @@ -622,7 +636,7 @@ class User extends Authenticatable implements JWTSubject ->orderBy('u.lastname', 'asc') ->orderBy('u.firstname', 'asc') ->get() - ->map(fn($r) => (array) $r) + ->map(fn ($r) => (array) $r) ->all(); } @@ -664,7 +678,7 @@ class User extends Authenticatable implements JWTSubject ->orderBy('users.lastname', 'asc') ->orderBy('users.firstname', 'asc') ->get() - ->map(fn($r) => (array) $r) + ->map(fn ($r) => (array) $r) ->all(); } @@ -676,15 +690,15 @@ class User extends Authenticatable implements JWTSubject public static function registrationRules(): array { return [ - 'password' => ['required', 'string', 'min:8', 'regex:/[A-Z]/', 'regex:/[a-z]/', 'regex:/[0-9]/', 'regex:/[\W_]/'], - 'lastname' => ['required', 'string', 'max:120'], - 'firstname' => ['required', 'string', 'max:120'], - 'cellphone' => ['required', 'regex:/^\d{10}$/'], - 'email' => ['required', 'email', 'max:255'], - 'address_street' => ['required', 'string', 'max:255'], - 'city' => ['required', 'string', 'max:120'], - 'state' => ['required', 'regex:/^[A-Z]{2}$/'], - 'zip' => ['required', 'regex:/^\d{5}(-\d{4})?$/'], + 'password' => ['required', 'string', 'min:8', 'regex:/[A-Z]/', 'regex:/[a-z]/', 'regex:/[0-9]/', 'regex:/[\W_]/'], + 'lastname' => ['required', 'string', 'max:120'], + 'firstname' => ['required', 'string', 'max:120'], + 'cellphone' => ['required', 'regex:/^\d{10}$/'], + 'email' => ['required', 'email', 'max:255'], + 'address_street' => ['required', 'string', 'max:255'], + 'city' => ['required', 'string', 'max:120'], + 'state' => ['required', 'regex:/^[A-Z]{2}$/'], + 'zip' => ['required', 'regex:/^\d{5}(-\d{4})?$/'], 'accept_school_policy' => ['required', 'in:1,true,on'], ]; } @@ -706,7 +720,9 @@ class User extends Authenticatable implements JWTSubject */ public function setPasswordAttribute($value): void { - if ($value === null || $value === '') return; + if ($value === null || $value === '') { + return; + } $value = (string) $value; $hashInfo = password_get_info($value); diff --git a/app/Models/UserNotification.php b/app/Models/UserNotification.php index bbd394a3..b2bcd8bc 100644 --- a/app/Models/UserNotification.php +++ b/app/Models/UserNotification.php @@ -3,7 +3,6 @@ namespace App\Models; use Illuminate\Database\Eloquent\Builder; -use App\Models\BaseModel; use Illuminate\Database\Eloquent\Relations\BelongsTo; class UserNotification extends BaseModel @@ -25,10 +24,10 @@ class UserNotification extends BaseModel protected $casts = [ 'notification_id' => 'integer', - 'user_id' => 'integer', - 'is_read' => 'boolean', - 'delivered' => 'boolean', - 'delivered_at' => 'datetime', + 'user_id' => 'integer', + 'is_read' => 'boolean', + 'delivered' => 'boolean', + 'delivered_at' => 'datetime', ]; /* ============================================================ @@ -112,10 +111,10 @@ class UserNotification extends BaseModel return [ 'notification_id' => [$req, 'integer', 'min:1', 'exists:notifications,id'], - 'user_id' => [$req, 'integer', 'min:1', 'exists:users,id'], - 'is_read' => ['nullable', 'boolean'], - 'delivered' => ['nullable', 'boolean'], - 'delivered_at' => ['nullable', 'date'], + 'user_id' => [$req, 'integer', 'min:1', 'exists:users,id'], + 'is_read' => ['nullable', 'boolean'], + 'delivered' => ['nullable', 'boolean'], + 'delivered_at' => ['nullable', 'date'], ]; } -} \ No newline at end of file +} diff --git a/app/Models/UserRole.php b/app/Models/UserRole.php index 0c65ccce..383157b1 100644 --- a/app/Models/UserRole.php +++ b/app/Models/UserRole.php @@ -3,9 +3,8 @@ namespace App\Models; use Illuminate\Database\Eloquent\Builder; -use App\Models\BaseModel; -use Illuminate\Database\Eloquent\SoftDeletes; use Illuminate\Database\Eloquent\Relations\BelongsTo; +use Illuminate\Database\Eloquent\SoftDeletes; use Illuminate\Support\Facades\DB; class UserRole extends BaseModel @@ -28,12 +27,12 @@ class UserRole extends BaseModel ]; protected $casts = [ - 'user_id' => 'integer', - 'role_id' => 'integer', - 'updated_by' => 'integer', - 'created_at' => 'datetime', - 'updated_at' => 'datetime', - 'deleted_at' => 'datetime', + 'user_id' => 'integer', + 'role_id' => 'integer', + 'updated_by' => 'integer', + 'created_at' => 'datetime', + 'updated_at' => 'datetime', + 'deleted_at' => 'datetime', ]; /* ============================================================ @@ -68,7 +67,10 @@ class UserRole extends BaseModel public function scopeForSchoolYear(Builder $q, ?string $schoolYear): Builder { - if ($schoolYear === null || trim($schoolYear) === '') return $q; + if ($schoolYear === null || trim($schoolYear) === '') { + return $q; + } + return $q->where('school_year', $schoolYear); } @@ -118,12 +120,13 @@ class UserRole extends BaseModel $existing->updated_by = $editorId; $existing->save(); } + return true; } static::query()->create([ - 'user_id' => $userId, - 'role_id' => $roleId, + 'user_id' => $userId, + 'role_id' => $roleId, 'updated_by' => $editorId, ]); @@ -170,11 +173,11 @@ class UserRole extends BaseModel $req = $updating ? 'sometimes' : 'required'; return [ - 'user_id' => [$req, 'integer', 'min:1', 'exists:users,id'], - 'role_id' => [$req, 'integer', 'min:1', 'exists:roles,id'], - 'semester' => ['nullable', 'string', 'max:20'], - 'school_year'=> ['nullable', 'string', 'max:20'], + 'user_id' => [$req, 'integer', 'min:1', 'exists:users,id'], + 'role_id' => [$req, 'integer', 'min:1', 'exists:roles,id'], + 'semester' => ['nullable', 'string', 'max:20'], + 'school_year' => ['nullable', 'string', 'max:20'], 'updated_by' => ['nullable', 'integer'], ]; } -} \ No newline at end of file +} diff --git a/app/Models/WhatsappGroupLink.php b/app/Models/WhatsappGroupLink.php index 2dcfe772..5fea9dfb 100644 --- a/app/Models/WhatsappGroupLink.php +++ b/app/Models/WhatsappGroupLink.php @@ -3,7 +3,6 @@ namespace App\Models; use Illuminate\Database\Eloquent\Builder; -use App\Models\BaseModel; class WhatsappGroupLink extends BaseModel { @@ -17,13 +16,14 @@ class WhatsappGroupLink extends BaseModel 'invite_link', 'active', ]; + public $timestamps = true; // requires created_at/updated_at protected $casts = [ 'class_section_id' => 'integer', - 'active' => 'boolean', - 'created_at' => 'datetime', - 'updated_at' => 'datetime', + 'active' => 'boolean', + 'created_at' => 'datetime', + 'updated_at' => 'datetime', ]; /* ============================================================ @@ -52,7 +52,7 @@ class WhatsappGroupLink extends BaseModel if ($allowNullSemester) { return $q->where(function (Builder $w) use ($sem) { $w->where('semester', $sem) - ->orWhereNull('semester'); + ->orWhereNull('semester'); }); } @@ -72,7 +72,7 @@ class WhatsappGroupLink extends BaseModel public function scopeHasInviteLink(Builder $q): Builder { return $q->whereNotNull('invite_link') - ->where('invite_link', '!=', ''); + ->where('invite_link', '!=', ''); } /* ============================================================ @@ -83,11 +83,11 @@ class WhatsappGroupLink extends BaseModel /** * Get the link for a specific section in a specific term. * - * @param int $sectionId Class/section code (not PK). - * @param string $year School year, e.g. "2025-2026". - * @param string $sem Semester, e.g. "Fall". - * @param bool $onlyActive If true, require active=1. - * @param bool $allowNullSemester If true, accept rows with semester IS NULL as well. + * @param int $sectionId Class/section code (not PK). + * @param string $year School year, e.g. "2025-2026". + * @param string $sem Semester, e.g. "Fall". + * @param bool $onlyActive If true, require active=1. + * @param bool $allowNullSemester If true, accept rows with semester IS NULL as well. */ public static function getLinkForSection( int $sectionId, @@ -114,11 +114,8 @@ class WhatsappGroupLink extends BaseModel /** * Get all links for a term. * - * @param string $year - * @param string $sem - * @param bool|null $onlyActive true: active only, false: inactive only, null: both - * @param bool $allowNullSemester If true, include rows with semester IS NULL. - * + * @param bool|null $onlyActive true: active only, false: inactive only, null: both + * @param bool $allowNullSemester If true, include rows with semester IS NULL. * @return array */ public static function getAllForTerm( @@ -157,16 +154,16 @@ class WhatsappGroupLink extends BaseModel ): self { $payload = [ 'class_section_name' => $sectionName, - 'invite_link' => trim($inviteLink), - 'active' => $active ? 1 : 0, + 'invite_link' => trim($inviteLink), + 'active' => $active ? 1 : 0, ]; // NOTE: semester is part of the uniqueness key like your legacy version. return static::query()->updateOrCreate( [ 'class_section_id' => $sectionId, - 'school_year' => trim($year), - 'semester' => trim($sem), + 'school_year' => trim($year), + 'semester' => trim($sem), ], $payload ); @@ -182,12 +179,12 @@ class WhatsappGroupLink extends BaseModel $req = $updating ? 'sometimes' : 'required'; return [ - 'class_section_id' => [$req, 'integer', 'min:1'], + 'class_section_id' => [$req, 'integer', 'min:1'], 'class_section_name' => [$req, 'string', 'max:255'], - 'school_year' => [$req, 'string', 'max:20'], - 'semester' => ['nullable', 'string', 'max:20'], - 'invite_link' => [$req, 'string', 'max:2000'], - 'active' => ['nullable', 'boolean'], + 'school_year' => [$req, 'string', 'max:20'], + 'semester' => ['nullable', 'string', 'max:20'], + 'invite_link' => [$req, 'string', 'max:2000'], + 'active' => ['nullable', 'boolean'], ]; } -} \ No newline at end of file +} diff --git a/app/Models/WhatsappGroupMembership.php b/app/Models/WhatsappGroupMembership.php index 6467f542..51ee49a5 100644 --- a/app/Models/WhatsappGroupMembership.php +++ b/app/Models/WhatsappGroupMembership.php @@ -3,7 +3,6 @@ namespace App\Models; use Illuminate\Database\Eloquent\Builder; -use App\Models\BaseModel; class WhatsappGroupMembership extends BaseModel { @@ -21,16 +20,17 @@ class WhatsappGroupMembership extends BaseModel 'verified_by', 'verified_at', ]; + public $timestamps = true; // created_at, updated_at protected $casts = [ 'class_section_id' => 'integer', - 'subject_id' => 'integer', - 'is_member' => 'boolean', - 'verified_by' => 'integer', - 'verified_at' => 'datetime', - 'created_at' => 'datetime', - 'updated_at' => 'datetime', + 'subject_id' => 'integer', + 'is_member' => 'boolean', + 'verified_by' => 'integer', + 'verified_at' => 'datetime', + 'created_at' => 'datetime', + 'updated_at' => 'datetime', ]; /* ============================================================ @@ -38,7 +38,8 @@ class WhatsappGroupMembership extends BaseModel * ============================================================ */ public const TYPE_PRIMARY = 'primary'; - public const TYPE_SECOND = 'second'; + + public const TYPE_SECOND = 'second'; public static function normType(string $type): string { @@ -53,6 +54,7 @@ class WhatsappGroupMembership extends BaseModel public function scopeForSections(Builder $q, array $sectionIds): Builder { $ids = array_values(array_unique(array_filter(array_map('intval', $sectionIds)))); + return empty($ids) ? $q->whereRaw('1=0') : $q->whereIn('class_section_id', $ids); } @@ -64,13 +66,14 @@ class WhatsappGroupMembership extends BaseModel public function scopeForSemester(Builder $q, string $sem): Builder { $sem = trim($sem); + return $sem === '' ? $q : $q->where('semester', $sem); } public function scopeForSubject(Builder $q, string $type, int $subjectId): Builder { return $q->where('subject_type', static::normType($type)) - ->where('subject_id', $subjectId); + ->where('subject_id', $subjectId); } /* ============================================================ @@ -85,10 +88,12 @@ class WhatsappGroupMembership extends BaseModel public static function getBySectionsAndTerm(array $sectionIds, string $year, string $sem): array { $ids = array_values(array_unique(array_filter(array_map('intval', $sectionIds)))); - if (empty($ids)) return []; + if (empty($ids)) { + return []; + } $year = trim($year); - $sem = trim($sem); + $sem = trim($sem); $q = static::query() ->forSections($ids) @@ -105,8 +110,8 @@ class WhatsappGroupMembership extends BaseModel $out = []; foreach ($rows as $m) { - $k = sprintf('%d:%s:%d', (int)$m->class_section_id, (string)$m->subject_type, (int)$m->subject_id); - if (!isset($out[$k])) { + $k = sprintf('%d:%s:%d', (int) $m->class_section_id, (string) $m->subject_type, (int) $m->subject_id); + if (! isset($out[$k])) { $out[$k] = $m->toArray(); } } @@ -134,15 +139,15 @@ class WhatsappGroupMembership extends BaseModel ): int { $type = static::normType($type); $year = trim($year); - $sem = trim($sem); + $sem = trim($sem); $payload = [ 'class_section_id' => $classSectionId, - 'school_year' => $year, - 'semester' => $sem, - 'subject_type' => $type, - 'subject_id' => $subjectId, - 'is_member' => $isMember ? 1 : 0, + 'school_year' => $year, + 'semester' => $sem, + 'subject_type' => $type, + 'subject_id' => $subjectId, + 'is_member' => $isMember ? 1 : 0, ]; if ($verifiedBy) { @@ -155,13 +160,14 @@ class WhatsappGroupMembership extends BaseModel $row = static::query()->updateOrCreate( [ 'class_section_id' => $classSectionId, - 'school_year' => $year, - 'semester' => $sem, - 'subject_type' => $type, - 'subject_id' => $subjectId, + 'school_year' => $year, + 'semester' => $sem, + 'subject_type' => $type, + 'subject_id' => $subjectId, ], $payload ); + return (int) $row->id; } @@ -177,7 +183,7 @@ class WhatsappGroupMembership extends BaseModel if ($existing) { // Keep semester from the existing row if present (legacy behavior). - if (!empty($existing->semester)) { + if (! empty($existing->semester)) { $payload['semester'] = (string) $existing->semester; } @@ -189,6 +195,7 @@ class WhatsappGroupMembership extends BaseModel // No existing row => create a new one (semester remains blank) $created = static::query()->create($payload); + return (int) $created->id; } @@ -203,13 +210,13 @@ class WhatsappGroupMembership extends BaseModel return [ 'class_section_id' => [$req, 'integer', 'min:1'], - 'school_year' => [$req, 'string', 'max:20'], - 'semester' => ['nullable', 'string', 'max:20'], - 'subject_type' => [$req, 'string', 'in:' . implode(',', [self::TYPE_PRIMARY, self::TYPE_SECOND])], - 'subject_id' => [$req, 'integer', 'min:1'], - 'is_member' => ['nullable', 'boolean'], - 'verified_by' => ['nullable', 'integer'], - 'verified_at' => ['nullable', 'date'], + 'school_year' => [$req, 'string', 'max:20'], + 'semester' => ['nullable', 'string', 'max:20'], + 'subject_type' => [$req, 'string', 'in:'.implode(',', [self::TYPE_PRIMARY, self::TYPE_SECOND])], + 'subject_id' => [$req, 'integer', 'min:1'], + 'is_member' => ['nullable', 'boolean'], + 'verified_by' => ['nullable', 'integer'], + 'verified_at' => ['nullable', 'date'], ]; } -} \ No newline at end of file +} diff --git a/app/Models/WhatsappInviteLog.php b/app/Models/WhatsappInviteLog.php index 0c966510..76ae6705 100644 --- a/app/Models/WhatsappInviteLog.php +++ b/app/Models/WhatsappInviteLog.php @@ -3,7 +3,6 @@ namespace App\Models; use Illuminate\Database\Eloquent\Builder; -use App\Models\BaseModel; use Illuminate\Database\Eloquent\Relations\BelongsTo; class WhatsappInviteLog extends BaseModel @@ -26,17 +25,18 @@ class WhatsappInviteLog extends BaseModel ]; protected $casts = [ - 'parent_id' => 'integer', + 'parent_id' => 'integer', 'class_section_id' => 'integer', - 'link_id' => 'integer', - 'sent_at' => 'datetime', + 'link_id' => 'integer', + 'sent_at' => 'datetime', ]; /* ============================================================ * Status constants (optional but helpful) * ============================================================ */ - public const STATUS_SENT = 'sent'; + public const STATUS_SENT = 'sent'; + public const STATUS_FAILED = 'failed'; /* ============================================================ @@ -103,13 +103,13 @@ class WhatsappInviteLog extends BaseModel ?int $linkId = null ): bool { static::query()->create([ - 'parent_id' => $parentId, - 'email' => $email, + 'parent_id' => $parentId, + 'email' => $email, 'class_section_id' => $classSectionId, - 'link_id' => $linkId, - 'status' => self::STATUS_SENT, - 'error_message' => null, - 'sent_at' => now(), + 'link_id' => $linkId, + 'status' => self::STATUS_SENT, + 'error_message' => null, + 'sent_at' => now(), ]); return true; @@ -126,13 +126,13 @@ class WhatsappInviteLog extends BaseModel ?int $linkId = null ): bool { static::query()->create([ - 'parent_id' => $parentId, - 'email' => $email, + 'parent_id' => $parentId, + 'email' => $email, 'class_section_id' => $classSectionId, - 'link_id' => $linkId, - 'status' => self::STATUS_FAILED, - 'error_message' => $error, - 'sent_at' => now(), + 'link_id' => $linkId, + 'status' => self::STATUS_FAILED, + 'error_message' => $error, + 'sent_at' => now(), ]); return true; @@ -167,13 +167,13 @@ class WhatsappInviteLog extends BaseModel $req = $updating ? 'sometimes' : 'required'; return [ - 'parent_id' => [$req, 'integer', 'min:1'], - 'email' => [$req, 'email', 'max:255'], - 'status' => [$req, 'string', 'max:20'], + 'parent_id' => [$req, 'integer', 'min:1'], + 'email' => [$req, 'email', 'max:255'], + 'status' => [$req, 'string', 'max:20'], 'class_section_id' => ['nullable', 'integer'], - 'link_id' => ['nullable', 'integer'], - 'error_message' => ['nullable', 'string', 'max:5000'], - 'sent_at' => ['nullable', 'date'], + 'link_id' => ['nullable', 'integer'], + 'error_message' => ['nullable', 'string', 'max:5000'], + 'sent_at' => ['nullable', 'date'], ]; } -} \ No newline at end of file +} diff --git a/app/Policies/NavItemPolicy.php b/app/Policies/NavItemPolicy.php index f6022037..44c57ee6 100644 --- a/app/Policies/NavItemPolicy.php +++ b/app/Policies/NavItemPolicy.php @@ -2,7 +2,6 @@ namespace App\Policies; -use App\Models\NavItem; use App\Models\User; class NavItemPolicy @@ -10,6 +9,7 @@ class NavItemPolicy public function manage(User $user): bool { $roles = $user->roles()->pluck('roles.name')->map(fn ($name) => strtolower((string) $name))->toArray(); + return in_array('admin', $roles, true) || in_array('administrator', $roles, true); } } diff --git a/app/Providers/AppServiceProvider.php b/app/Providers/AppServiceProvider.php index 2b03f011..742d555a 100644 --- a/app/Providers/AppServiceProvider.php +++ b/app/Providers/AppServiceProvider.php @@ -2,42 +2,42 @@ namespace App\Providers; +use App\Models\ClassProgressReport; +use App\Models\ClassSection; +use App\Models\IpAttempt; +use App\Models\LateSlipLog; +use App\Models\Message; +use App\Models\NavItem; +use App\Models\Preferences; +use App\Models\Setting; +use App\Models\Staff; +use App\Models\SupportRequest; +use App\Policies\ClassProgressReportPolicy; +use App\Policies\ClassSectionPolicy; +use App\Policies\IpAttemptPolicy; +use App\Policies\LateSlipLogPolicy; +use App\Policies\MessagePolicy; +use App\Policies\NavItemPolicy; +use App\Policies\PreferencesPolicy; +use App\Policies\SettingPolicy; +use App\Policies\StaffPolicy; +use App\Policies\SupportRequestPolicy; +use App\Services\ApplicationUrlService; +use App\Services\Attendance\AttendanceAutoPublishService; use App\Services\Attendance\AttendancePolicyService; use App\Services\Attendance\AttendanceRecordSyncService; use App\Services\Attendance\SemesterRangeService; use App\Services\Attendance\StudentAttendanceWriterService; use App\Services\Attendance\TeacherAttendanceSubmissionService; -use App\Services\Attendance\AttendanceAutoPublishService; use App\Services\AttendanceTracking\AttendanceMailerService; use App\Services\AttendanceTracking\DefaultAttendanceMailerService; use App\Services\Invoices\InvoiceConfigService; use App\Services\Invoices\InvoiceGradeService; use App\Services\Invoices\InvoiceTuitionService; use App\Services\Staff\StaffTimeOffLinkService; -use App\Models\NavItem; -use App\Models\ClassSection; -use App\Models\IpAttempt; -use App\Models\LateSlipLog; -use App\Models\Message; -use App\Models\Preferences; -use App\Models\Staff; -use App\Models\SupportRequest; -use App\Models\Setting; -use App\Models\ClassProgressReport; -use App\Policies\NavItemPolicy; -use App\Policies\ClassSectionPolicy; -use App\Policies\IpAttemptPolicy; -use App\Policies\LateSlipLogPolicy; -use App\Policies\MessagePolicy; -use App\Policies\PreferencesPolicy; -use App\Policies\StaffPolicy; -use App\Policies\SupportRequestPolicy; -use App\Policies\SettingPolicy; -use App\Policies\ClassProgressReportPolicy; -use Illuminate\Support\Facades\Gate; use Illuminate\Foundation\Http\FormRequest; use Illuminate\Routing\Redirector; -use App\Services\ApplicationUrlService; +use Illuminate\Support\Facades\Gate; use Illuminate\Support\ServiceProvider; class AppServiceProvider extends ServiceProvider @@ -58,10 +58,12 @@ class AppServiceProvider extends ServiceProvider $this->app->singleton(InvoiceConfigService::class); $this->app->bind(InvoiceGradeService::class, function ($app) { $config = $app->make(InvoiceConfigService::class); + return new InvoiceGradeService($config->getGradeFee()); }); $this->app->bind(InvoiceTuitionService::class, function ($app) { $config = $app->make(InvoiceConfigService::class); + return new InvoiceTuitionService( $app->make(InvoiceGradeService::class), $config->getGradeFee(), diff --git a/app/Providers/EventServiceProvider.php b/app/Providers/EventServiceProvider.php index a7435f14..871de827 100644 --- a/app/Providers/EventServiceProvider.php +++ b/app/Providers/EventServiceProvider.php @@ -2,8 +2,8 @@ namespace App\Providers; -use App\Events\WhatsappInvitesSend; use App\Events\DeleteUnverifiedUser; +use App\Events\WhatsappInvitesSend; use App\Listeners\AttendanceConsequenceListener; use App\Listeners\BelowSixtyEmailListener; use App\Listeners\DeleteUnverifiedUserListener; @@ -15,94 +15,94 @@ class EventServiceProvider extends ServiceProvider { protected $listen = [ 'attendance.follow_up' => [ - AttendanceConsequenceListener::class . '@followUp', + AttendanceConsequenceListener::class.'@followUp', ], 'attendance.final_warning' => [ - AttendanceConsequenceListener::class . '@finalWarning', + AttendanceConsequenceListener::class.'@finalWarning', ], 'attendance.dismissal' => [ - AttendanceConsequenceListener::class . '@dismissal', + AttendanceConsequenceListener::class.'@dismissal', ], 'below60.email' => [ BelowSixtyEmailListener::class, ], 'admissionUnderReview' => [ - SchoolEventListener::class . '@handleAdmissionUnderReview', + SchoolEventListener::class.'@handleAdmissionUnderReview', ], 'paymentPending' => [ - SchoolEventListener::class . '@handlePaymentPending', + SchoolEventListener::class.'@handlePaymentPending', ], 'studentEnrolled' => [ - SchoolEventListener::class . '@handleStudentEnrolled', + SchoolEventListener::class.'@handleStudentEnrolled', ], 'withdrawUnderReview' => [ - SchoolEventListener::class . '@handleWithdrawUnderReview', + SchoolEventListener::class.'@handleWithdrawUnderReview', ], 'refundPending' => [ - SchoolEventListener::class . '@handleRefundPending', + SchoolEventListener::class.'@handleRefundPending', ], 'withdrawn' => [ - SchoolEventListener::class . '@handleWithdrawn', + SchoolEventListener::class.'@handleWithdrawn', ], 'denied' => [ - SchoolEventListener::class . '@handleDenied', + SchoolEventListener::class.'@handleDenied', ], 'waitlist' => [ - SchoolEventListener::class . '@handleWaitlist', + SchoolEventListener::class.'@handleWaitlist', ], 'enrollmentStatusChanged' => [ - SchoolEventListener::class . '@handleEnrollmentStatusChanged', + SchoolEventListener::class.'@handleEnrollmentStatusChanged', ], 'paymentReceived' => [ - SchoolEventListener::class . '@handlePaymentReceived', + SchoolEventListener::class.'@handlePaymentReceived', ], 'extraCharge' => [ - SchoolEventListener::class . '@handleExtraCharge', + SchoolEventListener::class.'@handleExtraCharge', ], 'newAccountAdded' => [ - SchoolEventListener::class . '@handleNewAccountAdded', + SchoolEventListener::class.'@handleNewAccountAdded', ], 'studentRegistered' => [ - SchoolEventListener::class . '@handleStudentRegistered', + SchoolEventListener::class.'@handleStudentRegistered', ], 'delete_unverified_user' => [ - SchoolEventListener::class . '@handleDeleteUnverifiedUser', + SchoolEventListener::class.'@handleDeleteUnverifiedUser', ], 'userRegistered' => [ - SchoolEventListener::class . '@handleUserRegistered', + SchoolEventListener::class.'@handleUserRegistered', ], 'userProfileUpdated' => [ - SchoolEventListener::class . '@handleUserProfileUpdated', + SchoolEventListener::class.'@handleUserProfileUpdated', ], 'userDeactivated' => [ - SchoolEventListener::class . '@handleUserDeactivated', + SchoolEventListener::class.'@handleUserDeactivated', ], 'scoresPosted' => [ - SchoolEventListener::class . '@handleScoresPosted', + SchoolEventListener::class.'@handleScoresPosted', ], 'finalScoreReleased' => [ - SchoolEventListener::class . '@handleFinalScoreReleased', + SchoolEventListener::class.'@handleFinalScoreReleased', ], 'studentMarkedAbsent' => [ - SchoolEventListener::class . '@handleStudentMarkedAbsent', + SchoolEventListener::class.'@handleStudentMarkedAbsent', ], 'studentMarkedLate' => [ - SchoolEventListener::class . '@handleStudentMarkedLate', + SchoolEventListener::class.'@handleStudentMarkedLate', ], 'paymentMissed' => [ - SchoolEventListener::class . '@handlePaymentMissed', + SchoolEventListener::class.'@handlePaymentMissed', ], 'classScheduleUpdated' => [ - SchoolEventListener::class . '@handleClassScheduleUpdated', + SchoolEventListener::class.'@handleClassScheduleUpdated', ], 'newMessageReceived' => [ - SchoolEventListener::class . '@handleNewMessageReceived', + SchoolEventListener::class.'@handleNewMessageReceived', ], 'systemAnnouncementPosted' => [ - SchoolEventListener::class . '@handleSystemAnnouncementPosted', + SchoolEventListener::class.'@handleSystemAnnouncementPosted', ], 'customNotification' => [ - SchoolEventListener::class . '@handleCustomNotification', + SchoolEventListener::class.'@handleCustomNotification', ], DeleteUnverifiedUser::class => [ DeleteUnverifiedUserListener::class, diff --git a/app/Services/Admin/CompetitionWinners/CompetitionWinnersAdminService.php b/app/Services/Admin/CompetitionWinners/CompetitionWinnersAdminService.php index f8358354..7de223bf 100644 --- a/app/Services/Admin/CompetitionWinners/CompetitionWinnersAdminService.php +++ b/app/Services/Admin/CompetitionWinners/CompetitionWinnersAdminService.php @@ -2,17 +2,19 @@ namespace App\Services\Admin\CompetitionWinners; +use App\Controllers\Admin\CompetitionWinnersController; use App\Models\ClassSection; use App\Models\Competition; use App\Models\CompetitionClassWinner; use App\Models\CompetitionScore; use App\Models\CompetitionWinner; use App\Models\Configuration; +use Illuminate\Support\Collection; use Illuminate\Support\Facades\DB; use Illuminate\Support\Facades\Schema; /** - * Administrative competition-winner workflows (ported from legacy {@see \App\Controllers\Admin\CompetitionWinnersController}). + * Administrative competition-winner workflows (ported from legacy {@see CompetitionWinnersController}). */ class CompetitionWinnersAdminService { @@ -180,6 +182,7 @@ class CompetitionWinnersAdminService if ($existing) { $existing->delete(); } + continue; } @@ -253,7 +256,7 @@ class CompetitionWinnersAdminService return $out; } - /** @return array{competitions: \Illuminate\Support\Collection, sectionMap: array} */ + /** @return array{competitions: Collection, sectionMap: array} */ public function indexData(): array { $competitions = Competition::query()->orderByDesc('id')->get(); diff --git a/app/Services/Admin/CompetitionWinners/CompetitionWinnersDomain.php b/app/Services/Admin/CompetitionWinners/CompetitionWinnersDomain.php index f0a1922a..a895943e 100644 --- a/app/Services/Admin/CompetitionWinners/CompetitionWinnersDomain.php +++ b/app/Services/Admin/CompetitionWinners/CompetitionWinnersDomain.php @@ -138,12 +138,14 @@ final class CompetitionWinnersDomain $override = $overrides[$classId] ?? null; if ($override !== null) { $map[$classId] = (int) $override; + continue; } $baseCount = $classCounts[$classId] ?? ($scoreCounts[$classId] ?? 0); if ($baseCount <= 0) { $map[$classId] = 0; + continue; } diff --git a/app/Services/Administrator/AdminNotificationSubjectService.php b/app/Services/Administrator/AdminNotificationSubjectService.php index 473bd07c..166a1ca4 100644 --- a/app/Services/Administrator/AdminNotificationSubjectService.php +++ b/app/Services/Administrator/AdminNotificationSubjectService.php @@ -9,8 +9,7 @@ class AdminNotificationSubjectService { public function __construct( protected AdminNotificationUserService $userService - ) { - } + ) {} public function subjectOptions(): array { @@ -31,7 +30,7 @@ class AdminNotificationSubjectService $assignedSubjects = []; $tableReady = DB::getSchemaBuilder()->hasTable('admin_notification_subjects'); - if ($tableReady && !empty($admins)) { + if ($tableReady && ! empty($admins)) { $adminIds = array_map('intval', array_column($admins, 'id')); $rows = AdminNotificationSubject::query() @@ -59,7 +58,7 @@ class AdminNotificationSubjectService public function save(array $posted): array { - if (!DB::getSchemaBuilder()->hasTable('admin_notification_subjects')) { + if (! DB::getSchemaBuilder()->hasTable('admin_notification_subjects')) { return [ 'success' => false, 'message' => 'Notification subject storage is missing. Run migrations first.', @@ -102,7 +101,7 @@ class AdminNotificationSubjectService if (is_array($subjectRaw)) { foreach ($subjectRaw as $key => $value) { $candidate = is_string($key) ? $key : $value; - if (!is_string($candidate)) { + if (! is_string($candidate)) { continue; } $candidate = trim($candidate); @@ -118,7 +117,7 @@ class AdminNotificationSubjectService $toDelete = array_values(array_diff($current, $selected)); $toInsert = array_values(array_diff($selected, $current)); - if (!empty($toDelete)) { + if (! empty($toDelete)) { AdminNotificationSubject::query() ->where('admin_id', $adminId) ->whereIn('subject', $toDelete) @@ -126,7 +125,7 @@ class AdminNotificationSubjectService $updates += count($toDelete); } - if (!empty($toInsert)) { + if (! empty($toInsert)) { $batch = []; foreach ($toInsert as $subject) { $batch[] = [ @@ -147,4 +146,4 @@ class AdminNotificationSubjectService 'status' => 200, ]; } -} \ No newline at end of file +} diff --git a/app/Services/Administrator/AdminNotificationUserService.php b/app/Services/Administrator/AdminNotificationUserService.php index 4cda5390..004216f8 100644 --- a/app/Services/Administrator/AdminNotificationUserService.php +++ b/app/Services/Administrator/AdminNotificationUserService.php @@ -37,7 +37,7 @@ class AdminNotificationUserService ->orderBy('u.lastname') ->orderBy('u.firstname') ->get() - ->map(fn($r) => (array) $r) + ->map(fn ($r) => (array) $r) ->all(); } -} \ No newline at end of file +} diff --git a/app/Services/Administrator/AdminPrintRecipientService.php b/app/Services/Administrator/AdminPrintRecipientService.php index da316754..0dae8fae 100644 --- a/app/Services/Administrator/AdminPrintRecipientService.php +++ b/app/Services/Administrator/AdminPrintRecipientService.php @@ -9,8 +9,7 @@ class AdminPrintRecipientService { public function __construct( protected AdminNotificationUserService $userService - ) { - } + ) {} public function data(): array { @@ -18,7 +17,7 @@ class AdminPrintRecipientService $tableReady = DB::getSchemaBuilder()->hasTable('admin_notification_subjects'); $assigned = []; - if ($tableReady && !empty($admins)) { + if ($tableReady && ! empty($admins)) { $adminIds = array_map('intval', array_column($admins, 'id')); $rows = AdminNotificationSubject::query() @@ -44,7 +43,7 @@ class AdminPrintRecipientService public function save(array $posted): array { - if (!DB::getSchemaBuilder()->hasTable('admin_notification_subjects')) { + if (! DB::getSchemaBuilder()->hasTable('admin_notification_subjects')) { return [ 'success' => false, 'message' => 'Notification subject storage is missing. Run migrations first.', @@ -77,7 +76,7 @@ class AdminPrintRecipientService ->where('subject', 'print_requests') ->whereIn('admin_id', $adminIds) ->pluck('admin_id') - ->map(fn($id) => (int) $id) + ->map(fn ($id) => (int) $id) ->unique() ->values() ->all(); @@ -85,14 +84,14 @@ class AdminPrintRecipientService $toDelete = array_values(array_diff($current, $selected)); $toInsert = array_values(array_diff($selected, $current)); - if (!empty($toDelete)) { + if (! empty($toDelete)) { AdminNotificationSubject::query() ->where('subject', 'print_requests') ->whereIn('admin_id', $toDelete) ->delete(); } - if (!empty($toInsert)) { + if (! empty($toInsert)) { $batch = []; foreach ($toInsert as $adminId) { $batch[] = [ @@ -113,4 +112,4 @@ class AdminPrintRecipientService 'status' => 200, ]; } -} \ No newline at end of file +} diff --git a/app/Services/Administrator/AdministratorAbsenceService.php b/app/Services/Administrator/AdministratorAbsenceService.php index b15c51a0..87a92609 100644 --- a/app/Services/Administrator/AdministratorAbsenceService.php +++ b/app/Services/Administrator/AdministratorAbsenceService.php @@ -2,11 +2,12 @@ namespace App\Services\Administrator; -use App\Services\ApplicationUrlService; -use App\Services\Staff\StaffTimeOffLinkService; use App\Models\StaffAttendance; use App\Models\User; +use App\Services\ApplicationUrlService; use App\Services\SemesterRangeService; +use App\Services\Staff\StaffTimeOffLinkService; +use Carbon\Carbon; use Illuminate\Http\Request; use Illuminate\Support\Facades\Log; use Illuminate\Support\Facades\Mail; @@ -20,14 +21,13 @@ class AdministratorAbsenceService protected SemesterRangeService $semesterRangeService, protected StaffTimeOffLinkService $staffTimeOffLinkService, protected ApplicationUrlService $urls, - ) { - } + ) {} public function getAbsenceFormData(int $userId): array { $admin = $this->userModel->find($userId); $displayName = $admin - ? trim(($admin->firstname ?? '') . ' ' . ($admin->lastname ?? '')) + ? trim(($admin->firstname ?? '').' '.($admin->lastname ?? '')) : 'Administrator'; $semester = $this->shared->getSemester(); @@ -76,7 +76,7 @@ class AdministratorAbsenceService } $reasonBase = $reasonType !== '' ? strtolower($reasonType) : ''; - $reason = $reasonBase !== '' ? ($reasonBase . ': ' . $reasonText) : $reasonText; + $reason = $reasonBase !== '' ? ($reasonBase.': '.$reasonText) : $reasonText; $allowedSet = array_fill_keys($this->shared->allowedAbsenceDates(), true); @@ -97,13 +97,15 @@ class AdministratorAbsenceService } try { - $parsed = \Carbon\Carbon::createFromFormat('Y-m-d', $d); + $parsed = Carbon::createFromFormat('Y-m-d', $d); if ($parsed->format('Y-m-d') !== $d || empty($allowedSet[$d])) { $invalid[] = $d; + continue; } } catch (\Throwable) { $invalid[] = $d; + continue; } @@ -129,10 +131,10 @@ class AdministratorAbsenceService } } - if (!empty($invalid)) { + if (! empty($invalid)) { return [ 'success' => false, - 'message' => 'Invalid dates: ' . implode(', ', $invalid), + 'message' => 'Invalid dates: '.implode(', ', $invalid), 'status' => 422, ]; } @@ -150,7 +152,7 @@ class AdministratorAbsenceService return [ 'success' => true, - 'message' => $saved . ' day(s) saved as absent.', + 'message' => $saved.' day(s) saved as absent.', 'saved' => $saved, 'dates' => $savedDates, 'status' => 200, @@ -169,10 +171,10 @@ class AdministratorAbsenceService ): void { try { $user = $this->userModel->find($userId); - $fullName = trim(($user->firstname ?? '') . ' ' . ($user->lastname ?? '')) ?: 'Administrator'; + $fullName = trim(($user->firstname ?? '').' '.($user->lastname ?? '')) ?: 'Administrator'; $userEmail = $user->email ?? ''; $role = $roleName ?: 'admin'; - $dateList = !empty($savedDates) ? implode(', ', $savedDates) : implode(', ', $dates); + $dateList = ! empty($savedDates) ? implode(', ', $savedDates) : implode(', ', $dates); $subject = sprintf( 'TimeOff Request: %s (%s) — %s', @@ -184,20 +186,20 @@ class AdministratorAbsenceService $submittedAt = now()->toDateTimeString(); $body = '
' - . '

New TimeOff Request

' - . '

A staff time-off request was submitted from the administrator portal.

' - . '' - . '' - . '' - . '' - . '' - . '' - . '' - . '' - . '' - . '' - . '
Name' . e($fullName) . '
Email' . e($userEmail) . '
Role' . e((string) $role) . '
Semester' . e($semester) . '
School Year' . e($schoolYear) . '
Reason Type' . e($reasonType ?: '-') . '
Reason' . e($reasonText) . '
Dates' . e($dateList ?: '-') . '
Submitted At' . e($submittedAt) . '
' - . '
'; + .'

New TimeOff Request

' + .'

A staff time-off request was submitted from the administrator portal.

' + .'' + .'' + .'' + .'' + .'' + .'' + .'' + .'' + .'' + .'' + .'
Name'.e($fullName).'
Email'.e($userEmail).'
Role'.e((string) $role).'
Semester'.e($semester).'
School Year'.e($schoolYear).'
Reason Type'.e($reasonType ?: '-').'
Reason'.e($reasonText).'
Dates'.e($dateList ?: '-').'
Submitted At'.e($submittedAt).'
' + .''; $token = $this->staffTimeOffLinkService->createToken([ 'uid' => $userId, @@ -213,9 +215,9 @@ class AdministratorAbsenceService $notifyUrl = $this->urls->timeoffNotifyUrl($token); - $body .= '

Click Send confirmation email to ' - . e($fullName) - . ' so the staff member is notified automatically. This link expires in 14 days.

'; + $body .= '

Click Send confirmation email to ' + .e($fullName) + .' so the staff member is notified automatically. This link expires in 14 days.

'; $principalEmail = env('PRINCIPAL_EMAIL', 'principal@alrahmaisgl.org'); @@ -223,7 +225,7 @@ class AdministratorAbsenceService $message->to($principalEmail)->subject($subject); }); } catch (\Throwable $e) { - Log::error('Failed to send TimeOff email (admin): ' . $e->getMessage()); + Log::error('Failed to send TimeOff email (admin): '.$e->getMessage()); } } } diff --git a/app/Services/Administrator/AdministratorDashboardService.php b/app/Services/Administrator/AdministratorDashboardService.php index 42261eaa..4db14733 100644 --- a/app/Services/Administrator/AdministratorDashboardService.php +++ b/app/Services/Administrator/AdministratorDashboardService.php @@ -7,8 +7,7 @@ class AdministratorDashboardService public function __construct( protected AdministratorMetricsService $metricsService, protected AdministratorUserSearchService $userSearchService, - ) { - } + ) {} public function metrics(): array { @@ -19,4 +18,4 @@ class AdministratorDashboardService { return $this->userSearchService->search($query); } -} \ No newline at end of file +} diff --git a/app/Services/Administrator/AdministratorEnrollmentEventService.php b/app/Services/Administrator/AdministratorEnrollmentEventService.php index 957d4029..4439f2db 100644 --- a/app/Services/Administrator/AdministratorEnrollmentEventService.php +++ b/app/Services/Administrator/AdministratorEnrollmentEventService.php @@ -10,8 +10,7 @@ class AdministratorEnrollmentEventService { public function __construct( private ApplicationUrlService $urls, - ) { - } + ) {} public function dispatchGroupedEvents( array $groupsByParentStatus, @@ -45,7 +44,7 @@ class AdministratorEnrollmentEventService ->first(); foreach ($byStatus as $status => $studentsArr) { - if (!isset($eventMap[$status])) { + if (! isset($eventMap[$status])) { continue; } @@ -77,4 +76,4 @@ class AdministratorEnrollmentEventService } } } -} \ No newline at end of file +} diff --git a/app/Services/Administrator/AdministratorEnrollmentQueryService.php b/app/Services/Administrator/AdministratorEnrollmentQueryService.php index 51c0d4e7..2fe70265 100644 --- a/app/Services/Administrator/AdministratorEnrollmentQueryService.php +++ b/app/Services/Administrator/AdministratorEnrollmentQueryService.php @@ -17,8 +17,7 @@ class AdministratorEnrollmentQueryService protected StudentClass $studentClassModel, protected Enrollment $enrollmentModel, protected ClassSection $classSectionModel, - ) { - } + ) {} public function enrollmentWithdrawalData(?string $selectedYear = null): array { @@ -59,7 +58,7 @@ class AdministratorEnrollmentQueryService $s['student_id'] = $studentId; $s['removed_previous_year'] = isset($removedPriorIds[$studentId]) ? 'Yes' : 'No'; - if (empty($s['parent_id']) && !empty($s['secondparent_user_id'])) { + if (empty($s['parent_id']) && ! empty($s['secondparent_user_id'])) { $s['parent_id'] = (int) $s['secondparent_user_id']; } else { $s['parent_id'] = (int) ($s['parent_id'] ?? 0); @@ -68,14 +67,14 @@ class AdministratorEnrollmentQueryService $pf = trim((string) ($s['parent_firstname'] ?? '')); $pl = trim((string) ($s['parent_lastname'] ?? '')); - if ($pf === '' && $pl === '' && !empty($s['parent_fullname'])) { + if ($pf === '' && $pl === '' && ! empty($s['parent_fullname'])) { $parts = preg_split('/\s+/', trim((string) $s['parent_fullname']), 2); $pf = $parts[0] ?? ''; $pl = $parts[1] ?? ''; } - $s['parent_label'] = trim($pf . ' ' . $pl) ?: 'Unknown Parent'; - $s['parent_sort'] = trim(($pl !== '' ? $pl : $pf) . ' ' . $pf) ?: 'ZZZ Unknown Parent'; + $s['parent_label'] = trim($pf.' '.$pl) ?: 'Unknown Parent'; + $s['parent_sort'] = trim(($pl !== '' ? $pl : $pf).' '.$pf) ?: 'ZZZ Unknown Parent'; $s['is_new'] = (int) ($s['is_new'] ?? 0); $s['new_student'] = $s['is_new'] === 1 ? 'Yes' : 'No'; @@ -84,7 +83,7 @@ class AdministratorEnrollmentQueryService ? $this->enrollmentModel->getEnrollmentStatus($studentId, $selectedYear) : null; - if (!empty($statusForYear)) { + if (! empty($statusForYear)) { $s['enrollment_status'] = $statusForYear; } elseif (($s['admission_status'] ?? null) === 'denied') { $s['enrollment_status'] = 'denied'; @@ -96,7 +95,7 @@ class AdministratorEnrollmentQueryService $s['class_section'] = $className ?: 'Class not Assigned'; - $s['registration_date_order'] = !empty($s['registration_date']) + $s['registration_date_order'] = ! empty($s['registration_date']) ? Carbon::parse($s['registration_date'])->format('Y-m-d') : ''; } @@ -172,10 +171,10 @@ class AdministratorEnrollmentQueryService $r['is_new'] = (int) ($r['is_new'] ?? 0); $r['new_student'] = $r['is_new'] === 1 ? 'Yes' : 'No'; - $r['modalIdContact'] = 'contact_' . $studentId; + $r['modalIdContact'] = 'contact_'.$studentId; $r['enrollment_status'] = $enrollmentStatus; - if (!empty($r['registration_date'])) { + if (! empty($r['registration_date'])) { try { $r['registration_date'] = Carbon::parse($r['registration_date'])->format('Y-m-d'); } catch (\Throwable) { @@ -191,4 +190,4 @@ class AdministratorEnrollmentQueryService 'total_new' => count($newStudents), ]; } -} \ No newline at end of file +} diff --git a/app/Services/Administrator/AdministratorEnrollmentRefundService.php b/app/Services/Administrator/AdministratorEnrollmentRefundService.php index 9d36a60c..6fc72392 100644 --- a/app/Services/Administrator/AdministratorEnrollmentRefundService.php +++ b/app/Services/Administrator/AdministratorEnrollmentRefundService.php @@ -11,8 +11,7 @@ class AdministratorEnrollmentRefundService { public function __construct( protected FeeCalculationService $feeCalculationService - ) { - } + ) {} public function processRefunds(array $parentIds, string $schoolYear, int $editorUserId): array { @@ -36,8 +35,9 @@ class AdministratorEnrollmentRefundService ->latest('created_at') ->first(); - if (!$invoice) { + if (! $invoice) { $errors[] = "No invoice found for parent ID {$pid} (for refund calc)."; + continue; } diff --git a/app/Services/Administrator/AdministratorEnrollmentService.php b/app/Services/Administrator/AdministratorEnrollmentService.php index f2c2068f..89d09bb7 100644 --- a/app/Services/Administrator/AdministratorEnrollmentService.php +++ b/app/Services/Administrator/AdministratorEnrollmentService.php @@ -7,8 +7,7 @@ class AdministratorEnrollmentService public function __construct( protected AdministratorEnrollmentQueryService $queryService, protected AdministratorEnrollmentStatusService $statusService, - ) { - } + ) {} public function enrollmentWithdrawalData(?string $selectedYear = null): array { diff --git a/app/Services/Administrator/AdministratorEnrollmentStatusService.php b/app/Services/Administrator/AdministratorEnrollmentStatusService.php index cda49b10..90a1d5cb 100644 --- a/app/Services/Administrator/AdministratorEnrollmentStatusService.php +++ b/app/Services/Administrator/AdministratorEnrollmentStatusService.php @@ -15,8 +15,7 @@ class AdministratorEnrollmentStatusService protected User $userModel, protected AdministratorEnrollmentRefundService $refundService, protected AdministratorEnrollmentEventService $eventService, - ) { - } + ) {} public function updateStatuses(array $enrollmentStatuses, int $editorUserId): array { @@ -53,8 +52,9 @@ class AdministratorEnrollmentStatusService foreach ($enrollmentStatuses as $studentId => $newEnrollmentStatus) { $studentId = (int) $studentId; - if (!in_array($newEnrollmentStatus, $validStatuses, true)) { + if (! in_array($newEnrollmentStatus, $validStatuses, true)) { $errors[] = "Invalid enrollment status '{$newEnrollmentStatus}' for student ID {$studentId}."; + continue; } @@ -68,7 +68,7 @@ class AdministratorEnrollmentStatusService $refundParents ); - if (!$result['success']) { + if (! $result['success']) { $errors[] = $result['message']; } } @@ -99,7 +99,7 @@ class AdministratorEnrollmentStatusService ]; } catch (\Throwable $e) { DB::rollBack(); - Log::error('Enrollment withdrawal error: ' . $e->getMessage()); + Log::error('Enrollment withdrawal error: '.$e->getMessage()); return [ 'success' => false, @@ -129,7 +129,7 @@ class AdministratorEnrollmentStatusService ->where('school_year', $schoolYear) ->first(); - if (!$enrollmentRow) { + if (! $enrollmentRow) { return $this->createEnrollmentRow( $studentId, $newEnrollmentStatus, @@ -145,7 +145,7 @@ class AdministratorEnrollmentStatusService $oldStatus = $enrollmentRow->enrollment_status ?? null; $parentId = (int) ($enrollmentRow->parent_id ?? 0); - if (!$parentId) { + if (! $parentId) { return [ 'success' => false, 'message' => "No parent ID found for student ID {$studentId}.", @@ -165,7 +165,7 @@ class AdministratorEnrollmentStatusService 'updated_at' => now(), ]); - if (!$updated) { + if (! $updated) { return [ 'success' => false, 'message' => "Failed to update enrollment for student ID {$studentId}.", @@ -201,7 +201,7 @@ class AdministratorEnrollmentStatusService $stu = is_array($stu) ? $stu : ($stu?->toArray() ?? []); $parentId = (int) ($stu['parent_id'] ?? ($stu['secondparent_user_id'] ?? 0)); - if (!$parentId) { + if (! $parentId) { return [ 'success' => false, 'message' => "No parent ID found for student ID {$studentId}.", @@ -223,7 +223,7 @@ class AdministratorEnrollmentStatusService 'updated_at' => now(), ]); - if (!$ok) { + if (! $ok) { return [ 'success' => false, 'message' => "Failed to create enrollment for student ID {$studentId}.", @@ -250,7 +250,7 @@ class AdministratorEnrollmentStatusService $studentRow = $this->studentModel->find($studentId); $studentData = is_array($studentRow) ? $studentRow : ($studentRow?->toArray() ?? []); - return trim(($studentData['firstname'] ?? '') . ' ' . ($studentData['lastname'] ?? '')) + return trim(($studentData['firstname'] ?? '').' '.($studentData['lastname'] ?? '')) ?: "Student #{$studentId}"; } diff --git a/app/Services/Administrator/AdministratorMetricsService.php b/app/Services/Administrator/AdministratorMetricsService.php index 7675461d..cfa99e28 100644 --- a/app/Services/Administrator/AdministratorMetricsService.php +++ b/app/Services/Administrator/AdministratorMetricsService.php @@ -12,8 +12,7 @@ class AdministratorMetricsService protected AdministratorSharedService $shared, protected User $userModel, protected LoginActivity $loginActivityModel, - ) { - } + ) {} public function metrics(): array { @@ -70,4 +69,4 @@ class AdministratorMetricsService ], ]; } -} \ No newline at end of file +} diff --git a/app/Services/Administrator/AdministratorNotificationService.php b/app/Services/Administrator/AdministratorNotificationService.php index acfd591e..42494180 100644 --- a/app/Services/Administrator/AdministratorNotificationService.php +++ b/app/Services/Administrator/AdministratorNotificationService.php @@ -7,8 +7,7 @@ class AdministratorNotificationService public function __construct( protected AdminNotificationSubjectService $subjectService, protected AdminPrintRecipientService $printRecipientService, - ) { - } + ) {} public function notificationsAlertsData(): array { @@ -29,4 +28,4 @@ class AdministratorNotificationService { return $this->printRecipientService->save($posted); } -} \ No newline at end of file +} diff --git a/app/Services/Administrator/AdministratorSharedService.php b/app/Services/Administrator/AdministratorSharedService.php index f860f780..7bb80145 100644 --- a/app/Services/Administrator/AdministratorSharedService.php +++ b/app/Services/Administrator/AdministratorSharedService.php @@ -12,8 +12,7 @@ class AdministratorSharedService public function __construct( protected Configuration $configuration - ) { - } + ) {} public function getSemester(?string $override = null): string { @@ -38,7 +37,7 @@ class AdministratorSharedService } if (preg_match('/^(\d{4})\s*-\s*(\d{4})$/', $schoolYear, $m)) { - return ((int) $m[1] - 1) . '-' . ((int) $m[2] - 1); + return ((int) $m[1] - 1).'-'.((int) $m[2] - 1); } if (preg_match('/^(\d{4})\s*-\s*(\d{2})$/', $schoolYear, $m)) { @@ -47,6 +46,7 @@ class AdministratorSharedService if ($end < 0) { $end += 100; } + return sprintf('%04d-%02d', $start, $end); } @@ -129,7 +129,7 @@ class AdministratorSharedService public function countUniqueEntities($rows): int { - if (!is_iterable($rows)) { + if (! is_iterable($rows)) { return 0; } diff --git a/app/Services/Administrator/AdministratorTeacherSubmissionService.php b/app/Services/Administrator/AdministratorTeacherSubmissionService.php index 5ccc2af9..c49ba590 100644 --- a/app/Services/Administrator/AdministratorTeacherSubmissionService.php +++ b/app/Services/Administrator/AdministratorTeacherSubmissionService.php @@ -9,8 +9,7 @@ class AdministratorTeacherSubmissionService public function __construct( protected TeacherSubmissionReportService $reportService, protected TeacherSubmissionNotificationService $notificationService, - ) { - } + ) {} public function report(array $filters = []): array { diff --git a/app/Services/Administrator/AdministratorUserSearchService.php b/app/Services/Administrator/AdministratorUserSearchService.php index 507bc29d..06a31c50 100644 --- a/app/Services/Administrator/AdministratorUserSearchService.php +++ b/app/Services/Administrator/AdministratorUserSearchService.php @@ -38,23 +38,23 @@ class AdministratorUserSearchService $v[] = sprintf('(%s)-%s-%s', substr($digits, 0, 3), substr($digits, 3, 3), substr($digits, 6)); $v[] = sprintf('%s-%s-%s', substr($digits, 0, 3), substr($digits, 3, 3), substr($digits, 6)); $v[] = sprintf('%s %s %s', substr($digits, 0, 3), substr($digits, 3, 3), substr($digits, 6)); - $v[] = '1' . $digits; - $v[] = '+1' . $digits; - $v[] = '+1 ' . sprintf('(%s) %s-%s', substr($digits, 0, 3), substr($digits, 3, 3), substr($digits, 6)); - $v[] = '+1-' . sprintf('%s-%s-%s', substr($digits, 0, 3), substr($digits, 3, 3), substr($digits, 6)); + $v[] = '1'.$digits; + $v[] = '+1'.$digits; + $v[] = '+1 '.sprintf('(%s) %s-%s', substr($digits, 0, 3), substr($digits, 3, 3), substr($digits, 6)); + $v[] = '+1-'.sprintf('%s-%s-%s', substr($digits, 0, 3), substr($digits, 3, 3), substr($digits, 6)); } elseif (strlen($digits) === 11 && str_starts_with($digits, '1')) { $ten = substr($digits, 1); $v[] = $ten; $v[] = sprintf('(%s)-%s-%s', substr($ten, 0, 3), substr($ten, 3, 3), substr($ten, 6)); $v[] = sprintf('%s-%s-%s', substr($ten, 0, 3), substr($ten, 3, 3), substr($ten, 6)); $v[] = sprintf('%s %s %s', substr($ten, 0, 3), substr($ten, 3, 3), substr($ten, 6)); - $v[] = '+1' . $ten; - $v[] = '+1 ' . sprintf('(%s) %s-%s', substr($ten, 0, 3), substr($ten, 3, 3), substr($ten, 6)); - $v[] = '+1-' . sprintf('%s-%s-%s', substr($ten, 0, 3), substr($ten, 3, 3), substr($ten, 6)); + $v[] = '+1'.$ten; + $v[] = '+1 '.sprintf('(%s) %s-%s', substr($ten, 0, 3), substr($ten, 3, 3), substr($ten, 6)); + $v[] = '+1-'.sprintf('%s-%s-%s', substr($ten, 0, 3), substr($ten, 3, 3), substr($ten, 6)); } } - if (!empty($v)) { + if (! empty($v)) { $phoneMap[$t] = array_values(array_unique($v)); } } @@ -63,13 +63,13 @@ class AdministratorUserSearchService foreach ($tokens as $t) { $qb->where(function ($sub) use ($columns, $phoneCols, $phoneMap, $t) { foreach ($columns as $col) { - $sub->orWhere($col, 'like', '%' . $t . '%'); + $sub->orWhere($col, 'like', '%'.$t.'%'); } - if (!empty($phoneMap[$t]) && !empty($phoneCols)) { + if (! empty($phoneMap[$t]) && ! empty($phoneCols)) { foreach ($phoneMap[$t] as $pv) { foreach ($phoneCols as $pcol) { - $sub->orWhere($pcol, 'like', '%' . $pv . '%'); + $sub->orWhere($pcol, 'like', '%'.$pv.'%'); } } } @@ -82,12 +82,12 @@ class AdministratorUserSearchService $uQB = DB::table('users') ->select('id', 'firstname', 'lastname', 'email', 'cellphone', 'school_id', 'city', 'state', 'school_year', 'semester'); $applyMultiTokenLike($uQB, ['firstname', 'lastname', 'email', 'cellphone', 'school_id', 'city', 'state'], $tokens, ['cellphone']); - $users = $uQB->limit(150)->get()->map(fn($r) => (array) $r)->all(); + $users = $uQB->limit(150)->get()->map(fn ($r) => (array) $r)->all(); $sQB = DB::table('students') ->select('id', 'parent_id', 'school_id', 'firstname', 'lastname', 'dob', 'gender', 'school_year', 'semester', 'rfid_tag'); $applyMultiTokenLike($sQB, ['firstname', 'lastname', 'school_id', 'rfid_tag', 'dob', 'gender'], $tokens); - $students = $sQB->limit(150)->get()->map(fn($r) => (array) $r)->all(); + $students = $sQB->limit(150)->get()->map(fn ($r) => (array) $r)->all(); $pQB = DB::table('parents') ->select('id', 'firstparent_id', 'secondparent_firstname', 'secondparent_lastname', 'secondparent_email', 'secondparent_phone', 'school_year', 'semester'); @@ -99,17 +99,17 @@ class AdministratorUserSearchService } } - $parents = $pQB->limit(150)->get()->map(fn($r) => (array) $r)->all(); + $parents = $pQB->limit(150)->get()->map(fn ($r) => (array) $r)->all(); $stQB = DB::table('staff') ->select('id', 'user_id', 'firstname', 'lastname', 'email', 'phone', 'role_name', 'school_year', 'active_role'); $applyMultiTokenLike($stQB, ['firstname', 'lastname', 'email', 'role_name', 'phone'], $tokens, ['phone']); - $staff = $stQB->limit(150)->get()->map(fn($r) => (array) $r)->all(); + $staff = $stQB->limit(150)->get()->map(fn ($r) => (array) $r)->all(); $ecQB = DB::table('emergency_contacts') ->select('id', 'parent_id', 'emergency_contact_name', 'relation', 'cellphone', 'email', 'school_year', 'semester'); $applyMultiTokenLike($ecQB, ['emergency_contact_name', 'relation', 'email', 'cellphone'], $tokens, ['cellphone']); - $emergency = $ecQB->limit(150)->get()->map(fn($r) => (array) $r)->all(); + $emergency = $ecQB->limit(150)->get()->map(fn ($r) => (array) $r)->all(); return [ 'query' => $q, @@ -125,4 +125,4 @@ class AdministratorUserSearchService 'total_found' => count($users) + count($students) + count($parents) + count($staff) + count($emergency), ]; } -} \ No newline at end of file +} diff --git a/app/Services/Administrator/TeacherSubmissionNotificationService.php b/app/Services/Administrator/TeacherSubmissionNotificationService.php index eae28b9d..bf78ade6 100644 --- a/app/Services/Administrator/TeacherSubmissionNotificationService.php +++ b/app/Services/Administrator/TeacherSubmissionNotificationService.php @@ -3,10 +3,10 @@ namespace App\Services\Administrator; use App\Mail\TeacherSubmissionReminderMail; -use App\Services\ApplicationUrlService; use App\Models\ClassSection; use App\Models\TeacherSubmissionNotificationHistory; use App\Models\User; +use App\Services\ApplicationUrlService; use Illuminate\Http\Request; use Illuminate\Support\Facades\Log; use Illuminate\Support\Facades\Mail; @@ -17,13 +17,12 @@ class TeacherSubmissionNotificationService protected AdministratorSharedService $shared, protected TeacherSubmissionSupportService $support, protected ApplicationUrlService $urls, - ) { - } + ) {} public function send(Request $request, int $adminId): array { $notify = $request->input('notify'); - if (!is_array($notify)) { + if (! is_array($notify)) { return [ 'success' => false, 'message' => 'Select at least one teacher to notify.', @@ -60,7 +59,7 @@ class TeacherSubmissionNotificationService ->keyBy('id'); $adminUser = User::find($adminId); - $adminName = trim(($adminUser->firstname ?? '') . ' ' . ($adminUser->lastname ?? '')) ?: 'Administrator'; + $adminName = trim(($adminUser->firstname ?? '').' '.($adminUser->lastname ?? '')) ?: 'Administrator'; $scoreUrl = $this->urls->docsHomeUrl(); $sentCount = 0; @@ -72,30 +71,30 @@ class TeacherSubmissionNotificationService $teacher = $teachers->get($teacherId); $sectionName = $classSections->get($classSectionId)?->class_section_name ?? "Section {$classSectionId}"; - $teacherName = trim(($teacher->firstname ?? '') . ' ' . ($teacher->lastname ?? '')) ?: 'Teacher'; + $teacherName = trim(($teacher->firstname ?? '').' '.($teacher->lastname ?? '')) ?: 'Teacher'; $missingItems = $this->resolveMissingItems($missingItemsPayload, $classSectionId, $teacherId); - $missingNote = !empty($missingItems) - ? '

Outstanding items: ' . e($this->support->formatMissingItemsText($missingItems)) . '.

' + $missingNote = ! empty($missingItems) + ? '

Outstanding items: '.e($this->support->formatMissingItemsText($missingItems)).'.

' : '

Our records show no outstanding submissions for this section, but please verify if anything still needs attention.

'; $subject = "Reminder: Complete submissions for {$sectionName}"; $body = "

Dear {$teacherName},

" - . "

Administration is gently reminding you to wrap up any remaining score submissions and/or comments for {$sectionName}.

" - . $missingNote - . "

Visit Teacher Score Submission to address any remaining items.

" - . "

Thank you,
{$adminName}

"; + ."

Administration is gently reminding you to wrap up any remaining score submissions and/or comments for {$sectionName}.

" + .$missingNote + ."

Visit Teacher Score Submission to address any remaining items.

" + ."

Thank you,
{$adminName}

"; $email = $teacher->email ?? ''; $status = 'failed'; - if (!empty($email) && filter_var($email, FILTER_VALIDATE_EMAIL)) { + if (! empty($email) && filter_var($email, FILTER_VALIDATE_EMAIL)) { try { Mail::to($email)->send(new TeacherSubmissionReminderMail($subject, $body)); $status = 'sent'; } catch (\Throwable $e) { - Log::error('Teacher submission notification failed: ' . $e->getMessage()); + Log::error('Teacher submission notification failed: '.$e->getMessage()); } } @@ -120,15 +119,15 @@ class TeacherSubmissionNotificationService $parts = []; if ($sentCount > 0) { - $parts[] = $sentCount . ' reminder' . ($sentCount === 1 ? '' : 's') . ' sent'; + $parts[] = $sentCount.' reminder'.($sentCount === 1 ? '' : 's').' sent'; } if ($failCount > 0) { - $parts[] = $failCount . ' reminder' . ($failCount === 1 ? '' : 's') . ' failed'; + $parts[] = $failCount.' reminder'.($failCount === 1 ? '' : 's').' failed'; } return [ 'success' => $failCount === 0, - 'message' => !empty($parts) ? implode(' and ', $parts) : 'No notifications were sent.', + 'message' => ! empty($parts) ? implode(' and ', $parts) : 'No notifications were sent.', 'sent' => $sentCount, 'failed' => $failCount, 'status' => $failCount === 0 ? 200 : 207, @@ -169,7 +168,7 @@ class TeacherSubmissionNotificationService foreach ($notify as $sectionIdRaw => $teachers) { $sectionId = (int) $sectionIdRaw; - if ($sectionId <= 0 || !is_array($teachers)) { + if ($sectionId <= 0 || ! is_array($teachers)) { continue; } @@ -202,7 +201,7 @@ class TeacherSubmissionNotificationService protected function resolveMissingItems($payload, int $classSectionId, int $teacherId): array { - if (!is_array($payload)) { + if (! is_array($payload)) { return []; } diff --git a/app/Services/Administrator/TeacherSubmissionReportService.php b/app/Services/Administrator/TeacherSubmissionReportService.php index 2e4b1563..28ff1f21 100644 --- a/app/Services/Administrator/TeacherSubmissionReportService.php +++ b/app/Services/Administrator/TeacherSubmissionReportService.php @@ -14,8 +14,7 @@ class TeacherSubmissionReportService public function __construct( protected AdministratorSharedService $shared, protected TeacherSubmissionSupportService $support - ) { - } + ) {} public function report(array $filters = []): array { @@ -62,14 +61,14 @@ class TeacherSubmissionReportService default => $roleKey !== '' ? ucfirst($roleKey) : 'Teacher', }; - $teacherFullName = trim(($assignment['firstname'] ?? '') . ' ' . ($assignment['lastname'] ?? '')); + $teacherFullName = trim(($assignment['firstname'] ?? '').' '.($assignment['lastname'] ?? '')); $teacherId = (int) ($assignment['teacher_id'] ?? 0); if ($teacherFullName === '' || $teacherId <= 0) { continue; } - if (!isset($teachersBySection[$sectionId])) { + if (! isset($teachersBySection[$sectionId])) { $teachersBySection[$sectionId] = [ 'class_section' => $assignment['class_section_name'] ?? "Section {$sectionId}", 'teachers' => [], @@ -119,7 +118,7 @@ class TeacherSubmissionReportService foreach ($scoreRecords as $score) { $sid = (int) ($score->student_id ?? 0); - if ($sid <= 0 || ($expected > 0 && !in_array($sid, $studentIds, true))) { + if ($sid <= 0 || ($expected > 0 && ! in_array($sid, $studentIds, true))) { continue; } @@ -135,7 +134,7 @@ class TeacherSubmissionReportService $midtermCommentStudents = []; $ptapCommentStudents = []; - if (!empty($studentIds)) { + if (! empty($studentIds)) { $comments = ScoreComment::query() ->select('student_id', 'score_type', 'comment') ->whereIn('student_id', $studentIds) @@ -183,7 +182,7 @@ class TeacherSubmissionReportService ); foreach ($teacherList as $teacherEntry) { - if (!empty($teacherEntry['id'])) { + if (! empty($teacherEntry['id'])) { $allTeacherIds[] = $teacherEntry['id']; } } @@ -270,7 +269,7 @@ class TeacherSubmissionReportService continue; } - $adminName = trim(($record->firstname ?? '') . ' ' . ($record->lastname ?? '')); + $adminName = trim(($record->firstname ?? '').' '.($record->lastname ?? '')); if ($adminName === '') { $adminName = 'Administrator'; } diff --git a/app/Services/Administrator/TeacherSubmissionSupportService.php b/app/Services/Administrator/TeacherSubmissionSupportService.php index a2d242c1..1ea89741 100644 --- a/app/Services/Administrator/TeacherSubmissionSupportService.php +++ b/app/Services/Administrator/TeacherSubmissionSupportService.php @@ -54,7 +54,7 @@ class TeacherSubmissionSupportService $items = []; foreach ($statusMap as $key => $status) { - if (!(bool) ($status['completed'] ?? true) && isset($labels[$key])) { + if (! (bool) ($status['completed'] ?? true) && isset($labels[$key])) { $items[] = $labels[$key]; } } @@ -80,7 +80,7 @@ class TeacherSubmissionSupportService return mb_strlen($text) <= $limit ? $text - : mb_substr($text, 0, $limit) . '…'; + : mb_substr($text, 0, $limit).'…'; } public function parseMissingItemsPayload($payload): array @@ -103,7 +103,7 @@ class TeacherSubmissionSupportService } $decoded = json_decode(base64_decode($payload, true) ?: '', true); - if (!is_array($decoded)) { + if (! is_array($decoded)) { return []; } @@ -130,10 +130,11 @@ class TeacherSubmissionSupportService return $items[0]; } if ($count === 2) { - return $items[0] . ' and ' . $items[1]; + return $items[0].' and '.$items[1]; } $last = array_pop($items); - return implode(', ', $items) . ' and ' . $last; + + return implode(', ', $items).' and '.$last; } } diff --git a/app/Services/Administrator/Trophy/TrophyReportService.php b/app/Services/Administrator/Trophy/TrophyReportService.php index 84bb2825..f3646853 100644 --- a/app/Services/Administrator/Trophy/TrophyReportService.php +++ b/app/Services/Administrator/Trophy/TrophyReportService.php @@ -305,7 +305,7 @@ class TrophyReportService } /** - * @return \Illuminate\Support\Collection> + * @return Collection> */ private function studentRows(string $schoolYear): Collection { @@ -393,7 +393,7 @@ class TrophyReportService } /** - * @param \Illuminate\Support\Collection> $rows + * @param Collection> $rows * @return array>}> */ private function sectionMap(Collection $rows): array diff --git a/app/Services/ApplicationUrlService.php b/app/Services/ApplicationUrlService.php index d30ad0f5..a175d085 100644 --- a/app/Services/ApplicationUrlService.php +++ b/app/Services/ApplicationUrlService.php @@ -2,6 +2,7 @@ namespace App\Services; +use App\Http\Controllers\Api\Reports\FilesController; use Illuminate\Support\Facades\Route; /** @@ -138,10 +139,10 @@ final class ApplicationUrlService return route('api.timeoff.notify', ['token' => $token]); } - /** Served by {@see \App\Http\Controllers\Api\Reports\FilesController::receipt} (uploads/receipts). */ + /** Served by {@see FilesController::receipt} (uploads/receipts). */ public function forReceiptStorage(?string $filename): ?string { - if (!$filename) { + if (! $filename) { return null; } $safe = basename(trim($filename)); @@ -149,10 +150,10 @@ final class ApplicationUrlService return $safe !== '' ? route('api.v1.files.receipt', ['name' => $safe]) : null; } - /** Served by {@see \App\Http\Controllers\Api\Reports\FilesController::reimb} (uploads/reimbursements). */ + /** Served by {@see FilesController::reimb} (uploads/reimbursements). */ public function forReimbursementStorage(?string $filename): ?string { - if (!$filename) { + if (! $filename) { return null; } $safe = basename(trim($filename)); diff --git a/app/Services/Assignment/AssignmentDataLoaderService.php b/app/Services/Assignment/AssignmentDataLoaderService.php index aea5efed..7fcbdacb 100644 --- a/app/Services/Assignment/AssignmentDataLoaderService.php +++ b/app/Services/Assignment/AssignmentDataLoaderService.php @@ -31,7 +31,7 @@ class AssignmentDataLoaderService ->get() ->map(function ($row) { $row->teacher_full_name = trim( - ((string) optional($row->teacher)->firstname) . ' ' . + ((string) optional($row->teacher)->firstname).' '. ((string) optional($row->teacher)->lastname) ); diff --git a/app/Services/Assignment/AssignmentSectionService.php b/app/Services/Assignment/AssignmentSectionService.php index 2c345fe6..5db2b2db 100644 --- a/app/Services/Assignment/AssignmentSectionService.php +++ b/app/Services/Assignment/AssignmentSectionService.php @@ -20,6 +20,7 @@ class AssignmentSectionService ->get() ->mapWithKeys(function ($section) { $name = $section->class_section_name ?? $section->section_name ?? $section->name ?? ''; + return [(int) $section->class_section_id => (string) $name]; }) ->all(); diff --git a/app/Services/Assignment/AssignmentService.php b/app/Services/Assignment/AssignmentService.php index f3d59b0d..67127e05 100644 --- a/app/Services/Assignment/AssignmentService.php +++ b/app/Services/Assignment/AssignmentService.php @@ -93,7 +93,7 @@ class AssignmentService ]); $students = $studentRows - ->filter(fn ($row) => !empty($row->student)) + ->filter(fn ($row) => ! empty($row->student)) ->map(function ($row) { $student = $row->student; @@ -122,10 +122,10 @@ class AssignmentService 'description' => (string) $descriptionMeta, ]; }) - ->filter() - ->sortBy('class_section_name', SORT_NATURAL | SORT_FLAG_CASE) - ->values() - ->all(); + ->filter() + ->sortBy('class_section_name', SORT_NATURAL | SORT_FLAG_CASE) + ->values() + ->all(); return [ 'classSections' => $classSections, @@ -220,7 +220,7 @@ class AssignmentService ]); $students = $studentRows - ->filter(fn ($row) => !empty($row->student)) + ->filter(fn ($row) => ! empty($row->student)) ->map(function ($row) { $student = $row->student; @@ -249,10 +249,10 @@ class AssignmentService 'description' => (string) $descriptionMeta, ]; }) - ->filter() - ->sortBy('class_section_name', SORT_NATURAL | SORT_FLAG_CASE) - ->values() - ->all(); + ->filter() + ->sortBy('class_section_name', SORT_NATURAL | SORT_FLAG_CASE) + ->values() + ->all(); return [ 'classSections' => $classSections, diff --git a/app/Services/AssignmentService.php b/app/Services/AssignmentService.php index e1f3985a..7b18c92b 100644 --- a/app/Services/AssignmentService.php +++ b/app/Services/AssignmentService.php @@ -4,6 +4,4 @@ namespace App\Services; use App\Services\Assignment\AssignmentService as AssignmentDomainService; -class AssignmentService extends AssignmentDomainService -{ -} +class AssignmentService extends AssignmentDomainService {} diff --git a/app/Services/Attendance/AttendanceAutoPublishJobService.php b/app/Services/Attendance/AttendanceAutoPublishJobService.php index 88e763af..e4d23f11 100644 --- a/app/Services/Attendance/AttendanceAutoPublishJobService.php +++ b/app/Services/Attendance/AttendanceAutoPublishJobService.php @@ -3,14 +3,11 @@ namespace App\Services\Attendance; use DateTimeImmutable; -use DateTimeZone; use Illuminate\Support\Facades\DB; class AttendanceAutoPublishJobService { - public function __construct(private AttendanceAutoPublishService $autoPublish) - { - } + public function __construct(private AttendanceAutoPublishService $autoPublish) {} public function run(?DateTimeImmutable $now = null): array { diff --git a/app/Services/Attendance/AttendanceAutoPublishService.php b/app/Services/Attendance/AttendanceAutoPublishService.php index c595e1e8..f856bf27 100644 --- a/app/Services/Attendance/AttendanceAutoPublishService.php +++ b/app/Services/Attendance/AttendanceAutoPublishService.php @@ -2,6 +2,7 @@ namespace App\Services\Attendance; +use Config\School; use DateTimeImmutable; use DateTimeZone; @@ -9,7 +10,7 @@ class AttendanceAutoPublishService { public function timezone(?string $tzFromCfg = null): DateTimeZone { - $tz = $tzFromCfg ?: (class_exists(\Config\School::class) + $tz = $tzFromCfg ?: (class_exists(School::class) ? (config('School')->attendance['timezone'] ?? null) : null); @@ -19,7 +20,7 @@ class AttendanceAutoPublishService public function secondSundayAfterEndOfDay(string $ymd, ?string $tz = null): string { $zone = $this->timezone($tz); - $day = new DateTimeImmutable($ymd . ' 00:00:00', $zone); + $day = new DateTimeImmutable($ymd.' 00:00:00', $zone); $weekday = (int) $day->format('w'); $daysToNextSunday = (7 - $weekday) % 7; diff --git a/app/Services/Attendance/AttendanceCommentService.php b/app/Services/Attendance/AttendanceCommentService.php index 87045929..ad651af7 100644 --- a/app/Services/Attendance/AttendanceCommentService.php +++ b/app/Services/Attendance/AttendanceCommentService.php @@ -29,13 +29,13 @@ class AttendanceCommentService $lastChar = $name !== '' ? substr($name, -1) : ''; $namePossessive = ($name === 'This student') ? "This student's" - : ($lastChar === 's' || $lastChar === 'S' ? ($name . "'") : ($name . "'s")); + : ($lastChar === 's' || $lastChar === 'S' ? ($name."'") : ($name."'s")); if (strpos($text, '{name}') !== false) { return str_replace('{name}', $namePossessive, $text); } - return $namePossessive . ' ' . $text; + return $namePossessive.' '.$text; } public function templateForScore(float $score): ?array diff --git a/app/Services/Attendance/AttendanceConsequenceService.php b/app/Services/Attendance/AttendanceConsequenceService.php index 6551d529..e5feb389 100644 --- a/app/Services/Attendance/AttendanceConsequenceService.php +++ b/app/Services/Attendance/AttendanceConsequenceService.php @@ -12,8 +12,7 @@ class AttendanceConsequenceService public function __construct( private EmailService $emailService, private UserNotificationDispatchService $notifier - ) { - } + ) {} public function sendFollowUp(array $payload): array { @@ -55,10 +54,10 @@ class AttendanceConsequenceService string $_viewName, string $logLevel ): array { - $studentName = trim((string) ($payload['student']['firstname'] ?? '') . ' ' . (string) ($payload['student']['lastname'] ?? '')); + $studentName = trim((string) ($payload['student']['firstname'] ?? '').' '.(string) ($payload['student']['lastname'] ?? '')); $parentEmail = (string) ($payload['parent']['email'] ?? ''); $parentName = (string) ($payload['parent']['name'] ?? ''); - $teacherName = trim((string) ($payload['teacher']['firstname'] ?? '') . ' ' . (string) ($payload['teacher']['lastname'] ?? '')); + $teacherName = trim((string) ($payload['teacher']['firstname'] ?? '').' '.(string) ($payload['teacher']['lastname'] ?? '')); $className = (string) ($payload['class']['class_section_name'] ?? ''); $semester = (string) ($payload['semester'] ?? ''); $schoolYear = (string) ($payload['school_year'] ?? ''); @@ -69,6 +68,7 @@ class AttendanceConsequenceService Log::warning("Attendance {$type}: missing parent email", [ 'student_id' => $payload['student_id'] ?? null, ]); + return ['ok' => false, 'message' => 'Missing parent email.']; } @@ -76,21 +76,21 @@ class AttendanceConsequenceService $html = trim((string) ($payload['html'] ?? '')); if ($html === '') { - $period = trim($semester . ' ' . $schoolYear); - $html = '

' . e($subject) . '

' - . '

Student: ' . e($studentName) . '

' - . '

Parent: ' . e($parentName) . '

' - . '

Teacher: ' . e($teacherName) . '

' - . '

Class: ' . e($className) . '

' - . ($period !== '' ? '

Term: ' . e($period) . '

' : '') - . '

Date: ' . e($dateFmt) . '

'; + $period = trim($semester.' '.$schoolYear); + $html = '

'.e($subject).'

' + .'

Student: '.e($studentName).'

' + .'

Parent: '.e($parentName).'

' + .'

Teacher: '.e($teacherName).'

' + .'

Class: '.e($className).'

' + .($period !== '' ? '

Term: '.e($period).'

' : '') + .'

Date: '.e($dateFmt).'

'; } $ok = false; try { $ok = $this->emailService->send($parentEmail, $subject, $html, 'attendance'); } catch (\Throwable $e) { - Log::error('Attendance email send failed: ' . $e->getMessage()); + Log::error('Attendance email send failed: '.$e->getMessage()); } $summary = $this->buildInAppSummary($type, $studentName, $dateFmt); @@ -106,11 +106,11 @@ class AttendanceConsequenceService ); } - if (!empty($payload['tracking_id'])) { + if (! empty($payload['tracking_id'])) { AttendanceTracking::markAsNotified((int) $payload['tracking_id']); } - Log::log($logLevel, "Attendance {$type} email " . ($ok ? 'sent' : 'failed'), [ + Log::log($logLevel, "Attendance {$type} email ".($ok ? 'sent' : 'failed'), [ 'student_id' => $payload['student_id'] ?? null, 'recipient' => $parentEmail, ]); @@ -122,7 +122,8 @@ class AttendanceConsequenceService { $name = $studentName !== '' ? " — {$studentName}" : ''; $date = $dateFmt !== '' ? " ({$dateFmt})" : ''; - return $base . $name . $date; + + return $base.$name.$date; } private function buildInAppSummary(string $type, string $studentName, string $dateFmt): string diff --git a/app/Services/Attendance/AttendanceDailySummaryService.php b/app/Services/Attendance/AttendanceDailySummaryService.php index 0a838a2c..76879333 100644 --- a/app/Services/Attendance/AttendanceDailySummaryService.php +++ b/app/Services/Attendance/AttendanceDailySummaryService.php @@ -11,18 +11,19 @@ class AttendanceDailySummaryService public function __construct( private UserNotificationDispatchService $notifier, private EmailService $emailService - ) { - } + ) {} public function sendAbsenteesSummary(): int { $rows = $this->fetchTodaySummary('absent'); + return $this->notifyParents($rows, 'absent'); } public function sendLatesSummary(): int { $rows = $this->fetchTodaySummary('late'); + return $this->notifyParents($rows, 'late'); } @@ -49,12 +50,12 @@ class AttendanceDailySummaryService if ($type === 'absent') { $query->where(function ($w) { - $w->whereIn(DB::raw("UPPER(TRIM(ad.status))"), ['ABSENT', 'ABS', 'A']) + $w->whereIn(DB::raw('UPPER(TRIM(ad.status))'), ['ABSENT', 'ABS', 'A']) ->orWhereRaw("UPPER(TRIM(ad.status)) LIKE 'ABS%'"); }); } else { $query->where(function ($w) { - $w->whereIn(DB::raw("UPPER(TRIM(ad.status))"), ['LATE', 'L']) + $w->whereIn(DB::raw('UPPER(TRIM(ad.status))'), ['LATE', 'L']) ->orWhereRaw("UPPER(TRIM(ad.status)) LIKE 'LATE%'"); }); } @@ -71,7 +72,7 @@ class AttendanceDailySummaryService continue; } - $studentName = trim((string) ($row['student_firstname'] ?? '') . ' ' . (string) ($row['student_lastname'] ?? '')); + $studentName = trim((string) ($row['student_firstname'] ?? '').' '.(string) ($row['student_lastname'] ?? '')); $date = substr((string) ($row['date'] ?? ''), 0, 10); $message = sprintf( @@ -85,7 +86,7 @@ class AttendanceDailySummaryService $email = (string) ($row['parent_email'] ?? ''); if ($email !== '') { - $body = '

' . e($message) . '

'; + $body = '

'.e($message).'

'; $this->emailService->send($email, 'Daily Attendance Update', $body, 'attendance'); } diff --git a/app/Services/Attendance/AttendancePolicyService.php b/app/Services/Attendance/AttendancePolicyService.php index af94f50f..62ab27ce 100644 --- a/app/Services/Attendance/AttendancePolicyService.php +++ b/app/Services/Attendance/AttendancePolicyService.php @@ -3,15 +3,14 @@ namespace App\Services\Attendance; use App\Models\Role; -use Illuminate\Contracts\Auth\Authenticatable; class AttendancePolicyService { public function canEditDay(array $dayRow, array $currentUser): bool { - $status = strtolower((string)($dayRow['status'] ?? 'draft')); - $roles = array_map([$this, 'normalizeRole'], (array)($currentUser['roles'] ?? [])); - $permissions = array_map('strtolower', (array)($currentUser['permissions'] ?? [])); + $status = strtolower((string) ($dayRow['status'] ?? 'draft')); + $roles = array_map([$this, 'normalizeRole'], (array) ($currentUser['roles'] ?? [])); + $permissions = array_map('strtolower', (array) ($currentUser['permissions'] ?? [])); $isAdmin = $this->isAdminLike($roles, $permissions); $isTeacher = in_array('teacher', $roles, true) || in_array('ta', $roles, true) || in_array('teacher_assistant', $roles, true); @@ -34,6 +33,7 @@ class AttendancePolicyService public function isTeacher(array $roles): bool { $roles = array_map([$this, 'normalizeRole'], $roles); + return in_array('teacher', $roles, true) || in_array('ta', $roles, true) || in_array('teacher_assistant', $roles, true) @@ -45,7 +45,7 @@ class AttendancePolicyService $roles = array_map([$this, 'normalizeRole'], $roles); $adminTokens = $this->adminRoleTokens(); - if (!empty($adminTokens) && count(array_intersect($roles, $adminTokens)) > 0) { + if (! empty($adminTokens) && count(array_intersect($roles, $adminTokens)) > 0) { return true; } @@ -61,7 +61,7 @@ class AttendancePolicyService public function normalizeRole(?string $role): string { - return str_replace([' ', '-'], '_', strtolower(trim((string)$role))); + return str_replace([' ', '-'], '_', strtolower(trim((string) $role))); } public function canManageEarlyDismissals(array $userRoles): bool @@ -123,6 +123,7 @@ class AttendancePolicyService } $cache = array_keys($tokens); + return $cache; } } diff --git a/app/Services/Attendance/AttendanceQueryService.php b/app/Services/Attendance/AttendanceQueryService.php index 0ce9ebc1..7af2d11c 100644 --- a/app/Services/Attendance/AttendanceQueryService.php +++ b/app/Services/Attendance/AttendanceQueryService.php @@ -42,7 +42,7 @@ class AttendanceQueryService $sectionCodes = array_keys($bySection); $labels = []; - if (!empty($sectionCodes)) { + if (! empty($sectionCodes)) { $rows = $this->classSection ->query() ->select(['class_section_id', 'class_section_name']) @@ -52,13 +52,13 @@ class AttendanceQueryService ->toArray(); foreach ($rows as $row) { - $code = (int)$row['class_section_id']; - $name = trim((string)($row['class_section_name'] ?? '')); - $labels[$code] = $name !== '' ? $name : 'Section #' . $code; + $code = (int) $row['class_section_id']; + $name = trim((string) ($row['class_section_name'] ?? '')); + $labels[$code] = $name !== '' ? $name : 'Section #'.$code; } foreach ($sectionCodes as $code) { - $labels[$code] ??= 'Section #' . $code; + $labels[$code] ??= 'Section #'.$code; } } @@ -67,10 +67,10 @@ class AttendanceQueryService if ($sectionCode > 0) { $assigned = $this->teacherClass->assignedForSectionTerm($sectionCode, $semester, $schoolYear); - $teacherIds = array_map(fn($r) => (int)$r['teacher_id'], $assigned); + $teacherIds = array_map(fn ($r) => (int) $r['teacher_id'], $assigned); $statusMap = []; - if (!empty($teacherIds)) { + if (! empty($teacherIds)) { $rows = DB::table('staff_attendance as sa') ->select('sa.user_id', 'sa.status', 'sa.reason') ->where('sa.semester', $semester) @@ -80,7 +80,7 @@ class AttendanceQueryService ->get(); foreach ($rows as $row) { - $statusMap[(int)$row->user_id] = [ + $statusMap[(int) $row->user_id] = [ 'status' => $row->status, 'reason' => $row->reason, ]; @@ -88,15 +88,15 @@ class AttendanceQueryService } foreach ($assigned as $teacher) { - $teacherId = (int)$teacher['teacher_id']; - $position = strtolower((string)($teacher['position'] ?? 'main')); + $teacherId = (int) $teacher['teacher_id']; + $position = strtolower((string) ($teacher['position'] ?? 'main')); $position = in_array($position, ['main', 'ta'], true) ? $position : 'main'; - $name = trim(($teacher['firstname'] ?? '') . ' ' . ($teacher['lastname'] ?? '')); + $name = trim(($teacher['firstname'] ?? '').' '.($teacher['lastname'] ?? '')); $cell = $statusMap[$teacherId] ?? ['status' => null, 'reason' => null]; $grid[] = [ 'teacher_id' => $teacherId, - 'name' => $name !== '' ? $name : 'User#' . $teacherId, + 'name' => $name !== '' ? $name : 'User#'.$teacherId, 'position' => $position, 'status' => $cell['status'], 'reason' => $cell['reason'], @@ -125,13 +125,13 @@ class AttendanceQueryService public function teacherAttendanceFormData(?int $userId, int $requestedSectionId = 0): array { - if (!$userId) { + if (! $userId) { throw new RuntimeException('User not logged in.'); } $teacher = $this->user->find($userId); $teacherName = $teacher - ? trim(($teacher->firstname ?? '') . ' ' . ($teacher->lastname ?? '')) + ? trim(($teacher->firstname ?? '').' '.($teacher->lastname ?? '')) : 'Unknown Teacher'; $semester = $this->attendanceService->currentSemester(); @@ -145,8 +145,8 @@ class AttendanceQueryService $activeSection = null; foreach ($assignments as $assignment) { - $cid = (int)($assignment['class_section_id'] ?? 0); - $pk = (int)($assignment['class_section_pk'] ?? 0); + $cid = (int) ($assignment['class_section_id'] ?? 0); + $pk = (int) ($assignment['class_section_pk'] ?? 0); if ($requestedSectionId > 0 && ($requestedSectionId === $cid || $requestedSectionId === $pk)) { $activeSection = $assignment; @@ -156,8 +156,8 @@ class AttendanceQueryService $activeSection ??= $assignments[0]; - $classSectionCode = (int)($activeSection['class_section_id'] ?? 0); - $classSectionPk = (int)($activeSection['class_section_pk'] ?? 0); + $classSectionCode = (int) ($activeSection['class_section_id'] ?? 0); + $classSectionPk = (int) ($activeSection['class_section_pk'] ?? 0); $classSectionId = $classSectionCode > 0 ? $classSectionCode : $classSectionPk; if ($classSectionId <= 0) { @@ -183,11 +183,11 @@ class AttendanceQueryService $lockedByStudent = []; $attendanceTable = $this->attendanceData->getTable(); - $dateInSundaysSql = 'DATE(`' . $attendanceTable . '`.`date`) IN (' . - implode(',', array_fill(0, count($sundayDates), '?')) . ')'; + $dateInSundaysSql = 'DATE(`'.$attendanceTable.'`.`date`) IN ('. + implode(',', array_fill(0, count($sundayDates), '?')).')'; foreach ($students as $student) { - $studentId = (int)$student['id']; + $studentId = (int) $student['id']; $rows = AttendanceData::query() ->select(['date', 'status', 'is_reported', 'reason', 'modified_by']) @@ -202,7 +202,7 @@ class AttendanceQueryService }) ->whereRaw($dateInSundaysSql, $sundayDates) ->get() - ->map(fn($row) => (array)$row) + ->map(fn ($row) => (array) $row) ->all(); $rows = $this->attendanceService->normalizeAttendanceEntries($rows); @@ -226,8 +226,8 @@ class AttendanceQueryService $recentHistory = []; foreach ($recentRows as $row) { $recentHistory[] = [ - 'date' => substr((string)($row['date'] ?? ''), 0, 10), - 'status' => strtolower((string)($row['status'] ?? '')), + 'date' => substr((string) ($row['date'] ?? ''), 0, 10), + 'status' => strtolower((string) ($row['status'] ?? '')), ]; } @@ -253,7 +253,7 @@ class AttendanceQueryService // missed rows (SQL/driver quirks), still show statuses that match the visible columns. foreach ($recentRows as $row) { $d = $this->calendarDateKey($row['date'] ?? null); - if ($d === '' || !in_array($d, $sundayDates, true)) { + if ($d === '' || ! in_array($d, $sundayDates, true)) { continue; } $rowStatus = $row['status'] ?? null; @@ -340,13 +340,13 @@ class AttendanceQueryService $teacherRows = []; - if (!empty($assigned)) { - $teacherIds = array_map(static fn($r) => (int)$r['teacher_id'], $assigned); + if (! empty($assigned)) { + $teacherIds = array_map(static fn ($r) => (int) $r['teacher_id'], $assigned); $statusMap = []; - if (!empty($teacherIds)) { - $staffDateInSql = 'DATE(`sa`.`date`) IN (' . - implode(',', array_fill(0, count($sundayDates), '?')) . ')'; + if (! empty($teacherIds)) { + $staffDateInSql = 'DATE(`sa`.`date`) IN ('. + implode(',', array_fill(0, count($sundayDates), '?')).')'; $staffQ = DB::table('staff_attendance as sa') ->select('sa.user_id', 'sa.date', 'sa.status', 'sa.reason') ->where('sa.semester', $semester) @@ -372,7 +372,7 @@ class AttendanceQueryService if ($dk === '') { continue; } - $statusMap[(int)$row->user_id][$dk] = [ + $statusMap[(int) $row->user_id][$dk] = [ 'status' => $row->status, 'reason' => $row->reason, ]; @@ -380,10 +380,10 @@ class AttendanceQueryService } foreach ($assigned as $teacher) { - $teacherId = (int)$teacher['teacher_id']; - $position = strtolower((string)($teacher['position'] ?? 'main')); + $teacherId = (int) $teacher['teacher_id']; + $position = strtolower((string) ($teacher['position'] ?? 'main')); $position = in_array($position, ['main', 'ta'], true) ? $position : 'main'; - $name = trim(($teacher['firstname'] ?? '') . ' ' . ($teacher['lastname'] ?? '')); + $name = trim(($teacher['firstname'] ?? '').' '.($teacher['lastname'] ?? '')); $history = []; foreach ($sundayDates as $date) { @@ -400,7 +400,7 @@ class AttendanceQueryService $teacherRows[] = [ 'teacher_id' => $teacherId, 'position' => $position, - 'name' => $name !== '' ? $name : 'User#' . $teacherId, + 'name' => $name !== '' ? $name : 'User#'.$teacherId, 'sunday_statuses' => $history, 'today' => $history[$currentSunday] ?? null, 'today_reason' => $todayReason, @@ -410,8 +410,8 @@ class AttendanceQueryService $noSchoolDays = []; $calendarTable = $this->calendar->getTable(); - $calDateInSql = 'DATE(`' . $calendarTable . '`.`date`) IN (' . - implode(',', array_fill(0, count($sundayDates), '?')) . ')'; + $calDateInSql = 'DATE(`'.$calendarTable.'`.`date`) IN ('. + implode(',', array_fill(0, count($sundayDates), '?')).')'; $calendarRows = $this->calendar->query() ->where('no_school', 1) ->whereRaw($calDateInSql, $sundayDates) @@ -438,9 +438,9 @@ class AttendanceQueryService 'class_section_id' => $classSectionId, 'sunday_dates' => $sundayDates, 'current_sunday' => $currentSunday, - 'enable_attendance' => (int)$this->configuration->getConfig('enable_attendance'), - 'can_edit' => ((int)$this->configuration->getConfig('enable_attendance') === 1), - 'class_id' => (int)$this->classSection->getClassId($classSectionId), + 'enable_attendance' => (int) $this->configuration->getConfig('enable_attendance'), + 'can_edit' => ((int) $this->configuration->getConfig('enable_attendance') === 1), + 'class_id' => (int) $this->classSection->getClassId($classSectionId), 'no_school_days' => $noSchoolDays, 'today' => $currentSunday, 'locked_by_student' => $lockedByStudent, @@ -463,7 +463,7 @@ class AttendanceQueryService ->where('sc.school_year', $schoolYear) ->groupBy('classSection.id', 'classSection.class_id', 'classSection.class_section_id', 'classSection.class_section_name') ->get() - ->map(fn($row) => $row->toArray()) + ->map(fn ($row) => $row->toArray()) ->all(); }; @@ -492,32 +492,32 @@ class AttendanceQueryService $sectionCounts = $this->studentClass->getStudentCountsBySection($effectiveTermYear); foreach ($classSections as $classSection) { - $secPk = (int)($classSection['id'] ?? 0); - $secCodeRaw = (string)($classSection['class_section_id'] ?? ''); - $secCode = $secCodeRaw !== '' ? $secCodeRaw : (string)$secPk; - $classId = (int)($classSection['class_id'] ?? 0); + $secPk = (int) ($classSection['id'] ?? 0); + $secCodeRaw = (string) ($classSection['class_section_id'] ?? ''); + $secCode = $secCodeRaw !== '' ? $secCodeRaw : (string) $secPk; + $classId = (int) ($classSection['class_id'] ?? 0); $studentsBySection[$secCode] = []; $datesBySection[$secCode] = []; $attendanceData[$secCode] = []; $attendanceRecord[$secCode] = []; - $countForCode = (int)($sectionCounts[$secCode] ?? 0); - $countForPk = (int)($sectionCounts[$secPk] ?? 0); + $countForCode = (int) ($sectionCounts[$secCode] ?? 0); + $countForPk = (int) ($sectionCounts[$secPk] ?? 0); $sectionHasStudents = ($countForCode + $countForPk) > 0; $students = $this->studentClass->getClassStudents($secCode, $effectiveTermYear); - if (!$students && $secPk && (string)$secPk !== (string)$secCode) { + if (! $students && $secPk && (string) $secPk !== (string) $secCode) { $students = $this->studentClass->getClassStudents($secPk, $effectiveTermYear); } - if (!$sectionHasStudents || !$students) { + if (! $sectionHasStudents || ! $students) { continue; } // Bulk-load student profiles for the whole section in one query $studentsArray = is_array($students) ? $students : $students->toArray(); - $studentIds = array_map(fn($sc) => (int)($sc['student_id'] ?? $sc->student_id ?? 0), $studentsArray); + $studentIds = array_map(fn ($sc) => (int) ($sc['student_id'] ?? $sc->student_id ?? 0), $studentsArray); $studentProfiles = $this->student ->query() ->select(['id', 'firstname', 'lastname', 'school_id']) @@ -525,12 +525,13 @@ class AttendanceQueryService ->where('is_active', 1) ->get() ->keyBy('id') - ->map(fn($row) => $row->toArray()) + ->map(fn ($row) => $row->toArray()) ->all(); $activeIds = array_keys($studentProfiles); if (empty($activeIds)) { unset($studentsBySection[$secCode], $datesBySection[$secCode], $attendanceData[$secCode], $attendanceRecord[$secCode]); + continue; } @@ -539,17 +540,17 @@ class AttendanceQueryService ->whereIn('student_id', $activeIds) ->where(function ($q) use ($secCode, $secPk) { $q->where('class_section_id', $secCode); - if ($secPk && (string)$secPk !== (string)$secCode) { + if ($secPk && (string) $secPk !== (string) $secCode) { $q->orWhere('class_section_id', $secPk); } }) - ->when($termSemester !== '', fn($q) => $q->where('semester', $termSemester)) - ->when($effectiveTermYear !== '', fn($q) => $q->where('school_year', $effectiveTermYear)) + ->when($termSemester !== '', fn ($q) => $q->where('semester', $termSemester)) + ->when($effectiveTermYear !== '', fn ($q) => $q->where('school_year', $effectiveTermYear)) ->orderBy('date') ->get() - ->map(fn($row) => $row->toArray()) + ->map(fn ($row) => $row->toArray()) ->groupBy('student_id') - ->map(fn($group) => $group->values()->all()) + ->map(fn ($group) => $group->values()->all()) ->all(); // Bulk-load all attendance records (summaries) for the section in one query @@ -557,36 +558,36 @@ class AttendanceQueryService ->whereIn('student_id', $activeIds) ->where(function ($q) use ($secCode, $secPk) { $q->where('class_section_id', $secCode); - if ($secPk && (string)$secPk !== (string)$secCode) { + if ($secPk && (string) $secPk !== (string) $secCode) { $q->orWhere('class_section_id', $secPk); } }) - ->when($termSemester !== '', fn($q) => $q->where('semester', $termSemester)) - ->when($effectiveTermYear !== '', fn($q) => $q->where('school_year', $effectiveTermYear)) + ->when($termSemester !== '', fn ($q) => $q->where('semester', $termSemester)) + ->when($effectiveTermYear !== '', fn ($q) => $q->where('school_year', $effectiveTermYear)) ->get() ->keyBy('student_id') - ->map(fn($row) => $row->toArray()) + ->map(fn ($row) => $row->toArray()) ->all(); $hasRoster = false; foreach ($studentIds as $studentId) { $student = $studentProfiles[$studentId] ?? null; - if (!$student) { + if (! $student) { continue; } $studentsBySection[$secCode][] = $student; - $studentSchoolMap[$studentId] = (string)($student['school_id'] ?? ''); + $studentSchoolMap[$studentId] = (string) ($student['school_id'] ?? ''); $hasRoster = true; $entries = $this->attendanceService->normalizeAttendanceEntries( - array_map(fn($r) => (array)$r, $allEntries[$studentId] ?? []) + array_map(fn ($r) => (array) $r, $allEntries[$studentId] ?? []) ); $attendanceData[$secCode][$studentId] = $entries; foreach ($entries as $entry) { - $d = (string)($entry['date'] ?? ''); + $d = (string) ($entry['date'] ?? ''); if ($d !== '') { $datesBySection[$secCode][$d] = true; } @@ -600,8 +601,9 @@ class AttendanceQueryService ]; } - if (!$hasRoster) { + if (! $hasRoster) { unset($studentsBySection[$secCode], $datesBySection[$secCode], $attendanceData[$secCode], $attendanceRecord[$secCode]); + continue; } @@ -610,7 +612,7 @@ class AttendanceQueryService foreach ($attendanceData as $secId => &$studentEntries) { foreach ($studentEntries as &$entries) { - usort($entries, static fn($a, $b) => strcmp((string)($a['date'] ?? ''), (string)($b['date'] ?? ''))); + usort($entries, static fn ($a, $b) => strcmp((string) ($a['date'] ?? ''), (string) ($b['date'] ?? ''))); } } unset($studentEntries, $entries); @@ -644,9 +646,9 @@ class AttendanceQueryService ->toArray(); foreach ($events as $event) { - $d = substr((string)($event['date'] ?? ''), 0, 10); + $d = substr((string) ($event['date'] ?? ''), 0, 10); if ($d !== '') { - $noSchoolDays[$d] = trim((string)($event['title'] ?? 'No School')) ?: 'No School'; + $noSchoolDays[$d] = trim((string) ($event['title'] ?? 'No School')) ?: 'No School'; } } @@ -668,12 +670,12 @@ class AttendanceQueryService foreach ($studentEntries as $studentId => $entries) { $statusByDate = []; foreach ($entries as $entry) { - $d = substr((string)($entry['date'] ?? ''), 0, 10); + $d = substr((string) ($entry['date'] ?? ''), 0, 10); if ($d === '' || empty($passedDatesSet[$d])) { continue; } - $st = strtolower(trim((string)($entry['status'] ?? ''))); - if (!in_array($st, ['present', 'absent', 'late'], true)) { + $st = strtolower(trim((string) ($entry['status'] ?? ''))); + if (! in_array($st, ['present', 'absent', 'late'], true)) { continue; } $statusByDate[$d] = $st; @@ -695,9 +697,9 @@ class AttendanceQueryService $sum = $p + $l + $a; $record = $attendanceRecord[$secCode][$studentId] ?? []; - $storedSum = (int)($record['total_presence'] ?? 0) - + (int)($record['total_late'] ?? 0) - + (int)($record['total_absence'] ?? 0); + $storedSum = (int) ($record['total_presence'] ?? 0) + + (int) ($record['total_late'] ?? 0) + + (int) ($record['total_absence'] ?? 0); if ($storedSum !== $totalPassedDays && $storedSum !== $sum) { DB::table('attendance_record') @@ -733,7 +735,7 @@ class AttendanceQueryService if (auth()->id()) { $u = $this->user->query()->select('firstname', 'lastname')->find(auth()->id()); if ($u) { - $adminName = trim(($u->firstname ?? '') . ' ' . ($u->lastname ?? '')); + $adminName = trim(($u->firstname ?? '').' '.($u->lastname ?? '')); } } @@ -773,8 +775,8 @@ class AttendanceQueryService return []; } - $staffDateInSql = 'DATE(`sa`.`date`) IN (' . - implode(',', array_fill(0, count($sundayDates), '?')) . ')'; + $staffDateInSql = 'DATE(`sa`.`date`) IN ('. + implode(',', array_fill(0, count($sundayDates), '?')).')'; $run = static function ( bool $requireTcSemester, @@ -830,8 +832,8 @@ class AttendanceQueryService return []; } - $staffDateInSql = 'DATE(`sa`.`date`) IN (' . - implode(',', array_fill(0, count($sundayDates), '?')) . ')'; + $staffDateInSql = 'DATE(`sa`.`date`) IN ('. + implode(',', array_fill(0, count($sundayDates), '?')).')'; $ids = []; @@ -905,7 +907,7 @@ class AttendanceQueryService */ private function calendarDateKey(mixed $value): string { - $s = trim((string)($value ?? '')); + $s = trim((string) ($value ?? '')); if ($s === '') { return ''; } diff --git a/app/Services/Attendance/AttendanceRecordSyncService.php b/app/Services/Attendance/AttendanceRecordSyncService.php index 47771b95..8933c88e 100644 --- a/app/Services/Attendance/AttendanceRecordSyncService.php +++ b/app/Services/Attendance/AttendanceRecordSyncService.php @@ -25,16 +25,16 @@ class AttendanceRecordSyncService ->where('school_year', $schoolYear) ->first(); - if (!$record) { + if (! $record) { return; } $newStatus = strtolower($newStatus); - $oldStatus = strtolower((string)$oldStatus); + $oldStatus = strtolower((string) $oldStatus); - $presence = (int)($record->total_presence ?? 0); - $absence = (int)($record->total_absence ?? 0); - $late = (int)($record->total_late ?? 0); + $presence = (int) ($record->total_presence ?? 0); + $absence = (int) ($record->total_absence ?? 0); + $late = (int) ($record->total_late ?? 0); if ($oldStatus === 'present') { $presence--; @@ -80,13 +80,14 @@ class AttendanceRecordSyncService if ($record) { $record->update([ - 'total_presence' => (int)($record->total_presence ?? 0) + ($status === 'present' ? 1 : 0), - 'total_absence' => (int)($record->total_absence ?? 0) + ($status === 'absent' ? 1 : 0), - 'total_late' => (int)($record->total_late ?? 0) + ($status === 'late' ? 1 : 0), - 'total_attendance' => (int)($record->total_attendance ?? 0) + 1, + 'total_presence' => (int) ($record->total_presence ?? 0) + ($status === 'present' ? 1 : 0), + 'total_absence' => (int) ($record->total_absence ?? 0) + ($status === 'absent' ? 1 : 0), + 'total_late' => (int) ($record->total_late ?? 0) + ($status === 'late' ? 1 : 0), + 'total_attendance' => (int) ($record->total_attendance ?? 0) + 1, 'updated_at' => now(), 'modified_by' => $modifiedBy, ]); + return; } @@ -141,13 +142,13 @@ class AttendanceRecordSyncService return $record->update($payload); } - $payload['class_section_id'] = (int)$classSectionId; + $payload['class_section_id'] = (int) $classSectionId; $payload['student_id'] = $studentId; $payload['school_id'] = $schoolId; $payload['semester'] = $semester; $payload['school_year'] = $schoolYear; $payload['created_at'] = now(); - return (bool)AttendanceRecord::query()->create($payload); + return (bool) AttendanceRecord::query()->create($payload); } -} \ No newline at end of file +} diff --git a/app/Services/Attendance/AttendanceService.php b/app/Services/Attendance/AttendanceService.php index 13985bc1..5dc287a7 100644 --- a/app/Services/Attendance/AttendanceService.php +++ b/app/Services/Attendance/AttendanceService.php @@ -27,24 +27,24 @@ class AttendanceService public function currentSemester(): string { - return (string)($this->configuration->getConfig('semester') ?? 'Fall'); + return (string) ($this->configuration->getConfig('semester') ?? 'Fall'); } public function currentSchoolYear(): string { - return (string)($this->configuration->getConfig('school_year') ?? now()->year . '-' . (now()->year + 1)); + return (string) ($this->configuration->getConfig('school_year') ?? now()->year.'-'.(now()->year + 1)); } public function updateAttendanceManagement(Authenticatable $user, array $data): array { - $classSectionId = (int)$data['class_section_id']; - $studentId = (int)$data['student_id']; - $status = strtolower((string)$data['status']); - $date = (string)$data['date']; + $classSectionId = (int) $data['class_section_id']; + $studentId = (int) $data['student_id']; + $status = strtolower((string) $data['status']); + $date = (string) $data['date']; $reason = $data['reason'] ?? null; $classId = $data['class_id'] ?? null; - $semester = (string)($data['semester'] ?? $this->currentSemester()); - $schoolYear = (string)($data['school_year'] ?? $this->currentSchoolYear()); + $semester = (string) ($data['semester'] ?? $this->currentSemester()); + $schoolYear = (string) ($data['school_year'] ?? $this->currentSchoolYear()); $dayRow = AttendanceDay::query() ->where('class_section_id', $classSectionId) @@ -67,11 +67,11 @@ class AttendanceService 'permissions' => method_exists($user, 'permissions') ? $user->permissions->pluck('name')->all() : [], ]; - if (!$this->attendancePolicyService->canEditDay($dayPolicyRow, $currentUser)) { + if (! $this->attendancePolicyService->canEditDay($dayPolicyRow, $currentUser)) { throw new RuntimeException('Attendance is locked for your role.'); } - $reportedRaw = strtolower((string)($data['is_reported'] ?? '')); + $reportedRaw = strtolower((string) ($data['is_reported'] ?? '')); $isReported = $status === 'present' ? 'no' : (in_array($reportedRaw, ['1', 'yes', 'y', 'true', 'on'], true) ? 'yes' : 'no'); @@ -98,7 +98,7 @@ class AttendanceService 'semester' => $semester, 'school_year' => $schoolYear, 'date' => $date, - 'class_id' => (int)$classId, + 'class_id' => (int) $classId, 'user_id' => $user->id, ]); @@ -136,13 +136,13 @@ class AttendanceService public function adminAddEntry(Authenticatable $user, array $data): array { - $classSectionId = (int)$data['class_section_id']; - $studentId = (int)$data['student_id']; - $status = strtolower((string)$data['status']); - $date = (string)($data['date'] ?? now()->toDateString()); + $classSectionId = (int) $data['class_section_id']; + $studentId = (int) $data['student_id']; + $status = strtolower((string) $data['status']); + $date = (string) ($data['date'] ?? now()->toDateString()); $reason = $data['reason'] ?? null; - $isReported = in_array(strtolower((string)($data['is_reported'] ?? 'no')), ['yes', 'no'], true) - ? strtolower((string)($data['is_reported'] ?? 'no')) + $isReported = in_array(strtolower((string) ($data['is_reported'] ?? 'no')), ['yes', 'no'], true) + ? strtolower((string) ($data['is_reported'] ?? 'no')) : 'no'; $section = $this->classSection @@ -152,7 +152,7 @@ class AttendanceService ->orWhere('class_section_id', $classSectionId) ->first(); - $resolvedClassId = (int)($section->class_id ?? 0); + $resolvedClassId = (int) ($section->class_id ?? 0); if ($resolvedClassId <= 0) { throw new RuntimeException('Cannot resolve class_id for this section.'); @@ -210,29 +210,32 @@ class AttendanceService $reportedRaw = $row['is_reported'] ?? ($row['reported'] ?? ''); $row['is_reported'] = $this->normalizeReportedFlag($reportedRaw); } + return $entries; } public function normalizeStatus(?string $status): string { - $status = strtolower(trim((string)$status)); + $status = strtolower(trim((string) $status)); + return in_array($status, ['present', 'absent', 'late'], true) ? $status : $status; } public function normalizeReportedFlag(mixed $flag): string { - $v = strtolower(trim((string)$flag)); + $v = strtolower(trim((string) $flag)); + return in_array($v, ['yes', '1', 'true', 'y', 'on'], true) ? 'yes' : 'no'; } public function detectExternalSubmission(?array $row): array { - if (empty($row) || !is_array($row)) { + if (empty($row) || ! is_array($row)) { return ['locked' => false, 'source' => null]; } - $reportedRaw = strtolower((string)($row['is_reported'] ?? '')); - $reason = strtolower((string)($row['reason'] ?? '')); + $reportedRaw = strtolower((string) ($row['is_reported'] ?? '')); + $reason = strtolower((string) ($row['reason'] ?? '')); $isParent = in_array($reportedRaw, ['yes', '1', 'true', 'y'], true) || str_contains($reason, 'parent-reported') @@ -242,7 +245,7 @@ class AttendanceService return ['locked' => true, 'source' => 'parent']; } - $modifierId = (int)($row['modified_by'] ?? 0); + $modifierId = (int) ($row['modified_by'] ?? 0); if ($modifierId > 0 && $this->isAdminLikeUserId($modifierId)) { return ['locked' => true, 'source' => 'admin']; } @@ -264,12 +267,12 @@ class AttendanceService $excluded = ['parent', 'guest', 'teacher', 'teacher_assistant', 'assistant_teacher', 'ta']; foreach ($roles as $role) { - $name = strtolower(str_replace([' ', '-'], '_', (string)($role['role_name'] ?? ''))); - if ($name !== '' && !in_array($name, $excluded, true)) { + $name = strtolower(str_replace([' ', '-'], '_', (string) ($role['role_name'] ?? ''))); + if ($name !== '' && ! in_array($name, $excluded, true)) { return true; } } return false; } -} \ No newline at end of file +} diff --git a/app/Services/Attendance/AttendanceSummaryRebuildService.php b/app/Services/Attendance/AttendanceSummaryRebuildService.php index 5752aec9..5137e21a 100644 --- a/app/Services/Attendance/AttendanceSummaryRebuildService.php +++ b/app/Services/Attendance/AttendanceSummaryRebuildService.php @@ -29,8 +29,8 @@ class AttendanceSummaryRebuildService $semester = (string) ($row['semester'] ?? ''); $status = strtolower((string) ($row['status'] ?? '')); - $key = $studentId . '-' . $schoolYear . '-' . $semester; - if (!isset($summary[$key])) { + $key = $studentId.'-'.$schoolYear.'-'.$semester; + if (! isset($summary[$key])) { $summary[$key] = [ 'student_id' => $studentId, 'school_year' => $schoolYear, diff --git a/app/Services/Attendance/LateSlipLogQueryService.php b/app/Services/Attendance/LateSlipLogQueryService.php index 5d27271c..f603438b 100644 --- a/app/Services/Attendance/LateSlipLogQueryService.php +++ b/app/Services/Attendance/LateSlipLogQueryService.php @@ -11,16 +11,16 @@ class LateSlipLogQueryService { $query = LateSlipLog::query(); - if (!empty($filters['school_year'])) { + if (! empty($filters['school_year'])) { $query->where('school_year', (string) $filters['school_year']); } - if (!empty($filters['semester'])) { + if (! empty($filters['semester'])) { $query->where('semester', (string) $filters['semester']); } - if (!empty($filters['q'])) { - $query->where('student_name', 'like', '%' . $filters['q'] . '%'); + if (! empty($filters['q'])) { + $query->where('student_name', 'like', '%'.$filters['q'].'%'); } $dateFrom = $this->toDbDate($filters['date_from'] ?? null); @@ -58,6 +58,7 @@ class LateSlipLogQueryService return null; } $ts = strtotime($value); + return $ts ? date('Y-m-d', $ts) : null; } } diff --git a/app/Services/Attendance/SemesterRangeService.php b/app/Services/Attendance/SemesterRangeService.php index 79d80a27..4c0870f3 100644 --- a/app/Services/Attendance/SemesterRangeService.php +++ b/app/Services/Attendance/SemesterRangeService.php @@ -9,7 +9,7 @@ class SemesterRangeService { public function normalizeSemester(?string $semester): string { - $value = strtolower(trim((string)$semester)); + $value = strtolower(trim((string) $semester)); return match ($value) { 'fall', 'autumn' => 'Fall', @@ -76,7 +76,7 @@ class SemesterRangeService protected function parseSchoolYear(string $schoolYear): array { if (preg_match('/^\s*(\d{4})\s*-\s*(\d{4})\s*$/', $schoolYear, $m)) { - return [(int)$m[1], (int)$m[2]]; + return [(int) $m[1], (int) $m[2]]; } $configured = trim((string) Configuration::getConfig('school_year')); @@ -85,7 +85,7 @@ class SemesterRangeService } $year = (int) date('Y'); - $derived = $year . '-' . ($year + 1); + $derived = $year.'-'.($year + 1); Configuration::setConfigValueByKey('school_year', $derived); return [$year, $year + 1]; diff --git a/app/Services/Attendance/StaffAttendanceService.php b/app/Services/Attendance/StaffAttendanceService.php index ea380676..170c8844 100644 --- a/app/Services/Attendance/StaffAttendanceService.php +++ b/app/Services/Attendance/StaffAttendanceService.php @@ -2,13 +2,14 @@ namespace App\Services\Attendance; +use App\Controllers\View\AttendanceController; use App\Models\AttendanceDay; +use App\Models\ClassSection; use App\Models\Configuration; use App\Models\StaffAttendance; use App\Models\TeacherClass; use Carbon\Carbon; use Illuminate\Contracts\Auth\Authenticatable; -use App\Models\ClassSection; use Illuminate\Support\Facades\DB; use Symfony\Component\HttpFoundation\StreamedResponse; @@ -23,7 +24,7 @@ class StaffAttendanceService ) {} /** - * legacy {@see \App\Controllers\View\AttendanceController::index} — single date / section teacher grid JSON. + * legacy {@see AttendanceController::index} — single date / section teacher grid JSON. */ public function teacherAttendanceDayIndex(?string $semester, ?string $schoolYear, string $dateYmd, int $sectionCode): array { @@ -44,7 +45,7 @@ class StaffAttendanceService ->where('class_section_id', $code) ->value('class_section_name'); $name = trim((string) $name); - $labels[$code] = $name !== '' ? $name : ('Section #' . $code); + $labels[$code] = $name !== '' ? $name : ('Section #'.$code); } $grid = []; @@ -76,7 +77,7 @@ class StaffAttendanceService $tid = (int) ($t['teacher_id'] ?? 0); $posRaw = strtolower((string) ($t['position'] ?? 'main')); $pos = in_array($posRaw, ['main', 'ta'], true) ? $posRaw : 'main'; - $name = trim(($t['firstname'] ?? '') . ' ' . ($t['lastname'] ?? '')) ?: ('User#' . $tid); + $name = trim(($t['firstname'] ?? '').' '.($t['lastname'] ?? '')) ?: ('User#'.$tid); $cell = $statusMap[$tid] ?? ['status' => null, 'reason' => null]; @@ -108,7 +109,7 @@ class StaffAttendanceService } /** - * Bulk save for legacy {@see \App\Controllers\View\AttendanceController::save} (method missing in legacy; implemented here). + * Bulk save for legacy {@see AttendanceController::save} (method missing in legacy; implemented here). */ public function saveTeacherAttendanceBulk(Authenticatable $user, array $payload): array { @@ -181,7 +182,7 @@ class StaffAttendanceService public function monthData(?string $semester, ?string $schoolYear): array { - $semester = $semester ?: (string)$this->configuration->getConfig('semester'); + $semester = $semester ?: (string) $this->configuration->getConfig('semester'); [ 'school_year' => $schoolYear, 'current_year' => $currentYear, @@ -232,7 +233,7 @@ class StaffAttendanceService $sectionCodes = array_keys($assignedBySection); $labels = []; - if (!empty($sectionCodes)) { + if (! empty($sectionCodes)) { $rows = $this->classSection->query() ->select(['class_section_id', 'class_section_name']) ->whereIn('class_section_id', $sectionCodes) @@ -240,20 +241,20 @@ class StaffAttendanceService ->toArray(); foreach ($rows as $row) { - $labels[(int)$row['class_section_id']] = trim((string)$row['class_section_name']) ?: 'Section #' . (int)$row['class_section_id']; + $labels[(int) $row['class_section_id']] = trim((string) $row['class_section_name']) ?: 'Section #'.(int) $row['class_section_id']; } } $teacherIds = []; foreach ($assignedBySection as $rows) { foreach ($rows as $row) { - $teacherIds[(int)$row['teacher_id']] = true; + $teacherIds[(int) $row['teacher_id']] = true; } } $teacherIds = array_keys($teacherIds); $statusByTeacher = []; - if (!empty($teacherIds) && !empty($days)) { + if (! empty($teacherIds) && ! empty($days)) { $query = DB::table('staff_attendance') ->select('user_id', 'date', 'status', 'reason') ->where('school_year', $schoolYear) @@ -267,8 +268,8 @@ class StaffAttendanceService $rows = $query->get(); foreach ($rows as $row) { - $statusByTeacher[(int)$row->user_id][(string)$row->date] = [ - 'status' => strtolower((string)$row->status), + $statusByTeacher[(int) $row->user_id][(string) $row->date] = [ + 'status' => strtolower((string) $row->status), 'reason' => $row->reason, ]; } @@ -284,7 +285,7 @@ class StaffAttendanceService $noSchoolDays = []; foreach ($noSchoolRows as $row) { - $noSchoolDays[] = (string)$row->date; + $noSchoolDays[] = (string) $row->date; } $sections = []; @@ -292,9 +293,9 @@ class StaffAttendanceService $teachersPayload = []; foreach (($assignedBySection[$code] ?? []) as $row) { - $teacherId = (int)$row['teacher_id']; - $name = trim(($row['firstname'] ?? '') . ' ' . ($row['lastname'] ?? '')) ?: ('User#' . $teacherId); - $position = strtolower((string)($row['position'] ?? 'main')); + $teacherId = (int) $row['teacher_id']; + $name = trim(($row['firstname'] ?? '').' '.($row['lastname'] ?? '')) ?: ('User#'.$teacherId); + $position = strtolower((string) ($row['position'] ?? 'main')); $position = in_array($position, ['main', 'ta'], true) ? $position : 'main'; $cells = []; @@ -302,7 +303,7 @@ class StaffAttendanceService foreach ($days as $date) { $cell = $statusByTeacher[$teacherId][$date] ?? null; - if (!$cell || empty($cell['status'])) { + if (! $cell || empty($cell['status'])) { continue; } @@ -327,15 +328,15 @@ class StaffAttendanceService } $sections[] = [ - 'section_code' => (int)$code, - 'label' => $labels[$code] ?? ('Section #' . (int)$code), + 'section_code' => (int) $code, + 'label' => $labels[$code] ?? ('Section #'.(int) $code), 'teachers' => $teachersPayload, ]; } $adminStatusByUser = []; $adminIds = []; - if (!empty($days)) { + if (! empty($days)) { $query = DB::table('staff_attendance') ->select('user_id', 'date', 'status', 'reason') ->where('school_year', $schoolYear) @@ -361,7 +362,7 @@ class StaffAttendanceService $adminIds = array_keys($adminIds); $admins = []; - if (!empty($adminIds)) { + if (! empty($adminIds)) { $rolesRows = DB::table('user_roles as ur') ->select('ur.user_id', 'r.name as role_name', 'r.slug as role_slug', 'r.priority') ->join('roles as r', 'r.id', '=', 'ur.role_id') @@ -389,20 +390,20 @@ class StaffAttendanceService $namesByUser = []; foreach ($userRows as $row) { - $namesByUser[(int) $row->id] = trim(((string) ($row->firstname ?? '')) . ' ' . ((string) ($row->lastname ?? ''))); + $namesByUser[(int) $row->id] = trim(((string) ($row->firstname ?? '')).' '.((string) ($row->lastname ?? ''))); } foreach ($adminIds as $uid) { $picked = null; foreach (($rolesByUser[$uid] ?? []) as $role) { $slug = strtolower((string) ($role['role_slug'] ?? $role['role_name'] ?? '')); - if (!in_array($slug, $excludeSlugs, true)) { + if (! in_array($slug, $excludeSlugs, true)) { $picked = $role; break; } } - if (!$picked) { + if (! $picked) { continue; } @@ -410,7 +411,7 @@ class StaffAttendanceService $cells = []; foreach ($days as $date) { $cell = $adminStatusByUser[$uid][$date] ?? null; - if (!$cell || empty($cell['status'])) { + if (! $cell || empty($cell['status'])) { continue; } $cells[$date] = $cell; @@ -425,7 +426,7 @@ class StaffAttendanceService $admins[] = [ 'user_id' => $uid, - 'name' => $namesByUser[$uid] !== '' ? $namesByUser[$uid] : ('User#' . $uid), + 'name' => $namesByUser[$uid] !== '' ? $namesByUser[$uid] : ('User#'.$uid), 'role' => (string) ($picked['role_name'] ?? 'Admin'), 'cells' => $cells, 'totals' => $totals, @@ -508,7 +509,7 @@ class StaffAttendanceService } } - if ($currentYear !== '' && !in_array($currentYear, $schoolYears, true)) { + if ($currentYear !== '' && ! in_array($currentYear, $schoolYears, true)) { array_unshift($schoolYears, $currentYear); } @@ -523,7 +524,7 @@ class StaffAttendanceService ? $requestedSchoolYear : ($currentYear !== '' ? $currentYear : ($schoolYears[0] ?? '')); - if ($resolvedYear !== '' && !in_array($resolvedYear, $schoolYears, true)) { + if ($resolvedYear !== '' && ! in_array($resolvedYear, $schoolYears, true)) { array_unshift($schoolYears, $resolvedYear); } @@ -539,7 +540,7 @@ class StaffAttendanceService protected function buildRangeLabel(string $schoolYear, string $semesterNorm, string $rangeStart, string $rangeEnd): string { if ($semesterNorm === '') { - return 'School Year ' . $schoolYear; + return 'School Year '.$schoolYear; } return sprintf( @@ -553,14 +554,14 @@ class StaffAttendanceService public function adminsAttendanceData(?string $date, ?string $semester, ?string $schoolYear): array { $date = $date ?: now()->toDateString(); - $semester = $semester ?: (string)$this->configuration->getConfig('semester'); - $schoolYear = $schoolYear ?: (string)$this->configuration->getConfig('school_year'); + $semester = $semester ?: (string) $this->configuration->getConfig('semester'); + $schoolYear = $schoolYear ?: (string) $this->configuration->getConfig('school_year'); $allStaff = $this->staffAttendance->staffWithStatusForDay($date, $semester, $schoolYear, []); $userIds = []; foreach ($allStaff as $row) { - $uid = (int)($row['user_id'] ?? 0); + $uid = (int) ($row['user_id'] ?? 0); if ($uid > 0) { $userIds[$uid] = true; } @@ -577,19 +578,19 @@ class StaffAttendanceService $rolesByUser = []; foreach ($rolesRows as $row) { - $rolesByUser[(int)$row->user_id][] = (array)$row; + $rolesByUser[(int) $row->user_id][] = (array) $row; } foreach ($rolesByUser as &$list) { - usort($list, fn($a, $b) => ((int)($a['priority'] ?? 100)) <=> ((int)($b['priority'] ?? 100))); + usort($list, fn ($a, $b) => ((int) ($a['priority'] ?? 100)) <=> ((int) ($b['priority'] ?? 100))); } $excludeSlugs = ['parent', 'guest', 'teacher', 'teacher_assistant', 'ta', 'assistant_teacher']; $staffByUser = []; foreach ($allStaff as $row) { - $uid = (int)($row['user_id'] ?? 0); - if ($uid > 0 && !isset($staffByUser[$uid])) { + $uid = (int) ($row['user_id'] ?? 0); + if ($uid > 0 && ! isset($staffByUser[$uid])) { $staffByUser[$uid] = $row; } } @@ -600,14 +601,14 @@ class StaffAttendanceService $picked = null; foreach ($roles as $role) { - $slug = strtolower((string)($role['role_slug'] ?? $role['role_name'] ?? '')); - if (!in_array($slug, $excludeSlugs, true)) { + $slug = strtolower((string) ($role['role_slug'] ?? $role['role_name'] ?? '')); + if (! in_array($slug, $excludeSlugs, true)) { $picked = $role; break; } } - if (!$picked) { + if (! $picked) { continue; } @@ -618,8 +619,9 @@ class StaffAttendanceService } usort($adminsGrid, function ($a, $b) { - $an = trim(($a['firstname'] ?? '') . ' ' . ($a['lastname'] ?? '')); - $bn = trim(($b['firstname'] ?? '') . ' ' . ($b['lastname'] ?? '')); + $an = trim(($a['firstname'] ?? '').' '.($a['lastname'] ?? '')); + $bn = trim(($b['firstname'] ?? '').' '.($b['lastname'] ?? '')); + return strcasecmp($an, $bn); }); @@ -633,20 +635,20 @@ class StaffAttendanceService public function saveAdmins(Authenticatable $user, array $data): array { - $date = (string)$data['date']; - $semester = (string)$data['semester']; - $schoolYear = (string)$data['school_year']; - $admins = (array)($data['admins'] ?? $data['staff'] ?? []); + $date = (string) $data['date']; + $semester = (string) $data['semester']; + $schoolYear = (string) $data['school_year']; + $admins = (array) ($data['admins'] ?? $data['staff'] ?? []); $allowedStatuses = ['present', 'absent', 'late']; $excludedRoleSlugs = ['parent', 'guest', 'teacher', 'teacher_assistant']; DB::transaction(function () use ($admins, $date, $semester, $schoolYear, $allowedStatuses, $excludedRoleSlugs) { foreach ($admins as $uid => $row) { - $uid = (int)$uid; - $status = strtolower(trim((string)($row['status'] ?? ''))); - $reason = trim((string)($row['reason'] ?? '')); - $roleName = (string)($row['role_name'] ?? ''); - $roleSlug = strtolower(str_replace(['-', ' '], '_', (string)($row['role_slug'] ?? $roleName))); + $uid = (int) $uid; + $status = strtolower(trim((string) ($row['status'] ?? ''))); + $reason = trim((string) ($row['reason'] ?? '')); + $roleName = (string) ($row['role_name'] ?? ''); + $roleSlug = strtolower(str_replace(['-', ' '], '_', (string) ($row['role_slug'] ?? $roleName))); if (in_array($roleSlug, $excludedRoleSlugs, true)) { continue; @@ -659,10 +661,11 @@ class StaffAttendanceService ->where('semester', $semester) ->where('school_year', $schoolYear) ->delete(); + continue; } - if (!in_array($status, $allowedStatuses, true)) { + if (! in_array($status, $allowedStatuses, true)) { continue; } @@ -692,13 +695,13 @@ class StaffAttendanceService public function saveCell(Authenticatable $user, array $data): array { - $date = (string)$data['date']; - $status = (string)($data['status'] ?? ''); - $targetId = (int)$data['user_id']; - $roleName = trim((string)($data['role_name'] ?? '')); - $semester = (string)$data['semester']; - $schoolYear = (string)$data['school_year']; - $reason = trim((string)($data['reason'] ?? '')); + $date = (string) $data['date']; + $status = (string) ($data['status'] ?? ''); + $targetId = (int) $data['user_id']; + $roleName = trim((string) ($data['role_name'] ?? '')); + $semester = (string) $data['semester']; + $schoolYear = (string) $data['school_year']; + $reason = trim((string) ($data['reason'] ?? '')); if ($status === '') { DB::table('staff_attendance') @@ -732,9 +735,9 @@ class StaffAttendanceService public function monthCsv(?string $semester, ?string $schoolYear, ?string $scope): StreamedResponse { - $semester = $semester ?: (string)$this->configuration->getConfig('semester'); - $schoolYear = $schoolYear ?: (string)$this->configuration->getConfig('school_year'); - $scope = strtolower((string)$scope); + $semester = $semester ?: (string) $this->configuration->getConfig('semester'); + $schoolYear = $schoolYear ?: (string) $this->configuration->getConfig('school_year'); + $scope = strtolower((string) $scope); [$rangeStart, $rangeEnd] = $this->semesterRangeService->getSchoolYearRange($schoolYear); $semesterNorm = $this->semesterRangeService->normalizeSemester($semester); @@ -748,9 +751,9 @@ class StaffAttendanceService $daysList = $this->semesterRangeService->buildSundayList($rangeStart, $rangeEnd); $filename = ($scope === 'admins' ? 'admin_attendance_' : 'teacher_attendance_') - . $semester . '_' . str_replace('/', '-', $schoolYear) . '.csv'; + .$semester.'_'.str_replace('/', '-', $schoolYear).'.csv'; - return response()->streamDownload(function () use ($semester, $schoolYear, $scope, $daysList, $semesterNorm, $rangeStart, $rangeEnd) { + return response()->streamDownload(function () use ($schoolYear, $scope, $semesterNorm, $rangeStart, $rangeEnd) { $out = fopen('php://output', 'w'); if ($scope === 'admins') { @@ -770,12 +773,13 @@ class StaffAttendanceService fputcsv($out, [ $row->user_id, $row->date, - strtoupper(substr((string)$row->status, 0, 1)), + strtoupper(substr((string) $row->status, 0, 1)), $row->reason, ]); } fclose($out); + return; } @@ -795,7 +799,7 @@ class StaffAttendanceService fputcsv($out, [ $row->user_id, $row->date, - strtoupper(substr((string)$row->status, 0, 1)), + strtoupper(substr((string) $row->status, 0, 1)), $row->reason, ]); } diff --git a/app/Services/Attendance/StudentAttendanceWriterService.php b/app/Services/Attendance/StudentAttendanceWriterService.php index 1f81b5d9..e6758416 100644 --- a/app/Services/Attendance/StudentAttendanceWriterService.php +++ b/app/Services/Attendance/StudentAttendanceWriterService.php @@ -70,7 +70,7 @@ class StudentAttendanceWriterService public function resolveStudentSchoolId(int $studentId, ?string $studentSchoolId = null): string { - $studentSchoolId = trim((string)$studentSchoolId); + $studentSchoolId = trim((string) $studentSchoolId); if ($studentSchoolId !== '') { return $studentSchoolId; @@ -78,25 +78,25 @@ class StudentAttendanceWriterService $studentRow = $this->student->query()->select('school_id')->find($studentId); - if (!$studentRow || empty($studentRow->school_id)) { + if (! $studentRow || empty($studentRow->school_id)) { throw new RuntimeException('Student school ID not found.'); } - return (string)$studentRow->school_id; + return (string) $studentRow->school_id; } public function saveOrUpdateStudentAttendance(array $payload): array { - $classSectionId = (int)$payload['class_section_id']; - $studentId = (int)$payload['student_id']; + $classSectionId = (int) $payload['class_section_id']; + $studentId = (int) $payload['student_id']; $studentSchoolId = $this->resolveStudentSchoolId($studentId, $payload['school_id'] ?? null); - $status = strtolower((string)$payload['status']); + $status = strtolower((string) $payload['status']); $reason = $payload['reason'] ?? null; $reported = $payload['is_reported'] ?? null; - $semester = (string)$payload['semester']; - $schoolYear = (string)$payload['school_year']; - $date = (string)$payload['date']; - $classId = (int)$payload['class_id']; + $semester = (string) $payload['semester']; + $schoolYear = (string) $payload['school_year']; + $date = (string) $payload['date']; + $classId = (int) $payload['class_id']; $userId = $payload['user_id'] ?? null; $existing = AttendanceData::query() @@ -110,7 +110,7 @@ class StudentAttendanceWriterService $oldStatus = $existing->status; $this->updateAttendanceData( - (int)$existing->id, + (int) $existing->id, $status, $reason, $reported, @@ -131,7 +131,7 @@ class StudentAttendanceWriterService return [ 'mode' => 'updated', - 'attendance_id' => (int)$existing->id, + 'attendance_id' => (int) $existing->id, 'old_status' => $oldStatus, 'new_status' => $status, 'school_id' => $studentSchoolId, @@ -172,10 +172,10 @@ class StudentAttendanceWriterService return [ 'mode' => 'created', - 'attendance_id' => (int)($created->id ?? 0), + 'attendance_id' => (int) ($created->id ?? 0), 'old_status' => null, 'new_status' => $status, 'school_id' => $studentSchoolId, ]; } -} \ No newline at end of file +} diff --git a/app/Services/Attendance/TeacherAttendanceSubmissionService.php b/app/Services/Attendance/TeacherAttendanceSubmissionService.php index 48798821..2b2c1ef5 100644 --- a/app/Services/Attendance/TeacherAttendanceSubmissionService.php +++ b/app/Services/Attendance/TeacherAttendanceSubmissionService.php @@ -31,7 +31,7 @@ class TeacherAttendanceSubmissionService callable $currentSemester, callable $currentSchoolYear ): array { - $rawSection = trim((string)$data['class_section_id']); + $rawSection = trim((string) $data['class_section_id']); if ($rawSection === '') { throw new RuntimeException('Missing or invalid class section.'); @@ -42,36 +42,36 @@ class TeacherAttendanceSubmissionService ->where('class_section_id', $rawSection) ->first(); - if (!$sectionRow && ctype_digit($rawSection)) { + if (! $sectionRow && ctype_digit($rawSection)) { $sectionRow = DB::table('classSection') ->select('id', 'class_id', 'class_section_id') - ->where('id', (int)$rawSection) + ->where('id', (int) $rawSection) ->first(); } - if (!$sectionRow) { + if (! $sectionRow) { throw new RuntimeException('Invalid class section.'); } - $classSectionId = (int)$sectionRow->class_section_id; - $classSectionPk = (int)($sectionRow->id ?? 0); + $classSectionId = (int) $sectionRow->class_section_id; + $classSectionPk = (int) ($sectionRow->id ?? 0); $alsoSectionId = ($classSectionPk > 0 && $classSectionPk !== $classSectionId) ? $classSectionPk : null; - $classId = (int)$sectionRow->class_id; + $classId = (int) $sectionRow->class_id; $attendanceData = $data['attendance'] ?? []; $teachersData = $data['teachers'] ?? []; - if (empty($attendanceData) || !is_array($attendanceData)) { + if (empty($attendanceData) || ! is_array($attendanceData)) { throw new RuntimeException('No student attendance data provided.'); } - if (empty($teachersData) || !is_array($teachersData)) { + if (empty($teachersData) || ! is_array($teachersData)) { throw new RuntimeException('No teacher attendance data provided.'); } $rawDate = $data['date'] ?? now()->toDateString(); $attendDate = Carbon::parse($rawDate)->toDateString(); - $semester = (string)$currentSemester(); - $schoolYear = (string)$currentSchoolYear(); + $semester = (string) $currentSemester(); + $schoolYear = (string) $currentSchoolYear(); $dayRow = AttendanceDay::query() ->where('class_section_id', $classSectionId) @@ -94,7 +94,7 @@ class TeacherAttendanceSubmissionService 'school_year' => $schoolYear, ]; - if (!$this->attendancePolicyService->canEditDay($dayPolicyRow, $currentUser)) { + if (! $this->attendancePolicyService->canEditDay($dayPolicyRow, $currentUser)) { throw new RuntimeException('Attendance is locked for your role.'); } @@ -107,7 +107,7 @@ class TeacherAttendanceSubmissionService $existingByStudent = []; foreach ($existingRows as $row) { - $existingByStudent[(int)$row->student_id] = $row->toArray(); + $existingByStudent[(int) $row->student_id] = $row->toArray(); } $allowedStatuses = ['present', 'absent', 'late']; @@ -129,10 +129,10 @@ class TeacherAttendanceSubmissionService } foreach ($assignedTeachers as $teacher) { - $teacherId = (int)$teacher['teacher_id']; - $status = strtolower((string)($teachersData[$teacherId]['status'] ?? '')); + $teacherId = (int) $teacher['teacher_id']; + $status = strtolower((string) ($teachersData[$teacherId]['status'] ?? '')); - if (!in_array($status, $allowedStatuses, true)) { + if (! in_array($status, $allowedStatuses, true)) { throw new RuntimeException('Please fill teacher attendance for all assigned teachers.'); } } @@ -168,13 +168,13 @@ class TeacherAttendanceSubmissionService ); foreach ($assignedTeachers as $teacher) { - $teacherId = (int)$teacher['teacher_id']; - $position = strtolower((string)($teacher['position'] ?? 'main')); + $teacherId = (int) $teacher['teacher_id']; + $position = strtolower((string) ($teacher['position'] ?? 'main')); $position = $position === 'ta' ? 'ta' : 'main'; $staffPayload = [ 'role_name' => 'Teacher', - 'status' => strtolower((string)($teachersData[$teacherId]['status'] ?? 'present')), + 'status' => strtolower((string) ($teachersData[$teacherId]['status'] ?? 'present')), 'reason' => ($teachersData[$teacherId]['reason'] ?? null) ?: null, 'updated_at' => now(), 'created_at' => now(), @@ -198,9 +198,9 @@ class TeacherAttendanceSubmissionService } foreach ($attendanceData as $row) { - $studentId = (int)($row['student_id'] ?? 0); - $status = strtolower(trim((string)($row['status'] ?? ''))); - $reason = trim((string)($row['reason'] ?? '')); + $studentId = (int) ($row['student_id'] ?? 0); + $status = strtolower(trim((string) ($row['status'] ?? ''))); + $reason = trim((string) ($row['reason'] ?? '')); $existing = $existingByStudent[$studentId] ?? null; if ($isTeacher && $existing) { diff --git a/app/Services/AttendanceManagement/AttendanceManagementService.php b/app/Services/AttendanceManagement/AttendanceManagementService.php index 5c9226c6..1a73bde3 100644 --- a/app/Services/AttendanceManagement/AttendanceManagementService.php +++ b/app/Services/AttendanceManagement/AttendanceManagementService.php @@ -7,17 +7,21 @@ use App\Models\LateSlipLog; use App\Models\Student; use App\Models\User; use Carbon\Carbon; -use Illuminate\Support\Arr; use Illuminate\Support\Facades\DB; use Illuminate\Support\Facades\Schema; class AttendanceManagementService { public const STATUS_PRESENT = 'present'; + public const STATUS_ABSENT_PENDING = 'absent_pending_verification'; + public const STATUS_ABSENT = 'absent'; + public const STATUS_LATE = 'late'; + public const STATUS_EARLY_DISMISSAL = 'early_dismissal'; + public const STATUS_LATE_WITHOUT_SLIP = 'late_without_slip'; public function dashboard(array $filters = []): array @@ -204,6 +208,7 @@ class AttendanceManagementService if ($person['type'] === 'student' && $status === self::STATUS_LATE) { $row['late_slip'] = $this->printLateSlip($row, $actor); } + return $row; } @@ -253,6 +258,7 @@ class AttendanceManagementService if (($data['exit_method'] ?? '') === 'manual_exit') { $this->recordBadgeException((int) $id, $person, $exitAt, 'manual exit entry', $badgeExceptions, $actor, $data['notes'] ?? null); } + return $row; } @@ -292,6 +298,7 @@ class AttendanceManagementService ]; $id = DB::table('attendance_management_events')->insertGetId($row); $row['id'] = $id; + return $row; } @@ -312,6 +319,7 @@ class AttendanceManagementService $update['report_status'] = $this->normalizeReportStatus($data['report_status']); } DB::table('attendance_management_events')->where('id', $id)->update($update); + return (array) DB::table('attendance_management_events')->where('id', $id)->first(); } @@ -347,6 +355,7 @@ class AttendanceManagementService 'final_decision' => 'Late slip reprinted', 'updated_at' => now(), ]); + return $row; } @@ -367,12 +376,24 @@ class AttendanceManagementService $student = null; $user = null; - if ($type === 'student' && $id > 0) $student = Student::query()->find($id); - if (in_array($type, ['staff', 'user', 'teacher', 'administrator'], true) && $id > 0) $user = User::query()->find($id); - if (! $student && $badge !== '') $student = Student::query()->where('rfid_tag', $badge)->first(); - if (! $student && ! $user && $badge !== '') $user = User::query()->where('rfid_tag', $badge)->first(); - if (! $student && ! $user && $id > 0) $student = Student::query()->find($id); - if (! $student && ! $user && $id > 0) $user = User::query()->find($id); + if ($type === 'student' && $id > 0) { + $student = Student::query()->find($id); + } + if (in_array($type, ['staff', 'user', 'teacher', 'administrator'], true) && $id > 0) { + $user = User::query()->find($id); + } + if (! $student && $badge !== '') { + $student = Student::query()->where('rfid_tag', $badge)->first(); + } + if (! $student && ! $user && $badge !== '') { + $user = User::query()->where('rfid_tag', $badge)->first(); + } + if (! $student && ! $user && $id > 0) { + $student = Student::query()->find($id); + } + if (! $student && ! $user && $id > 0) { + $user = User::query()->find($id); + } if ($student) { return [ @@ -397,6 +418,7 @@ class AttendanceManagementService if ($name === '') { throw new \InvalidArgumentException('Person could not be resolved. Provide person_id, badge_id, or person_name.'); } + return [ 'type' => $type !== '' ? $type : 'visitor', 'id' => $id > 0 ? $id : null, @@ -419,6 +441,7 @@ class AttendanceManagementService 'admin_name' => $actor ? trim(($actor->firstname ?? '').' '.($actor->lastname ?? '')) : 'System', ]; $logged = LateSlipLog::logSlip($payload, $actor?->id); + return [ 'printed' => $logged, 'slip_number' => 'AME-'.($event['id'] ?? time()), @@ -458,9 +481,16 @@ class AttendanceManagementService $absence = (clone $q)->where('attendance_status', self::STATUS_ABSENT)->count(); $late = (clone $q)->whereIn('attendance_status', [self::STATUS_LATE, self::STATUS_LATE_WITHOUT_SLIP])->count(); $ed = (clone $q)->where('attendance_status', self::STATUS_EARLY_DISMISSAL)->count(); - if ($newStatus === self::STATUS_ABSENT) $absence++; - if (in_array($newStatus, [self::STATUS_LATE, self::STATUS_LATE_WITHOUT_SLIP], true)) $late++; - if ($newStatus === self::STATUS_EARLY_DISMISSAL) $ed++; + if ($newStatus === self::STATUS_ABSENT) { + $absence++; + } + if (in_array($newStatus, [self::STATUS_LATE, self::STATUS_LATE_WITHOUT_SLIP], true)) { + $late++; + } + if ($newStatus === self::STATUS_EARLY_DISMISSAL) { + $ed++; + } + return ['absence_count' => $absence, 'late_count' => $late, 'early_dismissal_count' => $ed]; } @@ -528,6 +558,7 @@ class AttendanceManagementService private function schoolStartFor(Carbon $date): Carbon { $configured = Configuration::getConfigValueByKey('school_start_time') ?: config('attendance.school_start_time', '09:00:00'); + return Carbon::parse($date->toDateString().' '.$configured, $date->timezone); } @@ -616,12 +647,20 @@ class AttendanceManagementService })); } - private function dateTime($value): Carbon { return $value instanceof Carbon ? $value : Carbon::parse((string) $value); } - private function dateOnly($value): string { return Carbon::parse((string) $value)->toDateString(); } + private function dateTime($value): Carbon + { + return $value instanceof Carbon ? $value : Carbon::parse((string) $value); + } + + private function dateOnly($value): string + { + return Carbon::parse((string) $value)->toDateString(); + } private function normalizeReportStatus($value): string { $v = strtolower(str_replace(['-', ' '], '_', trim((string) $value))); + return in_array($v, ['reported', 'not_reported', 'pending_clarification'], true) ? $v : 'pending_clarification'; } @@ -641,30 +680,61 @@ class AttendanceManagementService private function combinationCode(int $abs, int $late, int $ed, int $badge): string { $parts = []; - if ($abs > 0) $parts[] = min($abs, 5).'ABS'; - if ($late > 0) $parts[] = min($late, 5).'Late'; - if ($ed > 0) $parts[] = min($ed, 3).'ED'; - if ($badge > 0) $parts[] = min($badge, 5).'Badge Exceptions'; + if ($abs > 0) { + $parts[] = min($abs, 5).'ABS'; + } + if ($late > 0) { + $parts[] = min($late, 5).'Late'; + } + if ($ed > 0) { + $parts[] = min($ed, 3).'ED'; + } + if ($badge > 0) { + $parts[] = min($badge, 5).'Badge Exceptions'; + } + return $parts ? implode(' + ', $parts) : 'Clear'; } private function riskLevel(int $abs, int $late, int $ed, int $badge): string { - if ($abs >= 5 && $late >= 5) return 'severe'; - if ($abs >= 5 || $late >= 5) return 'critical'; - if ($abs + $late + $ed + $badge >= 6 || $abs >= 4 || $late >= 4) return 'very_high'; - if ($abs + $late + $ed + $badge >= 4 || $abs >= 3 || $late >= 3 || $badge >= 4) return 'high'; - if ($abs + $late + $ed + $badge >= 2 || $badge >= 2) return 'medium'; + if ($abs >= 5 && $late >= 5) { + return 'severe'; + } + if ($abs >= 5 || $late >= 5) { + return 'critical'; + } + if ($abs + $late + $ed + $badge >= 6 || $abs >= 4 || $late >= 4) { + return 'very_high'; + } + if ($abs + $late + $ed + $badge >= 4 || $abs >= 3 || $late >= 3 || $badge >= 4) { + return 'high'; + } + if ($abs + $late + $ed + $badge >= 2 || $badge >= 2) { + return 'medium'; + } + return 'low'; } private function actionNeeded(string $status, string $reportStatus, int $badgeCount): string { - if ($badgeCount >= 5) return 'Leadership review for badge compliance'; - if ($badgeCount >= 4) return 'Formal badge follow-up required'; - if ($status === self::STATUS_LATE && $reportStatus !== 'reported') return 'Print late slip and contact parent or staff'; - if ($status === self::STATUS_ABSENT && $reportStatus !== 'reported') return 'Same-day contact required'; - if ($status === self::STATUS_EARLY_DISMISSAL && $reportStatus !== 'reported') return 'Verify authorization and contact guardian/supervisor'; + if ($badgeCount >= 5) { + return 'Leadership review for badge compliance'; + } + if ($badgeCount >= 4) { + return 'Formal badge follow-up required'; + } + if ($status === self::STATUS_LATE && $reportStatus !== 'reported') { + return 'Print late slip and contact parent or staff'; + } + if ($status === self::STATUS_ABSENT && $reportStatus !== 'reported') { + return 'Same-day contact required'; + } + if ($status === self::STATUS_EARLY_DISMISSAL && $reportStatus !== 'reported') { + return 'Verify authorization and contact guardian/supervisor'; + } + return 'Record and monitor'; } diff --git a/app/Services/AttendanceTracking/AttendanceCaseQueryService.php b/app/Services/AttendanceTracking/AttendanceCaseQueryService.php index 2afe6d83..a0ba324e 100644 --- a/app/Services/AttendanceTracking/AttendanceCaseQueryService.php +++ b/app/Services/AttendanceTracking/AttendanceCaseQueryService.php @@ -17,8 +17,7 @@ class AttendanceCaseQueryService protected AttendanceTracking $attendanceTrackingModel, protected ViolationRuleEngineService $violationRuleEngine, protected AttendanceParentLookupService $parentLookupService - ) { - } + ) {} public function getStudentCase( int $studentId, @@ -32,16 +31,16 @@ class AttendanceCaseQueryService ?string $defaultSemester = null ): array { $student = $this->studentModel->query()->find($studentId); - if (!$student) { + if (! $student) { return [ 'success' => false, 'message' => 'Student not found', - 'status' => 404, + 'status' => 404, ]; } $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 : ''; @@ -66,13 +65,13 @@ class AttendanceCaseQueryService } [$syStart, $syEnd] = $this->violationRuleEngine->deriveSchoolYearBounds($schoolYear ?: date('Y')); - $today = Carbon::today(); - $start = Carbon::parse($syStart)->startOfDay(); + $today = Carbon::today(); + $start = Carbon::parse($syStart)->startOfDay(); $rangeEnd = Carbon::parse(min($syEnd, $today->format('Y-m-d')))->startOfDay(); $windowWeeks = $this->violationRuleEngine->windowWeeksForViolationCode($codeParam); - $endYmd = $incidentDate !== '' ? $incidentDate : date('Y-m-d'); - $startYmd = $this->violationRuleEngine->activeWeekWindowStartYmd($endYmd, $windowWeeks, $schoolYear, $semester); + $endYmd = $incidentDate !== '' ? $incidentDate : date('Y-m-d'); + $startYmd = $this->violationRuleEngine->activeWeekWindowStartYmd($endYmd, $windowWeeks, $schoolYear, $semester); $studentIdValues = [(int) $studentId]; $studentCodeRaw = trim((string) ($student['student_code'] ?? $student['school_id'] ?? '')); @@ -108,7 +107,7 @@ class AttendanceCaseQueryService ]) ->where(function ($q) use ($studentIdValues, $studentCodeValues) { $q->whereIn('student_id', $studentIdValues); - if (!empty($studentCodeValues)) { + if (! empty($studentCodeValues)) { $q->orWhereIn('student_id', $studentCodeValues); } }) @@ -126,7 +125,7 @@ class AttendanceCaseQueryService if ($pendingOnly) { $this->violationRuleEngine->applyUnreportedAttendanceFilter($qb, $this->attendanceDataModel->getTable()); - if (!$includeNotified) { + if (! $includeNotified) { $qb->whereRaw("(LOWER(TRIM(COALESCE(is_notified, ''))) NOT IN ('yes','1'))"); } } @@ -142,21 +141,21 @@ class AttendanceCaseQueryService $attendanceRows = $qb->orderByDesc('date')->get()->toArray(); $data = [ - 'student' => $student, - 'attendance' => $attendanceRows, - 'violation_code' => $codeParam !== '' ? $codeParam : null, - 'violation_date' => $incidentDate, - 'violation_notified' => null, + 'student' => $student, + 'attendance' => $attendanceRows, + 'violation_code' => $codeParam !== '' ? $codeParam : null, + 'violation_date' => $incidentDate, + 'violation_notified' => null, 'show_violation_summary' => true, - 'school_year' => $schoolYear, - 'semester' => $semester, + 'school_year' => $schoolYear, + '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']; } @@ -172,7 +171,7 @@ class AttendanceCaseQueryService return [ 'success' => true, - 'data' => $data, + 'data' => $data, ]; } @@ -183,7 +182,7 @@ class AttendanceCaseQueryService ?string $defaultSemester = null ): array { $schoolYear = filled($schoolYearParam) ? (string) $schoolYearParam : (string) $defaultSchoolYear; - $semester = filled($semesterParam) ? (string) $semesterParam : (string) $defaultSemester; + $semester = filled($semesterParam) ? (string) $semesterParam : (string) $defaultSemester; $trackingData = $this->attendanceTrackingModel->query() ->where('school_year', $schoolYear) @@ -211,7 +210,7 @@ class AttendanceCaseQueryService if ($latest) { $schoolYear = (string) ($latest->school_year ?? $schoolYear); - $semester = (string) ($latest->semester ?? $semester); + $semester = (string) ($latest->semester ?? $semester); $trackingData = $this->attendanceTrackingModel->query() ->where('school_year', $schoolYear) @@ -229,9 +228,9 @@ class AttendanceCaseQueryService if (empty($trackingData)) { return [ - 'students' => [], - 'school_year' => $schoolYear, - 'semester' => $semester, + 'students' => [], + 'school_year' => $schoolYear, + 'semester' => $semester, 'show_actions' => false, ]; } @@ -258,9 +257,9 @@ class AttendanceCaseQueryService if (empty($trackingData)) { return [ - 'students' => [], - 'school_year' => $schoolYear, - 'semester' => $semester, + 'students' => [], + 'school_year' => $schoolYear, + 'semester' => $semester, 'show_actions' => false, ]; } @@ -289,12 +288,12 @@ class AttendanceCaseQueryService 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_id' => $row->class_section_id ?? null, 'class_section_name' => (string) ($row->class_section_name ?? ''), - 'class_id' => $row->class_id ?? null, - 'class_name' => (string) ($row->class_name ?? ''), + 'class_id' => $row->class_id ?? null, + 'class_name' => (string) ($row->class_name ?? ''), ]; } } @@ -304,7 +303,7 @@ class AttendanceCaseQueryService $mergedData = []; foreach ($trackingData as $row) { $sid = (int) ($row['student_id'] ?? 0); - if (!isset($studentMap[$sid])) { + if (! isset($studentMap[$sid])) { continue; } @@ -312,15 +311,15 @@ class AttendanceCaseQueryService 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'])) { + $merged['class_name'] = (string) ($classByStudent[$sid]['class_name'] ?? ''); + } elseif (! empty($merged['registration_grade'])) { $merged['grade'] = (string) $merged['registration_grade']; } try { $p = $this->parentLookupService->getPrimaryParentForStudent($sid); if ($p) { - $merged['parent_name'] = $p['parent_name'] ?? ''; + $merged['parent_name'] = $p['parent_name'] ?? ''; $merged['parent_email'] = $p['email'] ?? ''; $merged['parent_phone'] = $p['phone'] ?? ''; } @@ -331,9 +330,9 @@ class AttendanceCaseQueryService } return [ - 'students' => $mergedData, + 'students' => $mergedData, 'school_year' => $schoolYear, - 'semester' => $semester, + 'semester' => $semester, ]; } -} \ No newline at end of file +} diff --git a/app/Services/AttendanceTracking/AttendanceCommunicationSupportService.php b/app/Services/AttendanceTracking/AttendanceCommunicationSupportService.php index 7e8cb2e8..5c475832 100644 --- a/app/Services/AttendanceTracking/AttendanceCommunicationSupportService.php +++ b/app/Services/AttendanceTracking/AttendanceCommunicationSupportService.php @@ -10,6 +10,7 @@ use Illuminate\Support\Facades\DB; class AttendanceCommunicationSupportService { protected string $semester; + protected string $schoolYear; public function __construct( @@ -19,7 +20,7 @@ class AttendanceCommunicationSupportService protected AttendanceParentLookupService $parentLookupService, protected AttendanceEmailComposerService $emailComposerService ) { - $this->semester = (string) ($this->configModel->getConfig('semester') ?? ''); + $this->semester = (string) ($this->configModel->getConfig('semester') ?? ''); $this->schoolYear = (string) ($this->configModel->getConfig('school_year') ?? ''); } @@ -33,12 +34,12 @@ class AttendanceCommunicationSupportService return [ 'success' => false, 'message' => 'Missing student_id.', - 'status' => 422, + 'status' => 422, ]; } - $student = $this->studentModel->query()->find($studentId)?->toArray() ?? []; - $parent = $this->parentLookupService->getPrimaryParentForStudent($studentId); + $student = $this->studentModel->query()->find($studentId)?->toArray() ?? []; + $parent = $this->parentLookupService->getPrimaryParentForStudent($studentId); $secondary = $this->parentLookupService->getSecondaryParentForStudent($studentId, $this->schoolYear); $result = $this->emailComposerService->composePreview( @@ -52,7 +53,7 @@ class AttendanceCommunicationSupportService ); if (($result['success'] ?? false) && isset($result['data'])) { - $result['data']['semester'] = $this->semester; + $result['data']['semester'] = $this->semester; $result['data']['school_year'] = $this->schoolYear; } @@ -100,4 +101,4 @@ class AttendanceCommunicationSupportService ->values() ->all(); } -} \ No newline at end of file +} diff --git a/app/Services/AttendanceTracking/AttendanceEmailComposerService.php b/app/Services/AttendanceTracking/AttendanceEmailComposerService.php index 0ad29f50..4ff13e2d 100644 --- a/app/Services/AttendanceTracking/AttendanceEmailComposerService.php +++ b/app/Services/AttendanceTracking/AttendanceEmailComposerService.php @@ -4,27 +4,25 @@ namespace App\Services\AttendanceTracking; use App\Models\AttendanceEmailTemplate; use App\Support\MailHtml; -use Illuminate\Support\Str; class AttendanceEmailComposerService { public function __construct( protected AttendanceEmailTemplate $attendanceEmailTemplateModel - ) { - } + ) {} public function buildTemplateContext(array $violation, ?array $parent = null): array { $studentName = (string) ($violation['name'] ?? ''); - $incident = (string) ($violation['last_date'] ?? date('Y-m-d')); + $incident = (string) ($violation['last_date'] ?? date('Y-m-d')); return [ - '{{parent_name}}' => (string) ($parent['parent_name'] ?? 'Parent/Guardian'), - '{{student_name}}' => $studentName, - '{{incident_date}}' => $incident, - '{{school_phone}}' => '978-364-0219', + '{{parent_name}}' => (string) ($parent['parent_name'] ?? 'Parent/Guardian'), + '{{student_name}}' => $studentName, + '{{incident_date}}' => $incident, + '{{school_phone}}' => '978-364-0219', '{{voicemail_phone}}' => (string) ($parent['phone'] ?? '—'), - '{{class_time}}' => '10:00 AM', + '{{class_time}}' => '10:00 AM', ]; } @@ -42,7 +40,7 @@ class AttendanceEmailComposerService ->where('is_active', 1) ->first(); - if (!$row) { + if (! $row) { $row = $this->attendanceEmailTemplateModel->query() ->where('code', $code) ->where('variant', 'default') @@ -53,12 +51,12 @@ class AttendanceEmailComposerService $row = $row?->toArray(); } - if (!$row) { + if (! $row) { return null; } $subject = strtr($row['subject'], $context); - $body = strtr($row['body_html'], $context); + $body = strtr($row['body_html'], $context); return [$subject, $body]; } @@ -70,35 +68,35 @@ class AttendanceEmailComposerService public function pickVariant(string $code, ?string $globalVariantOverride = null): string { - if (!empty($globalVariantOverride)) { + if (! empty($globalVariantOverride)) { return $globalVariantOverride; } return match ($code) { 'ABS_2', 'ABS_2_IN3W' => 'no_answer', - default => 'default', + default => 'default', }; } public function buildFallbackAutoEmail(string $code, array $violation, string $parentName = ''): array { $subject = match ($code) { - 'ABS_1' => 'Attendance Notice: Unreported Absence', + 'ABS_1' => 'Attendance Notice: Unreported Absence', 'LATE_2' => 'Attendance Notice: Repeated Lateness', - default => 'Attendance Notice', + default => 'Attendance Notice', }; $studentName = (string) ($violation['name'] ?? ''); - $date = (string) ($violation['last_date'] ?? date('Y-m-d')); + $date = (string) ($violation['last_date'] ?? date('Y-m-d')); - $body = "

Dear " . ($parentName ?: 'Parent/Guardian') . "

" - . "

We'd like to inform you about your student's recent attendance:

" - . "
    " - . "
  • Student: {$studentName}
  • " - . "
  • Issue: " . (string) ($violation['violation'] ?? '') . "
  • " - . "
  • As of: {$date}
  • " - . "
" - . "

If you have any questions, please contact the school office.

"; + $body = '

Dear '.($parentName ?: 'Parent/Guardian').'

' + ."

We'd like to inform you about your student's recent attendance:

" + .'
    ' + ."
  • Student: {$studentName}
  • " + .'
  • Issue: '.(string) ($violation['violation'] ?? '').'
  • ' + ."
  • As of: {$date}
  • " + .'
' + .'

If you have any questions, please contact the school office.

'; return [$subject, $body]; } @@ -134,7 +132,8 @@ class AttendanceEmailComposerService static function ($m) { $url = $m[0]; $urlAttr = htmlspecialchars($url, ENT_QUOTES | ENT_SUBSTITUTE, 'UTF-8'); - return '' . $url . ''; + + return ''.$url.''; }, $escaped ); @@ -142,8 +141,9 @@ class AttendanceEmailComposerService $paragraphs = preg_split("/\n{2,}/", $escaped); $paragraphs = array_map(static function ($p) { - $p = preg_replace("/\n/", "
", $p); - return '

' . $p . '

'; + $p = preg_replace("/\n/", '
', $p); + + return '

'.$p.'

'; }, $paragraphs); return implode("\n", $paragraphs); @@ -152,10 +152,10 @@ class AttendanceEmailComposerService public function renderAutoEmailFor(array $violation, ?array $parent = null): ?array { $code = (string) ($violation['violation_code'] ?? $violation['code'] ?? ''); - $ctx = $this->buildTemplateContext($violation, $parent); - $tpl = $this->renderTemplate($code, 'default', $ctx); + $ctx = $this->buildTemplateContext($violation, $parent); + $tpl = $this->renderTemplate($code, 'default', $ctx); - if (!$tpl) { + if (! $tpl) { return $this->buildFallbackAutoEmail($code, $violation, (string) ($parent['parent_name'] ?? '')); } @@ -176,21 +176,21 @@ class AttendanceEmailComposerService : date('Y-m-d'); $violation = [ - 'id' => $studentId, - 'name' => trim(($student['firstname'] ?? '') . ' ' . ($student['lastname'] ?? '')), - 'last_date' => $lastDate, - 'violation' => $code, + 'id' => $studentId, + 'name' => trim(($student['firstname'] ?? '').' '.($student['lastname'] ?? '')), + 'last_date' => $lastDate, + 'violation' => $code, 'violation_code' => $code, ]; $ctx = $this->buildTemplateContext($violation, $primaryParent); $rendered = $this->renderTemplate($code, $variant, $ctx); - if (!$rendered) { + if (! $rendered) { return [ 'success' => false, 'message' => "Template not found for {$code} ({$variant}).", - 'status' => 404, + 'status' => 404, ]; } @@ -198,19 +198,19 @@ class AttendanceEmailComposerService return [ 'success' => true, - 'data' => [ - 'student_id' => $studentId, - 'student_name' => $violation['name'], - 'parent_email' => (string) ($primaryParent['email'] ?? ''), - 'parent_name' => (string) ($primaryParent['parent_name'] ?? ''), + 'data' => [ + 'student_id' => $studentId, + 'student_name' => $violation['name'], + 'parent_email' => (string) ($primaryParent['email'] ?? ''), + 'parent_name' => (string) ($primaryParent['parent_name'] ?? ''), 'secondary_email' => (string) ($secondaryParent['email'] ?? ''), - 'secondary_name' => (string) ($secondaryParent['parent_name'] ?? ''), - 'code' => $code, - 'variant' => $variant, - 'subject' => $subject, - 'body_html' => $body, - 'incident_date' => $lastDate, + 'secondary_name' => (string) ($secondaryParent['parent_name'] ?? ''), + 'code' => $code, + 'variant' => $variant, + 'subject' => $subject, + 'body_html' => $body, + 'incident_date' => $lastDate, ], ]; } -} \ No newline at end of file +} diff --git a/app/Services/AttendanceTracking/AttendanceMailerService.php b/app/Services/AttendanceTracking/AttendanceMailerService.php index 83f4bc5a..d574ebaf 100644 --- a/app/Services/AttendanceTracking/AttendanceMailerService.php +++ b/app/Services/AttendanceTracking/AttendanceMailerService.php @@ -7,4 +7,4 @@ interface AttendanceMailerService public function send(string $to, string $subject, string $html): bool; public function queueAttendanceEvent(array $payload): void; -} \ No newline at end of file +} diff --git a/app/Services/AttendanceTracking/AttendanceNotificationLogService.php b/app/Services/AttendanceTracking/AttendanceNotificationLogService.php index 18d40397..fe153019 100644 --- a/app/Services/AttendanceTracking/AttendanceNotificationLogService.php +++ b/app/Services/AttendanceTracking/AttendanceNotificationLogService.php @@ -10,8 +10,7 @@ class AttendanceNotificationLogService { public function __construct( protected ParentNotification $notificationModel - ) { - } + ) {} public function notificationAlreadySent( int $studentId, @@ -31,13 +30,14 @@ class AttendanceNotificationLogService ->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; } } @@ -61,37 +61,37 @@ class AttendanceNotificationLogService ->whereDate('incident_date', $ymd) ->where('channel', $channel); - if (!empty($to)) { + if (! empty($to)) { $existingQuery->where('to_address', $to); } $existing = $existingQuery->orderByDesc('id')->first(); - if ($existing && !empty($existing->id)) { + if ($existing && ! empty($existing->id)) { $existing->update([ - 'subject' => $subject, - 'status' => $status, - 'response' => $response, - 'semester' => $semester, - 'school_year' => $schoolYear, - 'updated_at' => now(), + 'subject' => $subject, + 'status' => $status, + 'response' => $response, + 'semester' => $semester, + 'school_year' => $schoolYear, + 'updated_at' => now(), ]); } else { $this->notificationModel->query()->create([ - 'student_id' => $studentId, - 'code' => $code, + 'student_id' => $studentId, + 'code' => $code, 'incident_date' => $ymd, - 'channel' => $channel, - 'to_address' => $to, - 'subject' => $subject, - 'status' => $status, - 'response' => $response, - 'semester' => $semester, - 'school_year' => $schoolYear, + 'channel' => $channel, + 'to_address' => $to, + 'subject' => $subject, + 'status' => $status, + 'response' => $response, + 'semester' => $semester, + 'school_year' => $schoolYear, ]); } } catch (Throwable $e) { - Log::debug('logNotification(): ' . $e->getMessage()); + Log::debug('logNotification(): '.$e->getMessage()); } } } diff --git a/app/Services/AttendanceTracking/AttendanceNotificationWorkflowService.php b/app/Services/AttendanceTracking/AttendanceNotificationWorkflowService.php index 72969ac5..4b81d7e6 100644 --- a/app/Services/AttendanceTracking/AttendanceNotificationWorkflowService.php +++ b/app/Services/AttendanceTracking/AttendanceNotificationWorkflowService.php @@ -7,7 +7,6 @@ use App\Models\AttendanceTracking; use App\Models\Configuration; use App\Models\Student; use App\Models\StudentClass; -use Carbon\Carbon; use Illuminate\Support\Facades\DB; use Illuminate\Support\Facades\Log; use Throwable; @@ -15,6 +14,7 @@ use Throwable; class AttendanceNotificationWorkflowService { protected string $semester; + protected string $schoolYear; public function __construct( @@ -29,33 +29,33 @@ class AttendanceNotificationWorkflowService protected AttendanceEmailComposerService $emailComposerService, protected AttendanceNotificationLogService $notificationLogService ) { - $this->semester = (string) ($this->configModel->getConfig('semester') ?? ''); + $this->semester = (string) ($this->configModel->getConfig('semester') ?? ''); $this->schoolYear = (string) ($this->configModel->getConfig('school_year') ?? ''); } public function record(array $data): array { - $sid = (int) $data['student_id']; - $ymd = substr((string) $data['date'], 0, 10); - $semester = $data['semester'] ?? $this->semester; + $sid = (int) $data['student_id']; + $ymd = substr((string) $data['date'], 0, 10); + $semester = $data['semester'] ?? $this->semester; $schoolYear = $data['school_year'] ?? $this->schoolYear; - $subject = $data['subject_type'] ?? 'Absent'; + $subject = $data['subject_type'] ?? 'Absent'; $student = $this->studentModel->query()->find($sid)?->toArray() ?? []; $parentEmail = $data['parent_email'] ?? ''; - $parentName = $data['parent_name'] ?? ''; + $parentName = $data['parent_name'] ?? ''; - if (!$parentEmail) { + if (! $parentEmail) { $parent = $this->parentLookupService->getPrimaryParentForStudent($sid) ?? []; $parentEmail = $parent['email'] ?? ''; - $parentName = $parent['parent_name'] ?? ''; + $parentName = $parent['parent_name'] ?? ''; } - if (!$parentEmail) { + if (! $parentEmail) { return [ 'success' => false, 'message' => 'No parent email found for this student.', - 'status' => 422, + 'status' => 422, ]; } @@ -87,37 +87,37 @@ class AttendanceNotificationWorkflowService : []; $payload = [ - 'student_id' => $sid, - 'student' => $student, - 'parent' => ['email' => $parentEmail, 'name' => $parentName], - 'semester' => $semester, + 'student_id' => $sid, + 'student' => $student, + 'parent' => ['email' => $parentEmail, 'name' => $parentName], + 'semester' => $semester, 'school_year' => $schoolYear, - 'class' => $class, - 'now' => now()->toDateTimeString(), - 'date' => $ymd, - 'subject' => $subject, + 'class' => $class, + 'now' => now()->toDateTimeString(), + 'date' => $ymd, + 'subject' => $subject, 'consequence' => $consequence ?: 'follow_up', ]; 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']); } return [ 'success' => true, 'message' => 'Notification queued.', - 'data' => $payload, + 'data' => $payload, ]; } catch (Throwable $e) { - Log::error('Attendance record queue failed: ' . $e->getMessage()); + Log::error('Attendance record queue failed: '.$e->getMessage()); return [ 'success' => false, 'message' => $e->getMessage(), - 'status' => 500, + 'status' => 500, ]; } } @@ -132,30 +132,32 @@ class AttendanceNotificationWorkflowService $details = []; foreach ($toSend as $v) { - $sid = (int) ($v['id'] ?? 0); - $code = (string) ($v['violation_code'] ?? $v['code'] ?? ''); - $date = (string) ($v['last_date'] ?? ''); - $to = trim((string) ($v['parent_email'] ?? '')); + $sid = (int) ($v['id'] ?? 0); + $code = (string) ($v['violation_code'] ?? $v['code'] ?? ''); + $date = (string) ($v['last_date'] ?? ''); + $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->notificationLogService->notificationAlreadySent($sid, $code, $date, 'email', $to)) { $skipped++; $details[] = ['student_id' => $sid, 'code' => $code, 'date' => $date, 'result' => 'duplicate']; + continue; } - $parent = $this->parentLookupService->getPrimaryParentForStudent($sid); + $parent = $this->parentLookupService->getPrimaryParentForStudent($sid); $context = $this->emailComposerService->buildTemplateContext($v, $parent); $variant = $this->emailComposerService->pickVariant($code, $variantOverride); - $tpl = $this->emailComposerService->renderTemplate($code, $variant, $context); + $tpl = $this->emailComposerService->renderTemplate($code, $variant, $context); - if (!$tpl) { + if (! $tpl) { [$subject, $body] = $this->emailComposerService->buildFallbackAutoEmail($code, $v, $pname); } else { [$subject, $body] = $tpl; @@ -212,7 +214,7 @@ class AttendanceNotificationWorkflowService $this->semester, $this->schoolYear ); - $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 { @@ -260,7 +262,7 @@ class AttendanceNotificationWorkflowService } } 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) { @@ -277,13 +279,13 @@ class AttendanceNotificationWorkflowService } $allDates = []; - $absDates = array_map(fn ($d) => substr((string) $d, 0, 10), (array) ($v['absences'] ?? [])); + $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)) { @@ -303,29 +305,29 @@ class AttendanceNotificationWorkflowService return [ 'success' => true, - 'sent' => $sent, + 'sent' => $sent, 'skipped' => $skipped, - 'errors' => $errors, + 'errors' => $errors, 'details' => $details, ]; } public function sendEmailManual(array $data, array $violationDates = []): array { - $sid = (int) $data['student_id']; - $to = trim((string) $data['to']); - $subject = (string) $data['subject']; + $sid = (int) $data['student_id']; + $to = trim((string) $data['to']); + $subject = (string) $data['subject']; $bodyInput = (string) $data['body_html']; - $code = (string) $data['code']; - $variant = (string) ($data['variant'] ?? 'default'); + $code = (string) $data['code']; + $variant = (string) ($data['variant'] ?? 'default'); $ymdRaw = (string) ($data['incident_date'] ?? ''); - $ymd = $ymdRaw !== '' ? substr($ymdRaw, 0, 10) : ''; + $ymd = $ymdRaw !== '' ? substr($ymdRaw, 0, 10) : ''; $safeHtmlBody = $this->emailComposerService->normalizeBodyToHtml($bodyInput); $wrapped = $this->emailComposerService->renderWithEmailLayout($subject, $safeHtmlBody); - $semester = $this->semester ?: (string) ($this->configModel->getConfig('semester') ?? ''); + $semester = $this->semester ?: (string) ($this->configModel->getConfig('semester') ?? ''); $schoolYear = $this->schoolYear ?: (string) ($this->configModel->getConfig('school_year') ?? ''); try { @@ -354,7 +356,7 @@ class AttendanceNotificationWorkflowService $to2 = trim((string) ($sec['email'] ?? '')); if ($to2 !== '' && strcasecmp($to2, $to) !== 0) { - if (!$this->notificationLogService->notificationAlreadySent($sid, $code, $ymd, 'email', $to2)) { + if (! $this->notificationLogService->notificationAlreadySent($sid, $code, $ymd, 'email', $to2)) { $ok2 = $this->attendanceMailerService->send($to2, $subject, $wrapped); $this->notificationLogService->logNotification( @@ -376,11 +378,11 @@ class AttendanceNotificationWorkflowService } } - if (!$anyOk) { + if (! $anyOk) { return [ 'success' => false, 'message' => 'Failed to send email.', - 'status' => 500, + 'status' => 500, ]; } @@ -394,7 +396,7 @@ class AttendanceNotificationWorkflowService ); } - if (!empty($violationDates) && method_exists($this->attendanceDataModel, 'markReportedAndNotified')) { + if (! empty($violationDates) && method_exists($this->attendanceDataModel, 'markReportedAndNotified')) { $this->attendanceDataModel->markReportedAndNotified( $sid, $violationDates, @@ -406,12 +408,12 @@ class AttendanceNotificationWorkflowService return [ 'success' => true, 'message' => 'Email sent successfully.', - 'data' => [ - 'student_id' => $sid, - 'to' => $to, - 'secondary_to' => $to2 ?? null, - 'subject' => $subject, - 'variant' => $variant, + 'data' => [ + 'student_id' => $sid, + 'to' => $to, + 'secondary_to' => $to2 ?? null, + 'subject' => $subject, + 'variant' => $variant, 'incident_date' => $ymd, ], ]; @@ -431,19 +433,19 @@ class AttendanceNotificationWorkflowService return [ 'success' => false, - 'message' => 'Error: ' . $e->getMessage(), - 'status' => 500, + 'message' => 'Error: '.$e->getMessage(), + 'status' => 500, ]; } } public function saveNotificationNote(array $data): array { - $sid = (int) $data['student_id']; - $code = (string) $data['code']; - $note = trim((string) $data['note']); + $sid = (int) $data['student_id']; + $code = (string) $data['code']; + $note = trim((string) $data['note']); $ymdRaw = (string) ($data['incident_date'] ?? ''); - $ymd = $ymdRaw !== '' ? substr($ymdRaw, 0, 10) : ''; + $ymd = $ymdRaw !== '' ? substr($ymdRaw, 0, 10) : ''; try { $day = $ymd !== '' ? $ymd : date('Y-m-d'); @@ -455,11 +457,11 @@ class AttendanceNotificationWorkflowService ->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) @@ -470,28 +472,28 @@ class AttendanceNotificationWorkflowService ->first(); } - if ($row && !empty($row->id)) { + if ($row && ! empty($row->id)) { DB::table($this->attendanceTrackingModel->getTable()) ->where('id', (int) $row->id) ->update([ 'notif_counter' => DB::raw('COALESCE(notif_counter,0)+1'), - 'note' => $note, - 'is_notified' => 1, - 'updated_at' => now(), + 'note' => $note, + 'is_notified' => 1, + 'updated_at' => now(), ]); } else { $this->attendanceTrackingModel->query()->create([ - 'student_id' => $sid, - 'date' => $start, - 'is_reported' => 0, - 'reason' => $code, - 'is_notified' => 1, + 'student_id' => $sid, + 'date' => $start, + 'is_reported' => 0, + 'reason' => $code, + 'is_notified' => 1, 'notif_counter' => 1, - 'semester' => $this->semester, - 'school_year' => $this->schoolYear, - 'note' => $note, - 'created_at' => now(), - 'updated_at' => now(), + 'semester' => $this->semester, + 'school_year' => $this->schoolYear, + 'note' => $note, + 'created_at' => now(), + 'updated_at' => now(), ]); } @@ -502,9 +504,9 @@ class AttendanceNotificationWorkflowService } catch (Throwable $e) { return [ 'success' => false, - 'message' => 'Failed to save note: ' . $e->getMessage(), - 'status' => 500, + 'message' => 'Failed to save note: '.$e->getMessage(), + 'status' => 500, ]; } } -} \ No newline at end of file +} diff --git a/app/Services/AttendanceTracking/AttendanceParentLookupService.php b/app/Services/AttendanceTracking/AttendanceParentLookupService.php index dfebc7bc..60d7d1c6 100644 --- a/app/Services/AttendanceTracking/AttendanceParentLookupService.php +++ b/app/Services/AttendanceTracking/AttendanceParentLookupService.php @@ -16,15 +16,15 @@ class AttendanceParentLookupService ->where('s.id', $studentId) ->first(); - if (!$row || empty($row->user_id)) { + if (! $row || empty($row->user_id)) { return null; } return [ - 'user_id' => (int) $row->user_id, - 'email' => (string) ($row->email ?? ''), + 'user_id' => (int) $row->user_id, + 'email' => (string) ($row->email ?? ''), 'parent_name' => (string) ($row->parent_name ?? ''), - 'phone' => $row->cellphone ?? null, + 'phone' => $row->cellphone ?? null, ]; } @@ -36,11 +36,11 @@ class AttendanceParentLookupService ->where('id', $studentId) ->first(); - if (!$stu) { + if (! $stu) { return null; } - $primaryId = (int) ($stu->parent_id ?? 0); + $primaryId = (int) ($stu->parent_id ?? 0); $schoolYear = (string) ($stu->school_year ?? $fallbackSchoolYear ?? ''); $pb = DB::table('parents')->where('firstparent_id', $primaryId); @@ -49,7 +49,7 @@ class AttendanceParentLookupService } $pRow = $pb->orderByDesc('updated_at')->first(); - if (!$pRow) { + if (! $pRow) { return null; } @@ -60,12 +60,12 @@ class AttendanceParentLookupService ->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 ?? '')), - 'phone' => (string) ($u2->cellphone ?? ''), + 'user_id' => (int) $u2->id, + 'email' => (string) $u2->email, + 'parent_name' => trim(($u2->firstname ?? '').' '.($u2->lastname ?? '')), + 'phone' => (string) ($u2->cellphone ?? ''), ]; } } @@ -73,18 +73,18 @@ class AttendanceParentLookupService $fallbackEmail = (string) ($pRow->secondparent_email ?? ''); $fallbackPhone = (string) ($pRow->secondparent_phone ?? ''); $fallbackFirst = (string) ($pRow->secondparent_firstname ?? ''); - $fallbackLast = (string) ($pRow->secondparent_lastname ?? ''); + $fallbackLast = (string) ($pRow->secondparent_lastname ?? ''); if ($fallbackEmail || $fallbackPhone || $fallbackFirst || $fallbackLast) { return [ - 'user_id' => $secondId ?: null, - 'email' => $fallbackEmail, - 'parent_name' => trim($fallbackFirst . ' ' . $fallbackLast) ?: null, - 'phone' => $fallbackPhone, + 'user_id' => $secondId ?: null, + 'email' => $fallbackEmail, + '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; @@ -96,7 +96,7 @@ class AttendanceParentLookupService return [ 'success' => false, 'message' => 'Missing student_id', - 'status' => 422, + 'status' => 422, ]; } @@ -106,15 +106,15 @@ class AttendanceParentLookupService ->where('id', $studentId) ->first(); - if (!$stu) { + if (! $stu) { return [ 'success' => false, 'message' => 'Student not found', - 'status' => 404, + 'status' => 404, ]; } - $primaryId = (int) $stu->parent_id; + $primaryId = (int) $stu->parent_id; $schoolYear = $stu->school_year ?: $currentSchoolYear; $u1 = DB::table('users') @@ -123,39 +123,39 @@ class AttendanceParentLookupService ->first(); $primary = $u1 ? [ - 'id' => (int) $u1->id, + 'id' => (int) $u1->id, 'firstname' => (string) $u1->firstname, - 'lastname' => (string) $u1->lastname, - 'email' => (string) $u1->email, + 'lastname' => (string) $u1->lastname, + 'email' => (string) $u1->email, 'cellphone' => (string) $u1->cellphone, ] : null; $secondaryRaw = $this->getSecondaryParentForStudent($studentId, $schoolYear); $secondary = $secondaryRaw ? [ - 'id' => $secondaryRaw['user_id'] ?? null, + 'id' => $secondaryRaw['user_id'] ?? null, 'firstname' => null, - 'lastname' => null, - 'email' => $secondaryRaw['email'] ?? '', + 'lastname' => null, + 'email' => $secondaryRaw['email'] ?? '', 'cellphone' => $secondaryRaw['phone'] ?? '', - 'name' => $secondaryRaw['parent_name'] ?? '', + 'name' => $secondaryRaw['parent_name'] ?? '', ] : null; return [ 'success' => true, - 'data' => [ - 'primary' => $primary, + 'data' => [ + 'primary' => $primary, 'secondary' => $secondary, ], ]; } catch (Throwable $e) { - Log::error('parentsInfo() failed: ' . $e->getMessage()); + Log::error('parentsInfo() failed: '.$e->getMessage()); return [ 'success' => false, 'message' => 'Server error', - 'status' => 500, + 'status' => 500, ]; } } -} \ No newline at end of file +} diff --git a/app/Services/AttendanceTracking/AttendancePendingViolationService.php b/app/Services/AttendanceTracking/AttendancePendingViolationService.php index 47160df7..b7c02912 100644 --- a/app/Services/AttendanceTracking/AttendancePendingViolationService.php +++ b/app/Services/AttendanceTracking/AttendancePendingViolationService.php @@ -13,8 +13,7 @@ class AttendancePendingViolationService protected AttendanceData $attendanceDataModel, protected ViolationRuleEngineService $violationRuleEngine, protected AttendanceViolationStudentResolverService $studentResolverService - ) { - } + ) {} public function getPendingViolations( string $defaultSchoolYear, @@ -22,52 +21,52 @@ class AttendancePendingViolationService ?string $semesterParam = null ): array { $schoolYear = filled($schoolYearParam) ? (string) $schoolYearParam : (string) $defaultSchoolYear; - $semester = filled($semesterParam) ? (string) $semesterParam : null; + $semester = filled($semesterParam) ? (string) $semesterParam : null; $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, ]; $resolved = $this->studentResolverService->resolveForSchoolYear($schoolYear, $semester); - $schoolYear = (string) ($resolved['school_year'] ?? $schoolYear); - $semester = $resolved['semester'] ?? $semester; - $studentIds = $resolved['student_ids'] ?? []; - $studentCodes = $resolved['student_codes'] ?? []; - $students = $resolved['students'] ?? []; + $schoolYear = (string) ($resolved['school_year'] ?? $schoolYear); + $semester = $resolved['semester'] ?? $semester; + $studentIds = $resolved['student_ids'] ?? []; + $studentCodes = $resolved['student_codes'] ?? []; + $students = $resolved['students'] ?? []; $studentCodeToId = $resolved['student_code_to_id'] ?? []; - $existingIds = $resolved['existing_ids'] ?? []; - $resolverDebug = $resolved['debug'] ?? []; + $existingIds = $resolved['existing_ids'] ?? []; + $resolverDebug = $resolved['debug'] ?? []; $debugInfo['class_students'] = $resolverDebug['class_students'] ?? 0; - $debugInfo['student_ids'] = $resolverDebug['student_ids'] ?? 0; + $debugInfo['student_ids'] = $resolverDebug['student_ids'] ?? 0; if (empty($studentIds) && empty($studentCodes)) { return [ - 'students' => [], + 'students' => [], 'school_year' => $schoolYear, - 'semester' => $semester, - 'error' => 'No student identifiers found for the current school year.', - 'debug' => $debugInfo, + 'semester' => $semester, + 'error' => 'No student identifiers found for the current school year.', + 'debug' => $debugInfo, ]; } if (empty($students)) { return [ - 'students' => [], + 'students' => [], 'school_year' => $schoolYear, - 'semester' => $semester, - 'error' => 'No students found matching attendance records.', - 'debug' => $debugInfo, + 'semester' => $semester, + 'error' => 'No students found matching attendance records.', + 'debug' => $debugInfo, ]; } @@ -91,14 +90,14 @@ class AttendancePendingViolationService '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); @@ -118,16 +117,16 @@ class AttendancePendingViolationService if (empty($termRows)) { $latestAttendanceTerm = DB::table($this->attendanceDataModel->getTable()) ->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; @@ -150,14 +149,14 @@ class AttendancePendingViolationService '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); @@ -177,10 +176,10 @@ class AttendancePendingViolationService if (empty($termRows)) { return [ - 'students' => [], + 'students' => [], 'school_year' => $schoolYear, - 'semester' => $semester, - 'debug' => $debugInfo, + 'semester' => $semester, + 'debug' => $debugInfo, ]; } } @@ -188,7 +187,7 @@ class AttendancePendingViolationService $attendanceRows = []; foreach ($termRows as $row) { $dStr = $row['date'] ?? null; - if (!$dStr) { + if (! $dStr) { continue; } @@ -198,7 +197,7 @@ class AttendancePendingViolationService continue; } - if ($d->lt($start) || !$d->lt($endEx)) { + if ($d->lt($start) || ! $d->lt($endEx)) { continue; } @@ -222,15 +221,15 @@ class AttendancePendingViolationService if (empty($attendanceRows)) { return [ - 'students' => [], + 'students' => [], 'school_year' => $schoolYear, - 'semester' => $semester, - 'debug' => $debugInfo, + 'semester' => $semester, + 'debug' => $debugInfo, ]; } $debugInfo['attendance_rows'] = count($termRows); - $debugInfo['filtered_rows'] = count($attendanceRows); + $debugInfo['filtered_rows'] = count($attendanceRows); $violations = $this->violationRuleEngine->computeViolations( $students, @@ -241,17 +240,17 @@ class AttendancePendingViolationService ); $ruleDebug = $this->violationRuleEngine->getDebugCompute(); - $debugInfo['violations'] = count($violations); + $debugInfo['violations'] = count($violations); $debugInfo['active_weeks'] = $ruleDebug['active_weeks'] ?? 0; - $debugInfo['by_student'] = $ruleDebug['by_student'] ?? 0; - $debugInfo['abs_last5'] = $ruleDebug['abs_last5'] ?? 0; - $debugInfo['late_last5'] = $ruleDebug['late_last5'] ?? 0; + $debugInfo['by_student'] = $ruleDebug['by_student'] ?? 0; + $debugInfo['abs_last5'] = $ruleDebug['abs_last5'] ?? 0; + $debugInfo['late_last5'] = $ruleDebug['late_last5'] ?? 0; return [ - 'students' => $violations, + 'students' => $violations, 'school_year' => $schoolYear, - 'semester' => $semester, - 'debug' => $debugInfo, + 'semester' => $semester, + 'debug' => $debugInfo, ]; } -} \ No newline at end of file +} diff --git a/app/Services/AttendanceTracking/AttendanceTrackingService.php b/app/Services/AttendanceTracking/AttendanceTrackingService.php index 2414a8fc..bb976248 100644 --- a/app/Services/AttendanceTracking/AttendanceTrackingService.php +++ b/app/Services/AttendanceTracking/AttendanceTrackingService.php @@ -2,11 +2,13 @@ namespace App\Services\AttendanceTracking; +use App\Http\Controllers\Api\AttendanceTracking\AttendanceTrackingController; use App\Models\Configuration; +use Carbon\Carbon; use Illuminate\Support\Facades\Validator; /** - * Facade used by {@see \App\Http\Controllers\Api\AttendanceTracking\AttendanceTrackingController}. + * Facade used by {@see AttendanceTrackingController}. */ class AttendanceTrackingService { @@ -15,8 +17,7 @@ class AttendanceTrackingService protected AttendanceCaseQueryService $caseQueryService, protected AttendanceNotificationWorkflowService $workflowService, protected AttendanceCommunicationSupportService $communicationSupportService, - ) { - } + ) {} protected function defaultSchoolYear(): string { @@ -152,7 +153,7 @@ class AttendanceTrackingService $ymd = null; if (! empty($data['date'])) { try { - $ymd = \Carbon\Carbon::parse((string) $data['date'])->format('Y-m-d'); + $ymd = Carbon::parse((string) $data['date'])->format('Y-m-d'); } catch (\Throwable) { $ymd = null; } diff --git a/app/Services/AttendanceTracking/AttendanceViolationStudentResolverService.php b/app/Services/AttendanceTracking/AttendanceViolationStudentResolverService.php index e54bc98c..28e7531a 100644 --- a/app/Services/AttendanceTracking/AttendanceViolationStudentResolverService.php +++ b/app/Services/AttendanceTracking/AttendanceViolationStudentResolverService.php @@ -13,14 +13,13 @@ class AttendanceViolationStudentResolverService protected Student $studentModel, protected StudentClass $studentClassModel, protected AttendanceData $attendanceDataModel - ) { - } + ) {} public function resolveForSchoolYear(string $schoolYear, ?string $semester = null): array { $debug = [ 'class_students' => 0, - 'student_ids' => 0, + 'student_ids' => 0, ]; $classStudentsQuery = $this->studentClassModel->query(); @@ -41,9 +40,9 @@ class AttendanceViolationStudentResolverService ->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; } @@ -73,14 +72,14 @@ class AttendanceViolationStudentResolverService if (empty($studentIds) && empty($studentCodes)) { return [ - 'school_year' => $schoolYear, - 'semester' => $semester, - 'student_ids' => [], - 'student_codes' => [], - 'students' => [], - 'student_code_to_id'=> [], - 'existing_ids' => [], - 'debug' => $debug, + 'school_year' => $schoolYear, + 'semester' => $semester, + 'student_ids' => [], + 'student_codes' => [], + 'students' => [], + 'student_code_to_id' => [], + 'existing_ids' => [], + 'debug' => $debug, ]; } @@ -91,14 +90,14 @@ class AttendanceViolationStudentResolverService [$studentCodeToId, $existingIds] = $this->buildLookupMaps($students); return [ - 'school_year' => $schoolYear, - 'semester' => $semester, - 'student_ids' => $studentIds, - 'student_codes' => $studentCodes, - 'students' => $students, + 'school_year' => $schoolYear, + 'semester' => $semester, + 'student_ids' => $studentIds, + 'student_codes' => $studentCodes, + 'students' => $students, 'student_code_to_id' => $studentCodeToId, - 'existing_ids' => $existingIds, - 'debug' => $debug, + 'existing_ids' => $existingIds, + 'debug' => $debug, ]; } @@ -114,12 +113,12 @@ class AttendanceViolationStudentResolverService return null; } - if (!isset($existingIds[$sid])) { + if (! isset($existingIds[$sid])) { $students[] = [ - 'id' => $sid, + 'id' => $sid, 'school_id' => (string) $sid, 'firstname' => (string) $sid, - 'lastname' => '', + 'lastname' => '', 'parent_id' => 0, ]; $existingIds[$sid] = true; @@ -139,12 +138,12 @@ class AttendanceViolationStudentResolverService $sid = abs(crc32($code)) ?: random_int(3000000, 3999999); $studentCodeToId[$code] = $sid; - if (!isset($existingIds[$sid])) { + if (! isset($existingIds[$sid])) { $students[] = [ - 'id' => $sid, + 'id' => $sid, 'school_id' => $code, 'firstname' => $code, - 'lastname' => '', + 'lastname' => '', 'parent_id' => 0, ]; $existingIds[$sid] = true; @@ -185,7 +184,7 @@ class AttendanceViolationStudentResolverService $attendanceStudents = DB::table($this->attendanceDataModel->getTable()) ->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(); @@ -207,7 +206,7 @@ class AttendanceViolationStudentResolverService protected function filterInactiveIdentifiers(array $studentIds, array $studentCodes): array { - if (!empty($studentIds)) { + if (! empty($studentIds)) { $inactiveRows = $this->studentModel->query() ->select('id') ->whereIn('id', $studentIds) @@ -222,11 +221,11 @@ class AttendanceViolationStudentResolverService $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) @@ -241,7 +240,7 @@ class AttendanceViolationStudentResolverService $studentCodes = array_values(array_filter( $studentCodes, - static fn ($code) => $code !== '' && !isset($inactiveCodeSet[$code]) + static fn ($code) => $code !== '' && ! isset($inactiveCodeSet[$code]) )); } @@ -252,7 +251,7 @@ class AttendanceViolationStudentResolverService { $students = []; - if (!empty($studentIds)) { + if (! empty($studentIds)) { $students = $this->studentModel->query() ->whereIn('id', $studentIds) ->where('is_active', 1) @@ -260,7 +259,7 @@ class AttendanceViolationStudentResolverService ->toArray(); } - if (!empty($studentCodes)) { + if (! empty($studentCodes)) { $byCode = $this->studentModel->query() ->whereIn('school_id', $studentCodes) ->where('is_active', 1) @@ -271,7 +270,7 @@ class AttendanceViolationStudentResolverService 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; } @@ -293,14 +292,14 @@ class AttendanceViolationStudentResolverService } foreach ($studentCodes as $code) { - if (!isset($knownCodes[$code])) { + if (! isset($knownCodes[$code])) { $virtualId = abs(crc32($code)) ?: random_int(1000000, 1999999); $students[] = [ - 'id' => $virtualId, - 'school_id' => $code, - 'firstname' => $code, - 'lastname' => '', - 'parent_id' => 0, + 'id' => $virtualId, + 'school_id' => $code, + 'firstname' => $code, + 'lastname' => '', + 'parent_id' => 0, 'class_name' => '', ]; } @@ -317,13 +316,13 @@ class AttendanceViolationStudentResolverService 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, - 'firstname' => (string) $sid, - 'lastname' => '', - 'parent_id' => 0, + 'id' => $sid, + 'school_id' => (string) $sid, + 'firstname' => (string) $sid, + 'lastname' => '', + 'parent_id' => 0, 'class_name' => '', ]; $existingIds[$sid] = true; @@ -331,14 +330,14 @@ class AttendanceViolationStudentResolverService } 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, - 'school_id' => $code, - 'firstname' => $code, - 'lastname' => '', - 'parent_id' => 0, + 'id' => $virtualId, + 'school_id' => $code, + 'firstname' => $code, + 'lastname' => '', + 'parent_id' => 0, 'class_name' => '', ]; diff --git a/app/Services/AttendanceTracking/DefaultAttendanceMailerService.php b/app/Services/AttendanceTracking/DefaultAttendanceMailerService.php index 0cb9946a..e39aa0a4 100644 --- a/app/Services/AttendanceTracking/DefaultAttendanceMailerService.php +++ b/app/Services/AttendanceTracking/DefaultAttendanceMailerService.php @@ -10,8 +10,7 @@ class DefaultAttendanceMailerService implements AttendanceMailerService public function __construct( protected EmailService $emailService, protected AttendanceEmailComposerService $emailComposerService - ) { - } + ) {} public function send(string $to, string $subject, string $html): bool { @@ -26,24 +25,24 @@ class DefaultAttendanceMailerService implements AttendanceMailerService } $student = (array) ($payload['student'] ?? []); - $studentName = trim((string) ($student['firstname'] ?? '') . ' ' . (string) ($student['lastname'] ?? '')); + $studentName = trim((string) ($student['firstname'] ?? '').' '.(string) ($student['lastname'] ?? '')); $subjectType = (string) ($payload['subject'] ?? 'Attendance'); $date = substr((string) ($payload['date'] ?? now()->toDateString()), 0, 10); $subject = "Attendance Notice: {$subjectType}"; - $body = '

Dear ' . e((string) data_get($payload, 'parent.name', 'Parent/Guardian')) . ',

' - . '

This is an attendance notification for your student.

' - . '
    ' - . '
  • Student: ' . e($studentName !== '' ? $studentName : 'Student') . '
  • ' - . '
  • Date: ' . e($date) . '
  • ' - . '
  • Attendance: ' . e($subjectType) . '
  • ' - . '
' - . '

If you have any questions, please contact the school office.

'; + $body = '

Dear '.e((string) data_get($payload, 'parent.name', 'Parent/Guardian')).',

' + .'

This is an attendance notification for your student.

' + .'
    ' + .'
  • Student: '.e($studentName !== '' ? $studentName : 'Student').'
  • ' + .'
  • Date: '.e($date).'
  • ' + .'
  • Attendance: '.e($subjectType).'
  • ' + .'
' + .'

If you have any questions, please contact the school office.

'; $html = $this->emailComposerService->renderWithEmailLayout($subject, $body); - if (!$this->send($to, $subject, $html)) { - Log::error('Attendance notification email failed for ' . $to); + if (! $this->send($to, $subject, $html)) { + Log::error('Attendance notification email failed for '.$to); throw new \RuntimeException('Failed to send attendance notification email.'); } } diff --git a/app/Services/AttendanceTracking/ViolationRuleEngineService.php b/app/Services/AttendanceTracking/ViolationRuleEngineService.php index 53cad6df..6524a29d 100644 --- a/app/Services/AttendanceTracking/ViolationRuleEngineService.php +++ b/app/Services/AttendanceTracking/ViolationRuleEngineService.php @@ -13,13 +13,13 @@ use Throwable; class ViolationRuleEngineService { protected array $debugCompute = []; + protected ?array $attendanceReportedColumns = null; public function __construct( protected AttendanceTracking $attendanceTrackingModel, protected AttendanceParentLookupService $parentLookupService - ) { - } + ) {} public function getDebugCompute(): array { @@ -54,10 +54,10 @@ class ViolationRuleEngineService continue; } - $ymd = substr((string) ($r['date'] ?? ''), 0, 10); + $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; } @@ -69,10 +69,10 @@ class ViolationRuleEngineService [$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; } @@ -86,9 +86,9 @@ class ViolationRuleEngineService return []; } - $weekIndex = array_flip($activeWeekKeys); - $currentWeekIdx = count($activeWeekKeys) - 1; - $windowStartIdx = max(0, $currentWeekIdx - 4); + $weekIndex = array_flip($activeWeekKeys); + $currentWeekIdx = count($activeWeekKeys) - 1; + $windowStartIdx = max(0, $currentWeekIdx - 4); $requiredWeeksCount = $currentWeekIdx - $windowStartIdx + 1; $keepLastWeeks = function (array $dates) use ($weekIndex, $windowStartIdx, $currentWeekIdx): array { @@ -105,17 +105,18 @@ class ViolationRuleEngineService } $out = array_values(array_unique($out)); rsort($out); + return $out; }; $classByStudent = []; try { $studentIdsForClass = array_values(array_unique(array_map( - static fn($s) => (int) ($s['id'] ?? 0), + static fn ($s) => (int) ($s['id'] ?? 0), $students ))); - if (!empty($studentIdsForClass)) { + if (! empty($studentIdsForClass)) { $rows = DB::table('student_class as sc') ->select([ 'sc.student_id', @@ -137,18 +138,18 @@ class ViolationRuleEngineService continue; } - if (!isset($classByStudent[$sid])) { + if (! isset($classByStudent[$sid])) { $classByStudent[$sid] = [ - 'class_section_id' => $row->class_section_id ?? null, + 'class_section_id' => $row->class_section_id ?? null, 'class_section_name' => (string) ($row->class_section_name ?? ''), - 'class_id' => $row->class_id ?? null, - 'class_name' => (string) ($row->class_name ?? ''), + 'class_id' => $row->class_id ?? null, + 'class_name' => (string) ($row->class_name ?? ''), ]; } } } } catch (Throwable $e) { - Log::debug('computeViolations(): class prefetch failed: ' . $e->getMessage()); + Log::debug('computeViolations(): class prefetch failed: '.$e->getMessage()); } $out = []; @@ -156,26 +157,26 @@ class ViolationRuleEngineService $lateCountLast5 = 0; foreach ($students as $stu) { - $sid = (int) ($stu['id'] ?? 0); - $name = trim(($stu['firstname'] ?? '') . ' ' . ($stu['lastname'] ?? '')); + $sid = (int) ($stu['id'] ?? 0); + $name = trim(($stu['firstname'] ?? '').' '.($stu['lastname'] ?? '')); - $absDatesAll = array_values(array_unique($byStudent[$sid]['absent'] ?? [])); + $absDatesAll = array_values(array_unique($byStudent[$sid]['absent'] ?? [])); $lateDatesAll = array_values(array_unique($byStudent[$sid]['late'] ?? [])); - $absDates = $keepLastWeeks($absDatesAll); + $absDates = $keepLastWeeks($absDatesAll); $lateDates = $keepLastWeeks($lateDatesAll); if (empty($absDates) && empty($lateDates)) { continue; } - $absWeekIdx = $this->datesToWeekIndices($absDates, $weekIndex); + $absWeekIdx = $this->datesToWeekIndices($absDates, $weekIndex); $lateWeekIdx = $this->datesToWeekIndices($lateDates, $weekIndex); - $absCountLast5 += count($absDates); + $absCountLast5 += count($absDates); $lateCountLast5 += count($lateDates); $absenceViolation = null; - if (!empty($absDates)) { + if (! empty($absDates)) { $lastAbsDate = $absDates[0]; $A2row = $this->hasNConsecutiveItems($absDates, 2, 7); @@ -188,47 +189,47 @@ class ViolationRuleEngineService if ($A4row || $A_in5w4) { $absenceViolation = [ - 'type' => 'absence', - 'code' => 'ABS_4', - 'severity' => 4, - 'action' => 'expel_notify', - 'color' => '#ff4d4f', - 'title' => '4 Absences (in a row or within last 5 active weeks)', + 'type' => 'absence', + 'code' => 'ABS_4', + 'severity' => 4, + 'action' => 'expel_notify', + 'color' => '#ff4d4f', + 'title' => '4 Absences (in a row or within last 5 active weeks)', 'description' => 'Notify team to inform parent about expulsion and send email.', - 'last_date' => $lastAbsDate, + 'last_date' => $lastAbsDate, ]; } elseif ($A3row || $A_in4w3) { $absenceViolation = [ - 'type' => 'absence', - 'code' => 'ABS_3', - 'severity' => 3, - 'action' => 'team_notify', - 'color' => '#fa8c16', - 'title' => '3 Absences (in a row or within last 4 active weeks)', + 'type' => 'absence', + 'code' => 'ABS_3', + 'severity' => 3, + 'action' => 'team_notify', + 'color' => '#fa8c16', + 'title' => '3 Absences (in a row or within last 4 active weeks)', 'description' => 'Team calls parent and sends email.', - 'last_date' => $lastAbsDate, + 'last_date' => $lastAbsDate, ]; } elseif ($A2row || $A_in3w2) { $absenceViolation = [ - 'type' => 'absence', - 'code' => 'ABS_2', - 'severity' => 2, - 'action' => 'team_notify', - 'color' => '#fadb14', - 'title' => '2 Absences (in a row or within last 3 active weeks)', + 'type' => 'absence', + 'code' => 'ABS_2', + 'severity' => 2, + 'action' => 'team_notify', + 'color' => '#fadb14', + 'title' => '2 Absences (in a row or within last 3 active weeks)', 'description' => 'Team calls parent and sends email.', - 'last_date' => $lastAbsDate, + 'last_date' => $lastAbsDate, ]; } } $lateViolation = null; - if (!empty($lateDates)) { + if (! empty($lateDates)) { $lastLateDate = $lateDates[0]; - $L2row = $this->hasNConsecutiveItems($lateDates, 2, 7); - $L3row = $this->hasNConsecutiveItems($lateDates, 3, 7); - $L4row = $this->hasNConsecutiveItems($lateDates, 4, 7); + $L2row = $this->hasNConsecutiveItems($lateDates, 2, 7); + $L3row = $this->hasNConsecutiveItems($lateDates, 3, 7); + $L4row = $this->hasNConsecutiveItems($lateDates, 4, 7); $L_in3w2 = $this->hasNInWActiveWeeks($lateWeekIdx, 2, 3); $L_in4w3 = $this->hasNInWActiveWeeks($lateWeekIdx, 3, 4); @@ -238,7 +239,7 @@ class ViolationRuleEngineService $lateWeeksSet = array_flip($lateWeekIdx); $lateCoversAllWindowWeeks = true; for ($i = $windowStartIdx; $i <= $currentWeekIdx; $i++) { - if (!isset($lateWeeksSet[$i])) { + if (! isset($lateWeeksSet[$i])) { $lateCoversAllWindowWeeks = false; break; } @@ -247,41 +248,41 @@ class ViolationRuleEngineService if ($L4row || $lateCoversAllWindowWeeks) { $lateViolation = [ - 'type' => 'late', - 'code' => 'LATE_4', - 'severity' => 4, - 'action' => 'team_notify', - 'color' => '#ff4d4f', - 'title' => '4 Lates in a row OR in each of the last 4 active weeks', + 'type' => 'late', + 'code' => 'LATE_4', + 'severity' => 4, + 'action' => 'team_notify', + 'color' => '#ff4d4f', + 'title' => '4 Lates in a row OR in each of the last 4 active weeks', 'description' => 'Team calls parent and sends email.', - 'last_date' => $lastLateDate, + 'last_date' => $lastLateDate, ]; } else { $twoLatesOneAbsWithin4 = $this->twoLatesOneAbsInWWeeks($lateWeekIdx, $absWeekIdx, 4); if ($L3row || $L_in4w3 || $twoLatesOneAbsWithin4) { $lateViolation = [ - 'type' => $twoLatesOneAbsWithin4 ? 'mix' : 'late', - 'code' => $twoLatesOneAbsWithin4 ? 'MIX_L2A1' : 'LATE_3', - 'severity' => 3, - 'action' => 'team_notify', - 'color' => '#002766', - 'title' => $twoLatesOneAbsWithin4 + 'type' => $twoLatesOneAbsWithin4 ? 'mix' : 'late', + 'code' => $twoLatesOneAbsWithin4 ? 'MIX_L2A1' : 'LATE_3', + 'severity' => 3, + 'action' => 'team_notify', + 'color' => '#002766', + 'title' => $twoLatesOneAbsWithin4 ? '2 Lates + 1 Absence (within last 4 active weeks)' : '3 Lates (in a row or within last 4 active weeks)', 'description' => 'Team calls parent and sends email.', - 'last_date' => $lastLateDate, + 'last_date' => $lastLateDate, ]; } elseif ($L2row || $L_in3w2) { $lateViolation = [ - 'type' => 'late', - 'code' => 'LATE_2', - 'severity' => 1, - 'action' => 'auto_email', - 'color' => '#bae7ff', - 'title' => '2 Lates (in a row or within last 3 active weeks)', + 'type' => 'late', + 'code' => 'LATE_2', + 'severity' => 1, + 'action' => 'auto_email', + 'color' => '#bae7ff', + 'title' => '2 Lates (in a row or within last 3 active weeks)', 'description' => 'Send automated email to parent.', - 'last_date' => $lastLateDate, + 'last_date' => $lastLateDate, ]; } } @@ -289,20 +290,20 @@ class ViolationRuleEngineService $chosen = $this->chooseHigherSeverity($absenceViolation, $lateViolation); - if (!$chosen && count($absDates) === 1) { + if (! $chosen && count($absDates) === 1) { $chosen = [ - 'type' => 'absence', - 'code' => 'ABS_1', - 'severity' => 1, - 'action' => 'auto_email', - 'color' => '#e6f7ff', - 'title' => '1 Unreported Absence', + 'type' => 'absence', + 'code' => 'ABS_1', + 'severity' => 1, + 'action' => 'auto_email', + 'color' => '#e6f7ff', + 'title' => '1 Unreported Absence', 'description' => 'Send automated email to parent.', - 'last_date' => $absDates[0], + 'last_date' => $absDates[0], ]; } - if (!$chosen) { + if (! $chosen) { continue; } @@ -315,51 +316,51 @@ class ViolationRuleEngineService ->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); } - $tracking = $trackingQ->orderByDesc('date')->first(); - $tracking = $tracking?->toArray() ?? []; + $tracking = $trackingQ->orderByDesc('date')->first(); + $tracking = $tracking?->toArray() ?? []; $isNotified = (bool) ($tracking['is_notified'] ?? 0); - $notifCnt = (int) ($tracking['notif_counter'] ?? 0); + $notifCnt = (int) ($tracking['notif_counter'] ?? 0); $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']; } $out[] = [ - 'id' => $sid, - 'name' => $name, - 'class_name' => $classLabel, + 'id' => $sid, + 'name' => $name, + 'class_name' => $classLabel, 'class_section_name' => (string) ($cls['class_section_name'] ?? ''), - 'className' => (string) ($cls['class_name'] ?? ''), - 'class_id' => $cls['class_id'] ?? null, + 'className' => (string) ($cls['class_name'] ?? ''), + 'class_id' => $cls['class_id'] ?? null, 'class_section_id' => $cls['class_section_id'] ?? null, - 'parent_email' => $parent['email'] ?? '', - 'parent_name' => $parent['parent_name'] ?? '', - 'parent_phone' => $parent['phone'] ?? null, - 'violation' => $chosen['title'], - 'violation_code' => $chosen['code'], - 'type' => $chosen['type'], - 'severity' => $chosen['severity'], - 'action' => $chosen['action'], - 'color' => $chosen['color'], - 'last_absence' => $absDates[0] ?? null, - 'last_late' => $lateDates[0] ?? null, - 'last_date' => $chosen['last_date'], - 'is_notified' => $isNotified, - 'notif_counter' => $notifCnt, - 'absences' => $absDates, - 'lates' => $lateDates, + 'parent_email' => $parent['email'] ?? '', + 'parent_name' => $parent['parent_name'] ?? '', + 'parent_phone' => $parent['phone'] ?? null, + 'violation' => $chosen['title'], + 'violation_code' => $chosen['code'], + 'type' => $chosen['type'], + 'severity' => $chosen['severity'], + 'action' => $chosen['action'], + 'color' => $chosen['color'], + 'last_absence' => $absDates[0] ?? null, + 'last_late' => $lateDates[0] ?? null, + 'last_date' => $chosen['last_date'], + 'is_notified' => $isNotified, + 'notif_counter' => $notifCnt, + 'absences' => $absDates, + 'lates' => $lateDates, ]; } - $this->debugCompute['abs_last5'] = $absCountLast5; + $this->debugCompute['abs_last5'] = $absCountLast5; $this->debugCompute['late_last5'] = $lateCountLast5; return $out; @@ -370,8 +371,10 @@ class ViolationRuleEngineService 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; } + return $a['severity'] > $b['severity'] ? $a : $b; } @@ -386,7 +389,7 @@ class ViolationRuleEngineService $this->attendanceReportedColumns = [ 'is_reported' => Schema::hasColumn($table, 'is_reported'), - 'reported' => Schema::hasColumn($table, 'reported'), + 'reported' => Schema::hasColumn($table, 'reported'), ]; return $this->attendanceReportedColumns; @@ -396,15 +399,15 @@ class ViolationRuleEngineService { $cols = $this->attendanceReportedColumns($table); - 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'))"); } $qb->whereRaw("(LOWER(COALESCE(reason, '')) NOT LIKE '%parent-reported%')") - ->whereRaw("(LOWER(COALESCE(reason, '')) NOT LIKE '%parent reported%')"); + ->whereRaw("(LOWER(COALESCE(reason, '')) NOT LIKE '%parent reported%')"); if (Schema::hasTable('parent_attendance_reports')) { $qb->whereRaw("NOT EXISTS ( @@ -418,14 +421,16 @@ class ViolationRuleEngineService public function deriveSchoolYearBounds(string $schoolYear): array { - if (!preg_match('/^(\d{4})-(\d{4})$/', $schoolYear, $m)) { - $y = (int) date('Y'); + 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)]; } $startY = (int) $m[1]; - $endY = (int) $m[2]; + $endY = (int) $m[2]; + return [sprintf('%d-08-01', $startY), sprintf('%d-07-31', $endY)]; } @@ -446,9 +451,10 @@ class ViolationRuleEngineService public 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', ]; } @@ -461,10 +467,10 @@ class ViolationRuleEngineService ): array { $weeks = []; - if (!empty($weekRowsFallback)) { + if (! empty($weekRowsFallback)) { foreach ($weekRowsFallback as $r) { $d = $r['date'] ?? null; - if (!$d) { + if (! $d) { continue; } $ts = strtotime($d); @@ -490,10 +496,10 @@ class ViolationRuleEngineService $q->whereNull('reason') ->orWhere(function ($q2) { $q2->where('reason', 'not like', '%break%') - ->where('reason', 'not like', '%cancel%'); + ->where('reason', 'not like', '%cancel%'); }); }) - ->when($semester !== null && $semester !== '', fn($q) => $q->where('semester', $semester)) + ->when($semester !== null && $semester !== '', fn ($q) => $q->where('semester', $semester)) ->groupBy(DB::raw('DATE(date)')) ->orderBy('ymd') ->get(); @@ -521,6 +527,7 @@ class ViolationRuleEngineService } $idx = array_keys($set); sort($idx); + return $idx; } @@ -542,14 +549,16 @@ class ViolationRuleEngineService if (Str::startsWith($code, 'ABS_4')) { return 5; } + return 5; } public 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'); } @@ -571,14 +580,15 @@ class ViolationRuleEngineService 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]); @@ -662,11 +672,12 @@ class ViolationRuleEngineService while ($iL2 < count($L)) { $windowStart = $L[$iL1]; - $windowEnd = $windowStart + ($w - 1); + $windowEnd = $windowStart + ($w - 1); if ($L[$iL2] > $windowEnd) { $iL1++; $iL2 = $iL1 + 1; + continue; } @@ -687,4 +698,4 @@ class ViolationRuleEngineService return false; } -} \ No newline at end of file +} diff --git a/app/Services/Auth/ApiLoginSecurityService.php b/app/Services/Auth/ApiLoginSecurityService.php index 1c3b18b5..dd9abebd 100644 --- a/app/Services/Auth/ApiLoginSecurityService.php +++ b/app/Services/Auth/ApiLoginSecurityService.php @@ -2,9 +2,9 @@ namespace App\Services\Auth; +use App\Models\Configuration; use App\Models\IpAttempt; use App\Models\LoginActivity; -use App\Models\Configuration; use App\Models\User; use Carbon\CarbonImmutable; use Illuminate\Http\Request; @@ -22,7 +22,7 @@ class ApiLoginSecurityService public function isIpBlocked(string $ip): bool { $row = IpAttempt::query()->where('ip_address', $ip)->first(); - if (!$row || !$row->blocked_until) { + if (! $row || ! $row->blocked_until) { return false; } diff --git a/app/Services/Auth/AuthSessionService.php b/app/Services/Auth/AuthSessionService.php index 04cda57d..bc7f044f 100644 --- a/app/Services/Auth/AuthSessionService.php +++ b/app/Services/Auth/AuthSessionService.php @@ -3,12 +3,11 @@ namespace App\Services\Auth; use App\Models\Configuration; -use App\Services\ApplicationUrlService; use App\Models\LoginActivity; use App\Models\Preferences; use App\Models\Role; use App\Models\User; -use Illuminate\Http\Request; +use App\Services\ApplicationUrlService; use Illuminate\Support\Facades\Auth; use Illuminate\Support\Facades\Log; @@ -21,8 +20,7 @@ class AuthSessionService public function __construct( private ApplicationUrlService $urls, - ) { - } + ) {} /** * Sanitizes login redirects to same-host relative paths only. diff --git a/app/Services/Auth/PermissionCheckService.php b/app/Services/Auth/PermissionCheckService.php index 767e52ac..e570c625 100644 --- a/app/Services/Auth/PermissionCheckService.php +++ b/app/Services/Auth/PermissionCheckService.php @@ -12,7 +12,7 @@ class PermissionCheckService ->where('name', $permissionName) ->value('id'); - if (!$permissionId) { + if (! $permissionId) { return false; } diff --git a/app/Services/Auth/RegistrationCaptchaService.php b/app/Services/Auth/RegistrationCaptchaService.php index 90cfc3fd..f33cbf2d 100644 --- a/app/Services/Auth/RegistrationCaptchaService.php +++ b/app/Services/Auth/RegistrationCaptchaService.php @@ -9,7 +9,8 @@ class RegistrationCaptchaService $ip = (string) (request()?->ip() ?? 'unknown'); $ua = (string) (request()?->header('User-Agent') ?? ''); $hash = substr(sha1($ua), 0, 12); - return 'captcha:' . $ip . ':' . $hash; + + return 'captcha:'.$ip.':'.$hash; } public function generate(?int $length = null): string diff --git a/app/Services/Auth/RegistrationFormatterService.php b/app/Services/Auth/RegistrationFormatterService.php index 3d00572e..1d6e1b48 100644 --- a/app/Services/Auth/RegistrationFormatterService.php +++ b/app/Services/Auth/RegistrationFormatterService.php @@ -6,9 +6,7 @@ use App\Services\PhoneFormatterService; class RegistrationFormatterService { - public function __construct(private PhoneFormatterService $phoneFormatter) - { - } + public function __construct(private PhoneFormatterService $phoneFormatter) {} public function format(array $payload): array { @@ -30,7 +28,7 @@ class RegistrationFormatterService 'accept_school_policy' => (int) ($payload['accept_school_policy'] ?? 0), ]; - $noSecondInfo = !empty($payload['no_second_parent_info']); + $noSecondInfo = ! empty($payload['no_second_parent_info']); $secondFirstname = $this->formatName($g('second_firstname')); $secondLastname = $this->formatName($g('second_lastname')); @@ -38,7 +36,7 @@ class RegistrationFormatterService $secondEmail = $this->formatEmail($g('second_email')); $secondCellphone = $this->phoneFormatter->formatPhoneNumber($g('second_cellphone')); - $secondProvided = !$noSecondInfo && ( + $secondProvided = ! $noSecondInfo && ( $secondFirstname !== '' || $secondLastname !== '' || $secondGender !== '' || @@ -64,7 +62,7 @@ class RegistrationFormatterService return ''; } - return ucwords(strtolower($name), " -"); + return ucwords(strtolower($name), ' -'); } private function formatEmail(string $email): string diff --git a/app/Services/Auth/RegistrationService.php b/app/Services/Auth/RegistrationService.php index 94eb036c..5192562b 100644 --- a/app/Services/Auth/RegistrationService.php +++ b/app/Services/Auth/RegistrationService.php @@ -20,13 +20,12 @@ class RegistrationService private RegistrationFormatterService $formatter, private RegistrationCaptchaService $captchaService, private ApplicationUrlService $urls, - ) { - } + ) {} public function register(array $payload): array { $captcha = (string) ($payload['captcha'] ?? ''); - if (!$this->captchaService->verify($captcha)) { + if (! $this->captchaService->verify($captcha)) { return [ 'ok' => false, 'code' => 'captcha_invalid', @@ -34,15 +33,15 @@ class RegistrationService ]; } - $isParent = !empty($payload['is_parent']); - $noSecondInfo = !empty($payload['no_second_parent_info']); + $isParent = ! empty($payload['is_parent']); + $noSecondInfo = ! empty($payload['no_second_parent_info']); $formatted = $this->formatter->format($payload); $email = (string) ($formatted['email'] ?? ''); $existing = User::query()->where('email', $email)->first(); if ($existing) { - $pending = !empty($existing->token) && (int) ($existing->is_verified ?? 0) === 0; + $pending = ! empty($existing->token) && (int) ($existing->is_verified ?? 0) === 0; if ($pending) { return [ 'ok' => false, @@ -60,7 +59,7 @@ class RegistrationService $roleName = $isParent ? 'parent' : 'guest'; $role = Role::query()->where('name', $roleName)->first(); - if (!$role) { + if (! $role) { return [ 'ok' => false, 'code' => 'role_missing', @@ -113,7 +112,7 @@ class RegistrationService 'created_at' => now(), ]); - if ($isParent && !$noSecondInfo && !empty($formatted['second_firstname'])) { + if ($isParent && ! $noSecondInfo && ! empty($formatted['second_firstname'])) { ParentModel::query()->create([ 'secondparent_firstname' => $formatted['second_firstname'] ?? null, 'secondparent_lastname' => $formatted['second_lastname'] ?? null, @@ -128,12 +127,12 @@ class RegistrationService }); $activationLink = $this->urls->spaRegistrationConfirmUrl($token); - $recipientName = trim((string) ($formatted['firstname'] ?? '') . ' ' . (string) ($formatted['lastname'] ?? '')); + $recipientName = trim((string) ($formatted['firstname'] ?? '').' '.(string) ($formatted['lastname'] ?? '')); - $html = '

Hello ' . e($recipientName !== '' ? $recipientName : 'there') . ',

' - . '

Please confirm your email by clicking the link below:

' - . '

Activate your account

' - . '

Thank you.

'; + $html = '

Hello '.e($recipientName !== '' ? $recipientName : 'there').',

' + .'

Please confirm your email by clicking the link below:

' + .'

Activate your account

' + .'

Thank you.

'; $sent = $this->emailService->send($email, 'Email Confirmation', $html, 'general'); $this->captchaService->clear(); diff --git a/app/Services/Auth/UserRoleService.php b/app/Services/Auth/UserRoleService.php index b1f8abc8..1cf1a8a7 100644 --- a/app/Services/Auth/UserRoleService.php +++ b/app/Services/Auth/UserRoleService.php @@ -35,16 +35,16 @@ class UserRoleService return true; } - if ($crudAction === 'create' && !empty($row->can_create)) { + if ($crudAction === 'create' && ! empty($row->can_create)) { return true; } - if ($crudAction === 'read' && !empty($row->can_read)) { + if ($crudAction === 'read' && ! empty($row->can_read)) { return true; } - if ($crudAction === 'update' && !empty($row->can_update)) { + if ($crudAction === 'update' && ! empty($row->can_update)) { return true; } - if ($crudAction === 'delete' && !empty($row->can_delete)) { + if ($crudAction === 'delete' && ! empty($row->can_delete)) { return true; } } diff --git a/app/Services/BadgeScan/BadgeScanService.php b/app/Services/BadgeScan/BadgeScanService.php index 644437e8..fe137fe0 100644 --- a/app/Services/BadgeScan/BadgeScanService.php +++ b/app/Services/BadgeScan/BadgeScanService.php @@ -2,13 +2,14 @@ namespace App\Services\BadgeScan; +use App\Controllers\View\RFIDController; use App\Models\Configuration; use App\Models\User; use Illuminate\Support\Facades\DB; use Illuminate\Support\Facades\Schema; /** - * legacy {@see \App\Controllers\View\RFIDController} parity (badge scan + log listing). + * legacy {@see RFIDController} parity (badge scan + log listing). */ class BadgeScanService { diff --git a/app/Services/Badges/BadgeFormDataService.php b/app/Services/Badges/BadgeFormDataService.php index cd76097c..0e982b60 100644 --- a/app/Services/Badges/BadgeFormDataService.php +++ b/app/Services/Badges/BadgeFormDataService.php @@ -8,8 +8,7 @@ class BadgeFormDataService protected BadgeUserLookupService $lookupService, protected BadgeStudentLookupService $studentLookupService, protected BadgeTextFormatter $formatter - ) { - } + ) {} public function build( ?string $schoolYear, @@ -24,7 +23,7 @@ class BadgeFormDataService $students = $this->studentLookupService->fetchStudentList($schoolYear, $selectedStudentIds); foreach ($users as &$u) { - if (!isset($u['user_id']) && isset($u['id'])) { + if (! isset($u['user_id']) && isset($u['id'])) { $u['user_id'] = (int) $u['id']; } @@ -46,7 +45,7 @@ class BadgeFormDataService $u['class_section_id'] = $u['class_section_id'] ?? null; $u['class_section_name'] = $u['class_section_name'] ?? null; $u['entity_type'] = 'user'; - $u['row_id'] = 'user:' . (int) ($u['user_id'] ?? 0); + $u['row_id'] = 'user:'.(int) ($u['user_id'] ?? 0); $u['role_group'] = $this->detectRoleGroup( strtolower((string) ($u['roles_raw'] ?? ($u['role_name_raw'] ?? ''))) ); @@ -57,7 +56,7 @@ class BadgeFormDataService $rolesDetect = strtolower((string) ($u['roles_raw'] ?? ($u['role_name_raw'] ?? ''))); $isTeacherish = str_contains($rolesDetect, 'teacher') || preg_match('/\bta\b/', $rolesDetect); - if ($isTeacherish && !empty($u['user_id'])) { + if ($isTeacherish && ! empty($u['user_id'])) { $assign = $this->lookupService->getLatestClassForUser((int) $u['user_id'], $schoolYear); if ($assign) { $u['class_section_id'] = $assign['class_section_id'] ?? null; @@ -69,7 +68,7 @@ class BadgeFormDataService foreach ($students as &$student) { $student['entity_type'] = 'student'; - $student['row_id'] = 'student:' . (int) ($student['student_id'] ?? 0); + $student['row_id'] = 'student:'.(int) ($student['student_id'] ?? 0); $student['role_group'] = 'student'; $student['class_section_id'] = null; @@ -84,17 +83,17 @@ class BadgeFormDataService $rows = array_merge($users, $students); usort($rows, static function (array $a, array $b): int { return strcasecmp( - trim((string) ($a['lastname'] ?? '')) . ' ' . trim((string) ($a['firstname'] ?? '')), - trim((string) ($b['lastname'] ?? '')) . ' ' . trim((string) ($b['firstname'] ?? '')) + trim((string) ($a['lastname'] ?? '')).' '.trim((string) ($a['firstname'] ?? '')), + trim((string) ($b['lastname'] ?? '')).' '.trim((string) ($b['firstname'] ?? '')) ); }); $rolesTabs = [ - 'all' => 'All', + 'all' => 'All', 'teacher' => 'Teachers', - 'ta' => 'Teacher Assistants', - 'admin' => 'Admins', - 'staff' => 'Staff', + 'ta' => 'Teacher Assistants', + 'admin' => 'Admins', + 'staff' => 'Staff', 'student' => 'Students', ]; @@ -103,13 +102,13 @@ class BadgeFormDataService : 'all'; return [ - 'users' => $rows, - 'schoolYears' => $this->lookupService->getAvailableSchoolYears(), - 'selectedYear' => $schoolYear, - 'selectedUserIds' => $selectedUserIds, + 'users' => $rows, + 'schoolYears' => $this->lookupService->getAvailableSchoolYears(), + 'selectedYear' => $schoolYear, + 'selectedUserIds' => $selectedUserIds, 'selectedStudentIds' => $selectedStudentIds, - 'rolesTabs' => $rolesTabs, - 'active_role' => $activeRole, + 'rolesTabs' => $rolesTabs, + 'active_role' => $activeRole, ]; } diff --git a/app/Services/Badges/BadgePdfService.php b/app/Services/Badges/BadgePdfService.php index 20e7111f..761020b3 100644 --- a/app/Services/Badges/BadgePdfService.php +++ b/app/Services/Badges/BadgePdfService.php @@ -33,14 +33,13 @@ class BadgePdfService protected BadgeUserLookupService $userLookupService, protected BadgePrintLogService $printLogService, protected BadgeTextFormatter $formatter, - ) { - } + ) {} /** * Letter landscape PDF, 8 badges per page — students and/or staff (FPDF + QR PNG). * * @param int[] $studentIds Primary keys on `students.id` - * @param int[] $userIds Staff/admin/teacher keys on `users.id` + * @param int[] $userIds Staff/admin/teacher keys on `users.id` */ public function generate( array $studentIds, @@ -86,7 +85,7 @@ class BadgePdfService try { $qrPayload = (string) ($staffRow['user_id_for_qr'] ?? $staffRow['user_id'] ?? ''); - if ($qrPayload === '' || !preg_match('/^[A-Za-z0-9_-]+$/', $qrPayload)) { + if ($qrPayload === '' || ! preg_match('/^[A-Za-z0-9_-]+$/', $qrPayload)) { $qrPayload = (string) (int) ($staffRow['user_id'] ?? 0); } @@ -144,7 +143,7 @@ class BadgePdfService ): string { $html = $this->buildHtmlDocument($rows, $schoolName, $motto, $footerBlock); - $options = new Options(); + $options = new Options; $options->set('isHtml5ParserEnabled', true); $options->set('isRemoteEnabled', true); $options->set('defaultFont', 'DejaVu Sans'); @@ -168,7 +167,7 @@ class BadgePdfService $this->tempQrFiles = []; try { - if (!class_exists('FPDF', false)) { + if (! class_exists('FPDF', false)) { require_once base_path('app/ThirdParty/fpdf/fpdf.php'); } @@ -200,7 +199,7 @@ class BadgePdfService for ($gridCol = 0; $gridCol < 4; $gridCol++) { $idx = ($gridRow * 4) + $gridCol; $row = $slots[$idx]; - if (!is_array($row)) { + if (! is_array($row)) { continue; } @@ -262,7 +261,7 @@ class BadgePdfService array $classesMap ): ?array { $info = $this->userLookupService->getUserInfoById($userId, $schoolYear); - if (!$info || !is_array($info)) { + if (! $info || ! is_array($info)) { return null; } @@ -276,7 +275,7 @@ class BadgePdfService $roleResolved = $this->formatter->formatRole($roleResolved); $info['role_resolved'] = $roleResolved; - if (!empty($classesMap[$resolvedId])) { + if (! empty($classesMap[$resolvedId])) { $info['class_section_name'] = (string) $classesMap[$resolvedId]; } @@ -291,11 +290,11 @@ class BadgePdfService if ($isTeacherish && $class !== '') { if (strtolower($class) === 'youth') { - $display = 'Youth ' . $roleResolved; + $display = 'Youth '.$roleResolved; } elseif ($class === 'KG') { - $display = 'KG ' . $roleResolved; + $display = 'KG '.$roleResolved; } else { - $display = 'Grade ' . $class . ' ' . $roleResolved; + $display = 'Grade '.$class.' '.$roleResolved; } } elseif ($roleResolved !== '') { $display = $roleResolved; @@ -309,7 +308,7 @@ class BadgePdfService $year = $this->formatter->norm((string) ($info['school_year'] ?? ($schoolYear ?? ''))); $schoolId = trim((string) ($info['school_id'] ?? '')); - if ($schoolId === '' || !preg_match('/^[A-Za-z0-9_-]+$/', $schoolId)) { + if ($schoolId === '' || ! preg_match('/^[A-Za-z0-9_-]+$/', $schoolId)) { return null; } @@ -364,10 +363,10 @@ class BadgePdfService } return 'Badges PDF' - . $badgesHtml - . ''; + .$this->badgeCss() + .'' + .$badgesHtml + .''; } protected function buildBadgeHtml( @@ -376,8 +375,7 @@ class BadgePdfService string $motto, string $footerBlock, ?string $logoDataUri - ): string - { + ): string { $kind = (string) ($row['_badge_kind'] ?? 'student'); $idLabel = $kind === 'staff' ? 'User ID' : 'School ID'; $title = $this->escapeHtml((string) ($row['fullname'] ?? '')); @@ -404,8 +402,8 @@ class BadgePdfService
G - ' . $gradeLabel . ' - ' . $grade . ' + '.$gradeLabel.' + '.$grade.'
'; } @@ -415,15 +413,15 @@ class BadgePdfService Y Academic Year - ' . $year . ' + '.$year.'
ID - ' . $this->escapeHtml($idLabel) . ' - ' . $schoolId . ' + '.$this->escapeHtml($idLabel).' + '.$schoolId.'
'; @@ -432,18 +430,18 @@ class BadgePdfService
' - . ($logoDataUri !== null - ? 'School Logo' - : 'AS') . + .($logoDataUri !== null + ? 'School Logo' + : 'AS'). '
-
' . $this->escapeHtml($headerMainTitle) . '
- ' . ($schoolLevel !== '' - ? '
' . $this->escapeHtml($schoolLevel) . '
' - : '') . ' - ' . ($headerMotto !== '' - ? '
' . $this->escapeHtml($headerMotto) . '
' - : '') . ' +
'.$this->escapeHtml($headerMainTitle).'
+ '.($schoolLevel !== '' + ? '
'.$this->escapeHtml($schoolLevel).'
' + : '').' + '.($headerMotto !== '' + ? '
'.$this->escapeHtml($headerMotto).'
' + : '').'
@@ -451,25 +449,25 @@ class BadgePdfService
-
' . mb_strtoupper($title, 'UTF-8') . '
+
'.mb_strtoupper($title, 'UTF-8').'
-
' . mb_strtoupper($role, 'UTF-8') . '
+
'.mb_strtoupper($role, 'UTF-8').'
@@ -477,18 +475,18 @@ class BadgePdfService
- ' . $infoRows . ' + '.$infoRows.'
' - . ($qrSrc !== '' - ? 'QR Code' - : '') . + .($qrSrc !== '' + ? 'QR Code' + : ''). '
Scan to verify
-
' . $this->buildBarcodeHtml($barcodeValue) . '
-
' . $barcodeText . '
+
'.$this->buildBarcodeHtml($barcodeValue).'
+
'.$barcodeText.'
'; @@ -899,7 +897,7 @@ CSS; default => '4px', }; - $bars[] = ''; + $bars[] = ''; $bars[] = ''; } @@ -917,7 +915,7 @@ CSS; return null; } - $tmpPath = sys_get_temp_dir() . DIRECTORY_SEPARATOR . 'badge_qr_' . bin2hex(random_bytes(8)) . '.png'; + $tmpPath = sys_get_temp_dir().DIRECTORY_SEPARATOR.'badge_qr_'.bin2hex(random_bytes(8)).'.png'; if (@file_put_contents($tmpPath, $png) === false) { return null; } @@ -1002,11 +1000,11 @@ CSS; { return $this->formatter->firstExisting([ public_path($filename), - public_path('badges/' . $filename), - public_path('assets/images/' . $filename), - public_path('assets/images/badges/' . $filename), - storage_path('app/public/' . $filename), - storage_path('app/public/badges/' . $filename), + public_path('badges/'.$filename), + public_path('assets/images/'.$filename), + public_path('assets/images/badges/'.$filename), + storage_path('app/public/'.$filename), + storage_path('app/public/badges/'.$filename), ]); } @@ -1087,7 +1085,7 @@ CSS; /** * Shrink text until it fits in the available width. FPDF, bravely, does not do this for us. * - * @param array{0:int,1:int,2:int} $rgb + * @param array{0:int,1:int,2:int} $rgb */ protected function fitCenteredText( \FPDF $pdf, @@ -1149,7 +1147,7 @@ CSS; } /** - * @param array{0:int,1:int,2:int} $rgb + * @param array{0:int,1:int,2:int} $rgb */ protected function fitLeftText( \FPDF $pdf, @@ -1447,6 +1445,7 @@ CSS; foreach ($segments as [$type, $segmentWidth]) { if ($type === 'gap') { $cursor += $segmentWidth; + continue; } @@ -1510,7 +1509,7 @@ CSS; protected function preparedLogoPath(): ?string { $path = $this->logoFilePath(); - if ($path === null || !is_readable($path)) { + if ($path === null || ! is_readable($path)) { return null; } @@ -1519,7 +1518,7 @@ CSS; return $path; } - $tmpPath = sys_get_temp_dir() . DIRECTORY_SEPARATOR . 'badge_logo_' . bin2hex(random_bytes(8)) . '.png'; + $tmpPath = sys_get_temp_dir().DIRECTORY_SEPARATOR.'badge_logo_'.bin2hex(random_bytes(8)).'.png'; if (@file_put_contents($tmpPath, $png) === false) { return $path; } @@ -1532,7 +1531,7 @@ CSS; protected function logoDataUri(): ?string { $path = $this->logoFilePath(); - if ($path === null || !is_readable($path)) { + if ($path === null || ! is_readable($path)) { return null; } @@ -1544,10 +1543,9 @@ CSS; return null; } - return 'data:image/png;base64,' . base64_encode($bytes); + return 'data:image/png;base64,'.base64_encode($bytes); } - protected function headerMainTitle(string $schoolName): string { $name = trim($schoolName) !== '' ? trim($schoolName) : 'Al Rahma Sunday School'; @@ -1579,7 +1577,7 @@ CSS; { $address = trim((string) config('badges.footer_address', '')); if ($address !== '') { - return $schoolName . "\n" . $address; + return $schoolName."\n".$address; } return $schoolName; @@ -1587,7 +1585,7 @@ CSS; protected function makeCircularPngBytesFromPath(string $path): ?string { - if (!function_exists('imagecreatetruecolor') || !function_exists('imagepng')) { + if (! function_exists('imagecreatetruecolor') || ! function_exists('imagepng')) { return null; } @@ -1626,6 +1624,7 @@ CSS; $circle = imagecreatetruecolor($size, $size); if ($circle === false) { imagedestroy($square); + return null; } diff --git a/app/Services/Badges/BadgePrintLogService.php b/app/Services/Badges/BadgePrintLogService.php index e820f4ad..25fd6da3 100644 --- a/app/Services/Badges/BadgePrintLogService.php +++ b/app/Services/Badges/BadgePrintLogService.php @@ -10,8 +10,7 @@ class BadgePrintLogService public function __construct( protected BadgePrintLog $badgePrintLogModel, protected BadgeTextFormatter $formatter - ) { - } + ) {} public function getStatus(array $userIds, ?string $schoolYear): array { @@ -51,7 +50,7 @@ class BadgePrintLogService try { $this->log($userIds, $actorId, $schoolYear, $rolesMap, $classesMap, $copies); } catch (\Throwable $e) { - Log::error('Failed to log badge prints: ' . $e->getMessage()); + Log::error('Failed to log badge prints: '.$e->getMessage()); } } -} \ No newline at end of file +} diff --git a/app/Services/Badges/BadgeService.php b/app/Services/Badges/BadgeService.php index fdbcfb4c..c2bb7224 100644 --- a/app/Services/Badges/BadgeService.php +++ b/app/Services/Badges/BadgeService.php @@ -23,8 +23,7 @@ class BadgeService protected TeacherClass $teacherClassModel, protected ClassSection $classSectionModel, protected BadgePrintLog $badgePrintLogModel - ) { - } + ) {} public function generateBadgePdf( array $userIds, @@ -55,7 +54,7 @@ class BadgeService foreach ($userIds as $uid) { $info = $this->getUserInfoById($uid, $schoolYear); - if (!$info || !is_array($info)) { + if (! $info || ! is_array($info)) { continue; } @@ -70,13 +69,13 @@ class BadgeService $roleResolved = $this->formatRole($roleResolved); $info['role_resolved'] = $roleResolved; - if (!empty($classesMap[$userId])) { + if (! empty($classesMap[$userId])) { $info['class_section_name'] = (string) $classesMap[$userId]; } $class = $this->norm($info['class_section_name'] ?? ''); - $badgeKey = strtolower(trim($userId . '|' . $name . '|' . $roleResolved . '|' . $class)); + $badgeKey = strtolower(trim($userId.'|'.$name.'|'.$roleResolved.'|'.$class)); if (isset($seen[$badgeKey])) { continue; } @@ -117,7 +116,7 @@ class BadgeService 1 ); } catch (\Throwable $e) { - Log::error('Failed to log badge prints: ' . $e->getMessage()); + Log::error('Failed to log badge prints: '.$e->getMessage()); } return response($pdfString, 200, [ @@ -159,7 +158,7 @@ class BadgeService $users = $this->fetchStaffList($schoolYear, $selectedUserIds); foreach ($users as &$u) { - if (!isset($u['user_id'])) { + if (! isset($u['user_id'])) { if (isset($u['id'])) { $u['user_id'] = (int) $u['id']; } @@ -190,7 +189,7 @@ class BadgeService $rolesDetect = strtolower($u['roles_raw'] ?? ($u['role_name_raw'] ?? '')); $isTeacherish = str_contains($rolesDetect, 'teacher') || preg_match('/\bta\b/', $rolesDetect); - if ($isTeacherish && !empty($u['user_id'])) { + if ($isTeacherish && ! empty($u['user_id'])) { $assign = $this->getLatestClassForUser((int) $u['user_id'], $schoolYear); if ($assign) { $u['class_section_id'] = $assign['class_section_id'] ?? null; @@ -234,11 +233,11 @@ class BadgeService ]) ->groupBy('users.id', 'users.firstname', 'users.lastname'); - if (!empty($selectedUserIds)) { + if (! empty($selectedUserIds)) { $query->whereIn('users.id', $selectedUserIds); } - if (!empty($schoolYear) && Schema::hasColumn('user_roles', 'school_year')) { + if (! empty($schoolYear) && Schema::hasColumn('user_roles', 'school_year')) { $query->where('user_roles.school_year', $schoolYear); } @@ -255,7 +254,7 @@ class BadgeService ]) ->where('tc.teacher_id', $userId); - if (!empty($schoolYear)) { + if (! empty($schoolYear)) { $query->where('tc.school_year', $schoolYear); } @@ -265,7 +264,7 @@ class BadgeService ->orderByDesc('tc.id') ->first(); - if (!$row || empty($row->class_section_id)) { + if (! $row || empty($row->class_section_id)) { return null; } @@ -282,12 +281,12 @@ class BadgeService ->where('id', $id) ->first(); - if (!$u) { + if (! $u) { return null; } $userId = (int) $u->user_id; - $fullname = trim(($u->firstname ?? '') . ' ' . ($u->lastname ?? '')); + $fullname = trim(($u->firstname ?? '').' '.($u->lastname ?? '')); $rolesRows = DB::table('user_roles as ur') ->join('roles as r', 'r.id', '=', 'ur.role_id') @@ -310,7 +309,7 @@ class BadgeService ->select(['class_section_id', 'updated_at', 'id', 'position', 'school_year']) ->where('teacher_id', $userId); - if (!empty($schoolYear)) { + if (! empty($schoolYear)) { $tcQuery->where('school_year', $schoolYear); } @@ -322,7 +321,7 @@ class BadgeService $position = strtolower(trim((string) ($assignment->position ?? ''))); $className = null; - if (!empty($classId)) { + if (! empty($classId)) { $className = $this->resolveClassName((int) $classId); } @@ -330,7 +329,7 @@ class BadgeService $roleLabel = 'Teacher Assistant'; } elseif ($position === 'teacher' || $position === 'main') { $roleLabel = 'Teacher'; - } elseif (!empty($allRoles)) { + } elseif (! empty($allRoles)) { $roleLabel = $allRoles[0]; } else { $roleLabel = 'Staff'; @@ -347,11 +346,11 @@ class BadgeService ]); $jobTitle = ''; - if (!empty($roleLabel) && !empty($className)) { + if (! empty($roleLabel) && ! empty($className)) { $jobTitle = "{$roleLabel} - {$className}"; - } elseif (!empty($roleLabel)) { + } elseif (! empty($roleLabel)) { $jobTitle = $roleLabel; - } elseif (!empty($className)) { + } elseif (! empty($className)) { $jobTitle = $className; } @@ -379,10 +378,10 @@ class BadgeService $pdf->Rect($x, $y, $w, $h); $logoSize = 6; - if (!empty($data['school_logo']) && file_exists($data['school_logo'])) { + if (! empty($data['school_logo']) && file_exists($data['school_logo'])) { $pdf->Image($data['school_logo'], $x + $w - 6 - $logoSize, $y + 4, $logoSize + 3, $logoSize - 1); } - if (!empty($data['isgl_logo']) && file_exists($data['isgl_logo'])) { + if (! empty($data['isgl_logo']) && file_exists($data['isgl_logo'])) { $pdf->Image($data['isgl_logo'], $x + 4, $y + 4, $logoSize + 3, $logoSize - 1); } @@ -418,11 +417,11 @@ class BadgeService if ($isTeacherish && $class !== '') { if (strtolower($class) === 'youth') { - $display = 'Youth ' . $roleResolved; + $display = 'Youth '.$roleResolved; } elseif ($class === 'KG') { - $display = 'KG ' . $roleResolved; + $display = 'KG '.$roleResolved; } else { - $display = 'Grade ' . $class . ' ' . $roleResolved; + $display = 'Grade '.$class.' '.$roleResolved; } } elseif ($roleResolved !== '') { $display = $roleResolved; @@ -461,7 +460,7 @@ class BadgeService 'role', 'active_role', 'role_name', - fn ($r) => !empty($r['roles']) + fn ($r) => ! empty($r['roles']) ? (array_filter(array_map('trim', explode(',', (string) $r['roles'])))[0] ?? '') : '', 'job_title', @@ -479,7 +478,7 @@ class BadgeService return $this->norm($v); } } else { - if (!empty($info[$key])) { + if (! empty($info[$key])) { $v = $this->norm((string) $info[$key]); if ($v !== '') { return $v; @@ -579,6 +578,7 @@ class BadgeService if ($start === false) { $out[] = ucfirst(mb_strtolower($tok, 'UTF-8')); + continue; } @@ -594,12 +594,12 @@ class BadgeService $coreFmt = preg_replace_callback( '/\p{L}+/u', static fn ($m) => mb_strtoupper(mb_substr($m[0], 0, 1, 'UTF-8'), 'UTF-8') - . mb_strtolower(mb_substr($m[0], 1, null, 'UTF-8'), 'UTF-8'), + .mb_strtolower(mb_substr($m[0], 1, null, 'UTF-8'), 'UTF-8'), mb_strtolower($core, 'UTF-8') ); } - $out[] = $pre . $coreFmt . $suf; + $out[] = $pre.$coreFmt.$suf; } return implode(' ', $out); @@ -640,7 +640,7 @@ class BadgeService protected function firstExisting(array $paths): ?string { foreach ($paths as $path) { - if (!empty($path) && file_exists($path)) { + if (! empty($path) && file_exists($path)) { return $path; } } @@ -697,4 +697,4 @@ class BadgeService return $schoolYears; } -} \ No newline at end of file +} diff --git a/app/Services/Badges/BadgeStudentLookupService.php b/app/Services/Badges/BadgeStudentLookupService.php index ea519b0a..e8c4473a 100644 --- a/app/Services/Badges/BadgeStudentLookupService.php +++ b/app/Services/Badges/BadgeStudentLookupService.php @@ -12,8 +12,7 @@ class BadgeStudentLookupService { public function __construct( protected BadgeTextFormatter $formatter - ) { - } + ) {} /** * Active student roster for the badge picker. @@ -29,7 +28,7 @@ class BadgeStudentLookupService ->leftJoin('student_class as sc', function ($join) use ($schoolYear) { $join->on('sc.student_id', '=', 'students.id'); - if (!empty($schoolYear)) { + if (! empty($schoolYear)) { $join->where('sc.school_year', '=', $schoolYear); } }) @@ -55,9 +54,9 @@ class BadgeStudentLookupService ->orderBy('students.lastname') ->orderBy('students.firstname'); - if (!empty($selectedStudentIds)) { + if (! empty($selectedStudentIds)) { $query->whereIn('students.id', $selectedStudentIds); - } elseif (!empty($schoolYear)) { + } elseif (! empty($schoolYear)) { $query->where(function ($q) use ($schoolYear) { $q->where('students.school_year', $schoolYear) ->orWhereNotNull('sc.student_id'); @@ -74,7 +73,7 @@ class BadgeStudentLookupService } $schoolId = trim((string) ($row['school_id'] ?? '')); - if ($schoolId === '' || !preg_match('/^[A-Za-z0-9_-]+$/', $schoolId)) { + if ($schoolId === '' || ! preg_match('/^[A-Za-z0-9_-]+$/', $schoolId)) { continue; } @@ -117,7 +116,7 @@ class BadgeStudentLookupService $out = []; foreach ($rows as $student) { $schoolId = trim((string) ($student->school_id ?? '')); - if ($schoolId === '' || !preg_match('/^[A-Za-z0-9_-]+$/', $schoolId)) { + if ($schoolId === '' || ! preg_match('/^[A-Za-z0-9_-]+$/', $schoolId)) { continue; } diff --git a/app/Services/Badges/BadgeTextFormatter.php b/app/Services/Badges/BadgeTextFormatter.php index 2e72731d..1336def0 100644 --- a/app/Services/Badges/BadgeTextFormatter.php +++ b/app/Services/Badges/BadgeTextFormatter.php @@ -78,7 +78,7 @@ class BadgeTextFormatter } $special = [ - 'of' => 'of', + 'of' => 'of', 'head' => 'Head', ]; @@ -95,6 +95,7 @@ class BadgeTextFormatter if ($start === false) { $out[] = ucfirst(mb_strtolower($tok, 'UTF-8')); + continue; } @@ -110,12 +111,12 @@ class BadgeTextFormatter $coreFmt = preg_replace_callback( '/\p{L}+/u', static fn ($m) => mb_strtoupper(mb_substr($m[0], 0, 1, 'UTF-8'), 'UTF-8') - . mb_strtolower(mb_substr($m[0], 1, null, 'UTF-8'), 'UTF-8'), + .mb_strtolower(mb_substr($m[0], 1, null, 'UTF-8'), 'UTF-8'), mb_strtolower($core, 'UTF-8') ); } - $out[] = $pre . $coreFmt . $suf; + $out[] = $pre.$coreFmt.$suf; } return implode(' ', $out); @@ -163,7 +164,7 @@ class BadgeTextFormatter 'role', 'active_role', 'role_name', - fn ($r) => !empty($r['roles']) + fn ($r) => ! empty($r['roles']) ? (array_values(array_filter(array_map('trim', explode(',', (string) $r['roles']))))[0] ?? '') : '', 'job_title', @@ -181,7 +182,7 @@ class BadgeTextFormatter return $this->norm($v); } } else { - if (!empty($info[$key])) { + if (! empty($info[$key])) { $v = $this->norm((string) $info[$key]); if ($v !== '') { return $v; @@ -196,7 +197,7 @@ class BadgeTextFormatter public function firstExisting(array $paths): ?string { foreach ($paths as $path) { - if (!empty($path) && file_exists($path)) { + if (! empty($path) && file_exists($path)) { return $path; } } diff --git a/app/Services/Badges/BadgeUserLookupService.php b/app/Services/Badges/BadgeUserLookupService.php index f9ff53e6..6e501b32 100644 --- a/app/Services/Badges/BadgeUserLookupService.php +++ b/app/Services/Badges/BadgeUserLookupService.php @@ -9,8 +9,7 @@ class BadgeUserLookupService { public function __construct( protected BadgeTextFormatter $formatter - ) { - } + ) {} public function fetchStaffList(?string $schoolYear, array $selectedUserIds = []): array { @@ -25,7 +24,7 @@ class BadgeUserLookupService ]) ->groupBy('users.id', 'users.firstname', 'users.lastname'); - if (!empty($selectedUserIds)) { + if (! empty($selectedUserIds)) { $query->whereIn('users.id', $selectedUserIds); } @@ -65,7 +64,7 @@ class BadgeUserLookupService ]) ->where('tc.teacher_id', $userId); - if (!empty($schoolYear)) { + if (! empty($schoolYear)) { $query->where('tc.school_year', $schoolYear); } @@ -75,12 +74,12 @@ class BadgeUserLookupService ->orderByDesc('tc.id') ->first(); - if (!$row || empty($row->class_section_id)) { + if (! $row || empty($row->class_section_id)) { return null; } return [ - 'class_section_id' => (int) $row->class_section_id, + 'class_section_id' => (int) $row->class_section_id, 'class_section_name' => $row->class_section_name ?? null, ]; } @@ -92,12 +91,12 @@ class BadgeUserLookupService ->where('id', $id) ->first(); - if (!$u) { + if (! $u) { return null; } $userId = (int) $u->user_id; - $fullname = trim(($u->firstname ?? '') . ' ' . ($u->lastname ?? '')); + $fullname = trim(($u->firstname ?? '').' '.($u->lastname ?? '')); $schoolId = trim((string) ($u->school_id ?? '')); if ($schoolId === '') { $schoolId = trim((string) ($u->account_id ?? '')); @@ -124,7 +123,7 @@ class BadgeUserLookupService ->select(['class_section_id', 'updated_at', 'id', 'position', 'school_year']) ->where('teacher_id', $userId); - if (!empty($schoolYear)) { + if (! empty($schoolYear)) { $tcQuery->where('school_year', $schoolYear); } @@ -136,7 +135,7 @@ class BadgeUserLookupService $position = strtolower(trim((string) ($assignment->position ?? ''))); $className = null; - if (!empty($classId)) { + if (! empty($classId)) { $className = $this->resolveClassName((int) $classId); } @@ -144,7 +143,7 @@ class BadgeUserLookupService $roleLabel = 'Teacher Assistant'; } elseif ($position === 'teacher' || $position === 'main') { $roleLabel = 'Teacher'; - } elseif (!empty($allRoles)) { + } elseif (! empty($allRoles)) { $roleLabel = $allRoles[0]; } else { $roleLabel = 'Staff'; @@ -161,27 +160,27 @@ class BadgeUserLookupService ]); $jobTitle = ''; - if (!empty($roleLabel) && !empty($className)) { + if (! empty($roleLabel) && ! empty($className)) { $jobTitle = "{$roleLabel} - {$className}"; - } elseif (!empty($roleLabel)) { + } elseif (! empty($roleLabel)) { $jobTitle = $roleLabel; - } elseif (!empty($className)) { + } elseif (! empty($className)) { $jobTitle = $className; } return [ - 'user_id' => $userId, - 'name' => $fullname !== '' ? $fullname : 'STAFF', - 'role' => $roleLabel, - 'roles' => $rolesCsv, - 'class_section_id' => $classId ? (int) $classId : null, - 'class_section_name' => $className, - 'job_title' => $jobTitle, - 'school_name' => 'Al Rahma Sunday School', - 'school_id' => $schoolId, - 'school_logo' => $schoolLogo, - 'isgl_logo' => $isglLogo, - 'school_year' => $assignment->school_year ?? $schoolYear, + 'user_id' => $userId, + 'name' => $fullname !== '' ? $fullname : 'STAFF', + 'role' => $roleLabel, + 'roles' => $rolesCsv, + 'class_section_id' => $classId ? (int) $classId : null, + 'class_section_name' => $className, + 'job_title' => $jobTitle, + 'school_name' => 'Al Rahma Sunday School', + 'school_id' => $schoolId, + 'school_logo' => $schoolLogo, + 'isgl_logo' => $isglLogo, + 'school_year' => $assignment->school_year ?? $schoolYear, ]; } diff --git a/app/Services/Billing/BalanceCalculationService.php b/app/Services/Billing/BalanceCalculationService.php index 46ca0a0b..2d3a4a46 100644 --- a/app/Services/Billing/BalanceCalculationService.php +++ b/app/Services/Billing/BalanceCalculationService.php @@ -21,12 +21,10 @@ class BalanceCalculationService private FeeConfigService $config, private TuitionCalculationService $tuition, private BillingTotalsService $billingTotals - ) { - } + ) {} /** - * @param array> $students - * + * @param array> $students * @return array */ public function calculateFamilyAccount(array $students, int $parentId, ?string $schoolYear = null): array @@ -98,9 +96,8 @@ class BalanceCalculationService } /** - * @param array> $tuitionStudents - * @param array $eventByStudent - * + * @param array> $tuitionStudents + * @param array $eventByStudent * @return array> */ private function buildStudentBalances( diff --git a/app/Services/Billing/BillingTotalsService.php b/app/Services/Billing/BillingTotalsService.php index 92ce9a6c..b08188dc 100644 --- a/app/Services/Billing/BillingTotalsService.php +++ b/app/Services/Billing/BillingTotalsService.php @@ -96,7 +96,7 @@ class BillingTotalsService } /** - * @param array $rows + * @param array $rows */ private function sumAdditionalChargeRows(array $rows): float { diff --git a/app/Services/Billing/ChargeService.php b/app/Services/Billing/ChargeService.php index 6c2125ef..9167a7a3 100644 --- a/app/Services/Billing/ChargeService.php +++ b/app/Services/Billing/ChargeService.php @@ -26,8 +26,7 @@ class ChargeService private ExtraChargesInvoiceService $invoices, private ExtraChargesContextService $context, private InvoiceGenerationService $invoiceGeneration - ) { - } + ) {} public function createExtraCharge(array $payload): array { @@ -60,7 +59,7 @@ class ChargeService 'semester' => $payload['semester'] ?? $this->context->getSemester(), ])); - if (!($result['ok'] ?? false)) { + if (! ($result['ok'] ?? false)) { return $result; } @@ -70,14 +69,14 @@ class ChargeService 'id' => (int) ($result['id'] ?? 0), 'parent_id' => $parentId, 'invoice_id' => $result['invoice_id'] ?? null, - 'status' => !empty($payload['invoice_id']) ? 'applied' : 'pending', + 'status' => ! empty($payload['invoice_id']) ? 'applied' : 'pending', ]; } public function cancelExtraCharge(int $chargeId): array { $charge = AdditionalCharge::query()->find($chargeId); - if (!$charge) { + if (! $charge) { return ['ok' => false, 'message' => 'Extra charge not found.']; } @@ -99,7 +98,7 @@ class ChargeService public function applyExtraCharge(int $chargeId, int $invoiceId): array { $charge = AdditionalCharge::query()->find($chargeId); - if (!$charge) { + if (! $charge) { return ['ok' => false, 'message' => 'Extra charge not found.']; } @@ -165,7 +164,7 @@ class ChargeService $amount = isset($payload['amount']) ? (float) $payload['amount'] : null; if ($eventId > 0) { $event = Event::getEvent($eventId, $schoolYear); - if (!$event) { + if (! $event) { return ['ok' => false, 'message' => 'Event not found.']; } $eventName = (string) ($event->event_name ?? $eventName); @@ -225,7 +224,7 @@ class ChargeService public function cancelEventCharge(int $chargeId, bool $issueCredit = true): array { $charge = EventCharges::query()->find($chargeId); - if (!$charge) { + if (! $charge) { return ['ok' => false, 'message' => 'Event charge not found.']; } @@ -252,8 +251,8 @@ class ChargeService if ($issueCredit && $amount > 0 && $parentId > 0) { $credit = $this->createExtraCharge([ 'parent_id' => $parentId, - 'title' => 'Event credit: ' . $eventName, - 'description' => 'Account credit for canceled event charge #' . $chargeId, + 'title' => 'Event credit: '.$eventName, + 'description' => 'Account credit for canceled event charge #'.$chargeId, 'amount' => $amount, 'charge_type' => 'deduct', 'school_year' => $schoolYear, @@ -287,7 +286,7 @@ class ChargeService public function markEventChargePaid(int $chargeId, bool $paid, ?int $paymentId = null): array { $charge = EventCharges::query()->find($chargeId); - if (!$charge) { + if (! $charge) { return ['ok' => false, 'message' => 'Event charge not found.']; } @@ -389,7 +388,7 @@ class ChargeService private function resolveEventName(EventCharges $charge): string { - if (!empty($charge->event_name)) { + if (! empty($charge->event_name)) { return (string) $charge->event_name; } @@ -426,7 +425,7 @@ class ChargeService } /** - * @param array $row + * @param array $row */ private function formatEventCharge(array $row): array { diff --git a/app/Services/BroadcastEmail/BroadcastEmailComposerService.php b/app/Services/BroadcastEmail/BroadcastEmailComposerService.php index a0361dd3..cc58abcb 100644 --- a/app/Services/BroadcastEmail/BroadcastEmailComposerService.php +++ b/app/Services/BroadcastEmail/BroadcastEmailComposerService.php @@ -11,6 +11,7 @@ class BroadcastEmailComposerService $html = preg_replace('#<(script|iframe)[^>]*>.*?#is', '', $html); $html = preg_replace('/\\son\\w+="[^"]*"/i', '', $html); $html = preg_replace("/\\son\\w+='[^']*'/i", '', $html); + return $html ?? ''; } @@ -26,7 +27,7 @@ class BroadcastEmailComposerService ): string { $content = $personalize ? str_replace('{{name}}', $recipientName, $body) : $body; - if (!$wrapLayout) { + if (! $wrapLayout) { return $content; } diff --git a/app/Services/BroadcastEmail/BroadcastEmailDispatchService.php b/app/Services/BroadcastEmail/BroadcastEmailDispatchService.php index 5962317c..a7f2c3f9 100644 --- a/app/Services/BroadcastEmail/BroadcastEmailDispatchService.php +++ b/app/Services/BroadcastEmail/BroadcastEmailDispatchService.php @@ -7,6 +7,7 @@ use App\Services\EmailService; class BroadcastEmailDispatchService { private EmailService $mailer; + private BroadcastEmailComposerService $composer; public function __construct(EmailService $mailer, BroadcastEmailComposerService $composer) @@ -30,7 +31,7 @@ class BroadcastEmailDispatchService $ok = $this->mailer->send( $payload['test_email'], - '[TEST] ' . $payload['subject'], + '[TEST] '.$payload['subject'], $html, $payload['from_key'] ); diff --git a/app/Services/BroadcastEmail/BroadcastEmailImageService.php b/app/Services/BroadcastEmail/BroadcastEmailImageService.php index eddac183..255f2026 100644 --- a/app/Services/BroadcastEmail/BroadcastEmailImageService.php +++ b/app/Services/BroadcastEmail/BroadcastEmailImageService.php @@ -26,7 +26,7 @@ class BroadcastEmailImageService } $mime = strtolower((string) $file->getMimeType()); - if (!isset($this->allowedTypes[$mime])) { + if (! isset($this->allowedTypes[$mime])) { return [ 'ok' => false, 'status' => 415, @@ -43,17 +43,17 @@ class BroadcastEmailImageService } $targetDir = public_path('uploads/email'); - if (!is_dir($targetDir)) { + if (! is_dir($targetDir)) { @mkdir($targetDir, 0755, true); } $ext = $this->allowedTypes[$mime]; - $newName = 'em_' . bin2hex(random_bytes(16)) . '.' . $ext; + $newName = 'em_'.bin2hex(random_bytes(16)).'.'.$ext; $file->move($targetDir, $newName); return [ 'ok' => true, - 'url' => asset('uploads/email/' . $newName), + 'url' => asset('uploads/email/'.$newName), ]; } } diff --git a/app/Services/BroadcastEmail/BroadcastEmailRecipientService.php b/app/Services/BroadcastEmail/BroadcastEmailRecipientService.php index 0649ef0a..10cd1630 100644 --- a/app/Services/BroadcastEmail/BroadcastEmailRecipientService.php +++ b/app/Services/BroadcastEmail/BroadcastEmailRecipientService.php @@ -13,6 +13,7 @@ class BroadcastEmailRecipientService return array_values(array_filter($parents, static function ($parent) { $email = $parent['email'] ?? ''; + return is_string($email) && trim($email) !== ''; })); } diff --git a/app/Services/BroadcastEmail/BroadcastEmailSenderOptionsService.php b/app/Services/BroadcastEmail/BroadcastEmailSenderOptionsService.php index 294458b8..dde00441 100644 --- a/app/Services/BroadcastEmail/BroadcastEmailSenderOptionsService.php +++ b/app/Services/BroadcastEmail/BroadcastEmailSenderOptionsService.php @@ -9,12 +9,13 @@ class BroadcastEmailSenderOptionsService $json = getenv('MAIL_SENDERS') ?: env('MAIL_SENDERS', '{}'); $arr = json_decode($json, true); - if (!is_array($arr) || empty($arr)) { + if (! is_array($arr) || empty($arr)) { $smtpUser = getenv('SMTP_USER') ?: ''; $name = 'Al Rahma Sunday School'; + return [[ 'key' => 'general', - 'label' => $name . ($smtpUser ? " <{$smtpUser}>" : ''), + 'label' => $name.($smtpUser ? " <{$smtpUser}>" : ''), ]]; } @@ -24,7 +25,7 @@ class BroadcastEmailSenderOptionsService $email = $info['email'] ?? ''; $out[] = [ 'key' => (string) $key, - 'label' => trim($name . ($email ? " <{$email}>" : '')), + 'label' => trim($name.($email ? " <{$email}>" : '')), ]; } diff --git a/app/Services/Certificates/CertificateAdminService.php b/app/Services/Certificates/CertificateAdminService.php index 52e8668d..82540f38 100644 --- a/app/Services/Certificates/CertificateAdminService.php +++ b/app/Services/Certificates/CertificateAdminService.php @@ -4,7 +4,6 @@ declare(strict_types=1); namespace App\Services\Certificates; -use App\Models\CertificateRecord; use App\Models\Configuration; use App\Models\StudentDecision; use Illuminate\Support\Facades\DB; @@ -641,6 +640,7 @@ class CertificateAdminService if (preg_match('/grade\s*(\d+)/i', $name, $matches) === 1) { $grade = (int) $matches[1]; + return [ 'Grade '.$grade, '1_'.str_pad((string) $grade, 3, '0', STR_PAD_LEFT), diff --git a/app/Services/Certificates/CertificatePdfService.php b/app/Services/Certificates/CertificatePdfService.php index 8bc5cce2..c07690f9 100644 --- a/app/Services/Certificates/CertificatePdfService.php +++ b/app/Services/Certificates/CertificatePdfService.php @@ -7,12 +7,13 @@ namespace App\Services\Certificates; class CertificatePdfService { private string $fontDir; + private string $imgDir; public function __construct() { - $this->fontDir = public_path('assets/certificates/fonts') . DIRECTORY_SEPARATOR; - $this->imgDir = public_path('assets/certificates/images') . DIRECTORY_SEPARATOR; + $this->fontDir = public_path('assets/certificates/fonts').DIRECTORY_SEPARATOR; + $this->imgDir = public_path('assets/certificates/images').DIRECTORY_SEPARATOR; } /** @@ -20,9 +21,9 @@ class CertificatePdfService */ public function generate(array $students, string $certDate): string { - $edwardianFont = \TCPDF_FONTS::addTTFfont($this->fontDir . 'Edwardian Script ITC Regular.ttf', 'TrueTypeUnicode', '', 32); - $garamondBold = \TCPDF_FONTS::addTTFfont($this->fontDir . 'Garamond Bold.ttf', 'TrueTypeUnicode', '', 32); - $ebGaramond = \TCPDF_FONTS::addTTFfont($this->fontDir . 'EBGaramond-Regular.ttf', 'TrueTypeUnicode', '', 32); + $edwardianFont = \TCPDF_FONTS::addTTFfont($this->fontDir.'Edwardian Script ITC Regular.ttf', 'TrueTypeUnicode', '', 32); + $garamondBold = \TCPDF_FONTS::addTTFfont($this->fontDir.'Garamond Bold.ttf', 'TrueTypeUnicode', '', 32); + $ebGaramond = \TCPDF_FONTS::addTTFfont($this->fontDir.'EBGaramond-Regular.ttf', 'TrueTypeUnicode', '', 32); $pdf = new \TCPDF('L', 'pt', 'A4', true, 'UTF-8', false); $pdf->SetCreator('Al Rahma Sunday School'); @@ -39,7 +40,7 @@ class CertificatePdfService foreach ($students as $student) { $pdf->AddPage(); - $name = $student['firstname'] . ' ' . $student['lastname']; + $name = $student['firstname'].' '.$student['lastname']; $grade = $this->formatGrade((string) ($student['grade'] ?? '')); $certNumber = (string) ($student['cert_number'] ?? ''); $verifyUrl = is_string($student['verify_url'] ?? null) ? $student['verify_url'] : null; @@ -64,8 +65,8 @@ class CertificatePdfService private function drawCertificate( \TCPDF $pdf, - float $W, - float $H, + float $W, + float $H, string $name, string $grade, string $certDate, @@ -75,9 +76,9 @@ class CertificatePdfService string $garamondBold, string $ebGaramond ): void { - $pdf->Image($this->imgDir . 'title.png', 126, 0, 600); - $pdf->Image($this->imgDir . 'background.png', 280, 176, 291, 340); - $pdf->Image($this->imgDir . 'signature.png', 140, 410, 90, 80); + $pdf->Image($this->imgDir.'title.png', 126, 0, 600); + $pdf->Image($this->imgDir.'background.png', 280, 176, 291, 340); + $pdf->Image($this->imgDir.'signature.png', 140, 410, 90, 80); $pdf->SetFont('times', 'B', 24); $pdf->SetTextColor(0, 0, 0); @@ -135,7 +136,7 @@ class CertificatePdfService $pdf->SetFont('helvetica', '', 8); $pdf->SetTextColor(150, 150, 150); $pdf->SetXY(70.86, $H - 39.68); - $pdf->Cell(200, 12, 'Certificate No. ' . $certNumber, 0, 0, 'L'); + $pdf->Cell(200, 12, 'Certificate No. '.$certNumber, 0, 0, 'L'); $pdf->SetTextColor(0, 0, 0); } } @@ -161,7 +162,7 @@ class CertificatePdfService $lower = strtolower($clean); if (preg_match('/^\d+$/', $clean) && (int) $clean >= 1 && (int) $clean <= 9) { - return 'Grade ' . $clean; + return 'Grade '.$clean; } if ($lower === 'youth') { return 'Youth'; diff --git a/app/Services/ClassPreparation/ClassPreparationAdjustmentWriterService.php b/app/Services/ClassPreparation/ClassPreparationAdjustmentWriterService.php index fb2b0942..83d75c7e 100644 --- a/app/Services/ClassPreparation/ClassPreparationAdjustmentWriterService.php +++ b/app/Services/ClassPreparation/ClassPreparationAdjustmentWriterService.php @@ -16,7 +16,7 @@ class ClassPreparationAdjustmentWriterService foreach ($adjustments as $itemName => $adjustment) { $item = (string) $itemName; - if ($item === '' || !isset($allowed[$item])) { + if ($item === '' || ! isset($allowed[$item])) { continue; } @@ -32,6 +32,7 @@ class ClassPreparationAdjustmentWriterService 'adjustable' => 1, ]); $count++; + continue; } diff --git a/app/Services/ClassPreparation/ClassPreparationCalculatorService.php b/app/Services/ClassPreparation/ClassPreparationCalculatorService.php index 8b23c58e..96ff72dc 100644 --- a/app/Services/ClassPreparation/ClassPreparationCalculatorService.php +++ b/app/Services/ClassPreparation/ClassPreparationCalculatorService.php @@ -37,6 +37,7 @@ class ClassPreparationCalculatorService if ($numValue > 0 && $numValue < 30) { $firstDigit = (int) substr((string) $numValue, 0, 1); + return $firstDigit === 1 ? 1 : ($firstDigit === 2 ? 2 : 3); } @@ -69,10 +70,12 @@ class ClassPreparationCalculatorService if ($gradeMin !== null && $classLevel < (int) $gradeMin) { $items[$name] = 0; + continue; } if ($gradeMax !== null && $classLevel > (int) $gradeMax) { $items[$name] = 0; + continue; } diff --git a/app/Services/ClassPreparation/ClassPreparationContextService.php b/app/Services/ClassPreparation/ClassPreparationContextService.php index a3d80999..84591cdc 100644 --- a/app/Services/ClassPreparation/ClassPreparationContextService.php +++ b/app/Services/ClassPreparation/ClassPreparationContextService.php @@ -39,6 +39,7 @@ class ClassPreparationContextService ->count(); $this->rosterPresenceCache[$key] = $cnt > 0; + return $this->rosterPresenceCache[$key]; } } diff --git a/app/Services/ClassPreparation/ClassPreparationInventoryService.php b/app/Services/ClassPreparation/ClassPreparationInventoryService.php index c28c847a..2f443429 100644 --- a/app/Services/ClassPreparation/ClassPreparationInventoryService.php +++ b/app/Services/ClassPreparation/ClassPreparationInventoryService.php @@ -50,7 +50,7 @@ class ClassPreparationInventoryService } $needsFallback = array_filter($inventoryMap, static fn ($v) => (int) $v === 0); - if (!empty($needsFallback)) { + if (! empty($needsFallback)) { $fallbackRows = DB::table('inventory_items') ->select('name AS item_name, COALESCE(SUM(CASE WHEN good_qty IS NOT NULL AND good_qty > 0 THEN good_qty WHEN `condition`=\"good\" THEN quantity ELSE quantity END),0) AS available') ->where('type', 'classroom') @@ -79,6 +79,7 @@ class ClassPreparationInventoryService if ($goodQty > 0) { $inventoryMap[$name] = $goodQty; + continue; } diff --git a/app/Services/ClassPreparation/ClassPreparationLogService.php b/app/Services/ClassPreparation/ClassPreparationLogService.php index 23d51b96..42ff6c25 100644 --- a/app/Services/ClassPreparation/ClassPreparationLogService.php +++ b/app/Services/ClassPreparation/ClassPreparationLogService.php @@ -33,6 +33,7 @@ class ClassPreparationLogService 'prep_data' => json_encode($items), 'created_at' => $createdAt, ]); + return true; } catch (\Throwable $e) { return false; diff --git a/app/Services/ClassPreparation/ClassPreparationPrintService.php b/app/Services/ClassPreparation/ClassPreparationPrintService.php index ae9128f2..d1e06c45 100644 --- a/app/Services/ClassPreparation/ClassPreparationPrintService.php +++ b/app/Services/ClassPreparation/ClassPreparationPrintService.php @@ -13,8 +13,7 @@ class ClassPreparationPrintService private ClassPreparationCalculatorService $calculator, private ClassPreparationAdjustmentService $adjustments, private ClassPreparationLogService $logs - ) { - } + ) {} public function printPrep(string $classSectionId, string $schoolYear, ?string $semester = null): array { @@ -31,7 +30,7 @@ class ClassPreparationPrintService $printedAt = $this->utcNow(); $ok = $this->logs->createLog($classSectionId, $className, $schoolYear, $items, $printedAt); - if (!$ok) { + if (! $ok) { Log::warning('Failed to store class preparation log.', [ 'class_section_id' => $classSectionId, 'school_year' => $schoolYear, @@ -55,6 +54,7 @@ class ClassPreparationPrintService if (function_exists('utc_now')) { return (string) utc_now(); } + return now('UTC')->toDateTimeString(); } } diff --git a/app/Services/ClassPreparation/ClassPreparationRosterService.php b/app/Services/ClassPreparation/ClassPreparationRosterService.php index 3332d00b..16c16a12 100644 --- a/app/Services/ClassPreparation/ClassPreparationRosterService.php +++ b/app/Services/ClassPreparation/ClassPreparationRosterService.php @@ -35,6 +35,7 @@ class ClassPreparationRosterService } $row = $query->first(); + return (int) ($row->cnt ?? 0); } } diff --git a/app/Services/ClassPreparation/ClassPreparationService.php b/app/Services/ClassPreparation/ClassPreparationService.php index d24e210e..3a047a14 100644 --- a/app/Services/ClassPreparation/ClassPreparationService.php +++ b/app/Services/ClassPreparation/ClassPreparationService.php @@ -7,12 +7,19 @@ use App\Models\ClassSection; class ClassPreparationService { private ClassPreparationContextService $context; + private ClassPreparationRosterService $roster; + private ClassPreparationInventoryService $inventory; + private ClassPreparationCalculatorService $calculator; + private ClassPreparationAdjustmentService $adjustments; + private ClassPreparationLogService $logs; + private ClassPreparationAdjustmentWriterService $adjustmentWriter; + private ClassPreparationPrintService $printService; public function __construct( @@ -66,14 +73,14 @@ class ClassPreparationService $hasChanged = $this->logs->hasPrepChanged($baseItems, $oldPrep); $results[] = [ - 'class_section' => $className, + 'class_section' => $className, 'class_section_id' => $classSectionId, - 'student_count' => $studentCount, - 'class_level' => $classLevel, - 'prep_items' => $baseItems, - 'needs_print' => $hasChanged, - 'last_printed_at' => $oldSnap?->created_at, - 'adjustments' => $adjMap, + 'student_count' => $studentCount, + 'class_level' => $classLevel, + 'prep_items' => $baseItems, + 'needs_print' => $hasChanged, + 'last_printed_at' => $oldSnap?->created_at, + 'adjustments' => $adjMap, ]; } @@ -139,6 +146,7 @@ class ClassPreparationService if (function_exists('utc_now')) { return (string) utc_now(); } + return now('UTC')->toDateTimeString(); } } diff --git a/app/Services/ClassProgress/ClassProgressAttachmentService.php b/app/Services/ClassProgress/ClassProgressAttachmentService.php index b5b8559c..bf849b42 100644 --- a/app/Services/ClassProgress/ClassProgressAttachmentService.php +++ b/app/Services/ClassProgress/ClassProgressAttachmentService.php @@ -5,7 +5,6 @@ namespace App\Services\ClassProgress; use App\Models\ClassProgressAttachment; use Illuminate\Http\UploadedFile; use Illuminate\Support\Facades\Log; -use Illuminate\Support\Facades\Storage; class ClassProgressAttachmentService { @@ -17,7 +16,7 @@ class ClassProgressAttachmentService $stored = []; foreach ($files as $file) { - if (!$file instanceof UploadedFile || !$file->isValid()) { + if (! $file instanceof UploadedFile || ! $file->isValid()) { continue; } @@ -49,17 +48,17 @@ class ClassProgressAttachmentService $candidates = []; if (str_starts_with($path, 'storage/')) { $relative = ltrim(substr($path, strlen('storage/')), '/'); - $candidates[] = storage_path('app/public/' . $relative); + $candidates[] = storage_path('app/public/'.$relative); } if (str_starts_with($path, 'writable/uploads/')) { $relative = ltrim(substr($path, strlen('writable/uploads/')), '/'); - $candidates[] = storage_path('app/' . $relative); - $candidates[] = storage_path('app/public/' . $relative); + $candidates[] = storage_path('app/'.$relative); + $candidates[] = storage_path('app/public/'.$relative); } - $candidates[] = storage_path('app/' . ltrim($path, '/')); - $candidates[] = storage_path('app/public/' . ltrim($path, '/')); + $candidates[] = storage_path('app/'.ltrim($path, '/')); + $candidates[] = storage_path('app/public/'.ltrim($path, '/')); foreach ($candidates as $candidate) { if (is_file($candidate)) { @@ -84,6 +83,6 @@ class ClassProgressAttachmentService throw $e; } - return 'storage/' . ltrim($path, '/'); + return 'storage/'.ltrim($path, '/'); } } diff --git a/app/Services/ClassProgress/ClassProgressMetaService.php b/app/Services/ClassProgress/ClassProgressMetaService.php index 8b016aeb..bc04e930 100644 --- a/app/Services/ClassProgress/ClassProgressMetaService.php +++ b/app/Services/ClassProgress/ClassProgressMetaService.php @@ -140,7 +140,7 @@ class ClassProgressMetaService public function curriculumOptions(?int $classId, ?string $schoolYear = null): array { - if (!$classId) { + if (! $classId) { return []; } diff --git a/app/Services/ClassProgress/ClassProgressMutationService.php b/app/Services/ClassProgress/ClassProgressMutationService.php index 86602d0f..7bcaf3e6 100644 --- a/app/Services/ClassProgress/ClassProgressMutationService.php +++ b/app/Services/ClassProgress/ClassProgressMutationService.php @@ -7,10 +7,11 @@ use App\Models\ClassProgressReport; use App\Models\TeacherClass; use App\Models\User; use App\Services\SchoolYears\SchoolYearContextService; -use Illuminate\Support\Facades\Schema; +use Carbon\CarbonInterface; use Illuminate\Support\Collection; use Illuminate\Support\Facades\DB; use Illuminate\Support\Facades\Log; +use Illuminate\Support\Facades\Schema; class ClassProgressMutationService { @@ -34,11 +35,11 @@ class ClassProgressMutationService $weekStart = (string) ($payload['week_start'] ?? ''); $weekEnd = $this->rules->ensureWeekEnd($weekStart, $payload['week_end'] ?? null); - if (!$this->rules->isWeekEndValid($weekStart, $weekEnd)) { + if (! $this->rules->isWeekEndValid($weekStart, $weekEnd)) { throw new \InvalidArgumentException('Week end must be the same as or after the week start.'); } - if (!$this->rules->hasIslamicUnitSelection($payload)) { + if (! $this->rules->hasIslamicUnitSelection($payload)) { throw new \InvalidArgumentException('Please select at least one Islamic Studies unit.'); } @@ -53,7 +54,7 @@ class ClassProgressMutationService ->values() ->all(); - if ($existingIds !== [] && !$confirmOverwrite) { + if ($existingIds !== [] && ! $confirmOverwrite) { throw new \InvalidArgumentException('CONFIRM_OVERWRITE'); } @@ -150,7 +151,7 @@ class ClassProgressMutationService throw new \InvalidArgumentException('Please select at least one Islamic Studies unit.'); } - $originalWeekStart = $anchor->week_start instanceof \Carbon\CarbonInterface + $originalWeekStart = $anchor->week_start instanceof CarbonInterface ? $anchor->week_start->format('Y-m-d') : (string) $anchor->week_start; @@ -295,7 +296,7 @@ class ClassProgressMutationService return DB::transaction(function () use ($report, $payload, $attachments) { $weekStart = $payload['week_start'] ?? $report->week_start?->format('Y-m-d'); $weekEnd = $this->rules->ensureWeekEnd((string) $weekStart, $payload['week_end'] ?? null); - if ($weekStart && $weekEnd && !$this->rules->isWeekEndValid((string) $weekStart, (string) $weekEnd)) { + if ($weekStart && $weekEnd && ! $this->rules->isWeekEndValid((string) $weekStart, (string) $weekEnd)) { throw new \InvalidArgumentException('Week end must be the same as or after the week start.'); } @@ -364,7 +365,7 @@ class ClassProgressMutationService ->where('class_section_id', $classSectionId) ->exists(); - if (!$assigned) { + if (! $assigned) { throw new \InvalidArgumentException('No class assignment found for this report.'); } } diff --git a/app/Services/ClassProgress/ClassProgressQueryService.php b/app/Services/ClassProgress/ClassProgressQueryService.php index c3d4aa50..24c9ded0 100644 --- a/app/Services/ClassProgress/ClassProgressQueryService.php +++ b/app/Services/ClassProgress/ClassProgressQueryService.php @@ -8,8 +8,8 @@ use App\Models\Configuration; use App\Models\TeacherClass; use App\Models\User; use App\Services\SchoolYears\SchoolYearContextService; -use Illuminate\Support\Facades\Schema; use Illuminate\Contracts\Pagination\LengthAwarePaginator; +use Illuminate\Support\Facades\Schema; class ClassProgressQueryService { @@ -28,7 +28,7 @@ class ClassProgressQueryService ->with(['classSection', 'teacher']) ->orderBy($filters['sort_by'] ?? 'week_start', $filters['sort_dir'] ?? 'desc'); - if (!$this->isAdmin($user)) { + if (! $this->isAdmin($user)) { $relaxedSectionIds = TeacherClass::distinctSectionIdsForTeacher((int) $user->id); if (! empty($filters['class_section_id'])) { @@ -50,31 +50,31 @@ class ClassProgressQueryService $query->where('teacher_id', (int) $filters['teacher_id']); } - if (!empty($filters['class_section_id'])) { + if (! empty($filters['class_section_id'])) { $query->where('class_section_id', (int) $filters['class_section_id']); } - if (!empty($filters['subject'])) { + if (! empty($filters['subject'])) { $query->where('subject', (string) $filters['subject']); } - if (!empty($filters['status'])) { + if (! empty($filters['status'])) { $query->where('status', (string) $filters['status']); } - if (!empty($filters['school_year']) && Schema::hasColumn('class_progress_reports', 'school_year')) { + if (! empty($filters['school_year']) && Schema::hasColumn('class_progress_reports', 'school_year')) { $query->where('school_year', (string) $filters['school_year']); } - if (!empty($filters['semester']) && Schema::hasColumn('class_progress_reports', 'semester')) { + if (! empty($filters['semester']) && Schema::hasColumn('class_progress_reports', 'semester')) { $query->where('semester', (string) $filters['semester']); } - if (!empty($filters['week_start'])) { + if (! empty($filters['week_start'])) { $query->whereDate('week_start', '>=', $filters['week_start']); } - if (!empty($filters['week_end'])) { + if (! empty($filters['week_end'])) { $query->whereDate('week_end', '<=', $filters['week_end']); } @@ -114,6 +114,7 @@ class ClassProgressQueryService return $rows->map(function (ClassProgressReport $row) use ($attachments) { $row->setAttribute('status_label', $this->statusLabel($row->status)); $row->setAttribute('attachments', $attachments[$row->id] ?? []); + return $row; })->all(); } @@ -159,6 +160,7 @@ class ClassProgressQueryService private function statusLabel(?string $status): string { $options = (array) config('progress.status_options', []); + return $options[$status] ?? 'Unknown'; } diff --git a/app/Services/ClassProgress/ClassProgressRuleService.php b/app/Services/ClassProgress/ClassProgressRuleService.php index 99e9c154..3ca3c9ad 100644 --- a/app/Services/ClassProgress/ClassProgressRuleService.php +++ b/app/Services/ClassProgress/ClassProgressRuleService.php @@ -15,6 +15,7 @@ class ClassProgressRuleService public function normalizeFlags($flags): ?array { $values = array_values(array_filter((array) $flags, static fn ($item) => $item !== '' && $item !== null)); + return $values === [] ? null : $values; } @@ -80,6 +81,7 @@ class ClassProgressRuleService try { $dt = new \DateTime($weekStart); $dt->modify('+6 days'); + return $dt->format('Y-m-d'); } catch (\Exception $e) { return $weekStart; diff --git a/app/Services/ClassSections/ClassAttendanceService.php b/app/Services/ClassSections/ClassAttendanceService.php index 46e28922..3b09af7b 100644 --- a/app/Services/ClassSections/ClassAttendanceService.php +++ b/app/Services/ClassSections/ClassAttendanceService.php @@ -15,7 +15,7 @@ class ClassAttendanceService $semester = $semester ?: (string) (Configuration::getConfig('semester') ?? ''); $teacher = User::query()->find($teacherId); - $teacherName = trim((string) ($teacher?->firstname ?? '') . ' ' . (string) ($teacher?->lastname ?? '')); + $teacherName = trim((string) ($teacher?->firstname ?? '').' '.(string) ($teacher?->lastname ?? '')); $className = ClassSection::getClassSectionNameBySectionId($classSectionId) ?? (string) $classSectionId; $students = Student::getStudentInfoByClassSectionId($classSectionId, $semester, $schoolYear, $teacherId); diff --git a/app/Services/ClassSections/ClassSectionQueryService.php b/app/Services/ClassSections/ClassSectionQueryService.php index 1fc9e901..52e43933 100644 --- a/app/Services/ClassSections/ClassSectionQueryService.php +++ b/app/Services/ClassSections/ClassSectionQueryService.php @@ -13,27 +13,27 @@ class ClassSectionQueryService ->leftJoin('classes', 'classSection.class_id', '=', 'classes.id') ->select('classSection.*', 'classes.class_name'); - if (!empty($filters['search'])) { - $term = '%' . strtolower((string) $filters['search']) . '%'; + if (! empty($filters['search'])) { + $term = '%'.strtolower((string) $filters['search']).'%'; $query->where(function ($q) use ($term) { $q->whereRaw('LOWER(classSection.class_section_name) LIKE ?', [$term]) ->orWhereRaw('LOWER(classes.class_name) LIKE ?', [$term]); }); } - if (!empty($filters['class_id'])) { + if (! empty($filters['class_id'])) { $query->where('classSection.class_id', (int) $filters['class_id']); } - if (!empty($filters['school_year'])) { + if (! empty($filters['school_year'])) { $query->where('classSection.school_year', (string) $filters['school_year']); } - if (!empty($filters['semester'])) { + if (! empty($filters['semester'])) { $query->where('classSection.semester', (string) $filters['semester']); } - if (!empty($filters['with_students'])) { + if (! empty($filters['with_students'])) { $query->whereExists(function ($sub) use ($filters) { $sub->selectRaw('1') ->from('student_class as sc') @@ -41,7 +41,7 @@ class ClassSectionQueryService ->whereColumn('sc.class_section_id', 'classSection.class_section_id') ->where('s.is_active', 1); - if (!empty($filters['school_year'])) { + if (! empty($filters['school_year'])) { $sub->where('sc.school_year', (string) $filters['school_year']); } }); diff --git a/app/Services/Communication/CommunicationFamilyService.php b/app/Services/Communication/CommunicationFamilyService.php index af849bb0..18bec187 100644 --- a/app/Services/Communication/CommunicationFamilyService.php +++ b/app/Services/Communication/CommunicationFamilyService.php @@ -51,7 +51,7 @@ class CommunicationFamilyService } $names = array_map(static function ($row) { - return trim(($row['firstname'] ?? '') . ' ' . ($row['lastname'] ?? '')); + return trim(($row['firstname'] ?? '').' '.($row['lastname'] ?? '')); }, $rows); return implode(' & ', $names); diff --git a/app/Services/Communication/CommunicationLogService.php b/app/Services/Communication/CommunicationLogService.php index f6bca614..4ff6f3a3 100644 --- a/app/Services/Communication/CommunicationLogService.php +++ b/app/Services/Communication/CommunicationLogService.php @@ -9,7 +9,7 @@ class CommunicationLogService { public function log(array $payload): void { - if (!Schema::hasTable('communication_logs')) { + if (! Schema::hasTable('communication_logs')) { return; } diff --git a/app/Services/Communication/CommunicationPreviewService.php b/app/Services/Communication/CommunicationPreviewService.php index 0539b745..f6838f58 100644 --- a/app/Services/Communication/CommunicationPreviewService.php +++ b/app/Services/Communication/CommunicationPreviewService.php @@ -5,7 +5,9 @@ namespace App\Services\Communication; class CommunicationPreviewService { private CommunicationTemplateService $templates; + private CommunicationStudentService $students; + private CommunicationFamilyService $families; public function __construct( @@ -26,7 +28,7 @@ class CommunicationPreviewService string $teacherName ): array { $template = $this->templates->findTemplateByKey($templateKey); - if (!$template) { + if (! $template) { return [ 'ok' => false, 'status' => 404, @@ -35,7 +37,7 @@ class CommunicationPreviewService } $student = $this->students->getStudentBasic($studentId); - if (!$student) { + if (! $student) { return [ 'ok' => false, 'status' => 404, @@ -46,7 +48,7 @@ class CommunicationPreviewService $salutation = $this->families->guardianSalutation($familyId); $autoVars = [ - 'student_fullname' => trim(($student['firstname'] ?? '') . ' ' . ($student['lastname'] ?? '')), + 'student_fullname' => trim(($student['firstname'] ?? '').' '.($student['lastname'] ?? '')), 'student_grade' => $student['grade'] ?? '', 'parent_salutation' => $salutation, 'date' => $this->formatLocalDate('Y-m-d'), @@ -70,6 +72,7 @@ class CommunicationPreviewService { return (string) preg_replace_callback('/\\{\\{\\s*([a-zA-Z0-9_\\.]+)\\s*\\}\\}/', function ($m) use ($vars) { $key = $m[1]; + return htmlspecialchars((string) ($vars[$key] ?? ''), ENT_QUOTES, 'UTF-8'); }, $template); } @@ -79,6 +82,7 @@ class CommunicationPreviewService if (function_exists('local_date') && function_exists('utc_now')) { return (string) local_date(utc_now(), $format); } + return now()->format($format); } } diff --git a/app/Services/Communication/CommunicationSendService.php b/app/Services/Communication/CommunicationSendService.php index c045a9a8..9041e053 100644 --- a/app/Services/Communication/CommunicationSendService.php +++ b/app/Services/Communication/CommunicationSendService.php @@ -7,6 +7,7 @@ use App\Services\EmailService; class CommunicationSendService { private EmailService $mailer; + private CommunicationLogService $logService; public function __construct(EmailService $mailer, CommunicationLogService $logService) @@ -61,7 +62,7 @@ class CommunicationSendService $sendOk = false; } - if (!$sendOk && $error === null) { + if (! $sendOk && $error === null) { $error = 'Mailer returned false'; } @@ -90,6 +91,7 @@ class CommunicationSendService private function normalizeEmails(array $values): array { $out = array_values(array_unique(array_filter(array_map('strval', $values)))); + return array_values(array_filter($out, static function ($email) { return $email !== ''; })); diff --git a/app/Services/Communication/CommunicationStudentService.php b/app/Services/Communication/CommunicationStudentService.php index 00c59790..164d8e6e 100644 --- a/app/Services/Communication/CommunicationStudentService.php +++ b/app/Services/Communication/CommunicationStudentService.php @@ -33,13 +33,13 @@ class CommunicationStudentService ->orderBy('sc.id', 'desc') ->first(); - if (!$row) { + if (! $row) { return null; } $row = (array) $row; $grade = $row['registration_grade'] ?? ''; - if (!$grade && !empty($row['class_section_name'])) { + if (! $grade && ! empty($row['class_section_name'])) { $grade = $row['class_section_name']; } diff --git a/app/Services/Communication/CommunicationTemplateService.php b/app/Services/Communication/CommunicationTemplateService.php index 372c8da6..7f1663f8 100644 --- a/app/Services/Communication/CommunicationTemplateService.php +++ b/app/Services/Communication/CommunicationTemplateService.php @@ -17,6 +17,7 @@ class CommunicationTemplateService ->get() ->map(function ($row) use ($keyColumn, $bodyColumn) { $row = (array) $row; + return [ 'template_key' => (string) ($row[$keyColumn] ?? ''), 'subject' => (string) ($row['subject'] ?? ''), @@ -35,7 +36,7 @@ class CommunicationTemplateService ->where('is_active', 1) ->first(); - if (!$row) { + if (! $row) { return null; } @@ -52,6 +53,7 @@ class CommunicationTemplateService { $keyColumn = Schema::hasColumn('email_templates', 'template_key') ? 'template_key' : 'code'; $bodyColumn = Schema::hasColumn('email_templates', 'body') ? 'body' : 'body_html'; + return [$keyColumn, $bodyColumn]; } } diff --git a/app/Services/CompetitionScores/CompetitionScoresQueryService.php b/app/Services/CompetitionScores/CompetitionScoresQueryService.php index e59d0adf..6cc18268 100644 --- a/app/Services/CompetitionScores/CompetitionScoresQueryService.php +++ b/app/Services/CompetitionScores/CompetitionScoresQueryService.php @@ -42,7 +42,7 @@ class CompetitionScoresQueryService public function getQuestionCounts(array $competitionIds, int $classSectionId): array { - if (!$this->hasQuestionCount() || empty($competitionIds)) { + if (! $this->hasQuestionCount() || empty($competitionIds)) { return []; } @@ -93,7 +93,7 @@ class CompetitionScoresQueryService public function getQuestionCountForCompetition(int $competitionId, int $classSectionId): ?int { - if (!$this->hasQuestionCount()) { + if (! $this->hasQuestionCount()) { return null; } diff --git a/app/Services/CompetitionScores/CompetitionScoresRosterService.php b/app/Services/CompetitionScores/CompetitionScoresRosterService.php index 06727500..4c849c48 100644 --- a/app/Services/CompetitionScores/CompetitionScoresRosterService.php +++ b/app/Services/CompetitionScores/CompetitionScoresRosterService.php @@ -35,6 +35,7 @@ class CompetitionScoresRosterService } $row = (array) $builder->first(); + return (int) ($row['total'] ?? 0); } @@ -46,7 +47,7 @@ class CompetitionScoresRosterService $builder = DB::table('students as s') ->select('s.id', 's.school_id', 's.firstname', 's.lastname') - ->join($this->classStudentTable . ' as cs', 'cs.student_id', '=', 's.id') + ->join($this->classStudentTable.' as cs', 'cs.student_id', '=', 's.id') ->where('cs.class_section_id', $classSectionId); if (Schema::hasColumn($this->classStudentTable, 'school_year')) { diff --git a/app/Services/CompetitionScores/CompetitionScoresSaveService.php b/app/Services/CompetitionScores/CompetitionScoresSaveService.php index 5d0bf68a..f27ed494 100644 --- a/app/Services/CompetitionScores/CompetitionScoresSaveService.php +++ b/app/Services/CompetitionScores/CompetitionScoresSaveService.php @@ -16,8 +16,9 @@ class CompetitionScoresSaveService if ($scoreValue === '') { continue; } - if (!preg_match('/^\\d+$/', $scoreValue)) { + if (! preg_match('/^\\d+$/', $scoreValue)) { $invalidScores[] = (int) $studentId; + continue; } $cleanScores[(int) $studentId] = (int) $scoreValue; diff --git a/app/Services/Dashboard/DashboardRouteService.php b/app/Services/Dashboard/DashboardRouteService.php index e5074d16..beba360c 100644 --- a/app/Services/Dashboard/DashboardRouteService.php +++ b/app/Services/Dashboard/DashboardRouteService.php @@ -12,8 +12,9 @@ class DashboardRouteService public function resolveForUser(?User $user): array { - if (!$user) { + if (! $user) { Log::warning('Dashboard route requested without an authenticated user.'); + return [ 'route' => self::FALLBACK_ROUTE, 'role' => null, @@ -48,7 +49,7 @@ class DashboardRouteService $rows = Role::findByNamesOrSlugs($lookupRoles); - if (!empty($rows)) { + if (! empty($rows)) { $role = $rows[0]; $route = $role->dashboard_route ?: self::FALLBACK_ROUTE; diff --git a/app/Services/Discounts/DiscountApplyService.php b/app/Services/Discounts/DiscountApplyService.php index 08f7d9dd..3917d6e9 100644 --- a/app/Services/Discounts/DiscountApplyService.php +++ b/app/Services/Discounts/DiscountApplyService.php @@ -10,6 +10,7 @@ use Illuminate\Support\Facades\Log; class DiscountApplyService { private DiscountContextService $context; + private DiscountInvoiceService $invoiceService; public function __construct(DiscountContextService $context, DiscountInvoiceService $invoiceService) @@ -24,7 +25,7 @@ class DiscountApplyService $semester = $this->context->getSemester(); $voucher = DiscountVoucher::query()->find($voucherId); - if (!$voucher) { + if (! $voucher) { return ['ok' => false, 'message' => 'Voucher not found.']; } @@ -43,7 +44,7 @@ class DiscountApplyService return ['ok' => false, 'message' => 'This voucher has reached its maximum allowed uses.']; } - if (!$allowAdditional) { + if (! $allowAdditional) { $rows = DB::table('discount_usages as du') ->select('i.parent_id') ->join('invoices as i', 'i.id', '=', 'du.invoice_id') @@ -55,7 +56,7 @@ class DiscountApplyService ->all(); $blockedParentIds = array_map(static fn ($r) => (int) ($r['parent_id'] ?? 0), $rows); - if (!empty($blockedParentIds)) { + if (! empty($blockedParentIds)) { $parentIds = array_values(array_diff(array_map('intval', $parentIds), $blockedParentIds)); } if (empty($parentIds)) { @@ -81,6 +82,7 @@ class DiscountApplyService 'parent_id' => (int) $parentId, 'voucher_id' => $voucherId, ]); + continue; } @@ -99,7 +101,7 @@ class DiscountApplyService continue; } - if (!$allowAdditional) { + if (! $allowAdditional) { $exists = DB::table('discount_usages as du') ->join('invoices as i', 'du.invoice_id', '=', 'i.id') ->where('du.voucher_id', $voucherId) @@ -197,6 +199,7 @@ class DiscountApplyService DB::commit(); } catch (\Throwable $e) { DB::rollBack(); + return ['ok' => false, 'message' => 'Voucher application failed. Transaction rolled back.']; } @@ -286,7 +289,7 @@ class DiscountApplyService event('paymentReceived', [$eventData, $studentData]); } } catch (\Throwable $e) { - Log::warning('paymentReceived hook failed: ' . $e->getMessage()); + Log::warning('paymentReceived hook failed: '.$e->getMessage()); } } } diff --git a/app/Services/Discounts/DiscountInvoiceService.php b/app/Services/Discounts/DiscountInvoiceService.php index 12bec3d5..d5c42a85 100644 --- a/app/Services/Discounts/DiscountInvoiceService.php +++ b/app/Services/Discounts/DiscountInvoiceService.php @@ -17,13 +17,12 @@ class DiscountInvoiceService { public function __construct( private ApplicationUrlService $urls, - ) { - } + ) {} public function getCurrentInvoiceBalance(int $invoiceId, string $schoolYear): float { $invoice = Invoice::query()->find($invoiceId); - if (!$invoice) { + if (! $invoice) { return 0.0; } @@ -66,13 +65,14 @@ class DiscountInvoiceService $totalRefundPaid = (float) ($rowRefund['total_refund_paid'] ?? 0); $total = (float) ($invoice->total_amount ?? 0); + return max(0.0, round($total - $totalPaid - $totalDisc - $totalRefundPaid, 2)); } public function recalculateInvoice(int $invoiceId, string $schoolYear): void { $invoice = Invoice::query()->find($invoiceId); - if (!$invoice) { + if (! $invoice) { return; } @@ -118,7 +118,7 @@ class DiscountInvoiceService } $tuitionStudents = $registered; - if (!$refundAllowed) { + if (! $refundAllowed) { $tuitionStudents = array_merge($tuitionStudents, $withdrawn); } @@ -129,7 +129,7 @@ class DiscountInvoiceService foreach ($tuitionStudents as &$s) { $name = null; - if (!empty($s['class_section_id'])) { + if (! empty($s['class_section_id'])) { $name = ClassSection::getClassSectionNameBySectionId($s['class_section_id']); } $s['grade_name'] = is_string($name) ? strtoupper(trim($name)) : 'N/A'; @@ -236,13 +236,13 @@ class DiscountInvoiceService public function updateEnrollmentStatusIfPaid(int $invoiceId, string $schoolYear): int { $invoice = Invoice::query()->find($invoiceId); - if (!$invoice) { + if (! $invoice) { return 0; } $total = (float) ($invoice->total_amount ?? 0); $balance = (float) ($invoice->balance ?? 0); - if (!($total > 0 && $balance < $total)) { + if (! ($total > 0 && $balance < $total)) { return 0; } @@ -281,7 +281,7 @@ class DiscountInvoiceService ->orWhereRaw("LOWER(TRIM(REPLACE(enrollment_status, CHAR(160), ' '))) = 'payment pending'"); }); - if (!empty($paidEnrollmentIds)) { + if (! empty($paidEnrollmentIds)) { $query->whereIn('id', array_values(array_unique($paidEnrollmentIds))); } @@ -314,7 +314,7 @@ class DiscountInvoiceService string $semester ): array { $invoice = Invoice::query()->find($invoiceId); - if (!$invoice) { + if (! $invoice) { throw new \RuntimeException("Invoice {$invoiceId} not found"); } @@ -380,6 +380,7 @@ class DiscountInvoiceService if (preg_match('/^Y(?:OUTH)?\\s*(\\d+)?$/', $g, $m)) { $n = isset($m[1]) && $m[1] !== '' ? max(1, (int) $m[1]) : 1; + return $gradeFee + $n; } @@ -395,6 +396,7 @@ class DiscountInvoiceService if (function_exists('local_date') && function_exists('utc_now')) { return (string) local_date(utc_now(), $format); } + return now()->format($format); } } diff --git a/app/Services/Discounts/DiscountVoucherService.php b/app/Services/Discounts/DiscountVoucherService.php index 068919f3..d8f02426 100644 --- a/app/Services/Discounts/DiscountVoucherService.php +++ b/app/Services/Discounts/DiscountVoucherService.php @@ -33,6 +33,7 @@ class DiscountVoucherService $voucher = DiscountVoucher::query()->findOrFail($id); $voucher->fill($payload); $voucher->save(); + return $voucher; } diff --git a/app/Services/Docs/ApiDocsService.php b/app/Services/Docs/ApiDocsService.php index 425b8130..6055fa5c 100644 --- a/app/Services/Docs/ApiDocsService.php +++ b/app/Services/Docs/ApiDocsService.php @@ -9,8 +9,7 @@ class ApiDocsService { public function __construct( private ApplicationUrlService $urls, - ) { - } + ) {} /** * Bootstrap payload for the documentation SPA (replaces legacy swagger_ui / swagger_ui_public views). diff --git a/app/Services/Docs/DocsCatalogService.php b/app/Services/Docs/DocsCatalogService.php index adc9265e..17c1bcba 100644 --- a/app/Services/Docs/DocsCatalogService.php +++ b/app/Services/Docs/DocsCatalogService.php @@ -2,18 +2,18 @@ namespace App\Services\Docs; +use App\Controllers\DocsController; use App\Services\ApplicationUrlService; /** - * legacy parity: {@see \App\Controllers\DocsController::index} and + * legacy parity: {@see DocsController::index} and * {@see \App\Controllers\View\DocsController::swagger} (multi-spec Swagger UI payload). */ class DocsCatalogService { public function __construct( private ApplicationUrlService $urls, - ) { - } + ) {} /** * Hub metadata for the docs landing route (replaces Blade `docs/index`). diff --git a/app/Services/Docs/OpenApiRouteExporter.php b/app/Services/Docs/OpenApiRouteExporter.php index 82dec7ed..3d2a0fba 100644 --- a/app/Services/Docs/OpenApiRouteExporter.php +++ b/app/Services/Docs/OpenApiRouteExporter.php @@ -29,22 +29,22 @@ class OpenApiRouteExporter private function mergeRoute(array &$paths, Route $route): void { - if (!$this->isDocumentableRoute($route)) { + if (! $this->isDocumentableRoute($route)) { return; } $uriRaw = ltrim($route->uri(), '/'); if (str_starts_with($uriRaw, 'api/')) { - $uri = '/' . $uriRaw; + $uri = '/'.$uriRaw; } elseif (str_starts_with($uriRaw, 'v1/')) { - $uri = '/api/' . $uriRaw; + $uri = '/api/'.$uriRaw; } elseif ($uriRaw === 'v1') { $uri = '/api/v1'; } else { return; } - if (!str_starts_with($uri, '/api/v1')) { + if (! str_starts_with($uri, '/api/v1')) { return; } @@ -56,7 +56,7 @@ class OpenApiRouteExporter $tag = $this->resolveTag($uri); foreach ($methods as $method) { $lower = strtolower($method); - if (!isset($paths[$uri][$lower])) { + if (! isset($paths[$uri][$lower])) { $paths[$uri][$lower] = [ 'tags' => [$tag], 'summary' => $this->summaryFromAction($route), @@ -73,12 +73,12 @@ class OpenApiRouteExporter private function isDocumentableRoute(Route $route): bool { $uri = ltrim($route->uri(), '/'); - if (!str_starts_with($uri, 'api/v1') && !str_starts_with($uri, 'v1/')) { + if (! str_starts_with($uri, 'api/v1') && ! str_starts_with($uri, 'v1/')) { return false; } $action = $route->getActionName(); - if (!is_string($action)) { + if (! is_string($action)) { return false; } @@ -120,11 +120,13 @@ class OpenApiRouteExporter if (is_string($action) && str_contains($action, '@')) { [$controller, $method] = explode('@', $action, 2); $base = class_basename($controller); - return $base . '::' . $method; + + return $base.'::'.$method; } if (is_string($action)) { return $action; } + return 'Endpoint'; } } diff --git a/app/Services/Email/EmailAttachmentService.php b/app/Services/Email/EmailAttachmentService.php index 7ba2c497..a330d6da 100644 --- a/app/Services/Email/EmailAttachmentService.php +++ b/app/Services/Email/EmailAttachmentService.php @@ -11,17 +11,17 @@ class EmailAttachmentService if ($files instanceof UploadedFile) { $files = [$files]; } - if (!is_array($files)) { + if (! is_array($files)) { return []; } $out = []; foreach ($files as $file) { - if (!$file instanceof UploadedFile || !$file->isValid()) { + if (! $file instanceof UploadedFile || ! $file->isValid()) { continue; } $path = $file->getRealPath(); - if (!$path || !is_file($path)) { + if (! $path || ! is_file($path)) { continue; } $out[] = [ diff --git a/app/Services/Email/EmailDispatchService.php b/app/Services/Email/EmailDispatchService.php index 1a0b55b4..b65eb143 100644 --- a/app/Services/Email/EmailDispatchService.php +++ b/app/Services/Email/EmailDispatchService.php @@ -7,9 +7,7 @@ use PHPMailer\PHPMailer\PHPMailer; class EmailDispatchService { - public function __construct(private EmailProfileService $profiles) - { - } + public function __construct(private EmailProfileService $profiles) {} public function send( string $recipient, @@ -25,6 +23,7 @@ class EmailDispatchService if (empty($cfg['host']) || empty($cfg['user']) || $cfg['pass'] === '') { logger()->error("[mail:$profile] Missing SMTP config (host/user/pass). Check MAIL_{$this->profiles->envKeyFromProfile($profile)}_* or MAIL_DEFAULT_*/MAIL_*."); + return false; } @@ -61,7 +60,7 @@ class EmailDispatchService 'ssl' => [ 'verify_peer' => $verifyPeer, 'verify_peer_name' => $verifyPeer, - 'allow_self_signed' => !$verifyPeer, + 'allow_self_signed' => ! $verifyPeer, ], ]; @@ -69,17 +68,17 @@ class EmailDispatchService $mail->Debugoutput = 'error_log'; $mail->setFrom($cfg['fromEmail'], $cfg['fromName']); - if (!empty($cfg['returnPath'])) { + if (! empty($cfg['returnPath'])) { $mail->Sender = $cfg['returnPath']; } $mail->clearReplyTos(); $rtEmail = env('MAIL_DEFAULT_REPLY_TO'); $rtName = env('MAIL_DEFAULT_REPLY_TO_NAME'); - if (!$rtEmail || !filter_var($rtEmail, FILTER_VALIDATE_EMAIL)) { + if (! $rtEmail || ! filter_var($rtEmail, FILTER_VALIDATE_EMAIL)) { $rtEmail = $replyToEmail ?: ($cfg['replyTo'] ?: $cfg['fromEmail']); } - if (!$rtName) { + if (! $rtName) { $rtName = $replyToName ?: ($cfg['replyToName'] ?: $cfg['fromName']); } $rtName = $this->profiles->sanitizeReplyToName($rtName, $cfg['fromName']); @@ -87,7 +86,7 @@ class EmailDispatchService $mail->addReplyTo($rtEmail, $rtName); } - if (!empty($cfg['dkim']['domain']) && !empty($cfg['dkim']['private']) && !empty($cfg['dkim']['selector'])) { + if (! empty($cfg['dkim']['domain']) && ! empty($cfg['dkim']['private']) && ! empty($cfg['dkim']['selector'])) { $mail->DKIM_domain = $cfg['dkim']['domain']; $mail->DKIM_private = $cfg['dkim']['private']; $mail->DKIM_selector = $cfg['dkim']['selector']; @@ -97,7 +96,7 @@ class EmailDispatchService $mail->addAddress($recipient); foreach ($attachments as $att) { - if (!empty($att['path']) && is_file($att['path'])) { + if (! empty($att['path']) && is_file($att['path'])) { $mail->addAttachment($att['path'], $att['name'] ?? ''); } } @@ -111,14 +110,17 @@ class EmailDispatchService if ($ok) { logger()->info("[mail:$profile] Sent to {$recipient}, subj='{$subject}' via {$cfg['host']}:{$cfg['port']}/{$cfg['encryption']}"); + return true; } logger()->error("[mail:$profile] Failed: {$mail->ErrorInfo}. Debug: {$dbg}"); + return false; } catch (MailerException $e) { $dbg = $debugEnabled ? (ob_get_clean() ?: '') : ''; logger()->error("[mail:$profile] Exception: {$e->getMessage()} | PHPMailer: {$mail->ErrorInfo} | Debug: {$dbg}"); + return false; } } diff --git a/app/Services/Email/EmailExtractorService.php b/app/Services/Email/EmailExtractorService.php index 9219605e..39924558 100644 --- a/app/Services/Email/EmailExtractorService.php +++ b/app/Services/Email/EmailExtractorService.php @@ -57,7 +57,7 @@ class EmailExtractorService $needToAdd = []; foreach ($dbSet as $email => $_) { - if (!isset($csvSet[$email])) { + if (! isset($csvSet[$email])) { $needToAdd[] = $email; } } @@ -84,6 +84,7 @@ class EmailExtractorService $out[] = $email; } } + return array_values(array_unique($out)); } @@ -92,6 +93,7 @@ class EmailExtractorService $pattern = '/[A-Z0-9._%+-]+@[A-Z0-9.-]+\.[A-Z]{2,}/i'; preg_match_all($pattern, $text, $matches); $emails = $matches[0] ?? []; + return $this->normalizeEmailArray($emails); } } diff --git a/app/Services/Email/EmailProfileService.php b/app/Services/Email/EmailProfileService.php index 14b76eab..fa6638ee 100644 --- a/app/Services/Email/EmailProfileService.php +++ b/app/Services/Email/EmailProfileService.php @@ -37,6 +37,7 @@ class EmailProfileService return $v; } } + return $default; }; @@ -90,6 +91,7 @@ class EmailProfileService if ($trimmed === '' || preg_match('/^no[- ]?repl(?:y|ay)$/i', $trimmed)) { return $fallback; } + return $trimmed; } } diff --git a/app/Services/Email/EmailSenderOptionsService.php b/app/Services/Email/EmailSenderOptionsService.php index de6e4990..3f1a0324 100644 --- a/app/Services/Email/EmailSenderOptionsService.php +++ b/app/Services/Email/EmailSenderOptionsService.php @@ -9,7 +9,7 @@ class EmailSenderOptionsService $json = env('MAIL_SENDERS', '{}'); $arr = json_decode($json, true); - if (!is_array($arr) || empty($arr)) { + if (! is_array($arr) || empty($arr)) { $smtpUser = getenv('SMTP_USER') ?: ''; $name = 'Al Rahma Sunday School'; @@ -17,7 +17,7 @@ class EmailSenderOptionsService 'key' => 'general', 'name' => $name, 'email' => $smtpUser, - 'label' => trim($name . ($smtpUser ? " <{$smtpUser}>" : '')), + 'label' => trim($name.($smtpUser ? " <{$smtpUser}>" : '')), ]]; } @@ -29,7 +29,7 @@ class EmailSenderOptionsService 'key' => (string) $key, 'name' => (string) $name, 'email' => (string) $email, - 'label' => trim($name . ($email ? " <{$email}>" : '')), + 'label' => trim($name.($email ? " <{$email}>" : '')), ]; } diff --git a/app/Services/EmailService.php b/app/Services/EmailService.php index 6acf3238..81cbd8a2 100644 --- a/app/Services/EmailService.php +++ b/app/Services/EmailService.php @@ -14,8 +14,7 @@ class EmailService string $fromKey = 'general', array $cc = [], array $bcc = [] - ): bool - { + ): bool { $sender = $this->resolveSender($fromKey); try { @@ -24,16 +23,18 @@ class EmailService if ($sender['email'] !== '') { $message->from($sender['email'], $sender['name']); } - if (!empty($cc)) { + if (! empty($cc)) { $message->cc($cc); } - if (!empty($bcc)) { + if (! empty($bcc)) { $message->bcc($bcc); } }); + return true; } catch (\Throwable $e) { - Log::error('EmailService send failed: ' . $e->getMessage()); + Log::error('EmailService send failed: '.$e->getMessage()); + return false; } } @@ -46,6 +47,7 @@ class EmailService if (is_array($senders) && isset($senders[$fromKey])) { $info = $senders[$fromKey]; + return [ 'name' => (string) ($info['name'] ?? 'Sender'), 'email' => (string) ($info['email'] ?? ''), diff --git a/app/Services/EmergencyContacts/EmergencyContactCrudService.php b/app/Services/EmergencyContacts/EmergencyContactCrudService.php index 044749e6..2a40c62e 100644 --- a/app/Services/EmergencyContacts/EmergencyContactCrudService.php +++ b/app/Services/EmergencyContacts/EmergencyContactCrudService.php @@ -9,8 +9,7 @@ class EmergencyContactCrudService { public function __construct( private PhoneFormatterService $phoneFormatter - ) { - } + ) {} public function findForParent(int $contactId, int $parentId): array { diff --git a/app/Services/EmergencyContacts/EmergencyContactDirectoryService.php b/app/Services/EmergencyContacts/EmergencyContactDirectoryService.php index 89d8c706..666f0012 100644 --- a/app/Services/EmergencyContacts/EmergencyContactDirectoryService.php +++ b/app/Services/EmergencyContacts/EmergencyContactDirectoryService.php @@ -13,7 +13,7 @@ class EmergencyContactDirectoryService { public function groups(?array $parentIds = null): array { - $parentIds = !empty($parentIds) + $parentIds = ! empty($parentIds) ? array_values(array_filter(array_map('intval', $parentIds), static fn ($id) => $id > 0)) : EmergencyContact::query() ->distinct() @@ -28,7 +28,7 @@ class EmergencyContactDirectoryService $parent = User::query()->find($parentId); $parentName = $parent - ? trim((string) $parent->firstname . ' ' . (string) $parent->lastname) + ? trim((string) $parent->firstname.' '.(string) $parent->lastname) : 'Unknown Parent'; if ($parentName === '') { $parentName = 'Unknown Parent'; @@ -67,11 +67,11 @@ class EmergencyContactDirectoryService private function secondParentPhone(int $parentId): string { try { - if (!Schema::hasTable('parents')) { + if (! Schema::hasTable('parents')) { return ''; } - if (!Schema::hasColumn('parents', 'secondparent_phone')) { + if (! Schema::hasColumn('parents', 'secondparent_phone')) { return ''; } @@ -82,7 +82,8 @@ class EmergencyContactDirectoryService return $phone ? (string) $phone : ''; } catch (\Throwable $e) { - Log::debug('EmergencyContactDirectoryService: could not load second parent phone: ' . $e->getMessage()); + Log::debug('EmergencyContactDirectoryService: could not load second parent phone: '.$e->getMessage()); + return ''; } } diff --git a/app/Services/Events/EventChargeQueryService.php b/app/Services/Events/EventChargeQueryService.php index 754d6854..1a973208 100644 --- a/app/Services/Events/EventChargeQueryService.php +++ b/app/Services/Events/EventChargeQueryService.php @@ -4,7 +4,6 @@ namespace App\Services\Events; use App\Models\EventCharges; use Illuminate\Contracts\Pagination\LengthAwarePaginator; -use Illuminate\Support\Facades\DB; class EventChargeQueryService { @@ -26,16 +25,16 @@ class EventChargeQueryService ->leftJoin('students', 'students.id', '=', 'event_charges.student_id') ->leftJoin('events', 'events.id', '=', 'event_charges.event_id'); - if (!empty($filters['school_year'])) { + if (! empty($filters['school_year'])) { $query->where('event_charges.school_year', (string) $filters['school_year']); } - if (!empty($filters['semester'])) { + if (! empty($filters['semester'])) { $query->where('event_charges.semester', (string) $filters['semester']); } - if (!empty($filters['parent_id'])) { + if (! empty($filters['parent_id'])) { $query->where('event_charges.parent_id', (int) $filters['parent_id']); } - if (!empty($filters['event_id'])) { + if (! empty($filters['event_id'])) { $query->where('event_charges.event_id', (int) $filters['event_id']); } diff --git a/app/Services/Events/EventChargeService.php b/app/Services/Events/EventChargeService.php index cac21166..d9f1370d 100644 --- a/app/Services/Events/EventChargeService.php +++ b/app/Services/Events/EventChargeService.php @@ -9,9 +9,7 @@ use App\Services\Invoices\InvoiceGenerationService; class EventChargeService { - public function __construct(private InvoiceGenerationService $invoiceGeneration) - { - } + public function __construct(private InvoiceGenerationService $invoiceGeneration) {} public function seedChargesForEvent(Event $event, string $schoolYear, string $semester, float $amount, int $actorId): array { @@ -82,7 +80,7 @@ class EventChargeService int $actorId ): array { $event = Event::getEvent($eventId, $schoolYear); - if (!$event) { + if (! $event) { return ['ok' => false, 'message' => 'Event not found.']; } @@ -109,6 +107,7 @@ class EventChargeService $existing->delete(); $deleted++; } + continue; } diff --git a/app/Services/Events/EventListService.php b/app/Services/Events/EventListService.php index cd2283c9..996e3590 100644 --- a/app/Services/Events/EventListService.php +++ b/app/Services/Events/EventListService.php @@ -16,19 +16,19 @@ class EventListService $activeOnly = $filters['active'] ?? null; $allowedSorts = ['created_at', 'expiration_date', 'event_name']; - if (!in_array($sortBy, $allowedSorts, true)) { + if (! in_array($sortBy, $allowedSorts, true)) { $sortBy = 'created_at'; } $query = Event::query(); - if (!empty($filters['school_year'])) { + if (! empty($filters['school_year'])) { $query->where('school_year', (string) $filters['school_year']); } - if (!empty($filters['semester'])) { + if (! empty($filters['semester'])) { $query->where('semester', (string) $filters['semester']); } - if (!empty($filters['q'])) { + if (! empty($filters['q'])) { $term = trim((string) $filters['q']); $query->where('event_name', 'like', "%{$term}%"); } @@ -67,10 +67,10 @@ class EventListService private function activeCount(array $filters): int { $query = Event::query()->whereDate('expiration_date', '>=', now()->toDateString()); - if (!empty($filters['school_year'])) { + if (! empty($filters['school_year'])) { $query->where('school_year', (string) $filters['school_year']); } - if (!empty($filters['semester'])) { + if (! empty($filters['semester'])) { $query->where('semester', (string) $filters['semester']); } diff --git a/app/Services/Events/EventManagementService.php b/app/Services/Events/EventManagementService.php index ac327586..aa1fcd41 100644 --- a/app/Services/Events/EventManagementService.php +++ b/app/Services/Events/EventManagementService.php @@ -13,8 +13,7 @@ class EventManagementService public function __construct( private EventChargeService $charges, private InvoiceGenerationService $invoiceGeneration - ) { - } + ) {} public function create(array $payload, ?UploadedFile $flyer, int $actorId): array { @@ -54,7 +53,7 @@ class EventManagementService public function update(int $eventId, array $payload, ?UploadedFile $flyer): array { $event = Event::query()->find($eventId); - if (!$event) { + if (! $event) { return ['ok' => false, 'message' => 'Event not found.']; } @@ -81,7 +80,7 @@ class EventManagementService public function delete(int $eventId, int $actorId): array { $event = Event::query()->find($eventId); - if (!$event) { + if (! $event) { return ['ok' => false, 'message' => 'Event not found.']; } @@ -99,7 +98,7 @@ class EventManagementService private function storeFlyer(?UploadedFile $file): ?string { - if (!$file || !$file->isValid()) { + if (! $file || ! $file->isValid()) { return null; } @@ -120,13 +119,13 @@ class EventManagementService } $dir = public_path('uploads/event_flyers'); - if (!File::exists($dir)) { + if (! File::exists($dir)) { File::makeDirectory($dir, 0755, true); } - $name = bin2hex(random_bytes(16)) . '.' . $allowed[$mime]; + $name = bin2hex(random_bytes(16)).'.'.$allowed[$mime]; $file->move($dir, $name); - return 'event_flyers/' . $name; + return 'event_flyers/'.$name; } } diff --git a/app/Services/Events/EventStudentChargeService.php b/app/Services/Events/EventStudentChargeService.php index 26a2ca6c..c3d8790d 100644 --- a/app/Services/Events/EventStudentChargeService.php +++ b/app/Services/Events/EventStudentChargeService.php @@ -30,7 +30,7 @@ class EventStudentChargeService foreach ($students as $student) { $data[] = [ 'id' => (int) ($student['id'] ?? 0), - 'name' => trim(($student['firstname'] ?? '') . ' ' . ($student['lastname'] ?? '')), + 'name' => trim(($student['firstname'] ?? '').' '.($student['lastname'] ?? '')), 'charged' => isset($chargedMap[(int) ($student['id'] ?? 0)]), ]; } diff --git a/app/Services/Exams/ExamDraftAdminService.php b/app/Services/Exams/ExamDraftAdminService.php index 9f22c66a..13dff9ae 100644 --- a/app/Services/Exams/ExamDraftAdminService.php +++ b/app/Services/Exams/ExamDraftAdminService.php @@ -4,6 +4,7 @@ namespace App\Services\Exams; use App\Models\ClassSection; use App\Models\ExamDraft; +use Illuminate\Http\UploadedFile; use Illuminate\Support\Facades\DB; class ExamDraftAdminService @@ -11,8 +12,7 @@ class ExamDraftAdminService public function __construct( private ExamDraftConfigService $configService, private ExamDraftFileService $fileService - ) { - } + ) {} public function list(): array { @@ -29,7 +29,7 @@ class ExamDraftAdminService if ($teacherUserColumn !== '') { $draftsQuery - ->leftJoin('users as u', "u.id", '=', "ed.{$teacherUserColumn}") + ->leftJoin('users as u', 'u.id', '=', "ed.{$teacherUserColumn}") ->addSelect( 'u.firstname as teacher_first', 'u.lastname as teacher_last' @@ -82,15 +82,15 @@ class ExamDraftAdminService $legacyByClass = []; if ($context['has_legacy'] ?? false) { foreach ($drafts as $row) { - $isLegacy = !empty($row['is_legacy']); - if (!$isLegacy) { + $isLegacy = ! empty($row['is_legacy']); + if (! $isLegacy) { continue; } $cid = (int) ($row['class_section_id'] ?? 0); - if (!isset($legacyByClass[$cid])) { + if (! isset($legacyByClass[$cid])) { $legacyByClass[$cid] = [ 'class_section_id' => $cid, - 'class_section_name' => $row['class_section_name'] ?? 'Class ' . $cid, + 'class_section_name' => $row['class_section_name'] ?? 'Class '.$cid, 'items' => [], ]; } @@ -111,7 +111,7 @@ class ExamDraftAdminService ]; } - public function uploadLegacy(int $adminId, array $payload, \Illuminate\Http\UploadedFile $file): array + public function uploadLegacy(int $adminId, array $payload, UploadedFile $file): array { $context = $this->configService->context(); $adminUserColumn = (string) ($context['admin_user_column'] ?? ''); @@ -148,7 +148,7 @@ class ExamDraftAdminService $finalFilename = $stored['extension'] === 'pdf' ? $stored['original'] - : (pathinfo($stored['original'], PATHINFO_FILENAME) . '.pdf'); + : (pathinfo($stored['original'], PATHINFO_FILENAME).'.pdf'); $payload = [ 'class_section_id' => $classSectionId, @@ -193,7 +193,7 @@ class ExamDraftAdminService return ['ok' => true, 'draft' => $draft->toArray()]; } - public function review(int $adminId, array $payload, ?\Illuminate\Http\UploadedFile $file): array + public function review(int $adminId, array $payload, ?UploadedFile $file): array { $draftId = (int) ($payload['draft_id'] ?? 0); if ($draftId <= 0) { @@ -201,7 +201,7 @@ class ExamDraftAdminService } $draft = ExamDraft::query()->find($draftId); - if (!$draft) { + if (! $draft) { return ['ok' => false, 'message' => 'Submission not found.']; } @@ -209,7 +209,7 @@ class ExamDraftAdminService $statusInput = trim((string) ($payload['review_status'] ?? '')); $currentStatus = (string) ($draft->status ?? 'draft'); $status = $this->normalizeStatus($statusInput !== '' ? $statusInput : $currentStatus, $currentStatus); - if (!in_array($status, ExamDraftConfigService::STATUS_OPTIONS, true)) { + if (! in_array($status, ExamDraftConfigService::STATUS_OPTIONS, true)) { $status = 'reviewed'; } @@ -255,7 +255,7 @@ class ExamDraftAdminService if ($pdfName !== null && ($this->configService->context()['has_final_pdf'] ?? false)) { $update['final_pdf_file'] = $pdfName; } - } elseif ($status === 'finalized' && !empty($draft->teacher_file ?: $draft->author_file)) { + } elseif ($status === 'finalized' && ! empty($draft->teacher_file ?: $draft->author_file)) { $copied = $this->fileService->copyDraftToFinal( (string) ($draft->teacher_file ?? $draft->author_file), ExamDraftConfigService::TEACHER_UPLOAD_DIR, @@ -297,6 +297,7 @@ class ExamDraftAdminService if (isset($aliases[$input])) { $input = $aliases[$input]; } + return in_array($input, ExamDraftConfigService::STATUS_OPTIONS, true) ? $input : $default; } } diff --git a/app/Services/Exams/ExamDraftConfigService.php b/app/Services/Exams/ExamDraftConfigService.php index b7d0f68e..3bfcbdf8 100644 --- a/app/Services/Exams/ExamDraftConfigService.php +++ b/app/Services/Exams/ExamDraftConfigService.php @@ -8,9 +8,13 @@ use Illuminate\Support\Facades\Schema; class ExamDraftConfigService { public const TEACHER_UPLOAD_DIR = 'exams/drafts'; + public const FINAL_UPLOAD_DIR = 'exams/finals'; + public const MAX_UPLOAD_BYTES = 12 * 1024 * 1024; + public const ALLOWED_EXTENSIONS = ['doc', 'docx']; + public const ADMIN_ALLOWED_EXTENSIONS = ['doc', 'docx', 'pdf']; public const EXAM_TYPES = [ diff --git a/app/Services/Exams/ExamDraftFileService.php b/app/Services/Exams/ExamDraftFileService.php index d75a74e9..b880103e 100644 --- a/app/Services/Exams/ExamDraftFileService.php +++ b/app/Services/Exams/ExamDraftFileService.php @@ -9,21 +9,22 @@ class ExamDraftFileService public function storeUploadedFile(UploadedFile $file, string $subdir, array $allowedExtensions, int $maxBytes): ?array { $ext = strtolower($file->getClientOriginalExtension()); - if (!in_array($ext, $allowedExtensions, true)) { + if (! in_array($ext, $allowedExtensions, true)) { return null; } if ($file->getSize() > $maxBytes) { return null; } - $destination = storage_path('uploads/' . trim($subdir, '/')); - if (!is_dir($destination)) { + $destination = storage_path('uploads/'.trim($subdir, '/')); + if (! is_dir($destination)) { mkdir($destination, 0755, true); } - $filename = uniqid('exam_', true) . '.' . $ext; + $filename = uniqid('exam_', true).'.'.$ext; try { $file->move($destination, $filename); + return [ 'stored' => $filename, 'original' => $file->getClientOriginalName(), @@ -43,46 +44,49 @@ class ExamDraftFileService $ext = strtolower((string) $originalExt); if ($ext === 'pdf') { $path = $this->fullUploadPath($subdir, $finalFilename); + return is_file($path) ? $finalFilename : null; } + return $this->convertDocToPdf($this->fullUploadPath($subdir, $finalFilename), $subdir); } public function copyDraftToFinal(string $draftFilename, string $sourceDir, string $targetDir): ?string { $source = $this->fullUploadPath($sourceDir, $draftFilename); - if (!is_file($source)) { + if (! is_file($source)) { return null; } - $destinationDir = storage_path('uploads/' . trim($targetDir, '/')); - if (!is_dir($destinationDir)) { + $destinationDir = storage_path('uploads/'.trim($targetDir, '/')); + if (! is_dir($destinationDir)) { mkdir($destinationDir, 0755, true); } $ext = pathinfo($draftFilename, PATHINFO_EXTENSION); - $destName = uniqid('final_', true) . '.' . $ext; - $destPath = $destinationDir . '/' . $destName; - if (!@copy($source, $destPath)) { + $destName = uniqid('final_', true).'.'.$ext; + $destPath = $destinationDir.'/'.$destName; + if (! @copy($source, $destPath)) { return null; } + return $destName; } private function convertDocToPdf(string $sourcePath, string $targetSubdir): ?string { $ext = strtolower(pathinfo($sourcePath, PATHINFO_EXTENSION)); - if (!in_array($ext, ['doc', 'docx'], true)) { + if (! in_array($ext, ['doc', 'docx'], true)) { return null; } - $targetDir = storage_path('uploads/' . trim($targetSubdir, '/')); - if (!is_dir($targetDir)) { + $targetDir = storage_path('uploads/'.trim($targetSubdir, '/')); + if (! is_dir($targetDir)) { mkdir($targetDir, 0755, true); } $base = pathinfo($sourcePath, PATHINFO_FILENAME); - $targetPath = $targetDir . '/' . $base . '.pdf'; + $targetPath = $targetDir.'/'.$base.'.pdf'; - $cmd = 'soffice --headless --convert-to pdf --outdir ' . escapeshellarg($targetDir) . ' ' . escapeshellarg($sourcePath) . ' 2>/dev/null'; + $cmd = 'soffice --headless --convert-to pdf --outdir '.escapeshellarg($targetDir).' '.escapeshellarg($sourcePath).' 2>/dev/null'; @exec($cmd); return is_file($targetPath) ? basename($targetPath) : null; @@ -96,12 +100,13 @@ class ExamDraftFileService $path = $this->fullUploadPath($subdir, $filename); $base = pathinfo($path, PATHINFO_FILENAME); $dir = pathinfo($path, PATHINFO_DIRNAME); - $pdfPath = $dir . '/' . $base . '.pdf'; + $pdfPath = $dir.'/'.$base.'.pdf'; + return is_file($pdfPath) ? basename($pdfPath) : null; } private function fullUploadPath(string $subdir, string $filename): string { - return storage_path('uploads/' . trim($subdir, '/') . '/' . $filename); + return storage_path('uploads/'.trim($subdir, '/').'/'.$filename); } } diff --git a/app/Services/Exams/ExamDraftTeacherService.php b/app/Services/Exams/ExamDraftTeacherService.php index fa28bdf7..d099d67b 100644 --- a/app/Services/Exams/ExamDraftTeacherService.php +++ b/app/Services/Exams/ExamDraftTeacherService.php @@ -4,6 +4,7 @@ namespace App\Services\Exams; use App\Models\ExamDraft; use App\Models\TeacherClass; +use Illuminate\Http\UploadedFile; use Illuminate\Support\Facades\DB; class ExamDraftTeacherService @@ -11,8 +12,7 @@ class ExamDraftTeacherService public function __construct( private ExamDraftConfigService $configService, private ExamDraftFileService $fileService - ) { - } + ) {} public function listForTeacher(int $teacherId): array { @@ -33,7 +33,7 @@ class ExamDraftTeacherService } $legacyExams = []; - if (($context['has_legacy'] ?? false) && !empty($classSectionIds)) { + if (($context['has_legacy'] ?? false) && ! empty($classSectionIds)) { $legacyExams = DB::table('exam_drafts as ed') ->select('ed.*', 'cs.class_section_name') ->leftJoin('classSection as cs', 'cs.class_section_id', '=', 'ed.class_section_id') @@ -58,7 +58,7 @@ class ExamDraftTeacherService ]; } - public function store(int $teacherId, array $payload, ?\Illuminate\Http\UploadedFile $file): array + public function store(int $teacherId, array $payload, ?UploadedFile $file): array { $context = $this->configService->context(); $schoolYear = (string) ($context['school_year'] ?? ''); @@ -77,7 +77,7 @@ class ExamDraftTeacherService ->where('class_section_id', $classSectionId) ->first(); - if (!$assignment) { + if (! $assignment) { return ['ok' => false, 'message' => 'You are not assigned to the selected class section.']; } diff --git a/app/Services/Expenses/ExpenseReceiptService.php b/app/Services/Expenses/ExpenseReceiptService.php index e0720a5c..f748bcdf 100644 --- a/app/Services/Expenses/ExpenseReceiptService.php +++ b/app/Services/Expenses/ExpenseReceiptService.php @@ -9,8 +9,7 @@ class ExpenseReceiptService { public function __construct( private ApplicationUrlService $urls - ) { - } + ) {} public function storeReceipt(UploadedFile $file): string { @@ -34,7 +33,7 @@ class ExpenseReceiptService throw new \InvalidArgumentException('Receipt file too large. Max 5MB.'); } - $filename = bin2hex(random_bytes(16)) . '.' . $allowed[$mime]; + $filename = bin2hex(random_bytes(16)).'.'.$allowed[$mime]; $file->storeAs('receipts', $filename); return $filename; diff --git a/app/Services/Expenses/ExpenseStaffService.php b/app/Services/Expenses/ExpenseStaffService.php index 0a204b67..16fb4a30 100644 --- a/app/Services/Expenses/ExpenseStaffService.php +++ b/app/Services/Expenses/ExpenseStaffService.php @@ -26,7 +26,7 @@ class ExpenseStaffService if ($id <= 0 || in_array($roleName, $excludedRoles, true)) { continue; } - if (!isset($staff[$id])) { + if (! isset($staff[$id])) { $staff[$id] = [ 'id' => $id, 'firstname' => $row['firstname'] ?? '', @@ -36,8 +36,9 @@ class ExpenseStaffService } uasort($staff, static function ($a, $b) { - $nameA = trim(($a['firstname'] ?? '') . ' ' . ($a['lastname'] ?? '')); - $nameB = trim(($b['firstname'] ?? '') . ' ' . ($b['lastname'] ?? '')); + $nameA = trim(($a['firstname'] ?? '').' '.($a['lastname'] ?? '')); + $nameB = trim(($b['firstname'] ?? '').' '.($b['lastname'] ?? '')); + return strcasecmp($nameA, $nameB); }); diff --git a/app/Services/Expenses/ExpenseWriteService.php b/app/Services/Expenses/ExpenseWriteService.php index 0bea865d..a35d8eec 100644 --- a/app/Services/Expenses/ExpenseWriteService.php +++ b/app/Services/Expenses/ExpenseWriteService.php @@ -36,6 +36,7 @@ class ExpenseWriteService { $expense->fill($payload); $expense->save(); + return $expense; } } diff --git a/app/Services/ExtraCharges/ExtraChargesChargeService.php b/app/Services/ExtraCharges/ExtraChargesChargeService.php index 2bf33682..af1e25bf 100644 --- a/app/Services/ExtraCharges/ExtraChargesChargeService.php +++ b/app/Services/ExtraCharges/ExtraChargesChargeService.php @@ -58,7 +58,8 @@ class ExtraChargesChargeService DB::commit(); } catch (\Throwable $e) { DB::rollBack(); - Log::error('Extra charge create failed: ' . $e->getMessage()); + Log::error('Extra charge create failed: '.$e->getMessage()); + return ['ok' => false, 'message' => 'Failed to save charge.']; } @@ -93,15 +94,17 @@ class ExtraChargesChargeService } $charge->save(); - if ($charge->status === 'applied' && !empty($charge->invoice_id) && abs($delta) > 0.00001) { + if ($charge->status === 'applied' && ! empty($charge->invoice_id) && abs($delta) > 0.00001) { $this->invoiceService->adjustDelta((int) $charge->invoice_id, $delta); } DB::commit(); + return true; } catch (\Throwable $e) { DB::rollBack(); - Log::error('Extra charge update failed: ' . $e->getMessage()); + Log::error('Extra charge update failed: '.$e->getMessage()); + return false; } } @@ -124,10 +127,12 @@ class ExtraChargesChargeService $charge->save(); DB::commit(); + return true; } catch (\Throwable $e) { DB::rollBack(); - Log::error('Extra charge void failed: ' . $e->getMessage()); + Log::error('Extra charge void failed: '.$e->getMessage()); + return false; } } @@ -153,23 +158,25 @@ class ExtraChargesChargeService $charge->save(); DB::commit(); + return true; } catch (\Throwable $e) { DB::rollBack(); - Log::error('Extra charge reverse failed: ' . $e->getMessage()); + Log::error('Extra charge reverse failed: '.$e->getMessage()); + return false; } } private function triggerExtraChargeEvent(?array $parentUser, array $payload, int $chargeId, ?int $invoiceId, array $invoiceBefore, array $invoiceAfter): void { - if (!$parentUser) { + if (! $parentUser) { return; } $first = $parentUser['first_name'] ?? $parentUser['firstname'] ?? ''; $last = $parentUser['last_name'] ?? $parentUser['lastname'] ?? ''; - $parentName = trim($first . ' ' . $last); + $parentName = trim($first.' '.$last); $before = $this->normalizeRow($invoiceBefore); $after = $this->normalizeRow($invoiceAfter); @@ -187,7 +194,7 @@ class ExtraChargesChargeService 'school_year' => $payload['school_year'], 'semester' => $payload['semester'], 'invoice_id' => $invoiceId, - 'invoice_number' => $after['invoice_number'] ?? ($invoiceId ? 'INV-' . $invoiceId : null), + 'invoice_number' => $after['invoice_number'] ?? ($invoiceId ? 'INV-'.$invoiceId : null), 'charge_id' => $chargeId, 'charge_title' => $payload['title'], 'charge_desc' => $payload['description'], @@ -212,18 +219,19 @@ class ExtraChargesChargeService event('extraCharge', [$eventData, $students]); } } catch (\Throwable $e) { - Log::warning('extraCharge event failed: ' . $e->getMessage()); + Log::warning('extraCharge event failed: '.$e->getMessage()); } } private function normalizeRow($row): ?array { - if (!$row) { + if (! $row) { return null; } if (is_array($row) && isset($row[0]) && is_array($row[0])) { return $row[0]; } + return is_array($row) ? $row : null; } @@ -232,6 +240,7 @@ class ExtraChargesChargeService if (function_exists('utc_now')) { return (string) utc_now(); } + return now('UTC')->toDateTimeString(); } } diff --git a/app/Services/ExtraCharges/ExtraChargesInvoiceService.php b/app/Services/ExtraCharges/ExtraChargesInvoiceService.php index ddb6b12b..11465f0a 100644 --- a/app/Services/ExtraCharges/ExtraChargesInvoiceService.php +++ b/app/Services/ExtraCharges/ExtraChargesInvoiceService.php @@ -16,7 +16,7 @@ class ExtraChargesInvoiceService return array_map(function ($inv) { $id = (int) ($inv['id'] ?? 0); - $num = (string) ($inv['invoice_number'] ?? ('INV-' . $id)); + $num = (string) ($inv['invoice_number'] ?? ('INV-'.$id)); $status = strtolower((string) ($inv['status'] ?? '')); $balance = (float) ($inv['balance'] ?? 0); $issue = $inv['issue_date'] ?? null; @@ -28,8 +28,8 @@ class ExtraChargesInvoiceService 'balance' => $balance, 'issue_date' => $issue, 'text' => $num - . ($status ? ' — ' . ucfirst($status) : '') - . ' (Bal: $' . number_format($balance, 2) . ')', + .($status ? ' — '.ucfirst($status) : '') + .' (Bal: $'.number_format($balance, 2).')', ]; }, $rows); } diff --git a/app/Services/ExtraCharges/ExtraChargesMetaService.php b/app/Services/ExtraCharges/ExtraChargesMetaService.php index ae429e6a..0109c183 100644 --- a/app/Services/ExtraCharges/ExtraChargesMetaService.php +++ b/app/Services/ExtraCharges/ExtraChargesMetaService.php @@ -21,7 +21,7 @@ class ExtraChargesMetaService foreach ($rows as $row) { $val = (string) ($row['school_year'] ?? ''); - if ($val !== '' && !in_array($val, $schoolYears, true)) { + if ($val !== '' && ! in_array($val, $schoolYears, true)) { $schoolYears[] = $val; } } @@ -39,7 +39,7 @@ class ExtraChargesMetaService foreach ($rows as $row) { $val = (string) ($row['school_year'] ?? ''); - if ($val !== '' && !in_array($val, $schoolYears, true)) { + if ($val !== '' && ! in_array($val, $schoolYears, true)) { $schoolYears[] = $val; } } @@ -53,7 +53,7 @@ class ExtraChargesMetaService $start = (int) $start; if ($start > 0) { for ($i = 1; $i <= 3; $i++) { - $schoolYears[] = ($start - $i) . '-' . (($start - $i) + 1); + $schoolYears[] = ($start - $i).'-'.(($start - $i) + 1); } } } diff --git a/app/Services/ExtraCharges/ExtraChargesParentService.php b/app/Services/ExtraCharges/ExtraChargesParentService.php index d9b3ad37..c5593ed7 100644 --- a/app/Services/ExtraCharges/ExtraChargesParentService.php +++ b/app/Services/ExtraCharges/ExtraChargesParentService.php @@ -32,13 +32,14 @@ class ExtraChargesParentService ->all(); return array_map(function ($row) { - $name = trim(($row['lastname'] ?? '') . ', ' . ($row['firstname'] ?? '')); + $name = trim(($row['lastname'] ?? '').', '.($row['firstname'] ?? '')); $email = $row['email'] ?? ''; - $label = $name !== ',' ? $name : ('User #' . $row['id']); + $label = $name !== ',' ? $name : ('User #'.$row['id']); if ($email) { - $label .= ' — ' . $email; + $label .= ' — '.$email; } - $label .= ' (ID: ' . $row['id'] . ')'; + $label .= ' (ID: '.$row['id'].')'; + return [ 'id' => (int) $row['id'], 'text' => $label, diff --git a/app/Services/Families/FamilyMutationService.php b/app/Services/Families/FamilyMutationService.php index 78f188ab..78dd2fb4 100644 --- a/app/Services/Families/FamilyMutationService.php +++ b/app/Services/Families/FamilyMutationService.php @@ -67,7 +67,7 @@ class FamilyMutationService } $familyId = $this->familyIdFromStudentPrimary($studentId); - if (!$familyId) { + if (! $familyId) { return ['ok' => false, 'message' => 'No primary family found for this student']; } @@ -83,13 +83,13 @@ class FamilyMutationService } $familyId = $this->familyIdFromStudentPrimary($studentId); - if (!$familyId) { + if (! $familyId) { return ['ok' => false, 'message' => 'No primary family found for this student']; } return DB::transaction(function () use ($email, $firstname, $lastname, $relation, $familyId) { $user = User::query()->where('email', $email)->first(); - if (!$user) { + if (! $user) { $user = User::query()->create([ 'firstname' => (string) ($firstname ?? ''), 'lastname' => (string) ($lastname ?? ''), @@ -167,7 +167,7 @@ class FamilyMutationService { $legacyTable = 'parents'; $this->ensureTablesExist(['families', 'family_students', 'family_guardians']); - if (!Schema::hasTable($legacyTable)) { + if (! Schema::hasTable($legacyTable)) { return ['ok' => false, 'message' => "Legacy table '{$legacyTable}' not found"]; } @@ -192,6 +192,7 @@ class FamilyMutationService if ($firstId <= 0 || ($secondId <= 0 && $email === '')) { $skipped++; + continue; } @@ -203,7 +204,7 @@ class FamilyMutationService $userId = $secondId; } else { $user = User::query()->where('email', $email)->first(); - if (!$user) { + if (! $user) { $user = User::query()->create([ 'firstname' => (string) ($r['firstname'] ?? ''), 'lastname' => (string) ($r['lastname'] ?? ''), @@ -251,7 +252,7 @@ class FamilyMutationService private function ensureFamilyForPrimaryParent(int $primaryUserId, int &$createdCounter): int { - $code = 'FAM-' . $primaryUserId; + $code = 'FAM-'.$primaryUserId; $row = Family::query()->where('family_code', $code)->first(); if ($row) { return (int) $row->id; @@ -259,7 +260,7 @@ class FamilyMutationService $family = Family::query()->create([ 'family_code' => $code, - 'household_name' => 'Family of User ' . $primaryUserId, + 'household_name' => 'Family of User '.$primaryUserId, 'is_active' => 1, ]); $createdCounter++; @@ -309,11 +310,11 @@ class FamilyMutationService private function familyIdFromStudentPrimary(int $studentId): ?int { $student = Student::query()->select('parent_id')->find($studentId); - if (!$student || empty($student->parent_id)) { + if (! $student || empty($student->parent_id)) { return null; } - $code = 'FAM-' . (int) $student->parent_id; + $code = 'FAM-'.(int) $student->parent_id; $row = Family::query()->select('id')->where('family_code', $code)->first(); if ($row) { return (int) $row->id; @@ -331,7 +332,7 @@ class FamilyMutationService private function ensureTablesExist(array $tables): void { foreach ($tables as $table) { - if (!Schema::hasTable($table)) { + if (! Schema::hasTable($table)) { Log::warning('Family tables missing', ['table' => $table]); throw new \RuntimeException("Missing required table '{$table}'. Run migrations."); } diff --git a/app/Services/Families/FamilyNotificationService.php b/app/Services/Families/FamilyNotificationService.php index 7b31cbc4..119dd496 100644 --- a/app/Services/Families/FamilyNotificationService.php +++ b/app/Services/Families/FamilyNotificationService.php @@ -6,9 +6,7 @@ use App\Services\Email\EmailDispatchService; class FamilyNotificationService { - public function __construct(private EmailDispatchService $dispatch) - { - } + public function __construct(private EmailDispatchService $dispatch) {} public function sendComposeEmail( string $recipient, diff --git a/app/Services/Families/FamilyQueryService.php b/app/Services/Families/FamilyQueryService.php index 528bb667..cb0ac923 100644 --- a/app/Services/Families/FamilyQueryService.php +++ b/app/Services/Families/FamilyQueryService.php @@ -5,14 +5,11 @@ namespace App\Services\Families; use App\Models\Family; use App\Models\Student; use App\Models\StudentClass; -use App\Models\User; use Illuminate\Support\Facades\DB; class FamilyQueryService { - public function __construct(private FamilyFinanceService $finance) - { - } + public function __construct(private FamilyFinanceService $finance) {} public function listStudentsForSelect(): array { @@ -32,7 +29,7 @@ class FamilyQueryService return []; } - $qs = '%' . str_replace(['%', '_'], ['\\%', '\\_'], $query) . '%'; + $qs = '%'.str_replace(['%', '_'], ['\\%', '\\_'], $query).'%'; $students = DB::table('students') ->select('id', 'firstname', 'lastname') @@ -63,7 +60,7 @@ class FamilyQueryService $items[] = [ 'type' => 'student', 'id' => (int) ($s['id'] ?? 0), - 'label' => trim(($s['firstname'] ?? '') . ' ' . ($s['lastname'] ?? '')), + 'label' => trim(($s['firstname'] ?? '').' '.($s['lastname'] ?? '')), 'sub' => 'Student', ]; } @@ -71,8 +68,8 @@ class FamilyQueryService $items[] = [ 'type' => 'guardian', 'id' => (int) ($g['id'] ?? 0), - 'label' => trim(($g['firstname'] ?? '') . ' ' . ($g['lastname'] ?? '')), - 'sub' => trim(($g['email'] ?? '') . ' ' . ($g['cellphone'] ?? '')), + 'label' => trim(($g['firstname'] ?? '').' '.($g['lastname'] ?? '')), + 'sub' => trim(($g['email'] ?? '').' '.($g['cellphone'] ?? '')), ]; } @@ -93,7 +90,7 @@ class FamilyQueryService ->orderBy('s.firstname') ->select('s.id as student_id') ->first(); - if ($row && !empty($row->student_id)) { + if ($row && ! empty($row->student_id)) { return (int) $row->student_id; } @@ -236,7 +233,7 @@ class FamilyQueryService unset($fam); $data['families'] = $families; - if (!empty($families)) { + if (! empty($families)) { $familyId = (int) ($families[0]['id'] ?? 0); $data['guardians'] = $familyId ? $this->listGuardiansByFamily($familyId) : []; } @@ -306,7 +303,7 @@ class FamilyQueryService ) ->find($familyId); - if (!$family) { + if (! $family) { return null; } @@ -364,7 +361,7 @@ class FamilyQueryService foreach ($guardians as $g) { $gid = (int) ($g['user_id'] ?? 0); if ($gid > 0) { - $guardianMap[$gid] = trim(($g['firstname'] ?? '') . ' ' . ($g['lastname'] ?? '')); + $guardianMap[$gid] = trim(($g['firstname'] ?? '').' '.($g['lastname'] ?? '')); } } @@ -378,7 +375,7 @@ class FamilyQueryService foreach ($rows as &$row) { $pid = (int) ($row['parent_id'] ?? 0); - $row['parent_label'] = $guardianMap[$pid] ?? ('Parent #' . $pid); + $row['parent_label'] = $guardianMap[$pid] ?? ('Parent #'.$pid); } unset($row); diff --git a/app/Services/FeeCalculationService.php b/app/Services/FeeCalculationService.php index 619a7d74..ed9505bf 100644 --- a/app/Services/FeeCalculationService.php +++ b/app/Services/FeeCalculationService.php @@ -8,12 +8,12 @@ class FeeCalculationService { public function __construct( private FeeRefundCalculatorService $refundCalculator - ) { - } + ) {} public function calculateRefund(array $students, int $parentId): float { $result = $this->refundCalculator->calculateRefund($students, $parentId); + return (float) ($result['refund_amount'] ?? 0); } } diff --git a/app/Services/Fees/FeeGradeService.php b/app/Services/Fees/FeeGradeService.php index 5933fd76..9505b346 100644 --- a/app/Services/Fees/FeeGradeService.php +++ b/app/Services/Fees/FeeGradeService.php @@ -7,6 +7,7 @@ class FeeGradeService public function normalizeGrade(?string $grade): string { $normalized = strtoupper(trim((string) $grade)); + return str_replace([' ', '-'], '', $normalized); } diff --git a/app/Services/Fees/FeeRefundCalculatorService.php b/app/Services/Fees/FeeRefundCalculatorService.php index 65cc299e..2ba2391e 100644 --- a/app/Services/Fees/FeeRefundCalculatorService.php +++ b/app/Services/Fees/FeeRefundCalculatorService.php @@ -10,8 +10,7 @@ class FeeRefundCalculatorService public function __construct( private FeeConfigService $config, private FeeStudentFeeService $studentFees - ) { - } + ) {} public function calculateRefund(array $students, int $parentId): array { @@ -26,6 +25,7 @@ class FeeRefundCalculatorService 'parent_id' => $parentId, 'school_year' => $schoolYear, ]); + return $this->resultPayload(0.0, $totalPaid, $refundDeadline, $schoolEndDate, $weeksOfStudy, 0, []); } @@ -36,6 +36,7 @@ class FeeRefundCalculatorService 'parent_id' => $parentId, 'registered_count' => count($registered), ]); + return $this->resultPayload(0.0, $totalPaid, $refundDeadline, $schoolEndDate, $weeksOfStudy, 0, []); } @@ -57,7 +58,7 @@ class FeeRefundCalculatorService $withdrawnWithFees = array_values(array_filter( $allStudents, - fn (array $student): bool => !empty($student['_is_withdrawn']) + fn (array $student): bool => ! empty($student['_is_withdrawn']) )); $refundAmount = 0.0; @@ -82,29 +83,32 @@ class FeeRefundCalculatorService 'reason' => null, ]; - if (!$withdrawalDate) { + if (! $withdrawalDate) { $entry['reason'] = 'missing_withdrawal_date'; Log::warning('Refund skipped for student: missing withdrawal date', [ 'parent_id' => $parentId, 'student_id' => $studentId, ]); $studentBreakdown[] = $entry; + continue; } - if (!$refundDeadline || strtotime($withdrawalDate) > strtotime($refundDeadline)) { + if (! $refundDeadline || strtotime($withdrawalDate) > strtotime($refundDeadline)) { $entry['reason'] = 'after_refund_deadline'; $studentBreakdown[] = $entry; + continue; } - if (!$schoolEndDate) { + if (! $schoolEndDate) { $entry['reason'] = 'missing_school_end_date'; Log::warning('Refund skipped for student: missing school end date', [ 'parent_id' => $parentId, 'student_id' => $studentId, ]); $studentBreakdown[] = $entry; + continue; } @@ -117,6 +121,7 @@ class FeeRefundCalculatorService 'weeks_of_study' => $weeksOfStudy, ]); $studentBreakdown[] = $entry; + continue; } @@ -153,7 +158,7 @@ class FeeRefundCalculatorService if ($uncappedRefund > 0) { $ratio = $totalPaid / $uncappedRefund; foreach ($studentBreakdown as &$row) { - if (!empty($row['eligible'])) { + if (! empty($row['eligible'])) { $row['refund_amount'] = round($row['refund_amount'] * $ratio, 2); } } @@ -192,6 +197,7 @@ class FeeRefundCalculatorService foreach ($students as $student) { if ($this->studentFees->isWithdrawn($student)) { $withdrawn[] = $student; + continue; } @@ -213,6 +219,7 @@ class FeeRefundCalculatorService } $daysRemaining = $withdrawDateObj->diff($schoolEndDateObj)->days; + return (int) min($weeksOfStudy, max(0, (int) ceil($daysRemaining / 7))); } diff --git a/app/Services/Fees/FeeRefundDetailService.php b/app/Services/Fees/FeeRefundDetailService.php index 2affe056..ecb89d93 100644 --- a/app/Services/Fees/FeeRefundDetailService.php +++ b/app/Services/Fees/FeeRefundDetailService.php @@ -37,11 +37,12 @@ class FeeRefundDetailService $tuitionStudents = $registeredKids; foreach ($withdrawnKids as $kid) { $rawDate = $kid['withdrawal_date'] ?? null; - if (empty($rawDate) || !$deadline) { + if (empty($rawDate) || ! $deadline) { Log::warning('Missing withdrawal date or deadline; billing withdrawn student.', [ 'student_id' => $kid['student_id'] ?? null, ]); $tuitionStudents[] = $kid; + continue; } @@ -52,6 +53,7 @@ class FeeRefundDetailService 'student_id' => $kid['student_id'] ?? null, ]); $tuitionStudents[] = $kid; + continue; } @@ -62,12 +64,13 @@ class FeeRefundDetailService $tuitionStudents = array_values(array_filter($tuitionStudents, function ($student) use ($schoolYear) { $sid = (int) ($student['student_id'] ?? 0); + return $sid > 0 && StudentClass::hasNonEventAssignment($sid, $schoolYear); })); foreach ($tuitionStudents as &$student) { $name = null; - if (!empty($student['class_section_id'])) { + if (! empty($student['class_section_id'])) { $name = ClassSection::getClassSectionNameBySectionId($student['class_section_id']); } $student['grade_name'] = is_string($name) ? strtoupper(trim($name)) : 'N/A'; @@ -129,7 +132,7 @@ class FeeRefundDetailService if ($bookPriceRaw === null || $bookPriceRaw === '') { return ['ok' => false, 'error' => 'Book price is not configured.']; } - if (!is_numeric($bookPriceRaw)) { + if (! is_numeric($bookPriceRaw)) { return ['ok' => false, 'error' => 'Book price must be numeric.']; } $bookPrice = round((float) $bookPriceRaw, 2); @@ -146,9 +149,10 @@ class FeeRefundDetailService $status = (string) ($student['enrollment_status'] ?? ''); if (in_array($status, ['enrolled', 'payment pending'], true)) { $registered[] = $student; + continue; } - if (!in_array($status, ['withdrawn', 'refund pending', 'withdraw under review'], true)) { + if (! in_array($status, ['withdrawn', 'refund pending', 'withdraw under review'], true)) { continue; } $hasWithdrawn = true; @@ -169,7 +173,7 @@ class FeeRefundDetailService } } - if (!$hasWithdrawn) { + if (! $hasWithdrawn) { return ['ok' => false, 'error' => 'No withdrawn students found for refund evaluation.']; } @@ -203,7 +207,7 @@ class FeeRefundDetailService } } - $eligible = !empty($withdrawnEligible); + $eligible = ! empty($withdrawnEligible); $refundAmount = 0.0; $debitAmount = 0.0; @@ -230,9 +234,9 @@ class FeeRefundDetailService $refundableCash = min($totalPaid, $refundableTuition); $raw = round($refundableCash - $bookPrice, 2); - if ($raw > 0 && !$fullDiscount) { + if ($raw > 0 && ! $fullDiscount) { $refundAmount = $raw; - } elseif ($raw < 0 && !$fullDiscount) { + } elseif ($raw < 0 && ! $fullDiscount) { $debitAmount = abs($raw); } } @@ -259,6 +263,7 @@ class FeeRefundDetailService public function calculateRefund(array $students, int $parentId): float { $result = $this->calculateRefundDetails($students, $parentId); + return (float) ($result['refund_amount'] ?? 0.0); } @@ -266,6 +271,7 @@ class FeeRefundDetailService { $resolvedGradeFee = $gradeFee ?? (int) (Configuration::getConfig('grade_fee') ?? 9); $grades = new InvoiceGradeService($resolvedGradeFee); + return $grades->getGradeLevel($grade); } @@ -273,6 +279,7 @@ class FeeRefundDetailService { $resolvedGradeFee = $gradeFee ?? (int) (Configuration::getConfig('grade_fee') ?? 9); $grades = new InvoiceGradeService($resolvedGradeFee); + return $grades->compareGrades((string) $gradeA, (string) $gradeB); } diff --git a/app/Services/Fees/FeeStudentFeeService.php b/app/Services/Fees/FeeStudentFeeService.php index 546d92e1..7c6b1a14 100644 --- a/app/Services/Fees/FeeStudentFeeService.php +++ b/app/Services/Fees/FeeStudentFeeService.php @@ -8,19 +8,23 @@ use Illuminate\Support\Facades\Log; class FeeStudentFeeService { public const CATEGORY_FIRST_REGULAR = 'first_regular'; + public const CATEGORY_ADDITIONAL_REGULAR = 'additional_regular'; + public const CATEGORY_YOUTH = 'youth'; + public const CATEGORY_NOT_BILLABLE = 'not_billable'; private const BILLABLE_ENROLLMENT_STATUSES = ['enrolled', 'payment pending']; + private const WITHDRAWN_ENROLLMENT_STATUSES = ['withdrawn', 'refund pending', 'withdraw under review']; + private const ACCEPTED_ADMISSION_STATUS = 'accepted'; public function __construct( private FeeGradeService $grades, private FeeConfigService $config - ) { - } + ) {} /** * Normalize grades, sort by grade level, and assign a tuition fee to each @@ -130,6 +134,7 @@ class FeeStudentFeeService $nonBillable = array_map(function (array $student): array { $student['fee_category'] = self::CATEGORY_NOT_BILLABLE; $student['tuition_fee'] = 0.0; + return $student; }, $nonBillable); @@ -148,7 +153,7 @@ class FeeStudentFeeService $studentRows[] = [ 'student_id' => $student['student_id'] ?? null, - 'student_name' => trim(($student['firstname'] ?? '') . ' ' . ($student['lastname'] ?? '')), + 'student_name' => trim(($student['firstname'] ?? '').' '.($student['lastname'] ?? '')), 'grade' => $student['grade'] ?? null, 'grade_level' => $student['grade_level'] ?? null, 'enrollment_status' => $student['enrollment_status'] ?? null, @@ -192,6 +197,7 @@ class FeeStudentFeeService public function isWithdrawn(array $student): bool { $status = strtolower((string) ($student['enrollment_status'] ?? '')); + return in_array($status, self::WITHDRAWN_ENROLLMENT_STATUSES, true); } diff --git a/app/Services/Fees/TuitionCalculationService.php b/app/Services/Fees/TuitionCalculationService.php index cfeceb33..18d6d640 100644 --- a/app/Services/Fees/TuitionCalculationService.php +++ b/app/Services/Fees/TuitionCalculationService.php @@ -23,16 +23,14 @@ class TuitionCalculationService public function __construct( private FeeConfigService $config, private FeeStudentFeeService $studentFees - ) { - } + ) {} /** * Produce a family + per-student tuition calculation. * - * @param array $students Each item may include: student_id, firstname, - * lastname, grade, class_section_id, enrollment_status, - * admission_status, discount_amount. - * + * @param array $students Each item may include: student_id, firstname, + * lastname, grade, class_section_id, enrollment_status, + * admission_status, discount_amount. * @return array{ * school_year: string, * fees: array, diff --git a/app/Services/Files/ExamDraftDownloadNameService.php b/app/Services/Files/ExamDraftDownloadNameService.php index e08fd3a8..ca156993 100644 --- a/app/Services/Files/ExamDraftDownloadNameService.php +++ b/app/Services/Files/ExamDraftDownloadNameService.php @@ -13,15 +13,15 @@ class ExamDraftDownloadNameService $row = DB::table('exam_drafts as ed') ->select('ed.version', 'ed.exam_type', 'ed.class_section_id', 'cs.class_section_name') ->leftJoin('classSection as cs', 'cs.class_section_id', '=', 'ed.class_section_id') - ->where('ed.' . $column, $filename) + ->where('ed.'.$column, $filename) ->limit(1) ->first(); $row = $row ? (array) $row : []; - $classLabel = trim((string) ($row['class_section_name'] ?? ('Class' . ($row['class_section_id'] ?? '0')))); + $classLabel = trim((string) ($row['class_section_name'] ?? ('Class'.($row['class_section_id'] ?? '0')))); $typeLabel = trim((string) ($row['exam_type'] ?? 'Exam')); - $version = 'v' . max(1, (int) ($row['version'] ?? 1)); + $version = 'v'.max(1, (int) ($row['version'] ?? 1)); $parts = array_filter([ $this->slugify($classLabel), @@ -40,6 +40,7 @@ class ExamDraftDownloadNameService if ($value === '') { return 'Exam'; } + return mb_strtolower($value); } } diff --git a/app/Services/Files/FileServeService.php b/app/Services/Files/FileServeService.php index fb39b566..e0424468 100644 --- a/app/Services/Files/FileServeService.php +++ b/app/Services/Files/FileServeService.php @@ -35,7 +35,7 @@ class FileServeService $headers = [ 'Content-Type' => $meta['mime'], - 'Content-Disposition' => 'inline; filename="' . $meta['download_name'] . '"', + 'Content-Disposition' => 'inline; filename="'.$meta['download_name'].'"', 'Content-Length' => (string) $meta['size'], 'ETag' => $meta['etag'], 'Last-Modified' => $meta['last_modified'], @@ -53,20 +53,20 @@ class FileServeService } $ext = strtolower(pathinfo($name, PATHINFO_EXTENSION)); - if (!in_array($ext, $allowedExtensions, true)) { + if (! in_array($ext, $allowedExtensions, true)) { throw new HttpException(404, 'File not found'); } - $path = rtrim($baseDir, DIRECTORY_SEPARATOR) . DIRECTORY_SEPARATOR . $name; - if (!is_file($path)) { + $path = rtrim($baseDir, DIRECTORY_SEPARATOR).DIRECTORY_SEPARATOR.$name; + if (! is_file($path)) { throw new HttpException(404, 'File not found'); } $mime = $this->detectMime($path); $mtime = filemtime($path) ?: time(); $size = filesize($path) ?: 0; - $etag = md5($name . '|' . $mtime . '|' . $size); - $downloadName = $downloadName ? $downloadName . '.' . $ext : $name; + $etag = md5($name.'|'.$mtime.'|'.$size); + $downloadName = $downloadName ? $downloadName.'.'.$ext : $name; return [ 'name' => $name, @@ -76,7 +76,7 @@ class FileServeService 'size' => $size, 'etag' => $etag, 'download_name' => $downloadName, - 'last_modified' => gmdate('D, d M Y H:i:s', $mtime) . ' GMT', + 'last_modified' => gmdate('D, d M Y H:i:s', $mtime).' GMT', ]; } diff --git a/app/Services/Finance/FinanceNotificationLogService.php b/app/Services/Finance/FinanceNotificationLogService.php index 33229701..5a4531be 100644 --- a/app/Services/Finance/FinanceNotificationLogService.php +++ b/app/Services/Finance/FinanceNotificationLogService.php @@ -16,25 +16,36 @@ class FinanceNotificationLogService 'failed_at' => $status === 'failed' ? now() : null, 'created_by' => $actorId, ])); + return $log->toArray(); } public function list(array $filters): array { $q = FinanceNotificationLog::query(); - foreach (['status','notification_type','parent_id'] as $field) if (!empty($filters[$field])) $q->where($field, $filters[$field]); - if (!empty($filters['date_from'])) $q->whereDate('created_at', '>=', $filters['date_from']); - if (!empty($filters['date_to'])) $q->whereDate('created_at', '<=', $filters['date_to']); - return ['rows' => $q->orderByDesc('id')->paginate((int)($filters['per_page'] ?? 25))]; + foreach (['status', 'notification_type', 'parent_id'] as $field) { + if (! empty($filters[$field])) { + $q->where($field, $filters[$field]); + } + } + if (! empty($filters['date_from'])) { + $q->whereDate('created_at', '>=', $filters['date_from']); + } + if (! empty($filters['date_to'])) { + $q->whereDate('created_at', '<=', $filters['date_to']); + } + + return ['rows' => $q->orderByDesc('id')->paginate((int) ($filters['per_page'] ?? 25))]; } public function nextReceiptNumber(string $schoolYear): string { return DB::transaction(function () use ($schoolYear) { $seq = FinanceReceiptSequence::query()->lockForUpdate()->firstOrCreate(['school_year' => $schoolYear], ['prefix' => 'R', 'next_number' => 1]); - $number = $seq->prefix . '-' . $schoolYear . '-' . str_pad((string) $seq->next_number, 6, '0', STR_PAD_LEFT); + $number = $seq->prefix.'-'.$schoolYear.'-'.str_pad((string) $seq->next_number, 6, '0', STR_PAD_LEFT); $seq->next_number = $seq->next_number + 1; $seq->save(); + return $number; }); } diff --git a/app/Services/Finance/FinancialChartService.php b/app/Services/Finance/FinancialChartService.php index 8beea7a1..c2f7609c 100644 --- a/app/Services/Finance/FinancialChartService.php +++ b/app/Services/Finance/FinancialChartService.php @@ -60,9 +60,9 @@ class FinancialChartService private function downloadChart(array $chartData, string $filename): ?string { $baseUrl = 'https://quickchart.io/chart?c='; - $url = $baseUrl . urlencode(json_encode($chartData)); + $url = $baseUrl.urlencode(json_encode($chartData)); $targetDir = storage_path('app/reports'); - if (!is_dir($targetDir) && !mkdir($targetDir, 0775, true) && !is_dir($targetDir)) { + if (! is_dir($targetDir) && ! mkdir($targetDir, 0775, true) && ! is_dir($targetDir)) { return null; } @@ -71,8 +71,9 @@ class FinancialChartService if ($contents === false) { return null; } - $path = $targetDir . DIRECTORY_SEPARATOR . $filename; + $path = $targetDir.DIRECTORY_SEPARATOR.$filename; file_put_contents($path, $contents); + return $path; } catch (\Throwable $e) { return null; diff --git a/app/Services/Finance/FinancialPaymentService.php b/app/Services/Finance/FinancialPaymentService.php index 5f55bdf7..c22a7b6d 100644 --- a/app/Services/Finance/FinancialPaymentService.php +++ b/app/Services/Finance/FinancialPaymentService.php @@ -50,7 +50,7 @@ class FinancialPaymentService } $method = (string) ($row['method'] ?? ''); $amount = (float) ($row['amount'] ?? 0); - if (!isset($breakdown[$invoiceId])) { + if (! isset($breakdown[$invoiceId])) { $breakdown[$invoiceId] = []; } $breakdown[$invoiceId][$method] = $amount; @@ -85,13 +85,13 @@ class FinancialPaymentService { $query = DB::table('payments'); - if (!empty($schoolYear)) { + if (! empty($schoolYear)) { $query->where('school_year', $schoolYear); } - if (!empty($dateFrom)) { + if (! empty($dateFrom)) { $query->whereRaw('DATE(payment_date) >= ?', [$dateFrom]); } - if (!empty($dateTo)) { + if (! empty($dateTo)) { $query->whereRaw('DATE(payment_date) <= ?', [$dateTo]); } diff --git a/app/Services/Finance/FinancialPdfReportService.php b/app/Services/Finance/FinancialPdfReportService.php index 0f7c28b1..bd1c3be9 100644 --- a/app/Services/Finance/FinancialPdfReportService.php +++ b/app/Services/Finance/FinancialPdfReportService.php @@ -4,16 +4,14 @@ namespace App\Services\Finance; class FinancialPdfReportService { - public function __construct(private FinancialChartService $charts) - { - } + public function __construct(private FinancialChartService $charts) {} public function buildPdf(array $summary): string { - $pdf = new \FPDF(); + $pdf = new \FPDF; $pdf->AddPage(); $pdf->SetFont('Arial', 'B', 16); - $pdf->Cell(0, 10, 'Financial Report for School Year: ' . ($summary['schoolYear'] ?? ''), 0, 1, 'C'); + $pdf->Cell(0, 10, 'Financial Report for School Year: '.($summary['schoolYear'] ?? ''), 0, 1, 'C'); $pdf->Ln(10); $pdf->SetFont('Arial', 'B', 12); @@ -37,12 +35,12 @@ class FinancialPdfReportService $pdf->SetFont('Arial', '', 11); foreach ($rows as $label => $amount) { $pdf->Cell(130, 10, $label, 1); - $pdf->Cell(60, 10, '$' . number_format((float) $amount, 2), 1); + $pdf->Cell(60, 10, '$'.number_format((float) $amount, 2), 1); $pdf->Ln(); } $barChart = $this->charts->generateBarChart($summary); - if (!empty($barChart) && file_exists($barChart)) { + if (! empty($barChart) && file_exists($barChart)) { $pdf->Ln(10); $pdf->SetFont('Arial', 'B', 12); $pdf->Cell(0, 10, 'Summary Graph', 0, 1); @@ -50,7 +48,7 @@ class FinancialPdfReportService } $pieChart = $this->charts->generatePieChart($summary); - if (!empty($pieChart) && file_exists($pieChart)) { + if (! empty($pieChart) && file_exists($pieChart)) { $pdf->Ln(10); $pdf->SetFont('Arial', 'B', 12); $pdf->Cell(0, 10, 'Expense Breakdown', 0, 1); diff --git a/app/Services/Finance/FinancialReportService.php b/app/Services/Finance/FinancialReportService.php index 16ab30ee..1fe0c70c 100644 --- a/app/Services/Finance/FinancialReportService.php +++ b/app/Services/Finance/FinancialReportService.php @@ -9,8 +9,7 @@ class FinancialReportService public function __construct( private FinancialPaymentService $payments, private FinancialSchoolYearService $schoolYears - ) { - } + ) {} public function getReport(?string $dateFrom, ?string $dateTo, ?string $schoolYear): array { @@ -23,7 +22,7 @@ class FinancialReportService $expenseQuery = DB::table('expenses'); $reimbQuery = DB::table('reimbursements'); - if (!empty($schoolYear)) { + if (! empty($schoolYear)) { $invoiceQuery->where('invoices.school_year', $schoolYear); $refundQuery->where('school_year', $schoolYear); $discountQuery->where('school_year', $schoolYear); @@ -31,7 +30,7 @@ class FinancialReportService $reimbQuery->where('school_year', $schoolYear); } - if (!empty($dateFrom)) { + if (! empty($dateFrom)) { $invoiceQuery->whereRaw('DATE(COALESCE(invoices.issue_date, invoices.created_at)) >= ?', [$dateFrom]); $refundQuery->whereRaw('DATE(COALESCE(refunded_at, created_at)) >= ?', [$dateFrom]); $discountQuery->whereRaw('DATE(COALESCE(used_at, created_at)) >= ?', [$dateFrom]); @@ -39,7 +38,7 @@ class FinancialReportService $reimbQuery->whereRaw('DATE(created_at) >= ?', [$dateFrom]); } - if (!empty($dateTo)) { + if (! empty($dateTo)) { $invoiceQuery->whereRaw('DATE(COALESCE(invoices.issue_date, invoices.created_at)) <= ?', [$dateTo]); $refundQuery->whereRaw('DATE(COALESCE(refunded_at, created_at)) <= ?', [$dateTo]); $discountQuery->whereRaw('DATE(COALESCE(used_at, created_at)) <= ?', [$dateTo]); diff --git a/app/Services/Finance/FinancialSchoolYearService.php b/app/Services/Finance/FinancialSchoolYearService.php index 2cb61a51..04038ef4 100644 --- a/app/Services/Finance/FinancialSchoolYearService.php +++ b/app/Services/Finance/FinancialSchoolYearService.php @@ -19,7 +19,7 @@ class FinancialSchoolYearService ->values() ->all(); - if (empty($years) && !empty($fallbackYear)) { + if (empty($years) && ! empty($fallbackYear)) { $years[] = (string) $fallbackYear; } diff --git a/app/Services/Finance/FinancialSummaryService.php b/app/Services/Finance/FinancialSummaryService.php index c0df7c95..a5980ce1 100644 --- a/app/Services/Finance/FinancialSummaryService.php +++ b/app/Services/Finance/FinancialSummaryService.php @@ -10,8 +10,7 @@ class FinancialSummaryService { public function __construct( private FinancialDonationRecipientService $donationRecipients - ) { - } + ) {} public function getSummary(?string $dateFrom, ?string $dateTo, ?string $schoolYear): array { @@ -19,10 +18,10 @@ class FinancialSummaryService $derivedDateFrom = null; $derivedDateTo = null; - if (!empty($schoolYear) && empty($dateFrom) && empty($dateTo)) { + if (! empty($schoolYear) && empty($dateFrom) && empty($dateTo)) { if (preg_match('/^(\\d{4})[^\\d]?(\\d{4})$/', $schoolYear, $m)) { - $derivedDateFrom = $m[1] . '-01-01'; - $derivedDateTo = $m[2] . '-12-31'; + $derivedDateFrom = $m[1].'-01-01'; + $derivedDateTo = $m[2].'-12-31'; } } @@ -30,10 +29,10 @@ class FinancialSummaryService $invoiceDateTo = $dateTo ?: $derivedDateTo; $invoiceQuery = DB::table('invoices')->where('school_year', $schoolYear); - if (!empty($invoiceDateFrom)) { + if (! empty($invoiceDateFrom)) { $invoiceQuery->whereRaw('DATE(COALESCE(issue_date, created_at)) >= ?', [$invoiceDateFrom]); } - if (!empty($invoiceDateTo)) { + if (! empty($invoiceDateTo)) { $invoiceQuery->whereRaw('DATE(COALESCE(issue_date, created_at)) <= ?', [$invoiceDateTo]); } $invoices = $invoiceQuery->get()->map(fn ($row) => (array) $row)->all(); @@ -41,6 +40,7 @@ class FinancialSummaryService $invoiceIds = array_values(array_unique(array_filter(array_map(static function ($row) { $id = (int) ($row['id'] ?? 0); + return $id > 0 ? $id : null; }, $invoices)))); @@ -54,7 +54,7 @@ class FinancialSummaryService $paidByInvoice = []; $discountByInvoice = []; $refundByInvoice = []; - if (!empty($invoiceIds)) { + if (! empty($invoiceIds)) { $paidByInvoice = $this->paymentsByInvoiceIds($invoiceIds, $schoolYear, $invoiceDateFrom, $invoiceDateTo); $discountByInvoice = $this->discountsByInvoiceIds($invoiceIds, $dateFrom, $dateTo); $refundByInvoice = $this->refundsByInvoiceIds($invoiceIds, $dateFrom, $dateTo); @@ -99,14 +99,15 @@ class FinancialSummaryService ->where('ac.school_year', $schoolYear) ->where('ac.status', '!=', 'void'); - if (!empty($dateFrom)) { + if (! empty($dateFrom)) { $query->whereRaw('DATE(ac.due_date) >= ?', [$dateFrom]); } - if (!empty($dateTo)) { + if (! empty($dateTo)) { $query->whereRaw('DATE(ac.due_date) <= ?', [$dateTo]); } $row = (array) $query->first(); + return (float) ($row['amount'] ?? 0); } @@ -123,10 +124,10 @@ class FinancialSummaryService }) ->groupBy('ac.parent_id'); - if (!empty($dateFrom)) { + if (! empty($dateFrom)) { $query->whereRaw('DATE(ac.due_date) >= ?', [$dateFrom]); } - if (!empty($dateTo)) { + if (! empty($dateTo)) { $query->whereRaw('DATE(ac.due_date) <= ?', [$dateTo]); } @@ -146,10 +147,10 @@ class FinancialSummaryService private function sumPayments(string $schoolYear, ?string $dateFrom, ?string $dateTo): float { $query = DB::table('payments')->where('school_year', $schoolYear); - if (!empty($dateFrom)) { + if (! empty($dateFrom)) { $query->whereRaw('DATE(payment_date) >= ?', [$dateFrom]); } - if (!empty($dateTo)) { + if (! empty($dateTo)) { $query->whereRaw('DATE(payment_date) <= ?', [$dateTo]); } @@ -166,6 +167,7 @@ class FinancialSummaryService } $row = (array) $query->selectRaw('COALESCE(SUM(paid_amount), 0) AS paid_amount')->first(); + return (float) ($row['paid_amount'] ?? 0); } @@ -176,10 +178,10 @@ class FinancialSummaryService ->whereIn('invoice_id', $invoiceIds) ->where('school_year', $schoolYear); - if (!empty($dateFrom)) { + if (! empty($dateFrom)) { $query->whereRaw('DATE(payment_date) >= ?', [$dateFrom]); } - if (!empty($dateTo)) { + if (! empty($dateTo)) { $query->whereRaw('DATE(payment_date) <= ?', [$dateTo]); } @@ -203,6 +205,7 @@ class FinancialSummaryService $map[$invoiceId] = (float) ($row['total_paid'] ?? 0); } } + return $map; } @@ -212,10 +215,10 @@ class FinancialSummaryService ->selectRaw('invoice_id, COALESCE(SUM(discount_amount),0) AS total_disc') ->whereIn('invoice_id', $invoiceIds); - if (!empty($dateFrom)) { + if (! empty($dateFrom)) { $query->whereRaw('DATE(COALESCE(used_at, created_at)) >= ?', [$dateFrom]); } - if (!empty($dateTo)) { + if (! empty($dateTo)) { $query->whereRaw('DATE(COALESCE(used_at, created_at)) <= ?', [$dateTo]); } @@ -227,6 +230,7 @@ class FinancialSummaryService $map[$invoiceId] = (float) ($row['total_disc'] ?? 0); } } + return $map; } @@ -237,10 +241,10 @@ class FinancialSummaryService ->whereIn('invoice_id', $invoiceIds) ->whereIn('status', ['Partial', 'Paid']); - if (!empty($dateFrom)) { + if (! empty($dateFrom)) { $query->whereRaw('DATE(COALESCE(refunded_at, created_at)) >= ?', [$dateFrom]); } - if (!empty($dateTo)) { + if (! empty($dateTo)) { $query->whereRaw('DATE(COALESCE(refunded_at, created_at)) <= ?', [$dateTo]); } @@ -252,20 +256,22 @@ class FinancialSummaryService $map[$invoiceId] = (float) ($row['total_refund'] ?? 0); } } + return $map; } private function sumExpenses(string $schoolYear, ?string $dateFrom, ?string $dateTo): float { $query = DB::table('expenses')->where('school_year', $schoolYear); - if (!empty($dateFrom)) { + if (! empty($dateFrom)) { $query->whereRaw('DATE(created_at) >= ?', [$dateFrom]); } - if (!empty($dateTo)) { + if (! empty($dateTo)) { $query->whereRaw('DATE(created_at) <= ?', [$dateTo]); } $row = (array) $query->selectRaw('COALESCE(SUM(amount), 0) AS amount')->first(); + return (float) ($row['amount'] ?? 0); } @@ -277,7 +283,7 @@ class FinancialSummaryService ->selectRaw('COALESCE(SUM(r.amount),0) AS amount') ->leftJoin('expenses as e', 'e.id', '=', 'r.expense_id'); - if (!empty($schoolYear)) { + if (! empty($schoolYear)) { $query->where(function ($q) use ($schoolYear, $normalizedYear) { $q->where(function ($q2) use ($schoolYear, $normalizedYear) { $q2->where('r.school_year', $schoolYear) @@ -298,10 +304,10 @@ class FinancialSummaryService }); } - if (!empty($dateFrom)) { + if (! empty($dateFrom)) { $query->whereRaw('DATE(COALESCE(r.created_at, e.created_at)) >= ?', [$dateFrom]); } - if (!empty($dateTo)) { + if (! empty($dateTo)) { $query->whereRaw('DATE(COALESCE(r.created_at, e.created_at)) <= ?', [$dateTo]); } @@ -317,7 +323,7 @@ class FinancialSummaryService ->whereNull('bi.unassigned_at') ->whereNull('r.id'); - if (!empty($schoolYear)) { + if (! empty($schoolYear)) { $batchFallback->where(function ($q) use ($schoolYear, $normalizedYear) { $q->where('e.school_year', $schoolYear) ->orWhereRaw( @@ -327,10 +333,10 @@ class FinancialSummaryService }); } - if (!empty($dateFrom)) { + if (! empty($dateFrom)) { $batchFallback->whereRaw('DATE(COALESCE(e.created_at, b.closed_at)) >= ?', [$dateFrom]); } - if (!empty($dateTo)) { + if (! empty($dateTo)) { $batchFallback->whereRaw('DATE(COALESCE(e.created_at, b.closed_at)) <= ?', [$dateTo]); } @@ -346,10 +352,10 @@ class FinancialSummaryService ->where('school_year', $schoolYear) ->where('category', 'Donation'); - if (!empty($dateFrom)) { + if (! empty($dateFrom)) { $expenseQuery->whereRaw('DATE(created_at) >= ?', [$dateFrom]); } - if (!empty($dateTo)) { + if (! empty($dateTo)) { $expenseQuery->whereRaw('DATE(created_at) <= ?', [$dateTo]); } @@ -358,14 +364,14 @@ class FinancialSummaryService $donationReimb = 0.0; $recipientIds = $this->donationRecipients->recipientIds(); - if (!empty($recipientIds)) { + if (! empty($recipientIds)) { $normalizedYear = preg_replace('/[^0-9]/', '', (string) $schoolYear); $donationQuery = DB::table('reimbursements as r') ->selectRaw('COALESCE(SUM(r.amount),0) AS amount') ->leftJoin('expenses as e', 'e.id', '=', 'r.expense_id') ->whereIn('r.reimbursed_to', $recipientIds); - if (!empty($schoolYear)) { + if (! empty($schoolYear)) { $donationQuery->where(function ($q) use ($schoolYear, $normalizedYear) { $q->where(function ($q2) use ($schoolYear, $normalizedYear) { $q2->where('r.school_year', $schoolYear) @@ -386,10 +392,10 @@ class FinancialSummaryService }); } - if (!empty($dateFrom)) { + if (! empty($dateFrom)) { $donationQuery->whereRaw('DATE(COALESCE(r.created_at, e.created_at)) >= ?', [$dateFrom]); } - if (!empty($dateTo)) { + if (! empty($dateTo)) { $donationQuery->whereRaw('DATE(COALESCE(r.created_at, e.created_at)) <= ?', [$dateTo]); } @@ -410,14 +416,15 @@ class FinancialSummaryService ->whereIn('status', ['Partial', 'Paid']) ->whereNotNull('refund_paid_amount'); - if (!empty($dateFrom)) { + if (! empty($dateFrom)) { $query->whereRaw('DATE(COALESCE(refunded_at, created_at)) >= ?', [$dateFrom]); } - if (!empty($dateTo)) { + if (! empty($dateTo)) { $query->whereRaw('DATE(COALESCE(refunded_at, created_at)) <= ?', [$dateTo]); } $row = (array) $query->selectRaw('COALESCE(SUM(refund_paid_amount),0) AS refund_paid_amount')->first(); + return (float) ($row['refund_paid_amount'] ?? 0); } @@ -427,14 +434,15 @@ class FinancialSummaryService ->join('invoices as i', 'i.id', '=', 'du.invoice_id') ->where('i.school_year', $schoolYear); - if (!empty($dateFrom)) { + if (! empty($dateFrom)) { $query->whereRaw('DATE(COALESCE(du.used_at, du.created_at)) >= ?', [$dateFrom]); } - if (!empty($dateTo)) { + if (! empty($dateTo)) { $query->whereRaw('DATE(COALESCE(du.used_at, du.created_at)) <= ?', [$dateTo]); } $row = (array) $query->selectRaw('COALESCE(SUM(du.discount_amount),0) AS discount_amount')->first(); + return (float) ($row['discount_amount'] ?? 0); } @@ -454,14 +462,14 @@ class FinancialSummaryService $ref = (float) ($refundByInvoice[$invoiceId] ?? 0); $balance = max(0.0, round($total - $disc - $paid - $ref, 2)); - if (!isset($parentBalances[$parentId])) { + if (! isset($parentBalances[$parentId])) { $parentBalances[$parentId] = 0.0; } $parentBalances[$parentId] += $balance; } foreach ($extraByParent as $parentId => $extra) { - if (!isset($parentBalances[$parentId])) { + if (! isset($parentBalances[$parentId])) { $parentBalances[$parentId] = 0.0; } $parentBalances[$parentId] += (float) $extra; diff --git a/app/Services/Finance/FinancialUnpaidParentsService.php b/app/Services/Finance/FinancialUnpaidParentsService.php index fddd9a56..356f2955 100644 --- a/app/Services/Finance/FinancialUnpaidParentsService.php +++ b/app/Services/Finance/FinancialUnpaidParentsService.php @@ -8,9 +8,7 @@ use Illuminate\Support\Facades\Schema; class FinancialUnpaidParentsService { - public function __construct(private FinancialSchoolYearService $schoolYears) - { - } + public function __construct(private FinancialSchoolYearService $schoolYears) {} public function getUnpaidParents(?string $schoolYear): array { @@ -76,7 +74,7 @@ class FinancialUnpaidParentsService $total = (float) ($row['total_amount'] ?? 0); $balance = max(0.0, round($total - $discSum - $paidSum - $refSum, 2)); - if (!isset($byParent[$parentId])) { + if (! isset($byParent[$parentId])) { $byParent[$parentId] = [ 'parent_id' => $parentId, 'firstname' => (string) ($row['firstname'] ?? ''), @@ -96,7 +94,7 @@ class FinancialUnpaidParentsService } $extraRows = DB::table('additional_charges as ac') - ->selectRaw("ac.parent_id, u.firstname, u.lastname, u.email, COALESCE(SUM(ac.amount), 0) AS total_extra") + ->selectRaw('ac.parent_id, u.firstname, u.lastname, u.email, COALESCE(SUM(ac.amount), 0) AS total_extra') ->leftJoin('users as u', 'u.id', '=', 'ac.parent_id') ->where('ac.school_year', $schoolYear) ->where('ac.status', '!=', 'void') @@ -119,7 +117,7 @@ class FinancialUnpaidParentsService if (abs($extra) < 0.00001) { continue; } - if (!isset($byParent[$parentId])) { + if (! isset($byParent[$parentId])) { $byParent[$parentId] = [ 'parent_id' => $parentId, 'firstname' => (string) ($row['firstname'] ?? ''), @@ -150,8 +148,8 @@ class FinancialUnpaidParentsService } } - $monthsUntil = static function (? \DateTimeImmutable $end) use ($today): int { - if (!$end) { + $monthsUntil = static function (?\DateTimeImmutable $end) use ($today): int { + if (! $end) { return 0; } $y = (int) $end->format('Y') - (int) $today->format('Y'); @@ -160,6 +158,7 @@ class FinancialUnpaidParentsService if ((int) $end->format('j') > (int) $today->format('j')) { $months += 1; } + return max(0, $months); }; @@ -195,7 +194,7 @@ class FinancialUnpaidParentsService $hasPayments = []; $paidTotals = []; $paymentCounts = []; - if (!empty($parentIds)) { + if (! empty($parentIds)) { $pRows = DB::table('payments') ->selectRaw('parent_id, SUM(paid_amount) AS total_paid, COUNT(*) AS payment_count') ->whereIn('parent_id', $parentIds) @@ -217,7 +216,8 @@ class FinancialUnpaidParentsService $results = array_map(function (array $row) use ($hasPayments, $paidTotals, $paymentCounts, $nextInstallment) { $parentId = (int) ($row['parent_id'] ?? 0); - $name = trim((string) ($row['firstname'] ?? '') . ' ' . (string) ($row['lastname'] ?? '')); + $name = trim((string) ($row['firstname'] ?? '').' '.(string) ($row['lastname'] ?? '')); + return [ 'parent_id' => $parentId, 'parent_name' => $name, diff --git a/app/Services/Finance/InstallmentPlanService.php b/app/Services/Finance/InstallmentPlanService.php index aa949ca9..216628aa 100644 --- a/app/Services/Finance/InstallmentPlanService.php +++ b/app/Services/Finance/InstallmentPlanService.php @@ -7,31 +7,33 @@ use App\Models\InvoiceInstallmentPlan; use App\Models\PaymentInstallmentAllocation; use Carbon\Carbon; use Illuminate\Support\Facades\DB; -use Illuminate\Support\Facades\Schema; class InstallmentPlanService { public function list(array $filters): array { $q = InvoiceInstallmentPlan::query(); - foreach (['invoice_id','parent_id','school_year','status'] as $field) { - if (!empty($filters[$field])) $q->where($field, $filters[$field]); + foreach (['invoice_id', 'parent_id', 'school_year', 'status'] as $field) { + if (! empty($filters[$field])) { + $q->where($field, $filters[$field]); + } } - return ['rows' => $q->orderByDesc('id')->paginate((int)($filters['per_page'] ?? 25))]; + + return ['rows' => $q->orderByDesc('id')->paginate((int) ($filters['per_page'] ?? 25))]; } public function createForInvoice(int $invoiceId, array $payload, ?int $actorId): array { return DB::transaction(function () use ($invoiceId, $payload, $actorId) { $invoice = DB::table('invoices')->where('id', $invoiceId)->first(); - abort_if(!$invoice, 404, 'Invoice not found.'); + abort_if(! $invoice, 404, 'Invoice not found.'); $total = (float) ($payload['total_amount'] ?? ($invoice->balance ?? $invoice->total_amount ?? 0)); $count = (int) $payload['number_of_installments']; $plan = InvoiceInstallmentPlan::query()->create([ 'invoice_id' => $invoiceId, 'parent_id' => $invoice->parent_id, 'school_year' => $invoice->school_year ?? null, - 'plan_name' => $payload['plan_name'] ?? ('Invoice #' . $invoiceId . ' installment plan'), + 'plan_name' => $payload['plan_name'] ?? ('Invoice #'.$invoiceId.' installment plan'), 'total_amount' => $total, 'number_of_installments' => $count, 'status' => 'draft', @@ -65,6 +67,7 @@ class InstallmentPlanService { $plan = InvoiceInstallmentPlan::query()->findOrFail($id)->toArray(); $plan['installments'] = InvoiceInstallment::query()->where('installment_plan_id', $id)->orderBy('sequence')->get()->toArray(); + return $plan; } @@ -73,6 +76,7 @@ class InstallmentPlanService $plan = InvoiceInstallmentPlan::query()->findOrFail($id); $plan->fill(['status' => 'active', 'approved_by' => $actorId])->save(); InvoiceInstallment::query()->where('installment_plan_id', $id)->where('status', 'pending')->update(['status' => 'due']); + return $this->show($id); } @@ -80,6 +84,7 @@ class InstallmentPlanService { InvoiceInstallmentPlan::query()->whereKey($id)->update(['status' => 'cancelled']); InvoiceInstallment::query()->where('installment_plan_id', $id)->whereNotIn('status', ['paid'])->update(['status' => 'cancelled']); + return $this->show($id); } @@ -87,14 +92,16 @@ class InstallmentPlanService { return DB::transaction(function () use ($paymentId, $payload) { $payment = DB::table('payments')->where('id', $paymentId)->first(); - abort_if(!$payment, 404, 'Payment not found.'); + abort_if(! $payment, 404, 'Payment not found.'); $allocations = $payload['installments'] ?? null; - if (!$allocations) { + if (! $allocations) { $remaining = (float) ($payment->paid_amount ?? 0); $installments = InvoiceInstallment::query()->where('invoice_id', $payment->invoice_id)->where('balance', '>', 0)->orderBy('due_date')->lockForUpdate()->get(); $allocations = []; foreach ($installments as $inst) { - if ($remaining <= 0) break; + if ($remaining <= 0) { + break; + } $amount = min($remaining, (float) $inst->balance); $allocations[] = ['installment_id' => $inst->id, 'amount' => $amount]; $remaining -= $amount; @@ -105,7 +112,9 @@ class InstallmentPlanService foreach ($allocations as $a) { $inst = InvoiceInstallment::query()->lockForUpdate()->findOrFail($a['installment_id']); $amount = min((float) $a['amount'], (float) $inst->balance); - if ($amount <= 0) continue; + if ($amount <= 0) { + continue; + } $created[] = PaymentInstallmentAllocation::query()->create([ 'payment_id' => $paymentId, 'invoice_id' => $inst->invoice_id, @@ -118,6 +127,7 @@ class InstallmentPlanService $inst->paid_at = $inst->balance <= 0 ? now() : null; $inst->save(); } + return ['allocations' => $created]; }); } @@ -126,11 +136,12 @@ class InstallmentPlanService { $q = InvoiceInstallment::query()->where('balance', '>', 0); $overdue ? $q->whereDate('due_date', '<', now()->toDateString()) : $q->whereDate('due_date', '>=', now()->toDateString()); + return ['rows' => $q->orderBy('due_date')->get()->toArray()]; } public function markOverdue(): int { - return InvoiceInstallment::query()->where('balance', '>', 0)->whereDate('due_date', '<', now()->toDateString())->whereNotIn('status', ['paid','cancelled','waived'])->update(['status' => 'overdue']); + return InvoiceInstallment::query()->where('balance', '>', 0)->whereDate('due_date', '<', now()->toDateString())->whereNotIn('status', ['paid', 'cancelled', 'waived'])->update(['status' => 'overdue']); } } diff --git a/app/Services/Finance/ParentPaymentFollowUpService.php b/app/Services/Finance/ParentPaymentFollowUpService.php index fc084be1..630729bb 100644 --- a/app/Services/Finance/ParentPaymentFollowUpService.php +++ b/app/Services/Finance/ParentPaymentFollowUpService.php @@ -4,6 +4,7 @@ namespace App\Services\Finance; use App\Models\Configuration; use App\Models\FinanceFollowUpNote; +use Carbon\Carbon; use Illuminate\Support\Facades\DB; use Illuminate\Support\Facades\Schema; @@ -35,11 +36,11 @@ class ParentPaymentFollowUpService $discounted = (float) ($discountByInvoice[$invoiceId] ?? 0); $refunded = (float) ($refundByInvoice[$invoiceId] ?? 0); $open = max(0, $total - $paid - $discounted - $refunded); - if (!$includePaid && $open <= 0) { + if (! $includePaid && $open <= 0) { continue; } - if (!isset($parents[$parentId])) { + if (! isset($parents[$parentId])) { $parents[$parentId] = [ 'parent_id' => $parentId, 'parent_name' => $invoice->parent_name ?? null, @@ -71,7 +72,7 @@ class ParentPaymentFollowUpService ]; } - $row =& $parents[$parentId]; + $row = &$parents[$parentId]; $row['invoice_count']++; $row['total_invoiced'] += $total; $row['total_paid'] += $paid; @@ -99,7 +100,7 @@ class ParentPaymentFollowUpService $row['promise_to_pay_amount'] = $note->promise_to_pay_amount !== null ? (float) $note->promise_to_pay_amount : null; $row['escalation_level'] = (int) ($note->escalation_level ?? 0); } - $row['notes_count'] = FinanceFollowUpNote::query()->where('parent_id', $parentId)->when($schoolYear, fn($q) => $q->where('school_year', $schoolYear))->count(); + $row['notes_count'] = FinanceFollowUpNote::query()->where('parent_id', $parentId)->when($schoolYear, fn ($q) => $q->where('school_year', $schoolYear))->count(); $row['days_overdue'] = $this->daysOverdue($row['oldest_unpaid_invoice_date']); $row['aging_bucket'] = $this->agingBucket($row['days_overdue']); $row['risk_flags'] = $this->riskFlags($row); @@ -107,15 +108,26 @@ class ParentPaymentFollowUpService unset($row); $rows = array_values(array_filter($parents, function (array $row) use ($filters, $minimumBalance) { - if ($row['open_balance'] < $minimumBalance) return false; - if (!empty($filters['aging_bucket']) && $row['aging_bucket'] !== $filters['aging_bucket']) return false; - if (!empty($filters['follow_up_status']) && $row['follow_up_status'] !== $filters['follow_up_status']) return false; - if (!empty($filters['next_follow_up_due_before']) && (empty($row['next_follow_up_date']) || strtotime($row['next_follow_up_date']) > strtotime($filters['next_follow_up_due_before']))) return false; - if (!empty($filters['promise_to_pay_due_before']) && (empty($row['promise_to_pay_date']) || strtotime($row['promise_to_pay_date']) > strtotime($filters['promise_to_pay_due_before']))) return false; + if ($row['open_balance'] < $minimumBalance) { + return false; + } + if (! empty($filters['aging_bucket']) && $row['aging_bucket'] !== $filters['aging_bucket']) { + return false; + } + if (! empty($filters['follow_up_status']) && $row['follow_up_status'] !== $filters['follow_up_status']) { + return false; + } + if (! empty($filters['next_follow_up_due_before']) && (empty($row['next_follow_up_date']) || strtotime($row['next_follow_up_date']) > strtotime($filters['next_follow_up_due_before']))) { + return false; + } + if (! empty($filters['promise_to_pay_due_before']) && (empty($row['promise_to_pay_date']) || strtotime($row['promise_to_pay_date']) > strtotime($filters['promise_to_pay_due_before']))) { + return false; + } + return true; })); - usort($rows, fn($a, $b) => [$b['open_balance'], $b['days_overdue']] <=> [$a['open_balance'], $a['days_overdue']]); + usort($rows, fn ($a, $b) => [$b['open_balance'], $b['days_overdue']] <=> [$a['open_balance'], $a['days_overdue']]); return ['schoolYear' => $schoolYear, 'generatedAt' => now()->toISOString(), 'results' => $rows]; } @@ -141,56 +153,78 @@ class ParentPaymentFollowUpService public function csvRows(array $report): array { - $rows = [['Parent ID','Parent','Email','Phone','Students','School Year','Invoices','Open Invoices','Total Invoiced','Total Paid','Open Balance','Oldest Unpaid','Days Overdue','Aging','Status','Next Follow-Up','Promise Date','Promise Amount','Risk Flags']]; + $rows = [['Parent ID', 'Parent', 'Email', 'Phone', 'Students', 'School Year', 'Invoices', 'Open Invoices', 'Total Invoiced', 'Total Paid', 'Open Balance', 'Oldest Unpaid', 'Days Overdue', 'Aging', 'Status', 'Next Follow-Up', 'Promise Date', 'Promise Amount', 'Risk Flags']]; foreach ($report['results'] as $r) { $rows[] = [ $r['parent_id'], $r['parent_name'], $r['parent_email'], $r['parent_phone'], implode('; ', $r['student_names'] ?? []), $r['school_year'], $r['invoice_count'], $r['open_invoice_count'], $r['total_invoiced'], $r['total_paid'], $r['open_balance'], $r['oldest_unpaid_invoice_date'], $r['days_overdue'], $r['aging_bucket'], $r['follow_up_status'], $r['next_follow_up_date'], $r['promise_to_pay_date'], $r['promise_to_pay_amount'], implode('; ', $r['risk_flags'] ?? []), ]; } + return $rows; } private function invoiceRows(string $schoolYear, array $filters) { $q = DB::table('invoices')->where('invoices.school_year', $schoolYear); - if (!empty($filters['parent_id'])) $q->where('invoices.parent_id', $filters['parent_id']); - if (!empty($filters['date_from'])) $q->whereDate('invoices.created_at', '>=', $filters['date_from']); - if (!empty($filters['date_to'])) $q->whereDate('invoices.created_at', '<=', $filters['date_to']); + if (! empty($filters['parent_id'])) { + $q->where('invoices.parent_id', $filters['parent_id']); + } + if (! empty($filters['date_from'])) { + $q->whereDate('invoices.created_at', '>=', $filters['date_from']); + } + if (! empty($filters['date_to'])) { + $q->whereDate('invoices.created_at', '<=', $filters['date_to']); + } if (Schema::hasTable('users')) { $name = $this->nameExpression('users'); $q->leftJoin('users', 'users.id', '=', 'invoices.parent_id') - ->addSelect('invoices.*') - ->addSelect(DB::raw($name . ' as parent_name')) - ->addSelect('users.email as parent_email'); - if (Schema::hasColumn('users', 'phone')) $q->addSelect('users.phone as parent_phone'); - else $q->addSelect(DB::raw('NULL as parent_phone')); + ->addSelect('invoices.*') + ->addSelect(DB::raw($name.' as parent_name')) + ->addSelect('users.email as parent_email'); + if (Schema::hasColumn('users', 'phone')) { + $q->addSelect('users.phone as parent_phone'); + } else { + $q->addSelect(DB::raw('NULL as parent_phone')); + } } else { $q->select('invoices.*', DB::raw('NULL as parent_name'), DB::raw('NULL as parent_email'), DB::raw('NULL as parent_phone')); } + return $q->get(); } private function paymentTotalsByInvoice(string $schoolYear): array { - if (!Schema::hasTable('payments')) return []; + if (! Schema::hasTable('payments')) { + return []; + } + return DB::table('payments')->select('invoice_id', DB::raw('COALESCE(SUM(paid_amount),0) total'), DB::raw('MAX(payment_date) last_payment_date')) - ->where('school_year', $schoolYear)->whereNotIn('status', ['void','voided','failed','cancelled','canceled','reversed']) - ->groupBy('invoice_id')->pluck('total', 'invoice_id')->map(fn($v) => (float) $v)->all(); + ->where('school_year', $schoolYear)->whereNotIn('status', ['void', 'voided', 'failed', 'cancelled', 'canceled', 'reversed']) + ->groupBy('invoice_id')->pluck('total', 'invoice_id')->map(fn ($v) => (float) $v)->all(); } private function discountTotalsByInvoice(): array { - if (!Schema::hasTable('discount_usages')) return []; + if (! Schema::hasTable('discount_usages')) { + return []; + } $col = Schema::hasColumn('discount_usages', 'discount_amount') ? 'discount_amount' : 'amount'; - return DB::table('discount_usages')->select('invoice_id', DB::raw('COALESCE(SUM('.$col.'),0) total'))->groupBy('invoice_id')->pluck('total','invoice_id')->map(fn($v)=>(float)$v)->all(); + + return DB::table('discount_usages')->select('invoice_id', DB::raw('COALESCE(SUM('.$col.'),0) total'))->groupBy('invoice_id')->pluck('total', 'invoice_id')->map(fn ($v) => (float) $v)->all(); } private function refundTotalsByInvoice(): array { - if (!Schema::hasTable('refunds')) return []; + if (! Schema::hasTable('refunds')) { + return []; + } $col = Schema::hasColumn('refunds', 'refund_amount') ? 'refund_amount' : (Schema::hasColumn('refunds', 'amount') ? 'amount' : null); - if (!$col) return []; - return DB::table('refunds')->select('invoice_id', DB::raw('COALESCE(SUM('.$col.'),0) total'))->whereNotIn('status', ['void','voided','cancelled','canceled'])->groupBy('invoice_id')->pluck('total','invoice_id')->map(fn($v)=>(float)$v)->all(); + if (! $col) { + return []; + } + + return DB::table('refunds')->select('invoice_id', DB::raw('COALESCE(SUM('.$col.'),0) total'))->whereNotIn('status', ['void', 'voided', 'cancelled', 'canceled'])->groupBy('invoice_id')->pluck('total', 'invoice_id')->map(fn ($v) => (float) $v)->all(); } private function latestNotesByParent(string $schoolYear): array @@ -200,40 +234,66 @@ class ParentPaymentFollowUpService private function studentsByParent(): array { - if (!Schema::hasTable('students') || !Schema::hasColumn('students', 'parent_id')) return []; + if (! Schema::hasTable('students') || ! Schema::hasColumn('students', 'parent_id')) { + return []; + } $name = $this->nameExpression('students'); - return DB::table('students')->select('parent_id', DB::raw($name.' as student_name'))->whereNotNull('parent_id')->get()->groupBy('parent_id')->map(fn($rows) => $rows->pluck('student_name')->filter()->values()->all())->all(); + + return DB::table('students')->select('parent_id', DB::raw($name.' as student_name'))->whereNotNull('parent_id')->get()->groupBy('parent_id')->map(fn ($rows) => $rows->pluck('student_name')->filter()->values()->all())->all(); } private function nameExpression(string $table): string { - if (Schema::hasColumn($table, 'name')) return $table.'.name'; - if (Schema::hasColumn($table, 'full_name')) return $table.'.full_name'; + if (Schema::hasColumn($table, 'name')) { + return $table.'.name'; + } + if (Schema::hasColumn($table, 'full_name')) { + return $table.'.full_name'; + } $driver = DB::connection()->getDriverName(); $concat = $driver === 'sqlite' ? '%s || \' \' || %s' : "CONCAT(%s, ' ', %s)"; - if (Schema::hasColumn($table, 'firstname') && Schema::hasColumn($table, 'lastname')) return sprintf($concat, $table.'.firstname', $table.'.lastname'); - if (Schema::hasColumn($table, 'first_name') && Schema::hasColumn($table, 'last_name')) return sprintf($concat, $table.'.first_name', $table.'.last_name'); + if (Schema::hasColumn($table, 'firstname') && Schema::hasColumn($table, 'lastname')) { + return sprintf($concat, $table.'.firstname', $table.'.lastname'); + } + if (Schema::hasColumn($table, 'first_name') && Schema::hasColumn($table, 'last_name')) { + return sprintf($concat, $table.'.first_name', $table.'.last_name'); + } + return "CAST($table.id AS CHAR)"; } private function daysOverdue(?string $date): int { - if (!$date) return 0; - return max(0, now()->startOfDay()->diffInDays(\Carbon\Carbon::parse($date)->startOfDay(), false) * -1); + if (! $date) { + return 0; + } + + return max(0, now()->startOfDay()->diffInDays(Carbon::parse($date)->startOfDay(), false) * -1); } private function agingBucket(int $days): string { - return match (true) { $days <= 0 => 'current', $days <= 30 => '1-30 days overdue', $days <= 60 => '31-60 days overdue', $days <= 90 => '61-90 days overdue', default => '90+ days overdue' }; + return match (true) { + $days <= 0 => 'current', $days <= 30 => '1-30 days overdue', $days <= 60 => '31-60 days overdue', $days <= 90 => '61-90 days overdue', default => '90+ days overdue' + }; } private function riskFlags(array $row): array { $flags = []; - if ($row['days_overdue'] > 90) $flags[] = '90+ days overdue'; - if ($row['open_balance'] > 1000) $flags[] = 'high balance'; - if ($row['promise_to_pay_date'] && strtotime($row['promise_to_pay_date']) < strtotime(date('Y-m-d'))) $flags[] = 'missed promise-to-pay'; - if (!$row['parent_email'] && !$row['parent_phone']) $flags[] = 'missing contact info'; + if ($row['days_overdue'] > 90) { + $flags[] = '90+ days overdue'; + } + if ($row['open_balance'] > 1000) { + $flags[] = 'high balance'; + } + if ($row['promise_to_pay_date'] && strtotime($row['promise_to_pay_date']) < strtotime(date('Y-m-d'))) { + $flags[] = 'missed promise-to-pay'; + } + if (! $row['parent_email'] && ! $row['parent_phone']) { + $flags[] = 'missing contact info'; + } + return $flags; } } diff --git a/app/Services/Finance/PriorYearBalanceCarryforwardService.php b/app/Services/Finance/PriorYearBalanceCarryforwardService.php index 66b4a53b..e9ead37f 100644 --- a/app/Services/Finance/PriorYearBalanceCarryforwardService.php +++ b/app/Services/Finance/PriorYearBalanceCarryforwardService.php @@ -20,16 +20,20 @@ class PriorYearBalanceCarryforwardService $refunds = $this->refundTotals(); $q = DB::table('invoices')->where('school_year', $from); - if ($parentId) $q->where('parent_id', $parentId); + if ($parentId) { + $q->where('parent_id', $parentId); + } $invoices = $q->get(); $byParent = []; foreach ($invoices as $invoice) { $invoiceId = (int) $invoice->id; $balance = max(0, (float) ($invoice->total_amount ?? 0) - (float) ($payments[$invoiceId] ?? 0) - (float) ($discounts[$invoiceId] ?? 0) - (float) ($refunds[$invoiceId] ?? 0)); - if ($balance <= 0) continue; + if ($balance <= 0) { + continue; + } $pid = (int) $invoice->parent_id; - if (!isset($byParent[$pid])) { + if (! isset($byParent[$pid])) { $existing = FinanceBalanceCarryforward::query()->where('parent_id', $pid)->where('from_school_year', $from)->where('to_school_year', $to)->first(); $byParent[$pid] = [ 'parent_id' => $pid, @@ -41,15 +45,17 @@ class PriorYearBalanceCarryforwardService 'existing_carryforward_id' => $existing?->id, 'warnings' => [], ]; - if ($existing) $byParent[$pid]['warnings'][] = 'source invoice already carried forward'; + if ($existing) { + $byParent[$pid]['warnings'][] = 'source invoice already carried forward'; + } } $byParent[$pid]['source_invoice_ids'][] = $invoiceId; $byParent[$pid]['source_balance_amount'] += $balance; $byParent[$pid]['proposed_carryforward_amount'] += $balance; } - $rows = array_values(array_filter($byParent, fn($r) => $r['source_balance_amount'] >= $minimum)); - usort($rows, fn($a, $b) => $b['source_balance_amount'] <=> $a['source_balance_amount']); + $rows = array_values(array_filter($byParent, fn ($r) => $r['source_balance_amount'] >= $minimum)); + usort($rows, fn ($a, $b) => $b['source_balance_amount'] <=> $a['source_balance_amount']); return ['fromSchoolYear' => $from, 'toSchoolYear' => $to, 'generatedAt' => now()->toISOString(), 'rows' => $rows]; } @@ -59,7 +65,9 @@ class PriorYearBalanceCarryforwardService $preview = $this->preview($filters); $created = []; foreach ($preview['rows'] as $row) { - if (!empty($row['existing_carryforward_id'])) continue; + if (! empty($row['existing_carryforward_id'])) { + continue; + } $created[] = FinanceBalanceCarryforward::query()->create([ 'parent_id' => $row['parent_id'], 'from_school_year' => $row['from_school_year'], @@ -74,6 +82,7 @@ class PriorYearBalanceCarryforwardService 'metadata_json' => ['warnings' => $row['warnings']], ])->toArray(); } + return ['created' => $created, 'skipped' => count($preview['rows']) - count($created)]; } @@ -81,6 +90,7 @@ class PriorYearBalanceCarryforwardService { $cf = FinanceBalanceCarryforward::query()->findOrFail($id); $cf->fill(['status' => 'approved', 'approved_by' => $actorId, 'approved_at' => now()])->save(); + return $cf->fresh()->toArray(); } @@ -94,6 +104,7 @@ class PriorYearBalanceCarryforwardService $cf->reason = $reason ?: $cf->reason; $cf->approved_by = $cf->approved_by ?: $actorId; $cf->save(); + return $cf->fresh()->toArray(); } @@ -104,6 +115,7 @@ class PriorYearBalanceCarryforwardService $cf->remaining_amount = max(0, (float) $cf->carryforward_amount - (float) $cf->waived_amount + $amount); $cf->reason = $reason ?: $cf->reason; $cf->save(); + return $cf->fresh()->toArray(); } @@ -111,7 +123,7 @@ class PriorYearBalanceCarryforwardService { return DB::transaction(function () use ($id, $actorId) { $cf = FinanceBalanceCarryforward::query()->lockForUpdate()->findOrFail($id); - if (!in_array($cf->status, ['approved','draft','pending_review'], true)) { + if (! in_array($cf->status, ['approved', 'draft', 'pending_review'], true)) { return $cf->toArray() + ['warning' => 'Carryforward is not in a postable status.']; } @@ -120,14 +132,14 @@ class PriorYearBalanceCarryforwardService if (Schema::hasTable('invoices')) { $invoiceId = DB::table('invoices')->insertGetId([ 'parent_id' => $cf->parent_id, - 'invoice_number' => 'CF-' . $cf->to_school_year . '-' . $cf->id, + 'invoice_number' => 'CF-'.$cf->to_school_year.'-'.$cf->id, 'total_amount' => $amount, 'balance' => $amount, 'paid_amount' => 0, 'school_year' => $cf->to_school_year, 'issue_date' => now()->toDateString(), 'status' => $amount > 0 ? 'unpaid' : 'paid', - 'description' => 'Previous year balance from ' . $cf->from_school_year, + 'description' => 'Previous year balance from '.$cf->from_school_year, 'created_at' => now(), 'updated_at' => now(), 'updated_by' => $actorId, @@ -137,6 +149,7 @@ class PriorYearBalanceCarryforwardService $cf->status = 'posted_to_new_year'; $cf->remaining_amount = $amount; $cf->save(); + return $cf->fresh()->toArray(); }); } @@ -144,37 +157,54 @@ class PriorYearBalanceCarryforwardService public function report(array $filters): array { $q = FinanceBalanceCarryforward::query(); - foreach (['from_school_year','to_school_year','parent_id','status'] as $field) { - if (!empty($filters[$field])) $q->where($field, $filters[$field]); + foreach (['from_school_year', 'to_school_year', 'parent_id', 'status'] as $field) { + if (! empty($filters[$field])) { + $q->where($field, $filters[$field]); + } } + return ['generatedAt' => now()->toISOString(), 'rows' => $q->orderByDesc('remaining_amount')->get()->toArray()]; } public function csvRows(array $report): array { - $rows = [['ID','Parent ID','From Year','To Year','Source Invoices','Source Balance','Carryforward','Waived','Adjusted','Remaining','Status','Posted Invoice']]; + $rows = [['ID', 'Parent ID', 'From Year', 'To Year', 'Source Invoices', 'Source Balance', 'Carryforward', 'Waived', 'Adjusted', 'Remaining', 'Status', 'Posted Invoice']]; foreach ($report['rows'] as $r) { - $rows[] = [$r['id'],$r['parent_id'],$r['from_school_year'],$r['to_school_year'],json_encode($r['source_invoice_ids_json'] ?? []),$r['source_balance_amount'],$r['carryforward_amount'],$r['waived_amount'],$r['adjusted_amount'],$r['remaining_amount'],$r['status'],$r['posted_invoice_id'] ?? null]; + $rows[] = [$r['id'], $r['parent_id'], $r['from_school_year'], $r['to_school_year'], json_encode($r['source_invoice_ids_json'] ?? []), $r['source_balance_amount'], $r['carryforward_amount'], $r['waived_amount'], $r['adjusted_amount'], $r['remaining_amount'], $r['status'], $r['posted_invoice_id'] ?? null]; } + return $rows; } private function paymentTotals(string $schoolYear): array { - if (!Schema::hasTable('payments')) return []; - return DB::table('payments')->where('school_year', $schoolYear)->whereNotIn('status', ['void','voided','failed','cancelled','canceled','reversed'])->select('invoice_id', DB::raw('COALESCE(SUM(paid_amount),0) total'))->groupBy('invoice_id')->pluck('total','invoice_id')->map(fn($v)=>(float)$v)->all(); + if (! Schema::hasTable('payments')) { + return []; + } + + return DB::table('payments')->where('school_year', $schoolYear)->whereNotIn('status', ['void', 'voided', 'failed', 'cancelled', 'canceled', 'reversed'])->select('invoice_id', DB::raw('COALESCE(SUM(paid_amount),0) total'))->groupBy('invoice_id')->pluck('total', 'invoice_id')->map(fn ($v) => (float) $v)->all(); } + private function discountTotals(): array { - if (!Schema::hasTable('discount_usages')) return []; - $col = Schema::hasColumn('discount_usages','discount_amount') ? 'discount_amount' : 'amount'; - return DB::table('discount_usages')->select('invoice_id', DB::raw('COALESCE(SUM('.$col.'),0) total'))->groupBy('invoice_id')->pluck('total','invoice_id')->map(fn($v)=>(float)$v)->all(); + if (! Schema::hasTable('discount_usages')) { + return []; + } + $col = Schema::hasColumn('discount_usages', 'discount_amount') ? 'discount_amount' : 'amount'; + + return DB::table('discount_usages')->select('invoice_id', DB::raw('COALESCE(SUM('.$col.'),0) total'))->groupBy('invoice_id')->pluck('total', 'invoice_id')->map(fn ($v) => (float) $v)->all(); } + private function refundTotals(): array { - if (!Schema::hasTable('refunds')) return []; - $col = Schema::hasColumn('refunds','refund_amount') ? 'refund_amount' : (Schema::hasColumn('refunds','amount') ? 'amount' : null); - if (!$col) return []; - return DB::table('refunds')->whereNotIn('status', ['void','voided','cancelled','canceled'])->select('invoice_id', DB::raw('COALESCE(SUM('.$col.'),0) total'))->groupBy('invoice_id')->pluck('total','invoice_id')->map(fn($v)=>(float)$v)->all(); + if (! Schema::hasTable('refunds')) { + return []; + } + $col = Schema::hasColumn('refunds', 'refund_amount') ? 'refund_amount' : (Schema::hasColumn('refunds', 'amount') ? 'amount' : null); + if (! $col) { + return []; + } + + return DB::table('refunds')->whereNotIn('status', ['void', 'voided', 'cancelled', 'canceled'])->select('invoice_id', DB::raw('COALESCE(SUM('.$col.'),0) total'))->groupBy('invoice_id')->pluck('total', 'invoice_id')->map(fn ($v) => (float) $v)->all(); } } diff --git a/app/Services/Finance/StakeholderFinancialAnalysisService.php b/app/Services/Finance/StakeholderFinancialAnalysisService.php index ab9415ef..98f9c754 100644 --- a/app/Services/Finance/StakeholderFinancialAnalysisService.php +++ b/app/Services/Finance/StakeholderFinancialAnalysisService.php @@ -3,6 +3,7 @@ namespace App\Services\Finance; use App\Models\Configuration; +use Illuminate\Support\Carbon; use Illuminate\Support\Facades\DB; use Illuminate\Support\Facades\Schema; @@ -21,7 +22,7 @@ class StakeholderFinancialAnalysisService $current = $this->periodMetrics($schoolYear, $rangeFrom, $rangeTo); $comparison = null; - if (!empty($compareSchoolYear)) { + if (! empty($compareSchoolYear)) { [$compareFrom, $compareTo] = $this->resolveDateRange($compareSchoolYear, null, null); $comparison = $this->periodMetrics($compareSchoolYear, $compareFrom, $compareTo); } @@ -70,6 +71,7 @@ class StakeholderFinancialAnalysisService foreach ($value as $bucket => $amount) { $rows[] = ['Receivables Aging', $bucket, $amount]; } + continue; } $rows[] = ['Receivables', $key, is_scalar($value) ? $value : json_encode($value)]; @@ -81,14 +83,15 @@ class StakeholderFinancialAnalysisService $rows[] = ['Payment Method Mix', $method, $amount]; } foreach (($analysis['monthlyTrend'] ?? []) as $month => $data) { - $rows[] = ['Monthly Trend', $month . ' charges', $data['charges'] ?? 0]; - $rows[] = ['Monthly Trend', $month . ' collections', $data['collections'] ?? 0]; - $rows[] = ['Monthly Trend', $month . ' expenses', $data['expenses'] ?? 0]; - $rows[] = ['Monthly Trend', $month . ' netCash', $data['netCash'] ?? 0]; + $rows[] = ['Monthly Trend', $month.' charges', $data['charges'] ?? 0]; + $rows[] = ['Monthly Trend', $month.' collections', $data['collections'] ?? 0]; + $rows[] = ['Monthly Trend', $month.' expenses', $data['expenses'] ?? 0]; + $rows[] = ['Monthly Trend', $month.' netCash', $data['netCash'] ?? 0]; } foreach (($analysis['riskFlags'] ?? []) as $flag) { $rows[] = ['Risk Flag', $flag['level'] ?? '', $flag['message'] ?? '']; } + return $rows; } @@ -169,18 +172,19 @@ class StakeholderFinancialAnalysisService private function invoiceTotals(string $schoolYear, ?string $dateFrom, ?string $dateTo): array { - if (!Schema::hasTable('invoices')) { + if (! Schema::hasTable('invoices')) { return ['total' => 0.0, 'count' => 0]; } $q = DB::table('invoices')->where('school_year', $schoolYear); $this->applyDateRange($q, $this->dateColumn('invoices', ['issue_date', 'created_at']), $dateFrom, $dateTo); $row = (array) $q->selectRaw('COALESCE(SUM(total_amount),0) as total, COUNT(*) as count')->first(); + return ['total' => (float) ($row['total'] ?? 0), 'count' => (int) ($row['count'] ?? 0)]; } private function paymentTotals(string $schoolYear, ?string $dateFrom, ?string $dateTo): array { - if (!Schema::hasTable('payments')) { + if (! Schema::hasTable('payments')) { return ['total' => 0.0, 'familyCount' => 0, 'byMethod' => []]; } $base = DB::table('payments')->where('school_year', $schoolYear); @@ -198,12 +202,13 @@ class StakeholderFinancialAnalysisService $arr = (array) $row; $byMethod[(string) ($arr['method'] ?? 'unknown')] = (float) ($arr['total'] ?? 0); } + return ['total' => $total, 'familyCount' => $familyCount, 'byMethod' => $byMethod]; } private function refundTotals(string $schoolYear, ?string $dateFrom, ?string $dateTo): array { - if (!Schema::hasTable('refunds')) { + if (! Schema::hasTable('refunds')) { return ['total' => 0.0]; } $q = DB::table('refunds')->where('school_year', $schoolYear); @@ -212,13 +217,14 @@ class StakeholderFinancialAnalysisService } $this->applyDateRange($q, $this->dateColumn('refunds', ['refunded_at', 'approved_at', 'created_at']), $dateFrom, $dateTo); $amountColumn = Schema::hasColumn('refunds', 'refund_paid_amount') ? 'refund_paid_amount' : 'refund_amount'; - $row = (array) $q->selectRaw('COALESCE(SUM(' . $amountColumn . '),0) as total')->first(); + $row = (array) $q->selectRaw('COALESCE(SUM('.$amountColumn.'),0) as total')->first(); + return ['total' => (float) ($row['total'] ?? 0)]; } private function discountTotals(string $schoolYear, ?string $dateFrom, ?string $dateTo): array { - if (!Schema::hasTable('discount_usages')) { + if (! Schema::hasTable('discount_usages')) { return ['total' => 0.0]; } $q = DB::table('discount_usages as du'); @@ -228,19 +234,21 @@ class StakeholderFinancialAnalysisService $this->excludeStatuses($q, 'discount_usages', $this->excludedChargeStatuses, 'du.status'); $this->applyDateRange($q, Schema::hasColumn('discount_usages', 'created_at') ? 'du.created_at' : null, $dateFrom, $dateTo); $amountColumn = Schema::hasColumn('discount_usages', 'discount_amount') ? 'discount_amount' : 'amount'; - $row = (array) $q->selectRaw('COALESCE(SUM(du.' . $amountColumn . '),0) as total')->first(); + $row = (array) $q->selectRaw('COALESCE(SUM(du.'.$amountColumn.'),0) as total')->first(); + return ['total' => (float) ($row['total'] ?? 0)]; } private function additionalChargeTotals(string $schoolYear, ?string $dateFrom, ?string $dateTo): array { - if (!Schema::hasTable('additional_charges')) { + if (! Schema::hasTable('additional_charges')) { return ['total' => 0.0]; } $q = DB::table('additional_charges')->where('school_year', $schoolYear); $this->excludeStatuses($q, 'additional_charges', $this->excludedChargeStatuses); $this->applyDateRange($q, $this->dateColumn('additional_charges', ['due_date', 'created_at']), $dateFrom, $dateTo); $row = (array) $q->selectRaw('COALESCE(SUM(amount),0) as total')->first(); + return ['total' => (float) ($row['total'] ?? 0)]; } @@ -260,20 +268,21 @@ class StakeholderFinancialAnalysisService return ['total' => 0.0]; } $this->applyDateRange($q, $this->dateColumn($table, ['charge_date', 'created_at']), $dateFrom, $dateTo); - $row = (array) $q->selectRaw('COALESCE(SUM(' . $amountColumn . '),0) as total')->first(); + $row = (array) $q->selectRaw('COALESCE(SUM('.$amountColumn.'),0) as total')->first(); + return ['total' => (float) ($row['total'] ?? 0)]; } private function expenseTotals(string $schoolYear, ?string $dateFrom, ?string $dateTo): array { - if (!Schema::hasTable('expenses')) { + if (! Schema::hasTable('expenses')) { return ['total' => 0.0, 'byCategory' => []]; } $q = DB::table('expenses')->where('school_year', $schoolYear); $this->excludeStatuses($q, 'expenses', ['void', 'voided', 'rejected', 'cancelled', 'canceled']); $this->applyDateRange($q, $this->dateColumn('expenses', ['expense_date', 'date', 'created_at']), $dateFrom, $dateTo); $amountColumn = Schema::hasColumn('expenses', 'amount') ? 'amount' : 'total_amount'; - $total = (float) ((array) (clone $q)->selectRaw('COALESCE(SUM(' . $amountColumn . '),0) as total')->first())['total']; + $total = (float) ((array) (clone $q)->selectRaw('COALESCE(SUM('.$amountColumn.'),0) as total')->first())['total']; $categoryColumn = Schema::hasColumn('expenses', 'category') ? 'category' : (Schema::hasColumn('expenses', 'expense_category') ? 'expense_category' : null); $byCategory = []; if ($categoryColumn) { @@ -282,12 +291,13 @@ class StakeholderFinancialAnalysisService $byCategory[(string) ($arr['category'] ?? 'uncategorized')] = (float) ($arr['total'] ?? 0); } } + return ['total' => $total, 'byCategory' => $byCategory]; } private function reimbursementTotals(string $schoolYear, ?string $dateFrom, ?string $dateTo): array { - if (!Schema::hasTable('reimbursements')) { + if (! Schema::hasTable('reimbursements')) { return ['total' => 0.0]; } $q = DB::table('reimbursements')->where('school_year', $schoolYear); @@ -296,7 +306,8 @@ class StakeholderFinancialAnalysisService } $this->applyDateRange($q, $this->dateColumn('reimbursements', ['reimbursed_at', 'created_at']), $dateFrom, $dateTo); $amountColumn = Schema::hasColumn('reimbursements', 'amount') ? 'amount' : (Schema::hasColumn('reimbursements', 'total_amount') ? 'total_amount' : 'reimbursement_amount'); - $row = (array) $q->selectRaw('COALESCE(SUM(' . $amountColumn . '),0) as total')->first(); + $row = (array) $q->selectRaw('COALESCE(SUM('.$amountColumn.'),0) as total')->first(); + return ['total' => (float) ($row['total'] ?? 0)]; } @@ -304,7 +315,7 @@ class StakeholderFinancialAnalysisService { $buckets = ['current' => 0.0, '1_30_days' => 0.0, '31_60_days' => 0.0, '61_90_days' => 0.0, 'over_90_days' => 0.0]; $largest = []; - if (!Schema::hasTable('invoices')) { + if (! Schema::hasTable('invoices')) { return ['total' => 0.0, 'buckets' => $buckets, 'largestOpenInvoices' => []]; } $q = DB::table('invoices')->where('school_year', $schoolYear); @@ -327,8 +338,8 @@ class StakeholderFinancialAnalysisService $dueRaw = $arr['due_date'] ?? $arr['created_at'] ?? null; $days = 0; try { - if (!empty($dueRaw)) { - $days = max(0, (int) \Illuminate\Support\Carbon::parse($dueRaw)->startOfDay()->diffInDays($today, false)); + if (! empty($dueRaw)) { + $days = max(0, (int) Carbon::parse($dueRaw)->startOfDay()->diffInDays($today, false)); } } catch (\Throwable $e) { $days = 0; @@ -344,6 +355,7 @@ class StakeholderFinancialAnalysisService ]; } usort($largest, fn ($a, $b) => (float) $b['balance'] <=> (float) $a['balance']); + return ['total' => $total, 'buckets' => $buckets, 'largestOpenInvoices' => array_slice($largest, 0, 10)]; } @@ -367,12 +379,13 @@ class StakeholderFinancialAnalysisService ]; } ksort($months); + return $months; } private function monthlySum(string $table, string $amountColumn, string $schoolYear, ?string $dateColumn, ?string $dateFrom, ?string $dateTo, array $excludedStatuses = []): array { - if (!Schema::hasTable($table) || !Schema::hasColumn($table, $amountColumn) || $dateColumn === null) { + if (! Schema::hasTable($table) || ! Schema::hasColumn($table, $amountColumn) || $dateColumn === null) { return []; } $q = DB::table($table); @@ -385,17 +398,18 @@ class StakeholderFinancialAnalysisService $monthExpr = $driver === 'sqlite' ? "strftime('%Y-%m', $dateColumn)" : "DATE_FORMAT($dateColumn, '%Y-%m')"; - $rows = $q->selectRaw($monthExpr . ' as month, COALESCE(SUM(' . $amountColumn . '),0) as total') + $rows = $q->selectRaw($monthExpr.' as month, COALESCE(SUM('.$amountColumn.'),0) as total') ->groupBy('month') ->orderBy('month') ->get(); $out = []; foreach ($rows as $row) { $arr = (array) $row; - if (!empty($arr['month'])) { + if (! empty($arr['month'])) { $out[(string) $arr['month']] = (float) ($arr['total'] ?? 0); } } + return $out; } @@ -410,6 +424,7 @@ class StakeholderFinancialAnalysisService if ($comparison) { $summary['comparison'] = $this->deltas($current['keyMetrics'], $comparison['keyMetrics']); } + return $summary; } @@ -431,6 +446,7 @@ class StakeholderFinancialAnalysisService if (empty($flags)) { $flags[] = ['level' => 'ok', 'message' => 'No automatic risk flags were triggered for the selected period.']; } + return $flags; } @@ -448,15 +464,17 @@ class StakeholderFinancialAnalysisService 'changePct' => $prev != 0.0 ? $this->percent((($cur - $prev) / abs($prev)) * 100) : null, ]; } + return $out; } private function resolveDateRange(string $schoolYear, ?string $dateFrom, ?string $dateTo): array { - if ((!$dateFrom || !$dateTo) && preg_match('/^(\d{4})\D?(\d{4})$/', $schoolYear, $m)) { - $dateFrom = $dateFrom ?: $m[1] . '-01-01'; - $dateTo = $dateTo ?: $m[2] . '-12-31'; + if ((! $dateFrom || ! $dateTo) && preg_match('/^(\d{4})\D?(\d{4})$/', $schoolYear, $m)) { + $dateFrom = $dateFrom ?: $m[1].'-01-01'; + $dateTo = $dateTo ?: $m[2].'-12-31'; } + return [$dateFrom, $dateTo]; } @@ -465,17 +483,17 @@ class StakeholderFinancialAnalysisService if ($column === null) { return; } - if (!empty($dateFrom)) { - $query->whereRaw('DATE(' . $column . ') >= ?', [$dateFrom]); + if (! empty($dateFrom)) { + $query->whereRaw('DATE('.$column.') >= ?', [$dateFrom]); } - if (!empty($dateTo)) { - $query->whereRaw('DATE(' . $column . ') <= ?', [$dateTo]); + if (! empty($dateTo)) { + $query->whereRaw('DATE('.$column.') <= ?', [$dateTo]); } } private function excludeStatuses($query, string $table, array $statuses, ?string $qualifiedColumn = null): void { - if (empty($statuses) || !Schema::hasColumn($table, 'status')) { + if (empty($statuses) || ! Schema::hasColumn($table, 'status')) { return; } $column = $qualifiedColumn ?: 'status'; @@ -486,7 +504,7 @@ class StakeholderFinancialAnalysisService private function dateColumn(string $table, array $candidates): ?string { - if (!Schema::hasTable($table)) { + if (! Schema::hasTable($table)) { return null; } foreach ($candidates as $column) { @@ -494,6 +512,7 @@ class StakeholderFinancialAnalysisService return $column; } } + return null; } diff --git a/app/Services/Frontend/ContactSubmissionService.php b/app/Services/Frontend/ContactSubmissionService.php index 71ce244f..a0ee0228 100644 --- a/app/Services/Frontend/ContactSubmissionService.php +++ b/app/Services/Frontend/ContactSubmissionService.php @@ -2,8 +2,8 @@ namespace App\Services\Frontend; -use App\Models\ContactUs; use App\Models\Configuration; +use App\Models\ContactUs; use App\Services\Email\EmailDispatchService; use App\Services\System\GlobalConfigService; use Illuminate\Support\Facades\Log; @@ -13,8 +13,7 @@ class ContactSubmissionService public function __construct( private EmailDispatchService $emailService, private GlobalConfigService $configService - ) { - } + ) {} public function submit(array $payload): array { @@ -36,20 +35,20 @@ class ContactSubmissionService 'school_year' => $schoolYear ?: '2025-2026', ]); } catch (\Throwable $e) { - Log::error('Contact submission save failed: ' . $e->getMessage()); + Log::error('Contact submission save failed: '.$e->getMessage()); } $recipient = Configuration::getConfig('administrator_email') ?: (string) config('mail.from.address', '') ?: 'alrahma.isgl@gmail.com'; - $htmlMessage = nl2br('You have received a new message from ' . $email . ":\n\n" . $message); + $htmlMessage = nl2br('You have received a new message from '.$email.":\n\n".$message); $emailSent = false; - if (!app()->runningUnitTests()) { + if (! app()->runningUnitTests()) { $emailSent = $this->emailService->send($recipient, $subject, $htmlMessage, null, $email, $email); - if (!$emailSent) { - Log::error('Contact submission email failed for ' . $email); + if (! $emailSent) { + Log::error('Contact submission email failed for '.$email); } } diff --git a/app/Services/Frontend/LandingPageParentDashboardService.php b/app/Services/Frontend/LandingPageParentDashboardService.php index 67aee08d..27e39fef 100644 --- a/app/Services/Frontend/LandingPageParentDashboardService.php +++ b/app/Services/Frontend/LandingPageParentDashboardService.php @@ -21,7 +21,7 @@ class LandingPageParentDashboardService $semester = (string) ($context['semester'] ?? ''); $parentId = $this->primaryParentResolver->resolveFromCredentials($parentUserId, $userType); - if (!$parentId) { + if (! $parentId) { return [ 'ok' => false, 'message' => 'Unable to retrieve student data. Please contact support.', @@ -38,6 +38,7 @@ class LandingPageParentDashboardService $classSection = StudentClass::getClassSectionsByStudentId($student['id'], $schoolYear); $student['class_section'] = $classSection; $student['grade'] = $classSection; + return $student; }) ->all(); diff --git a/app/Services/Frontend/LandingPageRoleService.php b/app/Services/Frontend/LandingPageRoleService.php index 54f82436..c9785853 100644 --- a/app/Services/Frontend/LandingPageRoleService.php +++ b/app/Services/Frontend/LandingPageRoleService.php @@ -9,7 +9,7 @@ class LandingPageRoleService { public function resolveRole(?User $user): string { - if (!$user) { + if (! $user) { return 'guest'; } @@ -22,7 +22,7 @@ class LandingPageRoleService ->map(fn ($r) => strtolower((string) $r)) ->all(); - if (!empty($roles)) { + if (! empty($roles)) { return $roles[0]; } diff --git a/app/Services/Frontend/LandingPageService.php b/app/Services/Frontend/LandingPageService.php index 7bdb6376..0a31e4e7 100644 --- a/app/Services/Frontend/LandingPageService.php +++ b/app/Services/Frontend/LandingPageService.php @@ -11,8 +11,7 @@ class LandingPageService private LandingPageContextService $contextService, private LandingPageTeacherSummaryService $teacherSummaryService, private LandingPageParentDashboardService $parentDashboardService - ) { - } + ) {} public function dashboardForUser(?User $user, ?int $classSectionId = null): array { @@ -46,7 +45,7 @@ class LandingPageService public function teacher(?User $user, ?int $classSectionId = null): array { - if (!$user) { + if (! $user) { return ['role' => 'teacher', 'dashboard' => 'teacher', 'summary' => []]; } @@ -67,7 +66,7 @@ class LandingPageService public function parent(?User $user): array { - if (!$user) { + if (! $user) { return ['role' => 'parent', 'dashboard' => 'parent', 'summary' => []]; } diff --git a/app/Services/Frontend/LandingPageTeacherSummaryService.php b/app/Services/Frontend/LandingPageTeacherSummaryService.php index bd2fe95a..38ce2a95 100644 --- a/app/Services/Frontend/LandingPageTeacherSummaryService.php +++ b/app/Services/Frontend/LandingPageTeacherSummaryService.php @@ -8,7 +8,6 @@ use App\Models\Calendar; use App\Models\ClassSection; use App\Models\ScoreComment; use App\Models\SemesterScore; -use App\Models\StudentClass; use App\Models\TeacherClass; use DateTimeImmutable; use DateTimeZone; @@ -16,9 +15,7 @@ use Illuminate\Support\Facades\DB; class LandingPageTeacherSummaryService { - public function __construct(private LandingPageContextService $context) - { - } + public function __construct(private LandingPageContextService $context) {} public function summary(int $teacherId, ?int $requestedClassSectionId = null): array { @@ -35,7 +32,7 @@ class LandingPageTeacherSummaryService $activeId = null; if ($requestedClassSectionId && in_array($requestedClassSectionId, $availableIds, true)) { $activeId = $requestedClassSectionId; - } elseif (!empty($availableIds)) { + } elseif (! empty($availableIds)) { $activeId = $availableIds[0]; } @@ -48,7 +45,7 @@ class LandingPageTeacherSummaryService } $normalizedSemester = ucfirst(strtolower(trim((string) $semester))); - if (!in_array($normalizedSemester, ['Fall', 'Spring'], true)) { + if (! in_array($normalizedSemester, ['Fall', 'Spring'], true)) { $normalizedSemester = 'Fall'; } @@ -114,7 +111,7 @@ class LandingPageTeacherSummaryService ]; $uniqueStudentIds = []; - if (!empty($classSectionIds)) { + if (! empty($classSectionIds)) { $studentRows = DB::table('student_class as sc') ->select('sc.student_id', 'sc.class_section_id') ->join('students as s', 's.id', '=', 'sc.student_id') @@ -136,7 +133,7 @@ class LandingPageTeacherSummaryService $summary['attendanceSummary']['students'] = $totalStudents; $scoreRows = []; - if (!empty($classSectionIds)) { + if (! empty($classSectionIds)) { $scoreRows = SemesterScore::query() ->select(['student_id', 'class_section_id', 'midterm_exam_score', 'final_exam_score', 'participation_score']) ->whereIn('class_section_id', $classSectionIds) @@ -214,7 +211,7 @@ class LandingPageTeacherSummaryService ]; $commentRows = []; - if (!empty($uniqueStudentIds)) { + if (! empty($uniqueStudentIds)) { $commentRows = ScoreComment::query() ->select(['student_id', 'score_type', 'comment', 'comment_review']) ->whereIn('student_id', $uniqueStudentIds) @@ -238,7 +235,7 @@ class LandingPageTeacherSummaryService if ($type === '') { $type = 'general'; } - if (!isset($commentStats[$type])) { + if (! isset($commentStats[$type])) { $commentStats[$type] = ['pending' => 0, 'reviewed' => 0]; } @@ -261,7 +258,7 @@ class LandingPageTeacherSummaryService } foreach ($expectedCommentTypes as $type) { - if (!isset($commentStats[$type])) { + if (! isset($commentStats[$type])) { $commentStats[$type] = ['pending' => 0, 'reviewed' => 0]; } } @@ -285,7 +282,7 @@ class LandingPageTeacherSummaryService $completionPct = $totalStudents > 0 ? (int) round(min(100, ($filled / $totalStudents) * 100)) : 0; $commentTypes[$type] = [ - 'label' => ucfirst($type) . ' comments', + 'label' => ucfirst($type).' comments', 'pending' => $stats['pending'], 'reviewed' => $stats['reviewed'], 'filled' => $filled, @@ -312,7 +309,7 @@ class LandingPageTeacherSummaryService ]; $attendanceRows = []; - if (!empty($classSectionIds)) { + if (! empty($classSectionIds)) { $attendanceRows = AttendanceRecord::query() ->select(['student_id', 'class_section_id', 'total_absence']) ->whereIn('class_section_id', $classSectionIds) @@ -362,7 +359,7 @@ class LandingPageTeacherSummaryService ->get() ->toArray(); - if ($attendanceStatus['submitted'] === false && !empty($classSectionIds)) { + if ($attendanceStatus['submitted'] === false && ! empty($classSectionIds)) { $summary['attendanceSummary']['completionPct'] = 0; $summary['attendanceSummary']['recorded'] = 0; } @@ -377,20 +374,22 @@ class LandingPageTeacherSummaryService $notifications = []; $buildScoreLink = static function (?string $focus): string { $base = '/teacher/scores'; - return $focus ? $base . '?focus=' . urlencode($focus) : $base; + + return $focus ? $base.'?focus='.urlencode($focus) : $base; }; $determineFocus = static function (array $event): string { $eventTypeText = strtolower(trim((string) ($event['event_type'] ?? ''))); if ($eventTypeText !== '' && str_contains($eventTypeText, 'draft')) { return 'comments'; } - $text = strtolower(trim(($event['title'] ?? '') . ' ' . ($event['description'] ?? ''))); + $text = strtolower(trim(($event['title'] ?? '').' '.($event['description'] ?? ''))); if ($text === '') { return 'scores'; } if (str_contains($text, 'comment') || str_contains($text, 'ptap')) { return 'comments'; } + return 'scores'; }; $calendarLink = '/teacher/calendar'; @@ -443,12 +442,13 @@ class LandingPageTeacherSummaryService $notifications[] = [ 'type' => 'danger', 'message' => sprintf( - "Deadline today: %s is due. Submit the remaining entries before the day ends.", + 'Deadline today: %s is due. Submit the remaining entries before the day ends.', $matchedType ), 'link' => $buildScoreLink($eventFocus), 'linkText' => 'Submit now', ]; + continue; } @@ -460,7 +460,7 @@ class LandingPageTeacherSummaryService $notifications[] = [ 'type' => 'warning', 'message' => sprintf( - "%s is due on %s%s. Review it on the calendar so you don’t miss it.", + '%s is due on %s%s. Review it on the calendar so you don’t miss it.', $matchedType, $eventDateTime->format('M j, Y'), $relativeText @@ -470,13 +470,13 @@ class LandingPageTeacherSummaryService ]; } - if (!$activeDeadline) { + if (! $activeDeadline) { $scoreLabel = $summary['scoreSummary']['fieldLabel'] ?? 'Score'; - if (!empty($summary['scoreSummary']['missing'] ?? 0)) { + if (! empty($summary['scoreSummary']['missing'] ?? 0)) { $notifications[] = [ 'type' => 'warning', 'message' => sprintf( - "You have %s %s entries missing on the scores page. Please finish submitting them.", + 'You have %s %s entries missing on the scores page. Please finish submitting them.', (int) $summary['scoreSummary']['missing'], $scoreLabel ), @@ -493,20 +493,20 @@ class LandingPageTeacherSummaryService $notifications[] = [ 'type' => 'warning', 'message' => sprintf( - "%s comment missing for %s student(s). Leave comments on the scores page.", + '%s comment missing for %s student(s). Leave comments on the scores page.', ucfirst($type), $count ), 'link' => $buildScoreLink('comments'), - 'linkText' => ucfirst($type) . ' comments', + 'linkText' => ucfirst($type).' comments', ]; } - if (!empty($summary['participationSummary']['missing'] ?? 0)) { + if (! empty($summary['participationSummary']['missing'] ?? 0)) { $notifications[] = [ 'type' => 'warning', 'message' => sprintf( - "Participation entries are still missing for %s student(s). Please add them from the scores page.", + 'Participation entries are still missing for %s student(s). Please add them from the scores page.', (int) $summary['participationSummary']['missing'] ), 'link' => $buildScoreLink('scores'), @@ -515,11 +515,11 @@ class LandingPageTeacherSummaryService } } - if (!empty($summary['attendanceStatus']['missingSections'] ?? [])) { + if (! empty($summary['attendanceStatus']['missingSections'] ?? [])) { $missingNames = $summary['attendanceStatus']['missingNames'] ?? []; $label = ''; - if (!empty($missingNames)) { - $label = ' (' . implode(', ', array_slice($missingNames, 0, 3)) . (count($missingNames) > 3 ? '…' : '') . ')'; + if (! empty($missingNames)) { + $label = ' ('.implode(', ', array_slice($missingNames, 0, 3)).(count($missingNames) > 3 ? '…' : '').')'; } $notifications[] = [ 'type' => 'danger', @@ -564,13 +564,13 @@ class LandingPageTeacherSummaryService $missingSections = []; foreach ($classSectionIds as $classSectionId) { $status = $statusMap[$classSectionId] ?? ''; - if (!in_array($status, ['submitted', 'published', 'finalized'], true)) { + if (! in_array($status, ['submitted', 'published', 'finalized'], true)) { $missingSections[] = $classSectionId; } } $missingNames = []; - if (!empty($missingSections)) { + if (! empty($missingSections)) { $sectionRows = ClassSection::query() ->select(['class_section_id', 'class_section_name']) ->whereIn('class_section_id', $missingSections) diff --git a/app/Services/Frontend/ProfileIconService.php b/app/Services/Frontend/ProfileIconService.php index 97be1848..b58c44ef 100644 --- a/app/Services/Frontend/ProfileIconService.php +++ b/app/Services/Frontend/ProfileIconService.php @@ -9,7 +9,7 @@ class ProfileIconService public function buildForUser(int $userId): array { $user = User::query()->find($userId); - if (!$user) { + if (! $user) { return [ 'userName' => 'User not found', 'userInitials' => '??', @@ -28,7 +28,7 @@ class ProfileIconService } return [ - 'userName' => trim($firstname . ' ' . $lastname), + 'userName' => trim($firstname.' '.$lastname), 'userInitials' => $initials !== '' ? $initials : '??', ]; } diff --git a/app/Services/Frontend/StaticPageService.php b/app/Services/Frontend/StaticPageService.php index eea1eca6..3edc2387 100644 --- a/app/Services/Frontend/StaticPageService.php +++ b/app/Services/Frontend/StaticPageService.php @@ -7,9 +7,9 @@ class StaticPageService public function load(string $filename, string $type): array { $safeName = basename($filename); - $path = public_path('html/' . $safeName); + $path = public_path('html/'.$safeName); - if (!is_file($path) || !is_readable($path)) { + if (! is_file($path) || ! is_readable($path)) { return [ 'ok' => false, 'error' => 'Page content not available.', diff --git a/app/Services/Grading/BelowSixtyEmailService.php b/app/Services/Grading/BelowSixtyEmailService.php index 906c2a55..37357e66 100644 --- a/app/Services/Grading/BelowSixtyEmailService.php +++ b/app/Services/Grading/BelowSixtyEmailService.php @@ -2,16 +2,14 @@ namespace App\Services\Grading; -use App\Support\MailHtml; use App\Services\EmailService; +use App\Support\MailHtml; use Illuminate\Support\Facades\DB; use Illuminate\Support\Facades\Log; class BelowSixtyEmailService { - public function __construct(private EmailService $emailService) - { - } + public function __construct(private EmailService $emailService) {} public function send(array $payload): array { @@ -25,6 +23,7 @@ class BelowSixtyEmailService if ($studentId <= 0) { Log::warning('Below60Email: missing student_id'); + return ['ok' => false, 'message' => 'Missing student id.']; } @@ -42,6 +41,7 @@ class BelowSixtyEmailService if (empty($rows)) { Log::warning('Below60Email: no guardian emails', ['student_id' => $studentId]); + return ['ok' => false, 'message' => 'No guardian email addresses.']; } @@ -57,17 +57,17 @@ class BelowSixtyEmailService $primary = $rows[0] ?? null; $parentName = ''; if ($primary) { - $parentName = trim((string) ($primary->firstname ?? '') . ' ' . (string) ($primary->lastname ?? '')); + $parentName = trim((string) ($primary->firstname ?? '').' '.(string) ($primary->lastname ?? '')); } $subject = (string) ($payload['subject'] ?? ''); if ($subject === '') { $subject = 'Student Performance Alert'; if ($studentName !== '') { - $subject .= ' — ' . $studentName; + $subject .= ' — '.$studentName; } if ($semester !== '' || $schoolYear !== '') { - $subject .= ' (' . trim($semester . ' ' . $schoolYear) . ')'; + $subject .= ' ('.trim($semester.' '.$schoolYear).')'; } } diff --git a/app/Services/Grading/GradingBelowSixtyService.php b/app/Services/Grading/GradingBelowSixtyService.php index 6ceb5fa5..c804e95b 100644 --- a/app/Services/Grading/GradingBelowSixtyService.php +++ b/app/Services/Grading/GradingBelowSixtyService.php @@ -6,7 +6,6 @@ use App\Models\BelowSixtyDecision; use App\Models\CurrentFlag; use App\Models\ParentMeetingSchedule; use App\Models\ScoreComment; -use App\Models\Student; use App\Models\StudentDecision; use Illuminate\Support\Facades\DB; use Illuminate\Support\Facades\Event; @@ -58,7 +57,7 @@ class GradingBelowSixtyService $studentIds = array_values(array_filter($studentIds, static fn ($id) => $id > 0)); $commentMap = []; - if (!empty($studentIds)) { + if (! empty($studentIds)) { $commentRows = ScoreComment::query() ->select('student_id', 'comment', 'created_at') ->where('score_type', 'general') @@ -69,14 +68,14 @@ class GradingBelowSixtyService ->get(); foreach ($commentRows as $row) { $sid = (int) $row->student_id; - if ($sid > 0 && !isset($commentMap[$sid])) { + if ($sid > 0 && ! isset($commentMap[$sid])) { $commentMap[$sid] = (string) ($row->comment ?? ''); } } } $statusMap = []; - if (!empty($studentIds)) { + if (! empty($studentIds)) { $flagRows = CurrentFlag::query() ->select('student_id', 'flag_state') ->where('flag', 'grade') @@ -110,7 +109,7 @@ class GradingBelowSixtyService throw new RuntimeException('Student record not found for the selected term.'); } - $studentName = trim((string) ($row['firstname'] ?? '') . ' ' . (string) ($row['lastname'] ?? '')); + $studentName = trim((string) ($row['firstname'] ?? '').' '.(string) ($row['lastname'] ?? '')); $subject = $this->buildBelowSixtySubject($studentName, $semester, $schoolYear); $parentName = $this->fetchBelowSixtyParentName($studentId); @@ -146,7 +145,7 @@ class GradingBelowSixtyService public function updateStatus(int $studentId, string $semester, string $schoolYear, string $status, string $note, ?int $userId): void { $status = ucfirst(strtolower($status)); - if (!in_array($status, ['Open', 'Closed'], true)) { + if (! in_array($status, ['Open', 'Closed'], true)) { throw new RuntimeException('Invalid status.'); } @@ -170,21 +169,22 @@ class GradingBelowSixtyService $data['updated_by_open'] = $userId; if ($note !== '') { $prev = (string) ($existing->open_description ?? ''); - $data['open_description'] = trim($prev . PHP_EOL . $note); + $data['open_description'] = trim($prev.PHP_EOL.$note); } } else { $data['updated_by_closed'] = $userId; if ($note !== '') { $prev = (string) ($existing->close_description ?? ''); - $data['close_description'] = trim($prev . PHP_EOL . $note); + $data['close_description'] = trim($prev.PHP_EOL.$note); } } $existing->update($data); + return; } $row = $this->fetchBelowSixtyEmailRow($studentId, $schoolYear, $semester); - $studentName = trim((string) ($row['firstname'] ?? '') . ' ' . (string) ($row['lastname'] ?? '')); + $studentName = trim((string) ($row['firstname'] ?? '').' '.(string) ($row['lastname'] ?? '')); $grade = (string) ($row['class_section_name'] ?? ''); $data = [ 'student_id' => $studentId, @@ -298,7 +298,7 @@ class GradingBelowSixtyService continue; } - if (!isset($studentMap[$studentId])) { + if (! isset($studentMap[$studentId])) { $studentMap[$studentId] = [ 'student_id' => $studentId, 'school_id' => $row->school_id, @@ -355,7 +355,7 @@ class GradingBelowSixtyService $certificateMap = []; foreach ($certificateRows as $row) { $studentId = (int) ($row->student_id ?? 0); - if ($studentId > 0 && !isset($certificateMap[$studentId])) { + if ($studentId > 0 && ! isset($certificateMap[$studentId])) { $certificateMap[$studentId] = (string) ($row->certificate_number ?? ''); } } @@ -522,8 +522,8 @@ class GradingBelowSixtyService } $subject = 'Whole Year Academic Decision — ' - . (string) ($context['student_name'] ?? 'Student') - . ' (' . $schoolYear . ')'; + .(string) ($context['student_name'] ?? 'Student') + .' ('.$schoolYear.')'; return [ 'student_id' => $studentId, @@ -553,8 +553,8 @@ class GradingBelowSixtyService $resolvedSubject = trim((string) $subject); if ($resolvedSubject === '') { $resolvedSubject = 'Whole Year Academic Decision — ' - . (string) ($context['student_name'] ?? 'Student') - . ' (' . $schoolYear . ')'; + .(string) ($context['student_name'] ?? 'Student') + .' ('.$schoolYear.')'; } $payload = [ @@ -628,7 +628,7 @@ class GradingBelowSixtyService continue; } - if (!isset($studentMap[$studentId])) { + if (! isset($studentMap[$studentId])) { $studentMap[$studentId] = [ 'school_id' => $row->school_id, 'firstname' => $row->firstname, @@ -661,7 +661,7 @@ class GradingBelowSixtyService if ($studentId <= 0) { continue; } - if (!isset($belowMap[$studentId]) || trim((string) ($belowMap[$studentId]->decision ?? '')) === '') { + if (! isset($belowMap[$studentId]) || trim((string) ($belowMap[$studentId]->decision ?? '')) === '') { $belowMap[$studentId] = $row; } } @@ -754,7 +754,7 @@ class GradingBelowSixtyService return [ 'rows' => $rows, - 'generated' => !empty($savedMap), + 'generated' => ! empty($savedMap), 'school_year' => $schoolYear, ]; } @@ -789,7 +789,7 @@ class GradingBelowSixtyService continue; } - if (!isset($studentMap[$studentId])) { + if (! isset($studentMap[$studentId])) { $studentMap[$studentId] = [ 'firstname' => $row->firstname, 'lastname' => $row->lastname, @@ -818,7 +818,7 @@ class GradingBelowSixtyService if ($studentId <= 0) { continue; } - if (!isset($belowMap[$studentId]) || trim((string) ($belowMap[$studentId]->decision ?? '')) === '') { + if (! isset($belowMap[$studentId]) || trim((string) ($belowMap[$studentId]->decision ?? '')) === '') { $belowMap[$studentId] = $row; } } @@ -912,7 +912,7 @@ class GradingBelowSixtyService ->whereRaw('LOWER(TRIM(ss.semester)) = ?', [$semesterKey]) ->first(); - if (!$row) { + if (! $row) { return []; } @@ -972,7 +972,7 @@ class GradingBelowSixtyService ]; } - if (!empty($semesters)) { + if (! empty($semesters)) { $commentRows = DB::table('score_comments') ->select('semester', 'score_type', 'comment', 'created_at') ->where('student_id', $studentId) @@ -987,7 +987,7 @@ class GradingBelowSixtyService foreach ($commentRows as $row) { $semester = ucfirst(strtolower(trim((string) ($row->semester ?? '')))); $type = strtolower(trim((string) ($row->score_type ?? 'general'))); - if (isset($semesters[$semester]) && !isset($semesters[$semester]['comments'][$type])) { + if (isset($semesters[$semester]) && ! isset($semesters[$semester]['comments'][$type])) { $semesters[$semester]['comments'][$type] = (string) ($row->comment ?? ''); } } @@ -1001,23 +1001,24 @@ class GradingBelowSixtyService $parentName = 'Parent/Guardian'; try { $rows = DB::select( - "SELECT u.firstname, u.lastname + 'SELECT u.firstname, u.lastname FROM family_students fs JOIN family_guardians fg ON fg.family_id = fs.family_id JOIN users u ON u.id = fg.user_id WHERE fs.student_id = ? ORDER BY fg.is_primary DESC, u.lastname, u.firstname - LIMIT 1", + LIMIT 1', [$studentId] ); - if (!empty($rows[0])) { - $candidate = trim((string) ($rows[0]->firstname ?? '') . ' ' . (string) ($rows[0]->lastname ?? '')); + if (! empty($rows[0])) { + $candidate = trim((string) ($rows[0]->firstname ?? '').' '.(string) ($rows[0]->lastname ?? '')); if ($candidate !== '') { $parentName = $candidate; } } } catch (\Throwable $e) { } + return $parentName; } @@ -1041,7 +1042,7 @@ class GradingBelowSixtyService ]; } - $studentName = trim((string) ($student->firstname ?? '') . ' ' . (string) ($student->lastname ?? '')); + $studentName = trim((string) ($student->firstname ?? '').' '.(string) ($student->lastname ?? '')); if ($studentName === '') { $studentName = 'Student'; } @@ -1140,22 +1141,22 @@ class GradingBelowSixtyService $html = '

Dear Parent/Guardian,

- This message is regarding ' . e($studentName) . ' - for the ' . e($schoolYear) . ' school year. + This message is regarding '.e($studentName).' + for the '.e($schoolYear).' school year.

- Class: ' . e($classSectionName !== '' ? $classSectionName : 'N/A') . '
- Fall Score: ' . e($fallText) . '
- Spring Score: ' . e($springText) . '
- Whole-Year Score: ' . e($yearText) . '
- Decision: ' . e($decision) . ' + Class: '.e($classSectionName !== '' ? $classSectionName : 'N/A').'
+ Fall Score: '.e($fallText).'
+ Spring Score: '.e($springText).'
+ Whole-Year Score: '.e($yearText).'
+ Decision: '.e($decision).'

'; if ($notes !== '') { $html .= '

Decision Notes:
- ' . nl2br(e($notes)) . ' + '.nl2br(e($notes)).'

'; } @@ -1312,9 +1313,9 @@ class GradingBelowSixtyService $classSectionName = trim((string) ($semester['class_section_name'] ?? '')); $html .= '

- ' . e($semesterName) . ' Semester'; + '.e($semesterName).' Semester'; if ($classSectionName !== '') { - $html .= ' — ' . e($classSectionName); + $html .= ' — '.e($classSectionName); } $html .= '

@@ -1329,7 +1330,7 @@ class GradingBelowSixtyService $hasScoreRow = false; foreach ($scoreLabels as $key => $label) { - if (!array_key_exists($key, $semester)) { + if (! array_key_exists($key, $semester)) { continue; } $value = $semester[$key]; @@ -1340,13 +1341,13 @@ class GradingBelowSixtyService $fontWeight = $key === 'semester_score' ? 'font-weight:bold;' : ''; $html .= ' - ' . e($label) . ' - ' . e($scoreText) . ' + '.e($label).' + '.e($scoreText).' '; $hasScoreRow = true; } - if (!$hasScoreRow) { + if (! $hasScoreRow) { $html .= ' No scores recorded. @@ -1358,7 +1359,7 @@ class GradingBelowSixtyService '; $comments = is_array($semester['comments'] ?? null) ? $semester['comments'] : []; - if (!empty($comments)) { + if (! empty($comments)) { $deduped = []; $seen = []; foreach ($comments as $type => $text) { @@ -1367,7 +1368,7 @@ class GradingBelowSixtyService continue; } $label = $commentTypeLabels[$type] ?? (string) $type; - $hash = $label . '|' . $text; + $hash = $label.'|'.$text; if (isset($seen[$hash])) { continue; } @@ -1375,13 +1376,13 @@ class GradingBelowSixtyService $deduped[] = ['label' => $label, 'text' => $text]; } - if (!empty($deduped)) { + if (! empty($deduped)) { $html .= '

Comments

'; foreach ($deduped as $comment) { $html .= '

- ' . e($comment['label']) . ':
- ' . nl2br(e($comment['text'])) . ' + '.e($comment['label']).':
+ '.nl2br(e($comment['text'])).'

'; } } @@ -1395,11 +1396,12 @@ class GradingBelowSixtyService { $subject = 'Student Performance Alert'; if ($studentName !== '') { - $subject .= ' — ' . $studentName; + $subject .= ' — '.$studentName; } if ($semester !== '' || $schoolYear !== '') { - $subject .= ' (' . trim($semester . ' ' . $schoolYear) . ')'; + $subject .= ' ('.trim($semester.' '.$schoolYear).')'; } + return $subject; } @@ -1414,18 +1416,18 @@ class GradingBelowSixtyService $parentUserId = null; try { $pRows = DB::select( - "SELECT u.id AS user_id, u.firstname, u.lastname + 'SELECT u.id AS user_id, u.firstname, u.lastname FROM family_students fs JOIN family_guardians fg ON fg.family_id = fs.family_id JOIN users u ON u.id = fg.user_id WHERE fs.student_id = ? ORDER BY fg.is_primary DESC, u.lastname, u.firstname - LIMIT 1", + LIMIT 1', [$studentId] ); - if (!empty($pRows[0])) { + if (! empty($pRows[0])) { $parentUserId = (int) ($pRows[0]->user_id ?? 0) ?: null; - $parentName = trim((string) ($pRows[0]->firstname ?? '') . ' ' . (string) ($pRows[0]->lastname ?? '')); + $parentName = trim((string) ($pRows[0]->firstname ?? '').' '.(string) ($pRows[0]->lastname ?? '')); if ($parentName === '') { $parentName = 'Parent/Guardian'; } @@ -1436,16 +1438,16 @@ class GradingBelowSixtyService if ($parentUserId === null) { try { $srow = DB::selectOne( - "SELECT s.parent_id, u.firstname, u.lastname + 'SELECT s.parent_id, u.firstname, u.lastname FROM students s LEFT JOIN users u ON u.id = s.parent_id WHERE s.id = ? - LIMIT 1", + LIMIT 1', [$studentId] ); - if (!empty($srow)) { + if (! empty($srow)) { $parentUserId = (int) ($srow->parent_id ?? 0) ?: null; - $fallbackName = trim((string) ($srow->firstname ?? '') . ' ' . (string) ($srow->lastname ?? '')); + $fallbackName = trim((string) ($srow->firstname ?? '').' '.(string) ($srow->lastname ?? '')); if ($fallbackName !== '') { $parentName = $fallbackName; } @@ -1454,7 +1456,7 @@ class GradingBelowSixtyService } } - $studentName = trim((string) ($row['firstname'] ?? '') . ' ' . (string) ($row['lastname'] ?? '')); + $studentName = trim((string) ($row['firstname'] ?? '').' '.(string) ($row['lastname'] ?? '')); return [ 'student_name' => $studentName !== '' ? $studentName : 'Student', diff --git a/app/Services/Grading/GradingLockService.php b/app/Services/Grading/GradingLockService.php index c5ba695e..e343df9b 100644 --- a/app/Services/Grading/GradingLockService.php +++ b/app/Services/Grading/GradingLockService.php @@ -2,11 +2,11 @@ namespace App\Services\Grading; -use App\Models\GradingLock; use App\Models\ClassSection; -use RuntimeException; +use App\Models\GradingLock; use App\Services\Grading\Policy\GradingPolicyResolver; use App\Services\Grading\Validation\GradebookFinalizationValidator; +use RuntimeException; class GradingLockService { @@ -14,8 +14,8 @@ class GradingLockService private ?GradingPolicyResolver $policyResolver = null, private ?GradebookFinalizationValidator $finalizationValidator = null ) { - $this->policyResolver = $this->policyResolver ?? new GradingPolicyResolver(); - $this->finalizationValidator = $this->finalizationValidator ?? new GradebookFinalizationValidator(); + $this->policyResolver = $this->policyResolver ?? new GradingPolicyResolver; + $this->finalizationValidator = $this->finalizationValidator ?? new GradebookFinalizationValidator; } public function toggle(int $classSectionId, string $semester, string $schoolYear, ?int $userId): bool @@ -32,6 +32,7 @@ class GradingLockService 'locked_by' => null, 'locked_at' => null, ]); + return false; } @@ -97,7 +98,7 @@ class GradingLockService foreach ($sectionIds as $sid) { $this->assertCanLock($sid, $semester, $schoolYear); - if (!empty($existingBySection[$sid])) { + if (! empty($existingBySection[$sid])) { if ($existingBySection[$sid]->is_locked) { continue; } @@ -107,6 +108,7 @@ class GradingLockService 'locked_at' => $now, ]); $count++; + continue; } @@ -123,7 +125,7 @@ class GradingLockService $count++; } - if (!empty($insertRows)) { + if (! empty($insertRows)) { GradingLock::query()->insert($insertRows); } @@ -138,7 +140,7 @@ class GradingLockService } $result = $this->finalizationValidator->validateClassSectionBeforeLock($classSectionId, $semester, $schoolYear); - if (!$result['valid']) { + if (! $result['valid']) { $count = count($result['errors']); throw new RuntimeException("Cannot lock class section {$classSectionId}; strong grading validation failed with {$count} error(s)."); } diff --git a/app/Services/Grading/GradingOverviewService.php b/app/Services/Grading/GradingOverviewService.php index 3bfb14d9..72b67bd6 100644 --- a/app/Services/Grading/GradingOverviewService.php +++ b/app/Services/Grading/GradingOverviewService.php @@ -8,9 +8,7 @@ use Illuminate\Support\Facades\DB; class GradingOverviewService { - public function __construct() - { - } + public function __construct() {} public function overview(?int $classId, ?string $semester, ?string $schoolYear): array { @@ -38,7 +36,7 @@ class GradingOverviewService continue; } - if (!isset($grades[$classIdValue])) { + if (! isset($grades[$classIdValue])) { $grades[$classIdValue] = []; } $exists = false; @@ -48,7 +46,7 @@ class GradingOverviewService break; } } - if (!$exists) { + if (! $exists) { $grades[$classIdValue][] = [ 'class_section_id' => $sectionId, 'class_section_name' => $sectionName, @@ -248,6 +246,7 @@ class GradingOverviewService if ($value === null || $value === '') { return null; } + return round((float) $value, 2); } @@ -260,6 +259,7 @@ class GradingOverviewService return null; } } + return $score; } @@ -271,6 +271,7 @@ class GradingOverviewService return $option; } } + return $requestedSemester; } if ($fallbackSemester !== null && $fallbackSemester !== '') { @@ -279,8 +280,10 @@ class GradingOverviewService return $option; } } + return $fallbackSemester; } + return $semesterOptions[0] ?? ''; } @@ -306,7 +309,7 @@ class GradingOverviewService $semesters = ['Fall', 'Spring']; } - if ($fallbackSemester !== null && $fallbackSemester !== '' && !in_array($fallbackSemester, $semesters, true)) { + if ($fallbackSemester !== null && $fallbackSemester !== '' && ! in_array($fallbackSemester, $semesters, true)) { array_unshift($semesters, $fallbackSemester); } @@ -322,6 +325,7 @@ class GradingOverviewService if ($norm === 'spring') { return 'parent_scores_released_spring'; } + return null; } @@ -330,13 +334,14 @@ class GradingOverviewService $key = $this->getParentReleaseKey($semester); $raw = $key ? Configuration::getConfig($key) : null; $raw = (string) ($raw ?? ''); + return in_array(strtolower(trim($raw)), ['1', 'true', 'yes', 'y', 'on'], true); } private function ensureParentReleaseKeyExists(string $semester): void { $key = $this->getParentReleaseKey($semester); - if (!$key) { + if (! $key) { return; } diff --git a/app/Services/Grading/GradingPlacementService.php b/app/Services/Grading/GradingPlacementService.php index 63a36d2f..b0edfcf6 100644 --- a/app/Services/Grading/GradingPlacementService.php +++ b/app/Services/Grading/GradingPlacementService.php @@ -14,7 +14,7 @@ class GradingPlacementService { public function placementContext(?int $classSectionId, string $schoolYear, ?string $placementTest, ?string $open): array { - if (!$classSectionId) { + if (! $classSectionId) { $showStudents = ($placementTest !== '' && $open === '1'); $students = $showStudents ? $this->fetchActiveStudentsWithSection($schoolYear) : []; $batches = $this->fetchPlacementBatches($schoolYear); @@ -40,7 +40,7 @@ class GradingPlacementService ), static fn ($id) => $id > 0)); $levels = []; - if (!empty($studentIds)) { + if (! empty($studentIds)) { $rows = PlacementLevel::query() ->whereIn('student_id', $studentIds) ->where('school_year', $schoolYear) @@ -67,7 +67,7 @@ class GradingPlacementService } $level = $level === '' || $level === null ? null : (int) $level; - if ($level !== null && !in_array($level, [1, 2, 3], true)) { + if ($level !== null && ! in_array($level, [1, 2, 3], true)) { throw new RuntimeException('Invalid placement level.'); } @@ -80,6 +80,7 @@ class GradingPlacementService if ($existing) { $existing->delete(); } + return; } @@ -112,7 +113,7 @@ class GradingPlacementService $validSet = array_flip($validIds); $existingRows = []; - if (!empty($validIds)) { + if (! empty($validIds)) { $rows = PlacementLevel::query() ->whereIn('student_id', $validIds) ->where('school_year', $schoolYear) @@ -124,12 +125,12 @@ class GradingPlacementService foreach ($levels as $studentIdRaw => $levelRaw) { $studentId = (int) $studentIdRaw; - if (!isset($validSet[$studentId])) { + if (! isset($validSet[$studentId])) { continue; } $levelRaw = trim((string) $levelRaw); $level = $levelRaw === '' ? null : (int) $levelRaw; - if ($level !== null && !in_array($level, [1, 2, 3], true)) { + if ($level !== null && ! in_array($level, [1, 2, 3], true)) { continue; } @@ -137,6 +138,7 @@ class GradingPlacementService if (isset($existingRows[$studentId])) { $existingRows[$studentId]->delete(); } + continue; } @@ -179,7 +181,7 @@ class GradingPlacementService $savedCount = 0; foreach ($levels as $studentIdRaw => $levelRaw) { $studentId = (int) $studentIdRaw; - if (!isset($validSet[$studentId])) { + if (! isset($validSet[$studentId])) { continue; } $levelRaw = trim((string) $levelRaw); @@ -212,7 +214,7 @@ class GradingPlacementService public function getPlacementBatch(int $batchId): array { $batch = PlacementBatch::query()->find($batchId); - if (!$batch) { + if (! $batch) { throw new RuntimeException('Placement batch not found.'); } @@ -230,7 +232,7 @@ class GradingPlacementService public function updatePlacementBatch(int $batchId, string $schoolYear, array $levels, ?int $userId): void { $batch = PlacementBatch::query()->find($batchId); - if (!$batch) { + if (! $batch) { throw new RuntimeException('Placement batch not found.'); } @@ -245,7 +247,7 @@ class GradingPlacementService foreach ($levels as $studentIdRaw => $scoreRaw) { $studentId = (int) $studentIdRaw; - if (!isset($validSet[$studentId])) { + if (! isset($validSet[$studentId])) { continue; } $scoreRaw = trim((string) $scoreRaw); @@ -253,6 +255,7 @@ class GradingPlacementService if (isset($existing[$studentId])) { PlacementScore::query()->whereKey($existing[$studentId]['id'])->delete(); } + continue; } $score = (int) $scoreRaw; diff --git a/app/Services/Grading/GradingRefreshService.php b/app/Services/Grading/GradingRefreshService.php index d43c3589..d8b19ce3 100644 --- a/app/Services/Grading/GradingRefreshService.php +++ b/app/Services/Grading/GradingRefreshService.php @@ -10,9 +10,7 @@ use RuntimeException; class GradingRefreshService { - public function __construct(private SemesterScoreService $semesterScoreService) - { - } + public function __construct(private SemesterScoreService $semesterScoreService) {} public function refreshSemesterScores(int $classSectionId, string $semester, string $schoolYear): void { @@ -35,7 +33,7 @@ class GradingRefreshService private function refreshAttendanceComments(int $classSectionId, string $semester, string $schoolYear): void { - if (!function_exists('attendance_comment_from_score')) { + if (! function_exists('attendance_comment_from_score')) { return; } diff --git a/app/Services/Grading/GradingScoreService.php b/app/Services/Grading/GradingScoreService.php index 7896c093..2b52548f 100644 --- a/app/Services/Grading/GradingScoreService.php +++ b/app/Services/Grading/GradingScoreService.php @@ -2,6 +2,7 @@ namespace App\Services\Grading; +use App\Models\ClassSection; use App\Models\FinalExam; use App\Models\GradingLock; use App\Models\Homework; @@ -11,23 +12,20 @@ use App\Models\Quiz; use App\Models\ScoreComment; use App\Models\SemesterScore; use App\Models\Student; -use App\Models\ClassSection; use App\Services\Grading\Validation\ScoreValueValidator; use App\Services\Scores\SemesterScoreService; use RuntimeException; class GradingScoreService { - public function __construct(private SemesterScoreService $semesterScoreService) - { - } + public function __construct(private SemesterScoreService $semesterScoreService) {} public function show(string $type, int $classSectionId, int $studentId, string $semester, string $schoolYear): array { $model = $this->resolveModel($type); $student = Student::query()->find($studentId); - if (!$student) { + if (! $student) { throw new RuntimeException('Student not found.'); } @@ -73,9 +71,9 @@ class GradingScoreService $scores = $payload['scores'] ?? []; $comments = $payload['comments'] ?? []; - $validator = new ScoreValueValidator(); + $validator = new ScoreValueValidator; foreach ($scoreIds as $i => $id) { - $normalizedScore = $validator->normalizeNullable($scores[$i] ?? null, ScoreValueValidator::DEFAULT_MAX_POINTS, ucfirst($type) . ' score'); + $normalizedScore = $validator->normalizeNullable($scores[$i] ?? null, ScoreValueValidator::DEFAULT_MAX_POINTS, ucfirst($type).' score'); $model->newQuery()->whereKey($id)->update([ 'score' => $normalizedScore, 'max_points' => ScoreValueValidator::DEFAULT_MAX_POINTS, @@ -86,8 +84,8 @@ class GradingScoreService } } elseif (in_array($type, ['midterm', 'final', 'test'], true)) { $score = $payload['score'] ?? null; - $validator = new ScoreValueValidator(); - $normalizedScore = $validator->normalizeNullable($score, ScoreValueValidator::DEFAULT_MAX_POINTS, ucfirst($type) . ' score'); + $validator = new ScoreValueValidator; + $normalizedScore = $validator->normalizeNullable($score, ScoreValueValidator::DEFAULT_MAX_POINTS, ucfirst($type).' score'); $data = [ 'score' => $normalizedScore, @@ -135,7 +133,7 @@ class GradingScoreService } $studentTeacherInfo = Student::getStudentInfoByClassSectionId($classSectionId, $semester, $schoolYear, $updatedBy); - if (!empty($studentTeacherInfo)) { + if (! empty($studentTeacherInfo)) { try { $this->semesterScoreService->updateScoresForStudents($studentTeacherInfo, $semester, $schoolYear); } catch (RuntimeException $e) { @@ -189,13 +187,13 @@ class GradingScoreService private function resolveModel(string $type) { return match ($type) { - 'homework' => new Homework(), - 'quiz' => new Quiz(), - 'project' => new Project(), - 'midterm' => new MidtermExam(), - 'final' => new FinalExam(), - 'test' => new SemesterScore(), - 'comments' => new ScoreComment(), + 'homework' => new Homework, + 'quiz' => new Quiz, + 'project' => new Project, + 'midterm' => new MidtermExam, + 'final' => new FinalExam, + 'test' => new SemesterScore, + 'comments' => new ScoreComment, default => throw new RuntimeException("Invalid type: {$type}"), }; } diff --git a/app/Services/Grading/HomeworkTrackingCalendarService.php b/app/Services/Grading/HomeworkTrackingCalendarService.php index bc3568e1..4a5f2f7a 100644 --- a/app/Services/Grading/HomeworkTrackingCalendarService.php +++ b/app/Services/Grading/HomeworkTrackingCalendarService.php @@ -15,6 +15,7 @@ class HomeworkTrackingCalendarService } if ($startYear === null) { $today = new DateTime('today'); + return [$today, $today]; } @@ -31,6 +32,7 @@ class HomeworkTrackingCalendarService } } catch (\Throwable $e) { $today = new DateTime('today'); + return [$today, $today]; } @@ -73,7 +75,7 @@ class HomeworkTrackingCalendarService $dateToIndex = []; $idx = 0; foreach ($sundays as $ymd) { - if (!isset($eventDays[$ymd])) { + if (! isset($eventDays[$ymd])) { $idx++; $dateToIndex[$ymd] = $idx; } else { diff --git a/app/Services/Grading/HomeworkTrackingService.php b/app/Services/Grading/HomeworkTrackingService.php index d92690ca..94516870 100644 --- a/app/Services/Grading/HomeworkTrackingService.php +++ b/app/Services/Grading/HomeworkTrackingService.php @@ -9,9 +9,7 @@ class HomeworkTrackingService { private array $teacherAssignmentCache = []; - public function __construct(private HomeworkTrackingCalendarService $calendarService) - { - } + public function __construct(private HomeworkTrackingCalendarService $calendarService) {} public function report(?string $semester = null, ?string $schoolYear = null, int $page = 1): array { @@ -122,7 +120,7 @@ class HomeworkTrackingService $date = substr((string) ($row->hw_date ?? ''), 0, 10); $cnt = (int) $row->cnt; $firstCreated = substr((string) ($row->first_created ?? ''), 0, 10); - if ($csid <= 0 || !$date || $cnt <= 0) { + if ($csid <= 0 || ! $date || $cnt <= 0) { continue; } @@ -138,7 +136,7 @@ class HomeworkTrackingService } $j = $baseIndex; - while ($j >= 0 && !empty($eventDays[$sundays[$j]])) { + while ($j >= 0 && ! empty($eventDays[$sundays[$j]])) { $j--; } if ($j < 0) { @@ -177,7 +175,7 @@ class HomeworkTrackingService if ($sid <= 0) { continue; } - if (!isset($bySection[$sid])) { + if (! isset($bySection[$sid])) { $bySection[$sid] = [ 'class_section_id' => $sid, 'class_id' => (int) ($row->class_id ?? 0), @@ -186,7 +184,7 @@ class HomeworkTrackingService 'tas' => [], ]; } - $name = trim((string) ($row->firstname ?? '') . ' ' . (string) ($row->lastname ?? '')); + $name = trim((string) ($row->firstname ?? '').' '.(string) ($row->lastname ?? '')); $pos = strtolower((string) ($row->position ?? '')); if ($name !== '') { if ($pos === 'main') { @@ -202,15 +200,23 @@ class HomeworkTrackingService $ai = (int) ($a['class_id'] ?? 0); $bi = (int) ($b['class_id'] ?? 0); $order = function (int $cid): int { - if ($cid === 13) return 0; - if ($cid >= 1 && $cid <= 11) return $cid; - if ($cid === 12) return 99; + if ($cid === 13) { + return 0; + } + if ($cid >= 1 && $cid <= 11) { + return $cid; + } + if ($cid === 12) { + return 99; + } + return 200 + $cid; }; $cmp = $order($ai) <=> $order($bi); if ($cmp !== 0) { return $cmp; } + return strnatcasecmp($a['class_section_name'] ?? '', $b['class_section_name'] ?? ''); }); diff --git a/app/Services/Grading/Policy/GradingPolicyResolver.php b/app/Services/Grading/Policy/GradingPolicyResolver.php index a4b6501f..bf7b3ad6 100644 --- a/app/Services/Grading/Policy/GradingPolicyResolver.php +++ b/app/Services/Grading/Policy/GradingPolicyResolver.php @@ -7,14 +7,17 @@ use App\Models\Configuration; class GradingPolicyResolver { public const LEGACY_MODE = 'legacy'; + public const STRONG_MODE = 'strong'; + public const LEGACY_VERSION = 'legacy_v1'; + public const STRONG_VERSION = 'strong_v1'; public function calculationMode(?int $classSectionId = null, ?string $semester = null, ?string $schoolYear = null): string { $enabled = strtolower((string) (Configuration::getConfig('strong_grading_enabled') ?? '')); - if (!in_array($enabled, ['1', 'true', 'yes', 'on'], true)) { + if (! in_array($enabled, ['1', 'true', 'yes', 'on'], true)) { return self::LEGACY_MODE; } @@ -24,6 +27,7 @@ class GradingPolicyResolver } $ids = array_filter(array_map('trim', explode(',', $allowedSections)), fn ($v) => $v !== ''); + return $classSectionId !== null && in_array((string) $classSectionId, $ids, true) ? self::STRONG_MODE : self::LEGACY_MODE; diff --git a/app/Services/Grading/StrongCategoryAverageCalculator.php b/app/Services/Grading/StrongCategoryAverageCalculator.php index 4b390d29..54f2bddf 100644 --- a/app/Services/Grading/StrongCategoryAverageCalculator.php +++ b/app/Services/Grading/StrongCategoryAverageCalculator.php @@ -31,16 +31,19 @@ class StrongCategoryAverageCalculator if ($status === 'pending' || $status === '') { $pending[] = $row; + continue; } if (in_array($status, ['excused', 'not_assigned'], true)) { $excluded[] = $row; + continue; } if ($status === 'missing') { $included[] = 0.0; + continue; } diff --git a/app/Services/Grading/Validation/GradebookFinalizationValidator.php b/app/Services/Grading/Validation/GradebookFinalizationValidator.php index f9510a66..776cf6d6 100644 --- a/app/Services/Grading/Validation/GradebookFinalizationValidator.php +++ b/app/Services/Grading/Validation/GradebookFinalizationValidator.php @@ -15,7 +15,7 @@ class GradebookFinalizationValidator $errors = []; foreach ($this->scoreTables() as $table => $meta) { - if (!DB::getSchemaBuilder()->hasTable($table)) { + if (! DB::getSchemaBuilder()->hasTable($table)) { continue; } diff --git a/app/Services/Grading/Validation/ScoreValueValidator.php b/app/Services/Grading/Validation/ScoreValueValidator.php index 2141fb16..1c72c6f3 100644 --- a/app/Services/Grading/Validation/ScoreValueValidator.php +++ b/app/Services/Grading/Validation/ScoreValueValidator.php @@ -16,7 +16,7 @@ class ScoreValueValidator $max = $this->normalizeMaxPoints($maxPoints, $label); - if (!is_numeric($value)) { + if (! is_numeric($value)) { throw new InvalidArgumentException("{$label} must be numeric."); } @@ -34,6 +34,7 @@ class ScoreValueValidator if ($max <= 0) { throw new InvalidArgumentException("{$label} max points must be greater than 0."); } + return $max; } diff --git a/app/Services/Incidents/CurrentIncidentService.php b/app/Services/Incidents/CurrentIncidentService.php index 9d1261f6..ee0f08ef 100644 --- a/app/Services/Incidents/CurrentIncidentService.php +++ b/app/Services/Incidents/CurrentIncidentService.php @@ -11,9 +11,7 @@ use Illuminate\Support\Facades\DB; class CurrentIncidentService { - public function __construct(private IncidentLookupService $lookup) - { - } + public function __construct(private IncidentLookupService $lookup) {} public function listCurrent(): array { @@ -23,7 +21,7 @@ class CurrentIncidentService $updaterIds = []; foreach ($incidents as $incident) { foreach (['updated_by_open', 'updated_by_closed', 'updated_by_canceled'] as $key) { - if (!empty($incident[$key])) { + if (! empty($incident[$key])) { $updaterIds[] = (int) $incident[$key]; } } @@ -33,7 +31,7 @@ class CurrentIncidentService foreach ($incidents as &$incident) { foreach (['updated_by_open', 'updated_by_closed', 'updated_by_canceled'] as $key) { $id = (int) ($incident[$key] ?? 0); - $incident[$key . '_name'] = $id > 0 ? ($updaterMap[$id] ?? null) : null; + $incident[$key.'_name'] = $id > 0 ? ($updaterMap[$id] ?? null) : null; } } unset($incident); @@ -62,7 +60,7 @@ class CurrentIncidentService ->get() ->map(fn ($student) => [ 'id' => (int) $student->id, - 'name' => trim((string) $student->firstname . ' ' . (string) $student->lastname), + 'name' => trim((string) $student->firstname.' '.(string) $student->lastname), ]) ->all(); } @@ -77,7 +75,7 @@ class CurrentIncidentService $grade = (string) $payload['grade']; $student = Student::query()->find($studentId); - if (!$student) { + if (! $student) { return [ 'ok' => false, 'message' => 'Student not found.', @@ -121,7 +119,7 @@ class CurrentIncidentService $incident = CurrentIncident::query()->create([ 'student_id' => $student->id, - 'student_name' => trim((string) $student->firstname . ' ' . (string) $student->lastname), + 'student_name' => trim((string) $student->firstname.' '.(string) $student->lastname), 'grade' => $grade, 'incident' => $incidentType, 'incident_datetime' => $currentDateTime, @@ -149,7 +147,7 @@ class CurrentIncidentService public function closeIncident(int $incidentId, string $closeDescription, ?string $actionTaken, int $userId): array { $incident = CurrentIncident::query()->find($incidentId); - if (!$incident) { + if (! $incident) { return ['ok' => false, 'message' => 'Incident not found.']; } @@ -171,7 +169,7 @@ class CurrentIncidentService public function cancelIncident(int $incidentId, ?string $cancelDescription, ?string $actionTaken, int $userId): array { $incident = CurrentIncident::query()->find($incidentId); - if (!$incident) { + if (! $incident) { return ['ok' => false, 'message' => 'Incident not found.']; } @@ -228,6 +226,6 @@ class CurrentIncidentService return $append; } - return $existing . PHP_EOL . $append; + return $existing.PHP_EOL.$append; } } diff --git a/app/Services/Incidents/IncidentAnalysisService.php b/app/Services/Incidents/IncidentAnalysisService.php index 768b93e7..48d8aef8 100644 --- a/app/Services/Incidents/IncidentAnalysisService.php +++ b/app/Services/Incidents/IncidentAnalysisService.php @@ -6,9 +6,7 @@ use App\Models\Incident; class IncidentAnalysisService { - public function __construct(private IncidentLookupService $lookup) - { - } + public function __construct(private IncidentLookupService $lookup) {} public function analyze(?string $schoolYear, ?string $semester): array { @@ -31,7 +29,7 @@ class IncidentAnalysisService foreach ($incidents as $incident) { $studentId = (string) ($incident['student_id'] ?? ''); $studentName = (string) ($incident['student_name'] ?? ''); - $studentKey = $studentId !== '' ? 'id:' . $studentId : 'name:' . strtolower(trim($studentName)); + $studentKey = $studentId !== '' ? 'id:'.$studentId : 'name:'.strtolower(trim($studentName)); $gradeValue = $incident['grade'] ?? ''; $gradeLabel = $gradeValue; @@ -39,7 +37,7 @@ class IncidentAnalysisService $gradeLabel = $gradeMap[(int) $gradeValue] ?? (string) $gradeValue; } - if (!isset($students[$studentKey])) { + if (! isset($students[$studentKey])) { $students[$studentKey] = [ 'student_id' => $studentId, 'student_name' => $studentName, @@ -84,6 +82,7 @@ class IncidentAnalysisService if ($a['total'] === $b['total']) { return strcasecmp($a['student_name'], $b['student_name']); } + return $b['total'] <=> $a['total']; }); diff --git a/app/Services/Incidents/IncidentHistoryService.php b/app/Services/Incidents/IncidentHistoryService.php index 36af2499..f22f7779 100644 --- a/app/Services/Incidents/IncidentHistoryService.php +++ b/app/Services/Incidents/IncidentHistoryService.php @@ -6,9 +6,7 @@ use App\Models\Incident; class IncidentHistoryService { - public function __construct(private IncidentLookupService $lookup) - { - } + public function __construct(private IncidentLookupService $lookup) {} public function history(?string $schoolYear, ?string $semester): array { @@ -35,7 +33,7 @@ class IncidentHistoryService $updaterIds = []; foreach ($incidents as $incident) { foreach (['updated_by_open', 'updated_by_closed', 'updated_by_canceled'] as $key) { - if (!empty($incident[$key])) { + if (! empty($incident[$key])) { $updaterIds[] = (int) $incident[$key]; } } @@ -46,7 +44,7 @@ class IncidentHistoryService foreach ($incidents as &$incident) { foreach (['updated_by_open', 'updated_by_closed', 'updated_by_canceled'] as $key) { $id = (int) ($incident[$key] ?? 0); - $incident[$key . '_name'] = $id > 0 ? ($updaterMap[$id] ?? null) : null; + $incident[$key.'_name'] = $id > 0 ? ($updaterMap[$id] ?? null) : null; } $gradeValue = $incident['grade'] ?? ''; diff --git a/app/Services/Incidents/IncidentLookupService.php b/app/Services/Incidents/IncidentLookupService.php index 81119202..ca572ef4 100644 --- a/app/Services/Incidents/IncidentLookupService.php +++ b/app/Services/Incidents/IncidentLookupService.php @@ -4,6 +4,7 @@ namespace App\Services\Incidents; use App\Models\ClassSection; use App\Models\StudentClass; +use Illuminate\Contracts\Support\Arrayable; use Illuminate\Support\Facades\DB; class IncidentLookupService @@ -72,7 +73,7 @@ class IncidentLookupService ->select('id', 'firstname', 'lastname') ->whereIn('id', $userIds) ->get() - ->map(fn ($row) => $row instanceof \Illuminate\Contracts\Support\Arrayable ? $row->toArray() : (array) $row) + ->map(fn ($row) => $row instanceof Arrayable ? $row->toArray() : (array) $row) ->all(); $map = []; @@ -81,8 +82,8 @@ class IncidentLookupService if ($id === null) { continue; } - $name = trim((string) ($row['firstname'] ?? '') . ' ' . (string) ($row['lastname'] ?? '')); - $map[(int) $id] = $name !== '' ? $name : ('User #' . $id); + $name = trim((string) ($row['firstname'] ?? '').' '.(string) ($row['lastname'] ?? '')); + $map[(int) $id] = $name !== '' ? $name : ('User #'.$id); } return $map; diff --git a/app/Services/Inventory/InventoryCategoryService.php b/app/Services/Inventory/InventoryCategoryService.php index 96d9f2b1..69a513b5 100644 --- a/app/Services/Inventory/InventoryCategoryService.php +++ b/app/Services/Inventory/InventoryCategoryService.php @@ -3,7 +3,6 @@ namespace App\Services\Inventory; use App\Models\InventoryCategory; -use Illuminate\Support\Facades\DB; use Illuminate\Support\Facades\Log; class InventoryCategoryService @@ -16,6 +15,7 @@ class InventoryCategoryService if ($type) { $query->where('type', $this->normalizeType($type)); } + return $query->get()->toArray(); } @@ -37,7 +37,8 @@ class InventoryCategoryService try { $category = InventoryCategory::query()->create($payload); } catch (\Throwable $e) { - Log::error('Inventory category create failed: ' . $e->getMessage()); + Log::error('Inventory category create failed: '.$e->getMessage()); + return ['ok' => false, 'message' => 'Failed to save category.']; } @@ -47,7 +48,7 @@ class InventoryCategoryService public function update(int $id, array $data): array { $category = InventoryCategory::query()->find($id); - if (!$category) { + if (! $category) { return ['ok' => false, 'message' => 'Category not found.']; } @@ -68,7 +69,8 @@ class InventoryCategoryService try { $category->update($payload); } catch (\Throwable $e) { - Log::error('Inventory category update failed: ' . $e->getMessage()); + Log::error('Inventory category update failed: '.$e->getMessage()); + return ['ok' => false, 'message' => 'Failed to save category.']; } @@ -78,14 +80,15 @@ class InventoryCategoryService public function delete(int $id): array { $category = InventoryCategory::query()->find($id); - if (!$category) { + if (! $category) { return ['ok' => false, 'message' => 'Category not found.']; } try { $category->delete(); } catch (\Throwable $e) { - Log::error('Inventory category delete failed: ' . $e->getMessage()); + Log::error('Inventory category delete failed: '.$e->getMessage()); + return ['ok' => false, 'message' => 'Failed to delete category.']; } @@ -129,6 +132,7 @@ class InventoryCategoryService private function normalizeType(?string $type): string { $type = strtolower((string) $type); + return in_array($type, $this->types, true) ? $type : 'office'; } } diff --git a/app/Services/Inventory/InventoryItemService.php b/app/Services/Inventory/InventoryItemService.php index aaa14c20..9805872b 100644 --- a/app/Services/Inventory/InventoryItemService.php +++ b/app/Services/Inventory/InventoryItemService.php @@ -2,8 +2,8 @@ namespace App\Services\Inventory; -use App\Models\InventoryItem; use App\Models\InventoryCategory; +use App\Models\InventoryItem; use Illuminate\Support\Facades\DB; use Illuminate\Support\Facades\Log; @@ -14,8 +14,7 @@ class InventoryItemService public function __construct( private InventoryContextService $context, private InventoryMovementService $movementService - ) { - } + ) {} public function list(array $filters = []): array { @@ -32,7 +31,7 @@ class InventoryItemService $builder->where('semester', $semester); } if ($q !== '') { - $builder->where('name', 'like', '%' . $q . '%'); + $builder->where('name', 'like', '%'.$q.'%'); } $items = $builder->orderBy('name')->get()->toArray(); @@ -62,6 +61,7 @@ class InventoryItemService public function find(int $id): ?array { $item = InventoryItem::query()->find($id); + return $item ? $item->toArray() : null; } @@ -78,10 +78,12 @@ class InventoryItemService } else { $this->movementService->recalcQuantity((int) $item->id); } + return ['ok' => true, 'item' => $item->toArray()]; }); } catch (\Throwable $e) { - Log::error('Inventory item create failed: ' . $e->getMessage()); + Log::error('Inventory item create failed: '.$e->getMessage()); + return ['ok' => false, 'message' => 'Failed to create item.']; } } @@ -89,7 +91,7 @@ class InventoryItemService public function update(int $id, array $data, ?int $userId = null): array { $item = InventoryItem::query()->find($id); - if (!$item) { + if (! $item) { return ['ok' => false, 'message' => 'Item not found.']; } @@ -99,7 +101,8 @@ class InventoryItemService try { $item->update($payload); } catch (\Throwable $e) { - Log::error('Inventory item update failed: ' . $e->getMessage()); + Log::error('Inventory item update failed: '.$e->getMessage()); + return ['ok' => false, 'message' => 'Failed to update item.']; } @@ -109,14 +112,15 @@ class InventoryItemService public function delete(int $id): array { $item = InventoryItem::query()->find($id); - if (!$item) { + if (! $item) { return ['ok' => false, 'message' => 'Item not found.']; } try { $item->delete(); } catch (\Throwable $e) { - Log::error('Inventory item delete failed: ' . $e->getMessage()); + Log::error('Inventory item delete failed: '.$e->getMessage()); + return ['ok' => false, 'message' => 'Failed to delete item.']; } @@ -126,7 +130,7 @@ class InventoryItemService public function auditClassroom(int $itemId, array $data, ?int $userId = null): array { $item = InventoryItem::query()->find($itemId); - if (!$item || $item->type !== 'classroom') { + if (! $item || $item->type !== 'classroom') { return ['ok' => false, 'message' => 'Invalid classroom item.']; } @@ -159,7 +163,7 @@ class InventoryItemService $totalIn += -$deltaMiss; } - if ($totalOut > 0 && !$this->movementService->recordMovement($itemId, -$totalOut, 'out', 'Audit: loss/missing increased')) { + if ($totalOut > 0 && ! $this->movementService->recordMovement($itemId, -$totalOut, 'out', 'Audit: loss/missing increased')) { return ['ok' => false, 'message' => 'Not enough stock to apply audit.']; } if ($totalIn > 0) { @@ -182,7 +186,7 @@ class InventoryItemService public function adjustStock(int $itemId, array $data, ?int $userId = null): array { $item = InventoryItem::query()->find($itemId); - if (!$item) { + if (! $item) { return ['ok' => false, 'message' => 'Item not found.']; } @@ -199,7 +203,7 @@ class InventoryItemService $type = ($mode === 'out') ? 'out' : (($mode === 'in') ? 'in' : 'adjust'); $ok = $this->movementService->recordMovement($itemId, $delta, $type, $reason, $note, null, null, $userId); - if (!$ok) { + if (! $ok) { return ['ok' => false, 'message' => 'Not enough stock to apply adjustment.']; } @@ -242,6 +246,7 @@ class InventoryItemService private function normalizeType(?string $type): string { $type = strtolower((string) $type); + return in_array($type, $this->types, true) ? $type : 'classroom'; } @@ -267,8 +272,9 @@ class InventoryItemService } else { $out = []; foreach ($ids as $id) { - $out[$id] = 'User #' . $id; + $out[$id] = 'User #'.$id; } + return $out; } @@ -276,8 +282,9 @@ class InventoryItemService $map = []; foreach ($rows as $r) { $disp = trim((string) ($r->display_name ?? '')); - $map[(int) $r->id] = $disp !== '' ? $disp : ('User #' . $r->id); + $map[(int) $r->id] = $disp !== '' ? $disp : ('User #'.$r->id); } + return $map; } @@ -297,7 +304,7 @@ class InventoryItemService ->all(); $currentYear = $this->context->schoolYear(); - if ($currentYear && !in_array($currentYear, $years, true)) { + if ($currentYear && ! in_array($currentYear, $years, true)) { array_unshift($years, $currentYear); } diff --git a/app/Services/Inventory/InventoryMovementService.php b/app/Services/Inventory/InventoryMovementService.php index b6df0efc..c290b1a8 100644 --- a/app/Services/Inventory/InventoryMovementService.php +++ b/app/Services/Inventory/InventoryMovementService.php @@ -9,15 +9,13 @@ use Illuminate\Support\Facades\Log; class InventoryMovementService { - public function __construct(private InventoryContextService $context) - { - } + public function __construct(private InventoryContextService $context) {} public function list(array $filters = []): array { $selectedYear = trim((string) ($filters['school_year'] ?? $this->context->schoolYear())); $selectedSem = trim((string) ($filters['semester'] ?? $this->context->semester())); - $includeVoided = !empty($filters['include_voided']); + $includeVoided = ! empty($filters['include_voided']); $builder = DB::table('inventory_movements as m') ->select([ @@ -36,7 +34,7 @@ class InventoryMovementService ->orderBy('m.id', 'DESC'); // Exclude voided movements by default - if (!$includeVoided) { + if (! $includeVoided) { $builder->whereNull('m.voided_at'); } @@ -61,7 +59,7 @@ class InventoryMovementService $qtyChange = (int) ($data['qty_change'] ?? 0); $item = InventoryItem::query()->find($itemId); - if (!$item) { + if (! $item) { return ['ok' => false, 'message' => 'Selected item not found.']; } @@ -76,7 +74,8 @@ class InventoryMovementService InventoryMovement::query()->create($payload); $this->recalcQuantity($itemId); } catch (\Throwable $e) { - Log::error('Inventory movement create failed: ' . $e->getMessage()); + Log::error('Inventory movement create failed: '.$e->getMessage()); + return ['ok' => false, 'message' => 'Could not save movement.']; } @@ -90,7 +89,7 @@ class InventoryMovementService public function update(int $id, array $data): array { $movement = InventoryMovement::query()->find($id); - if (!$movement) { + if (! $movement) { return ['ok' => false, 'message' => 'Movement not found.']; } @@ -125,7 +124,8 @@ class InventoryMovementService $movement->update($payload); $this->recalcQuantity($itemId); } catch (\Throwable $e) { - Log::error('Inventory movement update failed: ' . $e->getMessage()); + Log::error('Inventory movement update failed: '.$e->getMessage()); + return ['ok' => false, 'message' => 'Could not update movement.']; } @@ -139,7 +139,7 @@ class InventoryMovementService public function delete(int $id): array { $movement = InventoryMovement::query()->find($id); - if (!$movement) { + if (! $movement) { return ['ok' => false, 'message' => 'Movement not found.']; } @@ -158,7 +158,8 @@ class InventoryMovementService $this->recalcQuantity($itemId); } } catch (\Throwable $e) { - Log::error('Inventory movement delete failed: ' . $e->getMessage()); + Log::error('Inventory movement delete failed: '.$e->getMessage()); + return ['ok' => false, 'message' => 'Could not delete movement.']; } @@ -200,7 +201,8 @@ class InventoryMovementService $this->recalcQuantity($itemId); } } catch (\Throwable $e) { - Log::error('Inventory movement bulk delete failed: ' . $e->getMessage()); + Log::error('Inventory movement bulk delete failed: '.$e->getMessage()); + return ['ok' => false, 'message' => 'Bulk delete failed.']; } @@ -223,7 +225,7 @@ class InventoryMovementService ): bool { $isInitial = ($type === 'initial'); - if (!$isInitial) { + if (! $isInitial) { $this->ensureInitialMovement($itemId); $this->ensureInitialMovementForYear($itemId, $this->context->schoolYear()); $this->recalcQuantity($itemId); @@ -252,6 +254,7 @@ class InventoryMovementService ]); $this->recalcQuantity($itemId); + return true; } @@ -262,7 +265,7 @@ class InventoryMovementService public function voidMovement(int $movementId, string $reason, int $voidedBy): array { $movement = InventoryMovement::query()->find($movementId); - if (!$movement) { + if (! $movement) { return ['ok' => false, 'message' => 'Movement not found.']; } @@ -288,7 +291,7 @@ class InventoryMovementService 'item_id' => $itemId, 'qty_change' => $reverseQty, 'movement_type' => 'adjust', - 'reason' => 'Correction: voided movement #' . $movement->id, + 'reason' => 'Correction: voided movement #'.$movement->id, 'note' => $reason, 'semester' => $this->context->semester(), 'school_year' => $this->context->schoolYear(), @@ -300,7 +303,8 @@ class InventoryMovementService $this->recalcQuantity($itemId); }); } catch (\Throwable $e) { - Log::error('Inventory movement void failed: ' . $e->getMessage()); + Log::error('Inventory movement void failed: '.$e->getMessage()); + return ['ok' => false, 'message' => 'Could not void movement.']; } @@ -314,7 +318,7 @@ class InventoryMovementService public function correctMovement(int $movementId, int $correctedQtyChange, string $reason, int $performedBy): array { $movement = InventoryMovement::query()->find($movementId); - if (!$movement) { + if (! $movement) { return ['ok' => false, 'message' => 'Movement not found.']; } @@ -333,7 +337,7 @@ class InventoryMovementService 'item_id' => $itemId, 'qty_change' => $correctedQtyChange, 'movement_type' => 'adjust', - 'reason' => 'Correction for movement #' . $movement->id, + 'reason' => 'Correction for movement #'.$movement->id, 'note' => $reason, 'semester' => $this->context->semester(), 'school_year' => $this->context->schoolYear(), @@ -343,7 +347,8 @@ class InventoryMovementService $this->recalcQuantity($itemId); } catch (\Throwable $e) { - Log::error('Inventory movement correction failed: ' . $e->getMessage()); + Log::error('Inventory movement correction failed: '.$e->getMessage()); + return ['ok' => false, 'message' => 'Could not correct movement.']; } @@ -364,7 +369,7 @@ class InventoryMovementService } $item = InventoryItem::query()->select('id', 'type', 'quantity', 'needs_repair_qty', 'good_qty')->find($itemId); - if (!$item) { + if (! $item) { return; } @@ -381,7 +386,7 @@ class InventoryMovementService } } - if (!empty($updates)) { + if (! empty($updates)) { $item->update($updates); } } @@ -453,7 +458,7 @@ class InventoryMovementService ->where('school_year', $schoolYear) ->exists(); - if (!$hasAnyThisYear) { + if (! $hasAnyThisYear) { $onHand = (int) (InventoryItem::query()->where('id', $itemId)->value('quantity') ?? 0); InventoryMovement::query()->create([ 'item_id' => $itemId, @@ -462,6 +467,7 @@ class InventoryMovementService 'school_year' => $schoolYear, 'semester' => $this->context->semester() ?: null, ]); + return; } @@ -471,7 +477,7 @@ class InventoryMovementService ->where('movement_type', 'initial') ->exists(); - if (!$hasInitial) { + if (! $hasInitial) { $onHand = (int) (InventoryItem::query()->where('id', $itemId)->value('quantity') ?? 0); $netYear = (int) (InventoryMovement::query() ->where('item_id', $itemId) @@ -494,12 +500,14 @@ class InventoryMovementService if (in_array($movementType, ['out', 'distribution'], true) && $qtyChange > 0) { return -abs($qtyChange); } + return $qtyChange; } private function wouldGoNegative(int $itemId, int $delta): bool { $onHand = (int) (InventoryItem::query()->where('id', $itemId)->value('quantity') ?? 0); + return $onHand + $delta < 0; } @@ -508,6 +516,7 @@ class InventoryMovementService if ($val === null || $val === '' || $val === '0') { return null; } + return (int) $val; } diff --git a/app/Services/Inventory/InventorySummaryService.php b/app/Services/Inventory/InventorySummaryService.php index 6d91e5e9..5254f847 100644 --- a/app/Services/Inventory/InventorySummaryService.php +++ b/app/Services/Inventory/InventorySummaryService.php @@ -7,9 +7,7 @@ use Illuminate\Support\Facades\DB; class InventorySummaryService { - public function __construct(private InventoryContextService $context) - { - } + public function __construct(private InventoryContextService $context) {} public function summary(string $type, ?string $schoolYear): array { @@ -24,7 +22,7 @@ class InventorySummaryService $itemIds = array_map(static fn ($i) => (int) ($i['id'] ?? 0), $items); $agg = []; - if (!empty($itemIds)) { + if (! empty($itemIds)) { $qb = DB::table('inventory_movements') ->selectRaw('item_id, SUM(CASE WHEN qty_change>0 THEN qty_change ELSE 0 END) AS received, @@ -65,7 +63,7 @@ class InventorySummaryService ->leftJoin('users as u', 'u.id', '=', 'i.updated_by') ->orderBy('i.name'); - if (!$isAllYears) { + if (! $isAllYears) { $qbItems->where('i.school_year', $selectedYear); } @@ -87,7 +85,7 @@ class InventorySummaryService 'variance' => 0, ]; - if (!empty($items)) { + if (! empty($items)) { $itemIds = array_map('intval', array_column($items, 'id')); $aggById = []; $qb = DB::table('inventory_movements as m') @@ -102,7 +100,7 @@ class InventorySummaryService ->whereIn('m.item_id', $itemIds) ->groupBy('m.item_id'); - if (!$isAllYears) { + if (! $isAllYears) { $qb->where('m.school_year', $selectedYear); } @@ -182,7 +180,7 @@ class InventorySummaryService ->all(); $currentYear = $this->context->schoolYear(); - if ($currentYear && !in_array($currentYear, $years, true)) { + if ($currentYear && ! in_array($currentYear, $years, true)) { array_unshift($years, $currentYear); } diff --git a/app/Services/Inventory/InventoryTeacherDistributionService.php b/app/Services/Inventory/InventoryTeacherDistributionService.php index e5b76634..25501c9c 100644 --- a/app/Services/Inventory/InventoryTeacherDistributionService.php +++ b/app/Services/Inventory/InventoryTeacherDistributionService.php @@ -9,20 +9,18 @@ use App\Models\StudentClass; use App\Models\TeacherClass; use App\Models\User; use Illuminate\Support\Facades\DB; -use Illuminate\Support\Facades\Log; class InventoryTeacherDistributionService { public function __construct( private InventoryContextService $context, private InventoryMovementService $movementService - ) { - } + ) {} public function formData(int $userId, ?int $classSectionId, ?int $itemId): array { $ctx = $this->buildTeacherClassContext($userId); - if (!$ctx['ok']) { + if (! $ctx['ok']) { return $ctx; } @@ -33,7 +31,7 @@ class InventoryTeacherDistributionService : (int) ($classSectionId ?? ($ctx['defaultClassSectionId'] ?? 0)); $classID = null; - if (!empty($selectedClassId)) { + if (! empty($selectedClassId)) { $classID = ClassSection::getClassId($selectedClassId); $classID = $classID !== null ? (int) $classID : null; } @@ -43,7 +41,7 @@ class InventoryTeacherDistributionService ->leftJoin('inventory_categories as c', 'c.id', '=', 'i.category_id') ->where('i.type', 'book'); - if (!empty($classID)) { + if (! empty($classID)) { $builder->where(function ($q) use ($classID) { $q->where(function ($inner) use ($classID) { $inner->whereNotNull('c.grade_min') @@ -68,27 +66,34 @@ class InventoryTeacherDistributionService $name = $r['category_name'] ?? 'Uncategorized'; $gmin = $r['grade_min'] ?? null; $gmax = $r['grade_max'] ?? null; - if ($gmin === null && $gmax === null) return $name; - if ($gmin !== null && $gmax !== null) return $name . ' (G' . (int) $gmin . '–G' . (int) $gmax . ')'; - if ($gmin !== null) return $name . ' (G' . (int) $gmin . '+)'; - return $name . ' (≤G' . (int) $gmax . ')'; + if ($gmin === null && $gmax === null) { + return $name; + } + if ($gmin !== null && $gmax !== null) { + return $name.' (G'.(int) $gmin.'–G'.(int) $gmax.')'; + } + if ($gmin !== null) { + return $name.' (G'.(int) $gmin.'+)'; + } + + return $name.' (≤G'.(int) $gmax.')'; }; $booksGrouped = []; foreach ($rows as $r) { $catId = (int) ($r['category_id'] ?? 0); - if (!isset($booksGrouped[$catId])) { + if (! isset($booksGrouped[$catId])) { $booksGrouped[$catId] = [ 'label' => $labelFor($r), 'items' => [], ]; } $display = $r['name']; - if (!empty($r['isbn'])) { - $display .= ' — ISBN ' . $r['isbn']; + if (! empty($r['isbn'])) { + $display .= ' — ISBN '.$r['isbn']; } - if (!empty($r['edition'])) { - $display .= ' (' . $r['edition'] . ')'; + if (! empty($r['edition'])) { + $display .= ' ('.$r['edition'].')'; } $booksGrouped[$catId]['items'][] = [ @@ -123,7 +128,9 @@ class InventoryTeacherDistributionService ->all(); foreach ($rowsHist as $r) { $sid = (int) ($r['student_id'] ?? 0); - if ($sid) $already[$sid] = (int) ($r['qty'] ?? 0); + if ($sid) { + $already[$sid] = (int) ($r['qty'] ?? 0); + } } } @@ -154,7 +161,7 @@ class InventoryTeacherDistributionService public function distribute(int $userId, array $payload): array { $ctx = $this->buildTeacherClassContext($userId); - if (!$ctx['ok']) { + if (! $ctx['ok']) { return $ctx; } @@ -168,12 +175,12 @@ class InventoryTeacherDistributionService ? (int) $classIds[0] : (in_array($postedClassId, $classIds, true) ? $postedClassId : 0); - if (!$classSectionId) { + if (! $classSectionId) { return ['ok' => false, 'message' => 'Invalid class selection.']; } $book = InventoryItem::query()->find($itemId); - if (!$book || $book->type !== 'book') { + if (! $book || $book->type !== 'book') { return ['ok' => false, 'message' => 'Invalid book.']; } @@ -181,7 +188,9 @@ class InventoryTeacherDistributionService $validIds = []; foreach ($students as $st) { $sid = $st['student_id'] ?? ($st['id'] ?? null); - if ($sid) $validIds[(int) $sid] = true; + if ($sid) { + $validIds[(int) $sid] = true; + } } $selectedIds = array_values(array_filter($selectedIds, fn ($sid) => isset($validIds[$sid]))); @@ -200,7 +209,9 @@ class InventoryTeacherDistributionService ->all(); foreach ($rows as $r) { $sid = (int) ($r['student_id'] ?? 0); - if ($sid) $already[$sid] = (int) ($r['qty'] ?? 0); + if ($sid) { + $already[$sid] = (int) ($r['qty'] ?? 0); + } } $toGive = []; @@ -243,7 +254,7 @@ class InventoryTeacherDistributionService } $user = User::query()->find($userId); - if (!$user) { + if (! $user) { return ['ok' => false, 'message' => 'User not found.']; } @@ -253,13 +264,13 @@ class InventoryTeacherDistributionService ->pluck('r.name') ->toArray(); - if (!in_array('teacher', $roles, true) && !in_array('teacher_assistant', $roles, true)) { + if (! in_array('teacher', $roles, true) && ! in_array('teacher_assistant', $roles, true)) { return ['ok' => false, 'message' => 'Access denied.']; } $classSectionIds = array_column($classes, 'class_section_id'); $studentsData = []; - if (!empty($classSectionIds)) { + if (! empty($classSectionIds)) { $students = StudentClass::getStudentsByClassSectionIds($classSectionIds); foreach ($students as $student) { $studentsData[$student['class_section_id']][] = $student; @@ -278,7 +289,7 @@ class InventoryTeacherDistributionService $taUser = User::query()->find($ta['teacher_id'] ?? 0); if ($taUser) { $csid = $ta['class_section_id']; - $taNames[$csid][] = trim(($taUser->firstname ?? '') . ' ' . ($taUser->lastname ?? '')); + $taNames[$csid][] = trim(($taUser->firstname ?? '').' '.($taUser->lastname ?? '')); } } diff --git a/app/Services/Inventory/SupplierService.php b/app/Services/Inventory/SupplierService.php index a852d638..1e6a8fde 100644 --- a/app/Services/Inventory/SupplierService.php +++ b/app/Services/Inventory/SupplierService.php @@ -13,9 +13,9 @@ class SupplierService $query = Supplier::query(); if ($q !== '') { $query->where(function ($builder) use ($q) { - $builder->where('name', 'like', '%' . $q . '%') - ->orWhere('email', 'like', '%' . $q . '%') - ->orWhere('phone', 'like', '%' . $q . '%'); + $builder->where('name', 'like', '%'.$q.'%') + ->orWhere('email', 'like', '%'.$q.'%') + ->orWhere('phone', 'like', '%'.$q.'%'); }); } @@ -37,7 +37,8 @@ class SupplierService try { $supplier = Supplier::query()->create($this->payload($data)); } catch (\Throwable $e) { - Log::error('Supplier create failed: ' . $e->getMessage()); + Log::error('Supplier create failed: '.$e->getMessage()); + return ['ok' => false, 'message' => 'Failed to save supplier.']; } @@ -47,14 +48,15 @@ class SupplierService public function update(int $id, array $data): array { $supplier = Supplier::query()->find($id); - if (!$supplier) { + if (! $supplier) { return ['ok' => false, 'message' => 'Supplier not found.']; } try { $supplier->update($this->payload($data)); } catch (\Throwable $e) { - Log::error('Supplier update failed: ' . $e->getMessage()); + Log::error('Supplier update failed: '.$e->getMessage()); + return ['ok' => false, 'message' => 'Failed to update supplier.']; } @@ -64,14 +66,15 @@ class SupplierService public function delete(int $id): array { $supplier = Supplier::query()->find($id); - if (!$supplier) { + if (! $supplier) { return ['ok' => false, 'message' => 'Supplier not found.']; } try { $supplier->delete(); } catch (\Throwable $e) { - Log::error('Supplier delete failed: ' . $e->getMessage()); + Log::error('Supplier delete failed: '.$e->getMessage()); + return ['ok' => false, 'message' => 'Failed to delete supplier.']; } diff --git a/app/Services/Inventory/SupplyCategoryService.php b/app/Services/Inventory/SupplyCategoryService.php index b435ea67..273553a3 100644 --- a/app/Services/Inventory/SupplyCategoryService.php +++ b/app/Services/Inventory/SupplyCategoryService.php @@ -19,7 +19,8 @@ class SupplyCategoryService 'name' => (string) ($data['name'] ?? ''), ]); } catch (\Throwable $e) { - Log::error('Supply category create failed: ' . $e->getMessage()); + Log::error('Supply category create failed: '.$e->getMessage()); + return ['ok' => false, 'message' => 'Failed to save category.']; } @@ -29,14 +30,15 @@ class SupplyCategoryService public function update(int $id, array $data): array { $category = SupplyCategory::query()->find($id); - if (!$category) { + if (! $category) { return ['ok' => false, 'message' => 'Category not found.']; } try { $category->update(['name' => (string) ($data['name'] ?? '')]); } catch (\Throwable $e) { - Log::error('Supply category update failed: ' . $e->getMessage()); + Log::error('Supply category update failed: '.$e->getMessage()); + return ['ok' => false, 'message' => 'Failed to update category.']; } @@ -46,14 +48,15 @@ class SupplyCategoryService public function delete(int $id): array { $category = SupplyCategory::query()->find($id); - if (!$category) { + if (! $category) { return ['ok' => false, 'message' => 'Category not found.']; } try { $category->delete(); } catch (\Throwable $e) { - Log::error('Supply category delete failed: ' . $e->getMessage()); + Log::error('Supply category delete failed: '.$e->getMessage()); + return ['ok' => false, 'message' => 'Failed to delete category.']; } diff --git a/app/Services/Invoices/InvoiceConfigService.php b/app/Services/Invoices/InvoiceConfigService.php index 1ccb9cc7..cff9bca1 100644 --- a/app/Services/Invoices/InvoiceConfigService.php +++ b/app/Services/Invoices/InvoiceConfigService.php @@ -19,6 +19,7 @@ class InvoiceConfigService public function getDueDate(): ?string { $value = Configuration::getConfig('due_date'); + return $value ? (string) $value : null; } @@ -45,7 +46,7 @@ class InvoiceConfigService public function getRefundDeadline(): ?string { $value = Configuration::getConfig('refund_deadline'); - if (!$value) { + if (! $value) { return null; } diff --git a/app/Services/Invoices/InvoiceGenerationService.php b/app/Services/Invoices/InvoiceGenerationService.php index ddc5c5db..7ed4f7dd 100644 --- a/app/Services/Invoices/InvoiceGenerationService.php +++ b/app/Services/Invoices/InvoiceGenerationService.php @@ -18,8 +18,7 @@ class InvoiceGenerationService public function __construct( private InvoiceConfigService $config, private InvoiceTuitionService $tuitionService - ) { - } + ) {} public function generateInvoice(int $parentId, ?string $schoolYear = null): array { @@ -64,11 +63,13 @@ class InvoiceGenerationService $registeredKids = array_values(array_filter($registeredKids, function ($student) use ($schoolYear) { $sid = (int) ($student['student_id'] ?? 0); + return $sid > 0 && StudentClass::hasNonEventAssignment($sid, $schoolYear); })); $withdrawnKids = array_values(array_filter($withdrawnKids, function ($student) use ($schoolYear) { $sid = (int) ($student['student_id'] ?? 0); + return $sid > 0 && StudentClass::hasNonEventAssignment($sid, $schoolYear); })); @@ -83,7 +84,7 @@ class InvoiceGenerationService $this->recalculateAndUpdateDiscount($parentId, $schoolYear, $tuitionFee, $enrollments); - $refundPaid = (new Refund())->getTotalApprovedRefundByParentIdAndSchoolYear($parentId, $schoolYear); + $refundPaid = (new Refund)->getTotalApprovedRefundByParentIdAndSchoolYear($parentId, $schoolYear); $totalPaid = Payment::getTotalPaidByParentId($parentId, $schoolYear); $totalAmount = max(0.0, (float) $tuitionFee) + (float) $eventChargeTotal; @@ -93,9 +94,9 @@ class InvoiceGenerationService $updatedIds = []; $insertId = null; - if (!empty($existingInvoices)) { + if (! empty($existingInvoices)) { foreach ($existingInvoices as $invoice) { - if (!isset($invoice['id'])) { + if (! isset($invoice['id'])) { continue; } @@ -127,14 +128,14 @@ class InvoiceGenerationService } else { $schoolId = User::getSchoolIdByUserId($parentId); $invoiceNumber = $schoolId - ? ('INV-' . $schoolId . '-' . uniqid()) + ? ('INV-'.$schoolId.'-'.uniqid()) : uniqid('INV-'); $issueUtc = (new DateTime('now', new DateTimeZone('UTC')))->format('Y-m-d H:i:s'); $dueUtc = null; $dueDate = $this->config->getDueDate(); - if (!empty($dueDate)) { - $dueLocal = new DateTime($dueDate . ' 19:59:59', new DateTimeZone($this->config->getTimezone())); + if (! empty($dueDate)) { + $dueLocal = new DateTime($dueDate.' 19:59:59', new DateTimeZone($this->config->getTimezone())); $dueLocal->setTimezone(new DateTimeZone('UTC')); $dueUtc = $dueLocal->format('Y-m-d H:i:s'); } @@ -180,7 +181,7 @@ class InvoiceGenerationService } foreach ($invoices as $invoice) { - if (!isset($invoice['id'])) { + if (! isset($invoice['id'])) { continue; } @@ -193,7 +194,7 @@ class InvoiceGenerationService ->where('i.school_year', $schoolYear) ->first(); - if (!$discountUsage) { + if (! $discountUsage) { continue; } diff --git a/app/Services/Invoices/InvoiceGradeService.php b/app/Services/Invoices/InvoiceGradeService.php index 41d54c1e..7609503a 100644 --- a/app/Services/Invoices/InvoiceGradeService.php +++ b/app/Services/Invoices/InvoiceGradeService.php @@ -4,13 +4,12 @@ namespace App\Services\Invoices; class InvoiceGradeService { - public function __construct(private int $gradeFee) - { - } + public function __construct(private int $gradeFee) {} public function isKindergarten(string $grade): bool { $g = strtolower(trim($grade)); + return in_array($g, ['KG', 'kg', 'k', 'kindergarten', 'k-g', 'k.g', 'k g'], true); } @@ -29,6 +28,7 @@ class InvoiceGradeService public function gradeLevelInt(string $grade): int { $gl = $this->getGradeLevel($grade); + return (int) ($gl['level'] ?? 0); } @@ -39,10 +39,11 @@ class InvoiceGradeService if ($num === 13) { return ['level' => 1, 'suffix' => '', 'classId' => 13]; } + return ['level' => $num, 'suffix' => '', 'classId' => null]; } - if (!is_string($grade)) { + if (! is_string($grade)) { return ['level' => 999, 'suffix' => '', 'classId' => null]; } @@ -63,6 +64,7 @@ class InvoiceGradeService if (preg_match('/^Y(?:OUTH)?(?:\s*(\d+))?$/', $g, $m)) { $n = isset($m[1]) && $m[1] !== '' ? max(1, (int) $m[1]) : 1; + return [ 'level' => $this->gradeFee + $n, 'suffix' => '', diff --git a/app/Services/Invoices/InvoiceManagementService.php b/app/Services/Invoices/InvoiceManagementService.php index b410ec7c..d81a0886 100644 --- a/app/Services/Invoices/InvoiceManagementService.php +++ b/app/Services/Invoices/InvoiceManagementService.php @@ -5,15 +5,12 @@ namespace App\Services\Invoices; use App\Models\Enrollment; use App\Models\Invoice; use App\Models\Student; -use App\Models\StudentClass; use App\Models\User; use Illuminate\Support\Facades\DB; class InvoiceManagementService { - public function __construct(private InvoiceConfigService $config) - { - } + public function __construct(private InvoiceConfigService $config) {} public function getManagementData(?string $schoolYear): array { @@ -47,7 +44,7 @@ class InvoiceManagementService ->all(); $parentData = [ - 'parent_name' => trim(($parent['firstname'] ?? '') . ' ' . ($parent['lastname'] ?? '')), + 'parent_name' => trim(($parent['firstname'] ?? '').' '.($parent['lastname'] ?? '')), 'parent_id' => (int) $parent['id'], 'enrolledKids' => [], 'withdrawnKids' => [], @@ -60,7 +57,7 @@ class InvoiceManagementService $invoices = Invoice::getInvoicesByParentId((int) $parent['id'], $schoolYear); foreach ($invoices as $invoice) { - if (!$invoice) { + if (! $invoice) { continue; } @@ -68,13 +65,13 @@ class InvoiceManagementService $parentData['last_updated'] = $invoice['updated_at'] ?? null; $parentData['invoice_id'] = $invoice['id'] ?? null; - if (!empty($invoice['issue_date'])) { + if (! empty($invoice['issue_date'])) { $tzName = $this->config->getTimezone(); $parentData['invoice_date'] = (new \DateTimeImmutable($invoice['issue_date'], new \DateTimeZone('UTC'))) ->setTimezone(new \DateTimeZone($tzName)) ->format('Y-m-d H:i:s'); } else { - $parentData['invoice_date'] = !empty($invoice['updated_at']) + $parentData['invoice_date'] = ! empty($invoice['updated_at']) ? date('Y-m-d H:i:s', strtotime($invoice['updated_at'])) : null; } @@ -115,7 +112,7 @@ class InvoiceManagementService foreach ($enrollments as $enrollment) { $kid = [ - 'name' => trim(($student['firstname'] ?? '') . ' ' . ($student['lastname'] ?? '')), + 'name' => trim(($student['firstname'] ?? '').' '.($student['lastname'] ?? '')), 'grade' => $grade, 'tuition_fee' => (float) ($enrollment['tuition_fee'] ?? 0), ]; @@ -136,7 +133,7 @@ class InvoiceManagementService } } - if (!empty($parentData['enrolledKids']) || !empty($parentData['withdrawnKids'])) { + if (! empty($parentData['enrolledKids']) || ! empty($parentData['withdrawnKids'])) { $invoiceData[] = $parentData; } } diff --git a/app/Services/Invoices/InvoicePaymentService.php b/app/Services/Invoices/InvoicePaymentService.php index ec1ad56c..51bb2275 100644 --- a/app/Services/Invoices/InvoicePaymentService.php +++ b/app/Services/Invoices/InvoicePaymentService.php @@ -8,9 +8,7 @@ use Illuminate\Support\Facades\DB; class InvoicePaymentService { - public function __construct(private InvoiceConfigService $config) - { - } + public function __construct(private InvoiceConfigService $config) {} public function getParentInvoiceSummary(int $parentId, ?string $schoolYear): array { @@ -26,9 +24,9 @@ class InvoicePaymentService ->all(); $selectedYear = $schoolYear ?: null; - if (!$selectedYear) { + if (! $selectedYear) { $hasCurrentYear = in_array($currentSchoolYear, array_column($schoolYears, 'school_year'), true); - $selectedYear = $hasCurrentYear ? $currentSchoolYear : (!empty($schoolYears) ? $schoolYears[0]['school_year'] : null); + $selectedYear = $hasCurrentYear ? $currentSchoolYear : (! empty($schoolYears) ? $schoolYears[0]['school_year'] : null); } $invoices = []; @@ -38,7 +36,7 @@ class InvoicePaymentService $invoiceIds = array_column($invoices, 'id'); $refunds = []; - if (!empty($invoiceIds)) { + if (! empty($invoiceIds)) { $refundResults = DB::table('refunds') ->selectRaw('invoice_id, COALESCE(SUM(refund_paid_amount),0) as refund_paid_amount') ->whereIn('invoice_id', $invoiceIds) @@ -54,7 +52,7 @@ class InvoicePaymentService } $lastPayments = []; - if (!empty($invoiceIds)) { + if (! empty($invoiceIds)) { $paymentResults = Payment::getPaymentsByInvoice($invoiceIds); foreach ($paymentResults as $payment) { $lastPayments[$payment['invoice_id']] = [ diff --git a/app/Services/Invoices/InvoicePdfService.php b/app/Services/Invoices/InvoicePdfService.php index ab47d5d6..a69482f1 100644 --- a/app/Services/Invoices/InvoicePdfService.php +++ b/app/Services/Invoices/InvoicePdfService.php @@ -9,7 +9,6 @@ use App\Models\Enrollment; use App\Models\EventCharges; use App\Models\Invoice; use App\Models\Payment; -use App\Models\Refund; use App\Models\Student; use App\Models\StudentClass; use App\Models\User; @@ -21,8 +20,7 @@ class InvoicePdfService public function __construct( private InvoiceConfigService $config, private InvoiceGradeService $grades - ) { - } + ) {} public function buildPdf(int $invoiceId): string { @@ -42,7 +40,7 @@ class InvoicePdfService private function prepareInvoiceData(int $invoiceId): array { $invoice = Invoice::query()->find($invoiceId); - if (!$invoice) { + if (! $invoice) { return ['error' => 'No invoice was generated. Please contact the school administration.']; } @@ -69,7 +67,7 @@ class InvoicePdfService $payments = $paymentsQuery->get()->map(fn ($r) => $r->toArray())->all(); $parent = User::query()->find($parentId); - if (!$parent) { + if (! $parent) { return ['error' => 'Parent associated with the invoice was not found.']; } @@ -89,7 +87,7 @@ class InvoicePdfService foreach ($enrollments as $enrollment) { $student = Student::query()->find($enrollment['student_id']); - if (!$student) { + if (! $student) { continue; } @@ -112,11 +110,13 @@ class InvoicePdfService $registeredKids = array_values(array_filter($registeredKids, function ($student) use ($schoolYear) { $sid = (int) ($student['student_id'] ?? 0); + return $sid > 0 && StudentClass::hasNonEventAssignment($sid, $schoolYear); })); $withdrawnKids = array_values(array_filter($withdrawnKids, function ($student) use ($schoolYear) { $sid = (int) ($student['student_id'] ?? 0); + return $sid > 0 && StudentClass::hasNonEventAssignment($sid, $schoolYear); })); @@ -148,8 +148,10 @@ class InvoicePdfService if ($isRegular) { $fee = $regularCount === 0 ? $firstFee : $secondFee; $regularCount++; + return $fee; } + return $youthFee; }; @@ -160,7 +162,7 @@ class InvoicePdfService $studentCharges[$student['student_id']] = ['unit_fee' => $unitFee, 'refund' => 0]; } - if (!$refundAllowed) { + if (! $refundAllowed) { foreach ($withdrawnKids as $student) { $gradeName = (string) ($student['grade'] ?? ''); $gradeLevel = $this->grades->gradeLevelInt($gradeName); @@ -173,7 +175,7 @@ class InvoicePdfService $studentIds = array_values(array_unique(array_map(static fn ($r) => (int) ($r['student_id'] ?? 0), array_merge($registeredKids, $withdrawnKids)))); $schoolIdMap = []; - if (!empty($studentIds)) { + if (! empty($studentIds)) { $rows = Student::query()->whereIn('id', $studentIds)->get(['id', 'school_id'])->all(); foreach ($rows as $row) { $schoolIdMap[(int) $row->id] = $row->school_id ?? 'N/A'; @@ -222,9 +224,9 @@ class InvoicePdfService $signed = abs($signed); } - $lineDate = !empty($ac['created_at']) + $lineDate = ! empty($ac['created_at']) ? date('Y-m-d', strtotime($ac['created_at'])) - : (!empty($invoice->created_at) ? local_date($invoice->created_at, 'Y-m-d') : local_date(utc_now(), 'Y-m-d')); + : (! empty($invoice->created_at) ? local_date($invoice->created_at, 'Y-m-d') : local_date(utc_now(), 'Y-m-d')); $desc = $ac['description'] ?? ''; if ($desc === '') { @@ -270,7 +272,7 @@ class InvoicePdfService $additionalChargesTotal = (float) ($data['additionalChargesTotal'] ?? 0); $refundsPaidTotal = (float) ($data['refundsPaidTotal'] ?? 0); - $pdf = new \FPDF(); + $pdf = new \FPDF; $pdf->AddPage(); $pdf->SetFont('Arial', 'B', 16); $pdf->Cell(0, 10, 'Invoice', 0, 1, 'C'); @@ -279,7 +281,7 @@ class InvoicePdfService $pdf->SetFont('Arial', 'B', 12); $pdf->Cell(40, 6, 'Parent Name:', 0, 0, 'L'); $pdf->SetFont('Arial', '', 12); - $pdf->Cell(0, 6, ($parent['firstname'] ?? '') . ' ' . ($parent['lastname'] ?? ''), 0, 1, 'L'); + $pdf->Cell(0, 6, ($parent['firstname'] ?? '').' '.($parent['lastname'] ?? ''), 0, 1, 'L'); $pdf->SetFont('Arial', 'B', 12); $pdf->Cell(40, 6, 'Invoice Number:', 0, 0, 'L'); @@ -306,7 +308,7 @@ class InvoicePdfService $tz = new \DateTimeZone($this->config->getTimezone()); $toLocal = function (?string $raw) use ($tz): \DateTimeImmutable { - if (!$raw) { + if (! $raw) { return new \DateTimeImmutable('now', $tz); } try { @@ -319,24 +321,24 @@ class InvoicePdfService foreach ($registeredKids as $student) { $id = $student['student_id']; $unit = (float) ($studentCharges[$id]['unit_fee'] ?? 0.0); - $name = $student['student_firstname'] . ' ' . $student['student_lastname']; + $name = $student['student_firstname'].' '.$student['student_lastname']; $gradeName = ClassSection::getClassSectionNameByClassId($student['grade']) ?? $student['grade']; - $push($toLocal($invoice['created_at'] ?? null), 'Registration of "' . $name . '" in ' . $gradeName, $unit, 'registration'); + $push($toLocal($invoice['created_at'] ?? null), 'Registration of "'.$name.'" in '.$gradeName, $unit, 'registration'); } foreach ($events as $event) { $studentName = 'N/A'; - if (!empty($event['student_id'])) { + if (! empty($event['student_id'])) { foreach ($students as $st) { if (($st['student_id'] ?? null) == $event['student_id']) { - $studentName = $st['student_firstname'] . ' ' . $st['student_lastname']; + $studentName = $st['student_firstname'].' '.$st['student_lastname']; break; } } } $amount = (float) ($event['charged'] ?? 0.0); $eventName = $event['event_name'] ?? 'Event'; - $push($toLocal($event['created_at'] ?? null), 'Event ' . $eventName . ' for "' . $studentName . '"', $amount, 'event'); + $push($toLocal($event['created_at'] ?? null), 'Event '.$eventName.' for "'.$studentName.'"', $amount, 'event'); } foreach ($additionalChargeLines as $line) { @@ -347,7 +349,7 @@ class InvoicePdfService foreach ($payments as $payment) { $amount = (float) ($payment['paid_amount'] ?? 0.0); $totalPaid += $amount; - $push($toLocal($payment['payment_date'] ?? null), 'Payment (' . ($payment['payment_method'] ?? 'Payment') . ')', -$amount, 'payment'); + $push($toLocal($payment['payment_date'] ?? null), 'Payment ('.($payment['payment_method'] ?? 'Payment').')', -$amount, 'payment'); } $totalDiscount = 0.0; @@ -377,6 +379,7 @@ class InvoicePdfService return $pa <=> $pb; } $cmp = $a['dt'] <=> $b['dt']; + return $cmp !== 0 ? $cmp : ($a['seq'] <=> $b['seq']); }); @@ -384,7 +387,7 @@ class InvoicePdfService foreach ($transactions as $t) { $pdf->Cell(30, 7, $t['dt']->format('m-d-Y'), 1); $pdf->Cell(130, 7, $t['description'], 1); - $pdf->Cell(30, 7, ($t['amount'] < 0 ? '-$' : '$') . number_format(abs($t['amount']), 2), 1, 1, 'R'); + $pdf->Cell(30, 7, ($t['amount'] < 0 ? '-$' : '$').number_format(abs($t['amount']), 2), 1, 1, 'R'); } $tuitionSubtotal = 0.0; @@ -406,38 +409,38 @@ class InvoicePdfService $pdf->SetFont('Arial', 'B', 12); $pdf->Cell(160, 7, 'Total Charges:', 0, 0, 'R'); $pdf->SetFont('Arial', '', 12); - $pdf->Cell(30, 7, '$' . number_format($totalAmount, 2), 0, 1, 'R'); + $pdf->Cell(30, 7, '$'.number_format($totalAmount, 2), 0, 1, 'R'); $pdf->SetFont('Arial', 'B', 12); $pdf->Cell(160, 7, 'Total Paid:', 0, 0, 'R'); $pdf->SetFont('Arial', '', 12); - $pdf->Cell(30, 7, '$' . number_format($totalPaid, 2), 0, 1, 'R'); + $pdf->Cell(30, 7, '$'.number_format($totalPaid, 2), 0, 1, 'R'); if ($totalDiscount > 0) { $pdf->SetFont('Arial', 'B', 12); $pdf->Cell(160, 7, 'Total Discount:', 0, 0, 'R'); $pdf->SetFont('Arial', '', 12); - $pdf->Cell(30, 7, '$' . number_format($totalDiscount, 2), 0, 1, 'R'); + $pdf->Cell(30, 7, '$'.number_format($totalDiscount, 2), 0, 1, 'R'); } if ($refundsPaidTotal > 0) { $pdf->SetFont('Arial', 'B', 12); $pdf->Cell(160, 7, 'Refunds Paid:', 0, 0, 'R'); $pdf->SetFont('Arial', '', 12); - $pdf->Cell(30, 7, '$' . number_format($refundsPaidTotal, 2), 0, 1, 'R'); + $pdf->Cell(30, 7, '$'.number_format($refundsPaidTotal, 2), 0, 1, 'R'); } $pdf->SetFont('Arial', 'B', 12); $pdf->Cell(160, 7, 'Balance Due:', 0, 0, 'R'); $pdf->SetFont('Arial', '', 12); - $pdf->Cell(30, 7, '$' . number_format($balance, 2), 0, 1, 'R'); + $pdf->Cell(30, 7, '$'.number_format($balance, 2), 0, 1, 'R'); return $pdf->Output('S'); } private function renderErrorPdf(string $message): string { - $pdf = new \FPDF(); + $pdf = new \FPDF; $pdf->AddPage(); $pdf->SetFont('Arial', 'B', 16); $pdf->Cell(0, 10, 'Error', 0, 1, 'C'); diff --git a/app/Services/Invoices/InvoiceService.php b/app/Services/Invoices/InvoiceService.php index 2325ab9d..0d6b03c5 100644 --- a/app/Services/Invoices/InvoiceService.php +++ b/app/Services/Invoices/InvoiceService.php @@ -13,8 +13,7 @@ class InvoiceService private InvoiceGenerationService $generation, private InvoiceTuitionService $tuition, private InvoiceGradeService $grades - ) { - } + ) {} public function getDefaultSchoolYear(): string { diff --git a/app/Services/Invoices/InvoiceTuitionService.php b/app/Services/Invoices/InvoiceTuitionService.php index 967c579e..b3df5523 100644 --- a/app/Services/Invoices/InvoiceTuitionService.php +++ b/app/Services/Invoices/InvoiceTuitionService.php @@ -13,8 +13,7 @@ class InvoiceTuitionService private float $secondStudentFee, private float $youthFee, private string $timezone - ) { - } + ) {} public function calculateTuitionFee(array $registeredKids, array $withdrawnKids, ?string $refundDeadline): float { @@ -61,7 +60,7 @@ class InvoiceTuitionService private function isRefundAllowed(?string $refundDeadline): bool { - if (!$refundDeadline) { + if (! $refundDeadline) { return true; } @@ -69,6 +68,7 @@ class InvoiceTuitionService $tz = new \DateTimeZone($this->timezone); $today = new \DateTimeImmutable('today', $tz); $deadline = new \DateTimeImmutable($refundDeadline, $tz); + return $today <= $deadline; } catch (\Throwable $e) { return true; diff --git a/app/Services/Messaging/MessageCommandService.php b/app/Services/Messaging/MessageCommandService.php index d4a8b54a..28ff9ab9 100644 --- a/app/Services/Messaging/MessageCommandService.php +++ b/app/Services/Messaging/MessageCommandService.php @@ -10,18 +10,16 @@ use RuntimeException; class MessageCommandService { - public function __construct(private GlobalConfigService $configService) - { - } + public function __construct(private GlobalConfigService $configService) {} public function create(int $senderId, array $payload, ?UploadedFile $attachment = null): Message { $recipientId = $payload['recipient_id'] ?? null; - if (!$recipientId && !empty($payload['recipient_role'])) { + if (! $recipientId && ! empty($payload['recipient_role'])) { $recipientId = $this->recipientIdFromRole($payload['recipient_role']); } - if (!$recipientId) { + if (! $recipientId) { throw new RuntimeException('Recipient is required.'); } @@ -42,7 +40,7 @@ class MessageCommandService 'message' => (string) $payload['message'], 'sent_datetime' => now(), 'read_status' => 0, - 'message_number' => 'MSG-' . now()->format('YmdHis'), + 'message_number' => 'MSG-'.now()->format('YmdHis'), 'priority' => $payload['priority'] ?? 'normal', 'attachment' => $path, 'status' => $payload['status'] ?? 'sent', @@ -50,7 +48,7 @@ class MessageCommandService 'school_year' => $payload['school_year'] ?? $this->configService->getSchoolYear() ?? '', ]); - if (!$message) { + if (! $message) { throw new RuntimeException('Failed to send message.'); } @@ -76,7 +74,7 @@ class MessageCommandService } } - if (!empty($updates)) { + if (! empty($updates)) { $message->fill($updates); $message->save(); } @@ -89,6 +87,7 @@ class MessageCommandService { return DB::transaction(function () use ($message) { $message->status = 'trashed'; + return (bool) $message->save(); }); } @@ -117,7 +116,7 @@ class MessageCommandService ]; $key = strtolower(trim($roleName)); - if (!isset($map[$key])) { + if (! isset($map[$key])) { throw new RuntimeException('Invalid recipient role.'); } diff --git a/app/Services/Messaging/MessageQueryService.php b/app/Services/Messaging/MessageQueryService.php index ed90bfb0..37008828 100644 --- a/app/Services/Messaging/MessageQueryService.php +++ b/app/Services/Messaging/MessageQueryService.php @@ -11,11 +11,12 @@ class MessageQueryService public function getUserRoleName(int $userId): ?string { $user = User::query()->with('roles')->find($userId); - if (!$user) { + if (! $user) { return null; } $role = $user->roles->sortBy('id')->first(); + return $role ? (string) $role->name : null; } @@ -67,7 +68,7 @@ class MessageQueryService ->pluck('id') ->all(); - if (!empty($ids)) { + if (! empty($ids)) { Message::query() ->whereIn('id', $ids) ->update([ @@ -90,7 +91,7 @@ class MessageQueryService { $query = Message::query()->with(['sender', 'recipient']); - if (!empty($filters['priority'])) { + if (! empty($filters['priority'])) { $query->where('priority', $filters['priority']); } @@ -98,8 +99,8 @@ class MessageQueryService $query->where('read_status', (int) $filters['read_status']); } - if (!empty($filters['search'])) { - $term = '%' . $filters['search'] . '%'; + if (! empty($filters['search'])) { + $term = '%'.$filters['search'].'%'; $query->where(function ($q) use ($term) { $q->where('subject', 'like', $term) ->orWhere('message', 'like', $term); diff --git a/app/Services/Messaging/MessageRecipientService.php b/app/Services/Messaging/MessageRecipientService.php index f75c6cdf..99fcc905 100644 --- a/app/Services/Messaging/MessageRecipientService.php +++ b/app/Services/Messaging/MessageRecipientService.php @@ -22,7 +22,7 @@ class MessageRecipientService return $rows ->map(fn ($row) => [ 'id' => (int) $row->teacher_id, - 'name' => trim($row->firstname . ' ' . $row->lastname), + 'name' => trim($row->firstname.' '.$row->lastname), ]) ->unique('id') ->values() @@ -81,7 +81,7 @@ class MessageRecipientService return $users ->map(fn ($row) => [ 'id' => (int) $row->id, - 'name' => trim($row->firstname . ' ' . $row->lastname), + 'name' => trim($row->firstname.' '.$row->lastname), ]) ->unique('id') ->values() diff --git a/app/Services/Navigation/NavBuilderService.php b/app/Services/Navigation/NavBuilderService.php index 93617a4d..4d8c18f8 100644 --- a/app/Services/Navigation/NavBuilderService.php +++ b/app/Services/Navigation/NavBuilderService.php @@ -6,13 +6,10 @@ use App\Models\NavItem; use App\Models\Role; use App\Models\RoleNavItem; use Illuminate\Support\Facades\DB; -use Illuminate\Support\Facades\Log; class NavBuilderService { - public function __construct(private NavbarService $navbarService) - { - } + public function __construct(private NavbarService $navbarService) {} public function getAdminPayload(): array { @@ -77,7 +74,7 @@ class NavBuilderService $menuParentId = null; } - if ($menuParentId !== null && !NavItem::query()->whereKey($menuParentId)->exists()) { + if ($menuParentId !== null && ! NavItem::query()->whereKey($menuParentId)->exists()) { return ['ok' => false, 'message' => 'Selected parent does not exist.']; } @@ -88,7 +85,7 @@ class NavBuilderService 'icon_class' => trim((string) ($payload['icon_class'] ?? '')) ?: null, 'target' => trim((string) ($payload['target'] ?? '')) ?: null, 'sort_order' => (int) ($payload['sort_order'] ?? 0), - 'is_enabled' => !empty($payload['is_enabled']) ? 1 : 0, + 'is_enabled' => ! empty($payload['is_enabled']) ? 1 : 0, ]; $roleIds = array_values(array_unique(array_filter( @@ -190,7 +187,7 @@ class NavBuilderService } $roleId = (int) ($row['role_id'] ?? 0); - $roleName = $row['role_name'] ?? ($roleId ? ('#' . $roleId) : null); + $roleName = $row['role_name'] ?? ($roleId ? ('#'.$roleId) : null); if ($roleId > 0) { $roleAssignments[$navId]['ids'][] = $roleId; @@ -226,7 +223,7 @@ class NavBuilderService ], ]; - if (!empty($node['children'])) { + if (! empty($node['children'])) { $output = array_merge($output, $this->flattenTreeForResponse($node['children'], $roleAssignments, $depth + 1)); } } @@ -239,6 +236,7 @@ class NavBuilderService $s = trim(preg_replace('/\s+/', ' ', $s)); $s = preg_replace('/^[^[:alnum:]]+/u', '', $s); $s = preg_replace('/^(?i)(the|an|a)\s+/', '', $s); + return mb_strtolower($s, 'UTF-8'); } @@ -250,7 +248,7 @@ class NavBuilderService return $result !== 0 ? $result : ((int) ($a['id'] ?? 0) <=> (int) ($b['id'] ?? 0)); }); foreach ($nodes as &$node) { - if (!empty($node['children'])) { + if (! empty($node['children'])) { $this->sortTreeAlpha($node['children']); } } diff --git a/app/Services/Navigation/NavbarService.php b/app/Services/Navigation/NavbarService.php index cf9d6778..6936396a 100644 --- a/app/Services/Navigation/NavbarService.php +++ b/app/Services/Navigation/NavbarService.php @@ -11,7 +11,7 @@ class NavbarService public function getMenuForRoles(array $roles): array { $roles = array_values(array_filter(array_map(fn ($r) => strtolower(trim((string) $r)), $roles))); - $cacheKey = 'navbar_' . md5(json_encode($roles)); + $cacheKey = 'navbar_'.md5(json_encode($roles)); return Cache::remember($cacheKey, 300, function () use ($roles) { $allowedIds = RoleNavItem::getNavItemIdsForRoles($roles); @@ -75,7 +75,7 @@ class NavbarService }); foreach ($nodes as &$node) { - if (!empty($node['children'])) { + if (! empty($node['children'])) { $this->sortTreeAlpha($node['children']); } } diff --git a/app/Services/Notifications/NotificationManagementService.php b/app/Services/Notifications/NotificationManagementService.php index d692725b..9bacc69d 100644 --- a/app/Services/Notifications/NotificationManagementService.php +++ b/app/Services/Notifications/NotificationManagementService.php @@ -9,7 +9,7 @@ class NotificationManagementService public function update(int $notificationId, array $payload): array { $notification = Notification::query()->find($notificationId); - if (!$notification) { + if (! $notification) { return ['ok' => false, 'message' => 'Notification not found.']; } @@ -33,7 +33,7 @@ class NotificationManagementService public function delete(int $notificationId): bool { $notification = Notification::query()->find($notificationId); - if (!$notification) { + if (! $notification) { return false; } diff --git a/app/Services/Notifications/NotificationReadService.php b/app/Services/Notifications/NotificationReadService.php index a8aef10a..595ec09c 100644 --- a/app/Services/Notifications/NotificationReadService.php +++ b/app/Services/Notifications/NotificationReadService.php @@ -13,7 +13,7 @@ class NotificationReadService ->where('notification_id', $notificationId) ->first(); - if (!$row) { + if (! $row) { return false; } diff --git a/app/Services/Notifications/NotificationRecipientService.php b/app/Services/Notifications/NotificationRecipientService.php index a857e941..9a06446b 100644 --- a/app/Services/Notifications/NotificationRecipientService.php +++ b/app/Services/Notifications/NotificationRecipientService.php @@ -9,6 +9,7 @@ class NotificationRecipientService public function getRecipients(string $targetGroup): array { $rows = User::getUsersByRole($targetGroup); + return is_array($rows) ? $rows : []; } } diff --git a/app/Services/Notifications/NotificationSendService.php b/app/Services/Notifications/NotificationSendService.php index 5fd6dc1d..cc701c64 100644 --- a/app/Services/Notifications/NotificationSendService.php +++ b/app/Services/Notifications/NotificationSendService.php @@ -12,8 +12,7 @@ class NotificationSendService public function __construct( private NotificationRecipientService $recipients, private NotificationDispatchService $dispatcher - ) { - } + ) {} public function send(array $payload, int $actorId): array { @@ -57,11 +56,11 @@ class NotificationSendService ]); $created++; - if (in_array('email', $channels, true) && !empty($user['email'])) { + if (in_array('email', $channels, true) && ! empty($user['email'])) { $this->dispatcher->sendEmail((string) $user['email'], (string) $payload['title'], (string) $payload['message']); } - if (in_array('sms', $channels, true) && !empty($user['phone'])) { + if (in_array('sms', $channels, true) && ! empty($user['phone'])) { $this->dispatcher->sendSms((string) $user['phone'], (string) $payload['message']); } } diff --git a/app/Services/Notifications/NotificationUserListService.php b/app/Services/Notifications/NotificationUserListService.php index 65cdd23b..0c9d3f0b 100644 --- a/app/Services/Notifications/NotificationUserListService.php +++ b/app/Services/Notifications/NotificationUserListService.php @@ -15,7 +15,7 @@ class NotificationUserListService $sortDir = strtolower((string) ($filters['sort_dir'] ?? 'desc')) === 'asc' ? 'asc' : 'desc'; $allowedSorts = ['created_at', 'scheduled_at', 'priority']; - if (!in_array($sortBy, $allowedSorts, true)) { + if (! in_array($sortBy, $allowedSorts, true)) { $sortBy = 'created_at'; } @@ -40,7 +40,7 @@ class NotificationUserListService } $paginator = $query - ->orderBy('notifications.' . $sortBy, $sortDir) + ->orderBy('notifications.'.$sortBy, $sortDir) ->paginate($perPage, ['*'], 'page', $page); return [ diff --git a/app/Services/Parents/AuthorizedUsersManagementService.php b/app/Services/Parents/AuthorizedUsersManagementService.php index e34a6a23..569a7d9e 100644 --- a/app/Services/Parents/AuthorizedUsersManagementService.php +++ b/app/Services/Parents/AuthorizedUsersManagementService.php @@ -2,6 +2,7 @@ namespace App\Services\Parents; +use App\Controllers\View\AuthorizedUsersController; use App\Models\AuthorizedUser; use App\Models\User; use App\Services\ApplicationUrlService; @@ -9,7 +10,7 @@ use App\Services\Email\EmailDispatchService; use Illuminate\Support\Facades\DB; /** - * legacy {@see \App\Controllers\View\AuthorizedUsersController} parity. + * legacy {@see AuthorizedUsersController} parity. */ class AuthorizedUsersManagementService { diff --git a/app/Services/Parents/ParentAttendanceReportCalendarService.php b/app/Services/Parents/ParentAttendanceReportCalendarService.php index f7f03c12..7e5428fd 100644 --- a/app/Services/Parents/ParentAttendanceReportCalendarService.php +++ b/app/Services/Parents/ParentAttendanceReportCalendarService.php @@ -6,9 +6,7 @@ use Illuminate\Support\Facades\DB; class ParentAttendanceReportCalendarService { - public function __construct(private ParentConfigService $configService) - { - } + public function __construct(private ParentConfigService $configService) {} /** * @return array{0: array, 1: string} @@ -33,7 +31,7 @@ class ParentAttendanceReportCalendarService $dates = []; for ($i = $weeksBefore; $i >= 1; $i--) { - $dates[] = $thisSunday->copy()->modify('-' . $i . ' week'); + $dates[] = $thisSunday->copy()->modify('-'.$i.' week'); } $cursor = $thisSunday->copy(); @@ -72,13 +70,13 @@ class ParentAttendanceReportCalendarService $filtered = []; foreach ($dates as $d) { $ymd = $d->format('Y-m-d'); - if (!isset($noSchool[$ymd]) && $d >= $today && $d <= $cap) { + if (! isset($noSchool[$ymd]) && $d >= $today && $d <= $cap) { $filtered[] = $d; } } $defaultYmd = $default->format('Y-m-d'); - if (!empty($filtered)) { + if (! empty($filtered)) { $defaultYmd = $filtered[0]->format('Y-m-d'); } @@ -101,8 +99,10 @@ class ParentAttendanceReportCalendarService } if (preg_match('/^(\\d{1,2}):(\\d{2})$/', $raw, $m)) { $hh = str_pad((string) ((int) $m[1]), 2, '0', STR_PAD_LEFT); - return $hh . ':' . $m[2]; + + return $hh.':'.$m[2]; } + return $default; } diff --git a/app/Services/Parents/ParentAttendanceReportService.php b/app/Services/Parents/ParentAttendanceReportService.php index 19a6812e..99a70ddc 100644 --- a/app/Services/Parents/ParentAttendanceReportService.php +++ b/app/Services/Parents/ParentAttendanceReportService.php @@ -2,12 +2,14 @@ namespace App\Services\Parents; +use App\Controllers\View\ParentAttendanceReportController; +use App\Http\Controllers\Api\Reports\FilesController; use App\Models\AttendanceData; use App\Models\AttendanceRecord; +use App\Models\ClassSection; use App\Models\EarlyDismissalSignature; use App\Models\ParentAttendanceReport; use App\Models\Student; -use App\Models\ClassSection; use App\Services\SemesterRangeService; use Illuminate\Http\UploadedFile; use Illuminate\Support\Facades\DB; @@ -18,8 +20,7 @@ class ParentAttendanceReportService private ParentConfigService $configService, private ParentAttendanceReportCalendarService $calendarService, private SemesterRangeService $semesterRangeService - ) { - } + ) {} public function formData(int $parentId): array { @@ -90,7 +91,7 @@ class ParentAttendanceReportService } $type = (string) ($payload['type'] ?? ''); - if (!in_array($type, ['absent', 'late', 'early_dismissal'], true)) { + if (! in_array($type, ['absent', 'late', 'early_dismissal'], true)) { throw new \RuntimeException('Invalid report type.'); } @@ -104,8 +105,8 @@ class ParentAttendanceReportService foreach ($rawDates as $entry) { $dt = \DateTime::createFromFormat('Y-m-d', $entry); - if (!$dt) { - throw new \RuntimeException('Invalid date selected: ' . $entry); + if (! $dt) { + throw new \RuntimeException('Invalid date selected: '.$entry); } if ((int) $dt->format('w') !== 0) { throw new \RuntimeException('Please select Sunday dates only.'); @@ -119,10 +120,10 @@ class ParentAttendanceReportService $validDates[$entry] = $dt; } - if ($type === 'late' && (!is_string($arrivalTime) || $arrivalTime === '')) { + if ($type === 'late' && (! is_string($arrivalTime) || $arrivalTime === '')) { throw new \RuntimeException('Please provide an expected arrival time for late reporting.'); } - if ($type === 'early_dismissal' && (!is_string($dismissTime) || $dismissTime === '')) { + if ($type === 'early_dismissal' && (! is_string($dismissTime) || $dismissTime === '')) { throw new \RuntimeException('Please provide a dismissal time for early dismissal.'); } if ($type !== 'early_dismissal') { @@ -147,7 +148,7 @@ class ParentAttendanceReportService ->first(); $classSectionId = $sectionRow->class_section_id ?? null; $student = Student::query()->select('firstname', 'lastname')->find($sid); - $studentName = $student ? trim($student->firstname . ' ' . $student->lastname) : ('Student #' . $sid); + $studentName = $student ? trim($student->firstname.' '.$student->lastname) : ('Student #'.$sid); $classLabel = $this->resolveClassSectionLabel($classSectionId); foreach ($validDates as $reportDate => $_dt) { @@ -163,7 +164,7 @@ class ParentAttendanceReportService ->whereIn('type', ['absent', 'late']) ->first(); if ($existingAL) { - $blocked[] = $studentName . ' (' . $reportDate . ')'; + $blocked[] = $studentName.' ('.$reportDate.')'; } else { $existingED = (clone $qb)->where('type', 'early_dismissal')->first(); if ($existingED) { @@ -195,7 +196,7 @@ class ParentAttendanceReportService ->whereIn('type', ['absent', 'late', 'early_dismissal']) ->first(); if ($existingAny) { - $blocked[] = $studentName . ' (' . $reportDate . ')'; + $blocked[] = $studentName.' ('.$reportDate.')'; } else { ParentAttendanceReport::query()->create([ 'parent_id' => $parentId, @@ -242,8 +243,8 @@ class ParentAttendanceReportService } if ($inserted <= 0) { - $err = !empty($blocked) - ? ('Already submitted and cannot be changed for: ' . implode(', ', array_slice($blocked, 0, 5)) . (count($blocked) > 5 ? '…' : '')) + $err = ! empty($blocked) + ? ('Already submitted and cannot be changed for: '.implode(', ', array_slice($blocked, 0, 5)).(count($blocked) > 5 ? '…' : '')) : 'Could not save your report. Please try again.'; throw new \RuntimeException($err); } @@ -278,7 +279,7 @@ class ParentAttendanceReportService }))); $type = (string) ($payload['type'] ?? ''); - if (empty($dateValues) || !in_array($type, ['absent', 'late', 'early_dismissal'], true)) { + if (empty($dateValues) || ! in_array($type, ['absent', 'late', 'early_dismissal'], true)) { throw new \RuntimeException('Invalid date or type.'); } @@ -312,7 +313,7 @@ class ParentAttendanceReportService if ($sid <= 0) { continue; } - if (!isset($map[$sid])) { + if (! isset($map[$sid])) { $map[$sid] = [ 'student_id' => $sid, 'firstname' => (string) ($row['firstname'] ?? ''), @@ -326,7 +327,7 @@ class ParentAttendanceReportService } $map[$sid]['dates'][$dVal] = $map[$sid]['dates'][$dVal] ?? []; $t = (string) ($row['type'] ?? ''); - if ($t !== '' && !in_array($t, $map[$sid]['dates'][$dVal], true)) { + if ($t !== '' && ! in_array($t, $map[$sid]['dates'][$dVal], true)) { $map[$sid]['dates'][$dVal][] = $t; } } @@ -337,7 +338,7 @@ class ParentAttendanceReportService public function updateReport(int $parentId, int $reportId, array $payload): void { $row = ParentAttendanceReport::query()->find($reportId); - if (!$row || (int) $row->parent_id !== $parentId) { + if (! $row || (int) $row->parent_id !== $parentId) { throw new \RuntimeException('Report not found.'); } @@ -347,7 +348,7 @@ class ParentAttendanceReportService $tzName = (string) (config('School')->attendance['timezone'] ?? 'UTC'); $tz = new \DateTimeZone($tzName ?: 'UTC'); - $cutoff = new \DateTime($row->report_date . ' 09:00:00', $tz); + $cutoff = new \DateTime($row->report_date.' 09:00:00', $tz); $now = new \DateTime('now', $tz); if ($now >= $cutoff) { throw new \RuntimeException('Editing window closed at 9:00 AM on the report date.'); @@ -363,13 +364,13 @@ class ParentAttendanceReportService ]; if ($type === 'late' && $arrival !== '') { - if (!preg_match('/^\\d{2}:\\d{2}(:\\d{2})?$/', $arrival)) { + if (! preg_match('/^\\d{2}:\\d{2}(:\\d{2})?$/', $arrival)) { throw new \RuntimeException('Invalid arrival time format.'); } $update['arrival_time'] = $arrival; } if ($type === 'early_dismissal' && $dismiss !== '') { - if (!preg_match('/^\\d{2}:\\d{2}(:\\d{2})?$/', $dismiss)) { + if (! preg_match('/^\\d{2}:\\d{2}(:\\d{2})?$/', $dismiss)) { throw new \RuntimeException('Invalid dismissal time format.'); } $update['dismiss_time'] = $dismiss; @@ -385,7 +386,7 @@ class ParentAttendanceReportService /** * Staff index: early dismissal rows grouped by date (+ signature metadata per date). * - * Parity with legacy {@see \App\Controllers\View\ParentAttendanceReportController::earlyDismissals}. + * Parity with legacy {@see ParentAttendanceReportController::earlyDismissals}. * * @return array{groups: array>>, school_year: string, semester: string, signature_by_date: array>} */ @@ -467,7 +468,7 @@ class ParentAttendanceReportService } /** - * Parity with legacy {@see \App\Controllers\View\ParentAttendanceReportController::addEarlyDismissalForm}. + * Parity with legacy {@see ParentAttendanceReportController::addEarlyDismissalForm}. * * @return array{students: array>, default_date: string, school_year: string, semester: string} */ @@ -505,7 +506,7 @@ class ParentAttendanceReportService } /** - * Parity with legacy {@see \App\Controllers\View\ParentAttendanceReportController::saveEarlyDismissal}. + * Parity with legacy {@see ParentAttendanceReportController::saveEarlyDismissal}. */ public function saveStaffEarlyDismissal(int $studentId, string $reportDate, string $dismissTime, string $reason): void { @@ -573,8 +574,8 @@ class ParentAttendanceReportService } /** - * Parity with legacy {@see \App\Controllers\View\ParentAttendanceReportController::uploadEarlyDismissalSignature}. - * Files live under {@see storage_path('uploads/early_dismissal_signatures')} (same as {@see \App\Http\Controllers\Api\Reports\FilesController::earlyDismissalSignature}). + * Parity with legacy {@see ParentAttendanceReportController::uploadEarlyDismissalSignature}. + * Files live under {@see storage_path('uploads/early_dismissal_signatures')} (same as {@see FilesController::earlyDismissalSignature}). */ public function storeEarlyDismissalSignature( UploadedFile $file, @@ -644,7 +645,7 @@ class ParentAttendanceReportService $oldFile = (string) ($existing->filename ?? ''); $existing->update($payload); if ($oldFile !== '' && $oldFile !== $filename) { - @unlink($targetDir . DIRECTORY_SEPARATOR . $oldFile); + @unlink($targetDir.DIRECTORY_SEPARATOR.$oldFile); } } else { EarlyDismissalSignature::query()->create($payload); @@ -680,7 +681,7 @@ class ParentAttendanceReportService return; } - if (!$classSectionId) { + if (! $classSectionId) { $row = DB::table('student_class') ->select('class_section_id') ->where('student_id', $studentId) @@ -703,19 +704,19 @@ class ParentAttendanceReportService $reasonParts = ['Parent-reported']; if ($type === 'late' && $arrivalTime) { - $reasonParts[] = 'ETA ' . substr($arrivalTime, 0, 5); + $reasonParts[] = 'ETA '.substr($arrivalTime, 0, 5); } - if (!empty($reason)) { + if (! empty($reason)) { $reasonParts[] = trim($reason); } $reasonText = implode(' — ', $reasonParts); - if (!$existing) { + if (! $existing) { $classId = ClassSection::query() ->where('class_section_id', $classSectionId) ->value('class_id'); $schoolId = Student::query()->whereKey($studentId)->value('school_id'); - if (!$classId || !$schoolId) { + if (! $classId || ! $schoolId) { return; } AttendanceData::query()->create([ @@ -730,6 +731,7 @@ class ParentAttendanceReportService 'school_year' => $schoolYear, ]); $this->bumpAttendanceRecord($studentId, $classSectionId, $schoolId, $status, $semester, $schoolYear); + return; } @@ -771,6 +773,7 @@ class ParentAttendanceReportService $updates['total_late']++; } $record->update($updates); + return; } diff --git a/app/Services/Parents/ParentAttendanceService.php b/app/Services/Parents/ParentAttendanceService.php index 9bf02154..09cf93f5 100644 --- a/app/Services/Parents/ParentAttendanceService.php +++ b/app/Services/Parents/ParentAttendanceService.php @@ -6,9 +6,7 @@ use Illuminate\Support\Facades\DB; class ParentAttendanceService { - public function __construct(private ParentConfigService $configService) - { - } + public function __construct(private ParentConfigService $configService) {} public function listAttendance(int $parentId, ?string $schoolYear = null): array { diff --git a/app/Services/Parents/ParentEmergencyContactService.php b/app/Services/Parents/ParentEmergencyContactService.php index c42d29b5..1baa6ab1 100644 --- a/app/Services/Parents/ParentEmergencyContactService.php +++ b/app/Services/Parents/ParentEmergencyContactService.php @@ -10,8 +10,7 @@ class ParentEmergencyContactService public function __construct( private ParentConfigService $configService, private PhoneFormatterService $phoneFormatter - ) { - } + ) {} public function list(int $parentId): array { diff --git a/app/Services/Parents/ParentEnrollmentService.php b/app/Services/Parents/ParentEnrollmentService.php index 7b5021ac..b08fa238 100644 --- a/app/Services/Parents/ParentEnrollmentService.php +++ b/app/Services/Parents/ParentEnrollmentService.php @@ -10,9 +10,7 @@ use Illuminate\Support\Facades\DB; class ParentEnrollmentService { - public function __construct(private ParentConfigService $configService) - { - } + public function __construct(private ParentConfigService $configService) {} public function overview(int $parentId, ?string $schoolYear = null): array { @@ -26,11 +24,11 @@ class ParentEnrollmentService ->get() ->map(function ($student) use ($selectedYear) { $classSections = StudentClass::getClassSectionsByStudentId($student->id, $selectedYear, true); - $student->class_section = !empty($classSections) + $student->class_section = ! empty($classSections) ? implode(', ', $classSections) : 'Class not Assigned'; - $isArabicClass = !empty($classSections) && array_reduce( + $isArabicClass = ! empty($classSections) && array_reduce( $classSections, static fn ($carry, $name) => $carry || (is_string($name) && stripos($name, 'arabic') === 0), false @@ -181,7 +179,7 @@ class ParentEnrollmentService ->where('is_withdrawn', 0) ->first(); - if (!$enrollment) { + if (! $enrollment) { continue; } @@ -206,7 +204,7 @@ class ParentEnrollmentService if ($refund) { $refund->update([ - 'reason' => 'Withdrawal under review for student ID ' . $studentId, + 'reason' => 'Withdrawal under review for student ID '.$studentId, 'note' => null, 'updated_by' => $parentId, ]); @@ -217,7 +215,7 @@ class ParentEnrollmentService 'requested_at' => now(), 'school_year' => $schoolYear, 'status' => Refund::STATUS_PENDING, - 'reason' => 'Withdrawal under review for student ID ' . $studentId, + 'reason' => 'Withdrawal under review for student ID '.$studentId, 'request' => 'new', 'semester' => $semester, 'refund_paid_amount' => 0.0, diff --git a/app/Services/Parents/ParentEventParticipationService.php b/app/Services/Parents/ParentEventParticipationService.php index e1bb7b57..7298cd05 100644 --- a/app/Services/Parents/ParentEventParticipationService.php +++ b/app/Services/Parents/ParentEventParticipationService.php @@ -2,9 +2,9 @@ namespace App\Services\Parents; +use App\Models\Enrollment; use App\Models\Event; use App\Models\EventCharges; -use App\Models\Enrollment; use App\Models\Student; use App\Services\Invoices\InvoiceGenerationService; @@ -13,8 +13,7 @@ class ParentEventParticipationService public function __construct( private ParentConfigService $configService, private InvoiceGenerationService $invoiceGeneration - ) { - } + ) {} public function overview(int $parentId): array { @@ -27,7 +26,7 @@ class ParentEventParticipationService $charges = []; foreach ($chargesList as $charge) { - $key = $charge['student_id'] . ':' . $charge['event_id']; + $key = $charge['student_id'].':'.$charge['event_id']; $charges[$key] = [ 'participation' => $charge['participation'], 'date' => $charge['updated_at'] ?? $charge['created_at'], @@ -107,6 +106,7 @@ class ParentEventParticipationService if ($existing) { $existing->delete(); } + continue; } diff --git a/app/Services/Parents/ParentProfileService.php b/app/Services/Parents/ParentProfileService.php index a04e231f..1ce98558 100644 --- a/app/Services/Parents/ParentProfileService.php +++ b/app/Services/Parents/ParentProfileService.php @@ -9,6 +9,7 @@ class ParentProfileService public function getProfile(int $parentId): ?array { $user = User::query()->find($parentId); + return $user ? $user->toArray() : null; } diff --git a/app/Services/Parents/ParentProgressQueryService.php b/app/Services/Parents/ParentProgressQueryService.php index 2dcbfa7b..8773cc86 100644 --- a/app/Services/Parents/ParentProgressQueryService.php +++ b/app/Services/Parents/ParentProgressQueryService.php @@ -2,13 +2,16 @@ namespace App\Services\Parents; +use App\Controllers\ParentProgressController; +use App\Http\Resources\ClassProgress\ClassProgressReportResource; use App\Models\ClassProgressReport; use App\Services\ClassProgress\ClassProgressQueryService; +use Carbon\CarbonInterface; use Illuminate\Support\Collection; use Illuminate\Support\Facades\DB; /** - * legacy {@see \App\Controllers\ParentProgressController} read-side logic. + * legacy {@see ParentProgressController} read-side logic. */ class ParentProgressQueryService { @@ -133,7 +136,7 @@ class ParentProgressQueryService $statusLabel = $statusOptions[$row->status] ?? 'Unknown'; $row->setAttribute('status_label', $statusLabel); - $weekStart = $row->week_start instanceof \Carbon\CarbonInterface + $weekStart = $row->week_start instanceof CarbonInterface ? $row->week_start->format('Y-m-d') : (string) $row->week_start; $sectionId = (int) $row->class_section_id; @@ -145,7 +148,7 @@ class ParentProgressQueryService if (! isset($reportGroups[$key])) { $reportGroups[$key] = [ 'week_start' => $weekStart, - 'week_end' => $row->week_end instanceof \Carbon\CarbonInterface + 'week_end' => $row->week_end instanceof CarbonInterface ? $row->week_end->format('Y-m-d') : (string) ($row->week_end ?? ''), 'class_section_name' => $row->class_section_name ?? '', @@ -159,7 +162,7 @@ class ParentProgressQueryService continue; } - $reportGroups[$key]['reports'][$subject] = (new \App\Http\Resources\ClassProgress\ClassProgressReportResource($row))->toArray(request()); + $reportGroups[$key]['reports'][$subject] = (new ClassProgressReportResource($row))->toArray(request()); } return $reportGroups; diff --git a/app/Services/Parents/ParentRegistrationService.php b/app/Services/Parents/ParentRegistrationService.php index ba0a7a7f..a675a49f 100644 --- a/app/Services/Parents/ParentRegistrationService.php +++ b/app/Services/Parents/ParentRegistrationService.php @@ -15,8 +15,7 @@ class ParentRegistrationService public function __construct( private ParentConfigService $configService, private SchoolIdService $schoolIdService - ) { - } + ) {} public function overview(int $parentId): array { @@ -39,6 +38,7 @@ class ParentRegistrationService $kids = $kids->map(function ($kid) use ($allergies, $conditions) { $kid->allergies = ($allergies[$kid->id] ?? collect())->pluck('allergy')->all(); $kid->medical_conditions = ($conditions[$kid->id] ?? collect())->pluck('condition_name')->all(); + return $kid->toArray(); })->all(); @@ -105,6 +105,7 @@ class ParentRegistrationService 'dob' => $student['dob'], 'reason' => 'already_registered', ]; + continue; } @@ -115,7 +116,7 @@ class ParentRegistrationService 'age' => $this->calculateAge($student['dob'], $ageReference), 'gender' => $student['gender'], 'registration_grade' => $student['registration_grade'] ?? null, - 'photo_consent' => !empty($student['photo_consent']) ? 1 : 0, + 'photo_consent' => ! empty($student['photo_consent']) ? 1 : 0, 'parent_id' => $parentId, 'year_of_registration' => (string) date('Y'), 'school_year' => $schoolYear, @@ -186,7 +187,7 @@ class ParentRegistrationService 'age' => $this->calculateAge($payload['dob'], $ageReference), 'gender' => $payload['gender'], 'registration_grade' => $payload['registration_grade'] ?? null, - 'photo_consent' => !empty($payload['photo_consent']) ? 1 : 0, + 'photo_consent' => ! empty($payload['photo_consent']) ? 1 : 0, ]); StudentMedicalCondition::query()->where('student_id', $studentId)->delete(); @@ -234,6 +235,7 @@ class ParentRegistrationService try { $dobObj = new \DateTimeImmutable($dob); $refObj = new \DateTimeImmutable($referenceDate); + return (int) $dobObj->diff($refObj)->y; } catch (\Throwable $e) { return 0; diff --git a/app/Services/Parents/ParentReportCardService.php b/app/Services/Parents/ParentReportCardService.php index e1ab3167..0972e662 100644 --- a/app/Services/Parents/ParentReportCardService.php +++ b/app/Services/Parents/ParentReportCardService.php @@ -2,14 +2,15 @@ namespace App\Services\Parents; +use App\Controllers\ParentReportCardController; use App\Models\Configuration; -use App\Services\ApplicationUrlService; use App\Models\ReportCardAcknowledgement; use App\Models\User; +use App\Services\ApplicationUrlService; use Illuminate\Support\Facades\DB; /** - * legacy {@see \App\Controllers\ParentReportCardController} parity (read + acknowledgement). + * legacy {@see ParentReportCardController} parity (read + acknowledgement). */ class ParentReportCardService { @@ -85,7 +86,7 @@ class ParentReportCardService } /** - * @param list $studentIds + * @param list $studentIds * @return array> */ public function acknowledgementMap(int $primaryParentUserId, array $studentIds, string $schoolYear, string $semester): array @@ -117,7 +118,7 @@ class ParentReportCardService } /** - * @param array $values viewed_at, signed_at, signed_name, signer_ip, … + * @param array $values viewed_at, signed_at, signed_name, signer_ip, … */ public function touchAcknowledgement( int $primaryParentUserId, diff --git a/app/Services/Parents/PrimaryParentUserResolver.php b/app/Services/Parents/PrimaryParentUserResolver.php index 42cf1b8d..ade3ba7c 100644 --- a/app/Services/Parents/PrimaryParentUserResolver.php +++ b/app/Services/Parents/PrimaryParentUserResolver.php @@ -2,13 +2,15 @@ namespace App\Services\Parents; +use App\Controllers\ParentReportCardController; +use App\Models\Student; use App\Models\User; use Illuminate\Support\Facades\DB; /** * Maps the logged-in parent-family account to the primary parent's user id - * (stored on {@see \App\Models\Student::$parent_id}), matching legacy - * {@see \App\Controllers\ParentReportCardController::resolvePrimaryParentId}. + * (stored on {@see Student::$parent_id}), matching legacy + * {@see ParentReportCardController::resolvePrimaryParentId}. */ final class PrimaryParentUserResolver { @@ -22,7 +24,7 @@ final class PrimaryParentUserResolver } /** - * @param int $userId Session user id (may be secondary/tertiary login). + * @param int $userId Session user id (may be secondary/tertiary login). */ public function resolveFromCredentials(int $userId, string $userType): ?int { diff --git a/app/Services/Payments/PaymentEnrollmentEventService.php b/app/Services/Payments/PaymentEnrollmentEventService.php index ad162049..8881e570 100644 --- a/app/Services/Payments/PaymentEnrollmentEventService.php +++ b/app/Services/Payments/PaymentEnrollmentEventService.php @@ -10,8 +10,7 @@ class PaymentEnrollmentEventService { public function __construct( private ApplicationUrlService $urls, - ) { - } + ) {} public function buildEventData( int $parentId, @@ -29,7 +28,7 @@ class PaymentEnrollmentEventService ->where('id', $parentId) ->first(); - if (!$parent) { + if (! $parent) { throw new \RuntimeException("Parent user #{$parentId} not found"); } @@ -104,7 +103,7 @@ class PaymentEnrollmentEventService } $seen[$sid] = true; - $teacherFull = trim(trim((string) ($row['t_firstname'] ?? '')) . ' ' . trim((string) ($row['t_lastname'] ?? ''))); + $teacherFull = trim(trim((string) ($row['t_firstname'] ?? '')).' '.trim((string) ($row['t_lastname'] ?? ''))); $teacherFull = $teacherFull !== '' ? $teacherFull : null; $students[] = [ @@ -112,7 +111,7 @@ class PaymentEnrollmentEventService 'student_id' => $sid, 'firstname' => (string) ($row['s_firstname'] ?? ''), 'lastname' => (string) ($row['s_lastname'] ?? ''), - 'name' => trim(((string) ($row['s_firstname'] ?? '')) . ' ' . ((string) ($row['s_lastname'] ?? ''))), + 'name' => trim(((string) ($row['s_firstname'] ?? '')).' '.((string) ($row['s_lastname'] ?? ''))), 'registration_grade' => (string) ($row['registration_grade'] ?? ''), 'class_section_id' => isset($row['class_section_id']) ? (int) $row['class_section_id'] : null, 'class_section_name' => $row['class_section_name'] ?? null, diff --git a/app/Services/Payments/PaymentEventChargesService.php b/app/Services/Payments/PaymentEventChargesService.php index 838da34a..2fd3b49e 100644 --- a/app/Services/Payments/PaymentEventChargesService.php +++ b/app/Services/Payments/PaymentEventChargesService.php @@ -11,9 +11,7 @@ use Illuminate\Support\Facades\DB; class PaymentEventChargesService { - public function __construct(private ChargeService $chargeService) - { - } + public function __construct(private ChargeService $chargeService) {} public function listCharges(?string $schoolYear, ?string $semester): array { @@ -56,7 +54,7 @@ class PaymentEventChargesService $count = 0; - if (!empty($studentIds)) { + if (! empty($studentIds)) { foreach ($studentIds as $studentId) { $result = $this->chargeService->createEventCharge([ 'parent_id' => $parentId, @@ -73,7 +71,7 @@ class PaymentEventChargesService $count++; } } - } elseif (!empty($payload['student_id'])) { + } elseif (! empty($payload['student_id'])) { $result = $this->chargeService->createEventCharge([ 'parent_id' => $parentId, 'student_id' => (int) $payload['student_id'], diff --git a/app/Services/Payments/PaymentManualService.php b/app/Services/Payments/PaymentManualService.php index 72c6788b..0a975af3 100644 --- a/app/Services/Payments/PaymentManualService.php +++ b/app/Services/Payments/PaymentManualService.php @@ -17,8 +17,7 @@ class PaymentManualService public function __construct( private DiscountInvoiceService $invoiceService, private PaymentEnrollmentEventService $enrollmentEventService - ) { - } + ) {} public function search(string $searchTerm, ?string $schoolYear = null): array { @@ -48,9 +47,9 @@ class PaymentManualService $builder->where(function ($q) use ($searchTerm, $searchClean, $searchFormatted) { $q->where('email', $searchTerm); - if (!empty($searchClean)) { + if (! empty($searchClean)) { if (strlen($searchClean) === 10) { - $formattedPhone = substr($searchClean, 0, 3) . '-' . substr($searchClean, 3, 3) . '-' . substr($searchClean, 6, 4); + $formattedPhone = substr($searchClean, 0, 3).'-'.substr($searchClean, 3, 3).'-'.substr($searchClean, 6, 4); $q->orWhere('cellphone', $formattedPhone); } @@ -60,13 +59,13 @@ class PaymentManualService ); } - if (!empty($searchFormatted)) { + if (! empty($searchFormatted)) { $q->orWhere('cellphone', $searchFormatted); } }); $parent = (array) $builder->first(); - if (!empty($parent)) { + if (! empty($parent)) { unset($parent['password']); $parentData = $parent; $parentId = (int) ($parent['id'] ?? 0); @@ -127,23 +126,23 @@ class PaymentManualService OR email LIKE ? OR cellphone LIKE ?"; - $params = ['%' . $q . '%', '%' . $q . '%', '%' . $q . '%']; + $params = ['%'.$q.'%', '%'.$q.'%', '%'.$q.'%']; if ($digits !== '') { $sql .= " OR REPLACE(REPLACE(REPLACE(REPLACE(cellphone, '(', ''), ')', ''), '-', ''), ' ', '') LIKE ?"; - $params[] = '%' . $digits . '%'; + $params[] = '%'.$digits.'%'; } - $sql .= ") ORDER BY lastname, firstname LIMIT 20"; + $sql .= ') ORDER BY lastname, firstname LIMIT 20'; $rows = DB::select($sql, $params); $items = []; foreach ($rows as $row) { - $label = trim(($row->firstname ?? '') . ' ' . ($row->lastname ?? '')); + $label = trim(($row->firstname ?? '').' '.($row->lastname ?? '')); $email = trim((string) ($row->email ?? '')); $phone = trim((string) ($row->cellphone ?? '')); - $sub = trim($email . ' ' . $phone); + $sub = trim($email.' '.$phone); $value = $email !== '' ? $email : ($phone !== '' ? $phone : $label); $items[] = [ @@ -172,7 +171,7 @@ class PaymentManualService $paymentMethod = strtolower(trim($paymentMethod)); $allowedMethods = ['cash', 'check', 'card']; - if (!in_array($paymentMethod, $allowedMethods, true)) { + if (! in_array($paymentMethod, $allowedMethods, true)) { throw new \InvalidArgumentException('Invalid payment method.'); } @@ -185,7 +184,7 @@ class PaymentManualService } $invoice = Invoice::query()->find($invoiceId); - if (!$invoice) { + if (! $invoice) { throw new \RuntimeException('Invoice not found.'); } @@ -209,7 +208,7 @@ class PaymentManualService try { $locked = DB::table('invoices')->where('id', $invoiceId)->lockForUpdate()->first(); - if (!$locked) { + if (! $locked) { DB::rollBack(); throw new \RuntimeException('Invoice not found.'); } @@ -230,7 +229,7 @@ class PaymentManualService $paidCount = $this->getSuccessfulPaymentCount($invoiceId); $installmentSeq = $paidCount + 1; - $transactionId = 'INV-' . $invoiceId . '-' . str_replace('.', '', (string) microtime(true)); + $transactionId = 'INV-'.$invoiceId.'-'.str_replace('.', '', (string) microtime(true)); $ok = $this->processPayment( $invoiceId, @@ -246,7 +245,7 @@ class PaymentManualService $userId ); - if (!$ok) { + if (! $ok) { DB::rollBack(); throw new \RuntimeException('Failed to record payment.'); } @@ -308,7 +307,7 @@ class PaymentManualService ]; } catch (\Throwable $e) { DB::rollBack(); - Log::error('[manualPayUpdate] ' . $e->getMessage()); + Log::error('[manualPayUpdate] '.$e->getMessage()); throw $e; } } @@ -328,12 +327,12 @@ class PaymentManualService } $payment = Payment::query()->find($paymentId); - if (!$payment) { + if (! $payment) { throw new \RuntimeException('Original payment not found.'); } $invoice = Invoice::query()->find((int) $payment->invoice_id); - if (!$invoice) { + if (! $invoice) { throw new \RuntimeException('Linked invoice not found.'); } @@ -367,10 +366,10 @@ class PaymentManualService { $filename = basename($filename); $roots = [ - storage_path('app/uploads/checks/' . $filename), - storage_path('app/uploads/cards/' . $filename), - storage_path('app/uploads/misc/' . $filename), - storage_path('app/uploads/' . $filename), + storage_path('app/uploads/checks/'.$filename), + storage_path('app/uploads/cards/'.$filename), + storage_path('app/uploads/misc/'.$filename), + storage_path('app/uploads/'.$filename), ]; $path = null; @@ -381,14 +380,14 @@ class PaymentManualService } } - if (!$path) { + if (! $path) { abort(404, 'Payment file not found.'); } if ($mode === 'inline') { return response()->file($path, [ 'Content-Type' => mime_content_type($path) ?: 'application/octet-stream', - 'Content-Disposition' => 'inline; filename="' . $filename . '"', + 'Content-Disposition' => 'inline; filename="'.$filename.'"', ]); } @@ -409,7 +408,7 @@ class PaymentManualService ?int $userId ): bool { $invoice = Invoice::query()->find($invoiceId); - if (!$invoice) { + if (! $invoice) { return false; } @@ -433,7 +432,7 @@ class PaymentManualService 'updated_by' => $userId, ]; - if (!Invoice::query()->whereKey($invoiceId)->update($invoiceUpdateData)) { + if (! Invoice::query()->whereKey($invoiceId)->update($invoiceUpdateData)) { return false; } @@ -466,7 +465,7 @@ class PaymentManualService private function getCurrentInvoiceBalanceExcludingPayment(int $invoiceId, int $excludePaymentId): float { $invoice = Invoice::query()->find($invoiceId); - if (!$invoice) { + if (! $invoice) { return 0.0; } @@ -530,7 +529,7 @@ class PaymentManualService $discountByInvoice = []; $refundByInvoice = []; - if (!empty($invoiceIds)) { + if (! empty($invoiceIds)) { $rows = Payment::query() ->selectRaw('invoice_id, COALESCE(SUM(paid_amount),0) AS total_paid') ->whereIn('invoice_id', $invoiceIds) @@ -581,7 +580,7 @@ class PaymentManualService $issueYmd = ''; foreach (['issue_date', 'start_date', 'created_at'] as $k) { - if (!empty($inv[$k])) { + if (! empty($inv[$k])) { $its = strtotime((string) $inv[$k]); if ($its !== false) { $issueYmd = date('Y-m-d', $its); @@ -613,7 +612,7 @@ class PaymentManualService $okTypes = ['image/jpeg', 'image/png', 'application/pdf']; $okExts = ['jpg', 'jpeg', 'png', 'pdf']; - if (!in_array($mime, $okTypes, true) || !in_array($ext, $okExts, true)) { + if (! in_array($mime, $okTypes, true) || ! in_array($ext, $okExts, true)) { throw new \InvalidArgumentException('Unsupported file type. Use JPG, PNG, or PDF.'); } @@ -623,9 +622,9 @@ class PaymentManualService $fileName = $file->hashName(); $subdir = $paymentMethod === 'check' ? 'checks' : ($paymentMethod === 'card' ? 'cards' : 'misc'); - $targetDir = storage_path('app/uploads/' . $subdir); + $targetDir = storage_path('app/uploads/'.$subdir); - if (!is_dir($targetDir)) { + if (! is_dir($targetDir)) { mkdir($targetDir, 0775, true); } @@ -641,7 +640,7 @@ class PaymentManualService event($name, $payload); } } catch (\Throwable $e) { - Log::warning($name . ' hook failed: ' . $e->getMessage()); + Log::warning($name.' hook failed: '.$e->getMessage()); } } } diff --git a/app/Services/Payments/PaymentMissedCheckService.php b/app/Services/Payments/PaymentMissedCheckService.php index 1ae3e5b7..fc9fa0a6 100644 --- a/app/Services/Payments/PaymentMissedCheckService.php +++ b/app/Services/Payments/PaymentMissedCheckService.php @@ -11,8 +11,7 @@ class PaymentMissedCheckService public function __construct( private UserNotificationDispatchService $notifier, private EmailService $emailService - ) { - } + ) {} public function findUsersWithMissedPayments(): array { @@ -46,7 +45,7 @@ class PaymentMissedCheckService foreach ($users as $user) { $userId = (int) ($user['user_id'] ?? 0); $email = (string) ($user['email'] ?? ''); - $name = trim((string) ($user['firstname'] ?? '') . ' ' . (string) ($user['lastname'] ?? '')); + $name = trim((string) ($user['firstname'] ?? '').' '.(string) ($user['lastname'] ?? '')); $this->notifier->notifyUser( $userId, @@ -59,8 +58,8 @@ class PaymentMissedCheckService if ($email !== '') { $subject = 'Payment Missed'; - $body = '

Dear ' . e($name !== '' ? $name : 'Parent') . ',

' - . '

You have a missed payment. Please pay to avoid penalty.

'; + $body = '

Dear '.e($name !== '' ? $name : 'Parent').',

' + .'

You have a missed payment. Please pay to avoid penalty.

'; $ok = $this->emailService->send($email, $subject, $body, 'finance'); $ok ? $sent++ : $failed++; } diff --git a/app/Services/Payments/PaymentNotificationDispatchService.php b/app/Services/Payments/PaymentNotificationDispatchService.php index 7bfa3d85..534f58db 100644 --- a/app/Services/Payments/PaymentNotificationDispatchService.php +++ b/app/Services/Payments/PaymentNotificationDispatchService.php @@ -2,9 +2,9 @@ namespace App\Services\Payments; +use App\Models\Configuration; use App\Models\Notification; use App\Models\UserNotification; -use App\Models\Configuration; class PaymentNotificationDispatchService { diff --git a/app/Services/Payments/PaymentNotificationService.php b/app/Services/Payments/PaymentNotificationService.php index 935f88f1..3797cab6 100644 --- a/app/Services/Payments/PaymentNotificationService.php +++ b/app/Services/Payments/PaymentNotificationService.php @@ -15,20 +15,19 @@ class PaymentNotificationService public function __construct( private EmailService $emailService, private PaymentNotificationDispatchService $dispatchService - ) { - } + ) {} public function listLogs(?string $from, ?string $to, ?string $type): array { $query = PaymentNotificationLog::query()->with('parent')->orderByDesc('sent_at'); - if (!empty($from)) { + if (! empty($from)) { $query->where('sent_at', '>=', $from); } - if (!empty($to)) { + if (! empty($to)) { $query->where('sent_at', '<=', $to); } - if (!empty($type)) { + if (! empty($type)) { $query->where('type', $type); } @@ -85,15 +84,17 @@ class PaymentNotificationService $type = $this->resolveType($parentId, $schoolYear, $typeInput); - if (!$force && PaymentNotificationLog::existsForPeriod($parentId, $year, $month, $type)) { + if (! $force && PaymentNotificationLog::existsForPeriod($parentId, $year, $month, $type)) { $results['skipped']++; $results['details'][] = ['parent_id' => $parentId, 'result' => 'already_sent']; + return $results; } - if ($balance <= 0 && !$force) { + if ($balance <= 0 && ! $force) { $results['skipped']++; $results['details'][] = ['parent_id' => $parentId, 'result' => 'no_balance']; + return $results; } @@ -228,13 +229,13 @@ class PaymentNotificationService private function composeMessage(int $parentId, float $balance, string $schoolYear, \DateTime $now): array { $user = User::query()->find($parentId); - $parentName = $user ? trim(($user->firstname ?? '') . ' ' . ($user->lastname ?? '')) : ''; + $parentName = $user ? trim(($user->firstname ?? '').' '.($user->lastname ?? '')) : ''; $greeting = $parentName !== '' ? "Dear {$parentName}," : 'Dear Parent,'; $monthYear = $now->format('F Y'); $subject = sprintf('Monthly Tuition Reminder — %s', $schoolYear); - $balanceFmt = '$' . number_format($balance, 2); - $intro = 'This is your monthly installment reminder for ' . $schoolYear . '.'; + $balanceFmt = '$'.number_format($balance, 2); + $intro = 'This is your monthly installment reminder for '.$schoolYear.'.'; $installmentInfo = $this->computeInstallmentSuggestion($balance); @@ -298,14 +299,14 @@ class PaymentNotificationService 'remaining_installments' => $remainingInstallments, 'max_installments' => $maxInstallments, 'installment_due' => round($installmentDue, 2), - 'installment_due_fmt' => '$' . number_format($installmentDue, 2), + 'installment_due_fmt' => '$'.number_format($installmentDue, 2), ]; } private function getSecondaryGuardianEmail(int $primaryUserId): ?string { $row = DB::table('family_guardians')->where('user_id', $primaryUserId)->first(); - if (!$row || empty($row->family_id)) { + if (! $row || empty($row->family_id)) { return null; } @@ -330,9 +331,10 @@ class PaymentNotificationService private function getUserEmail(int $userId): ?string { $email = (string) (DB::table('users')->where('id', $userId)->value('email') ?? ''); - if ($email === '' || !filter_var($email, FILTER_VALIDATE_EMAIL)) { + if ($email === '' || ! filter_var($email, FILTER_VALIDATE_EMAIL)) { return null; } + return $email; } @@ -353,7 +355,7 @@ class PaymentNotificationService ); } } catch (\Throwable $e) { - Log::warning('Unable to notify heads of finance: ' . $e->getMessage()); + Log::warning('Unable to notify heads of finance: '.$e->getMessage()); } } @@ -362,9 +364,10 @@ class PaymentNotificationService $base['sent'] += $update['sent'] ?? 0; $base['skipped'] += $update['skipped'] ?? 0; $base['failed'] += $update['failed'] ?? 0; - if (!empty($update['details'])) { + if (! empty($update['details'])) { $base['details'] = array_merge($base['details'], $update['details']); } + return $base; } @@ -385,6 +388,7 @@ class PaymentNotificationService if (function_exists('user_timezone')) { return (string) user_timezone(); } + return (string) (config('app.timezone') ?: 'UTC'); } } diff --git a/app/Services/Payments/PaymentTestNotificationService.php b/app/Services/Payments/PaymentTestNotificationService.php index 1ab86176..0d9ac2e9 100644 --- a/app/Services/Payments/PaymentTestNotificationService.php +++ b/app/Services/Payments/PaymentTestNotificationService.php @@ -14,14 +14,12 @@ use DateTimeZone; class PaymentTestNotificationService { - public function __construct(private EmailService $emailService) - { - } + public function __construct(private EmailService $emailService) {} public function send(string $email, string $type = 'no_payment'): array { $email = trim($email); - if ($email === '' || !filter_var($email, FILTER_VALIDATE_EMAIL)) { + if ($email === '' || ! filter_var($email, FILTER_VALIDATE_EMAIL)) { return ['ok' => false, 'message' => 'Invalid email address.']; } @@ -53,11 +51,11 @@ class PaymentTestNotificationService $ccEmail = $this->findSecondaryGuardianEmail($parentId, $email); - $parentName = $user ? trim(($user->firstname ?? '') . ' ' . ($user->lastname ?? '')) : ''; + $parentName = $user ? trim(($user->firstname ?? '').' '.($user->lastname ?? '')) : ''; $monthYear = $now->format('F Y'); $subject = sprintf('Monthly Tuition Reminder — %s', $schoolYear); $greeting = $parentName !== '' ? "Dear {$parentName}," : 'Dear Parent,'; - $balanceFmt = '$' . number_format($totalBalance, 2); + $balanceFmt = '$'.number_format($totalBalance, 2); $intro = ($type === 'no_payment') ? "We noticed there are outstanding tuition charges for {$schoolYear} but no payment has been recorded yet." : "This is your monthly installment reminder for {$schoolYear}."; @@ -79,11 +77,11 @@ class PaymentTestNotificationService HTML; - $bodyHtml .= "
    " - . "
  • Remaining installments: {$installmentInfo['remaining_installments']}
  • " - . "
  • Suggested installment this month: {$installmentInfo['installment_due_fmt']}
  • " - . "
  • Maximum installments available: {$installmentInfo['max_installments']}
  • " - . "
"; + $bodyHtml .= '
    ' + ."
  • Remaining installments: {$installmentInfo['remaining_installments']}
  • " + ."
  • Suggested installment this month: {$installmentInfo['installment_due_fmt']}
  • " + ."
  • Maximum installments available: {$installmentInfo['max_installments']}
  • " + .'
'; $body = MailHtml::document('Monthly Tuition Reminder', $bodyHtml); @@ -168,7 +166,7 @@ class PaymentTestNotificationService 'remaining_installments' => $remainingInstallments, 'max_installments' => $maxInstallments, 'installment_due' => round($installmentDue, 2), - 'installment_due_fmt' => '$' . number_format($installmentDue, 2), + 'installment_due_fmt' => '$'.number_format($installmentDue, 2), ]; } @@ -179,7 +177,7 @@ class PaymentTestNotificationService } $row = FamilyGuardian::query()->where('user_id', $parentId)->first(); - if (!$row || empty($row->family_id)) { + if (! $row || empty($row->family_id)) { return null; } diff --git a/app/Services/Payments/PaymentTransactionService.php b/app/Services/Payments/PaymentTransactionService.php index 0c011e0e..5138045a 100644 --- a/app/Services/Payments/PaymentTransactionService.php +++ b/app/Services/Payments/PaymentTransactionService.php @@ -17,7 +17,7 @@ class PaymentTransactionService 'payment_status' => $payload['payment_status'] ?? 'Pending', 'transaction_fee' => $payload['transaction_fee'] ?? null, 'payment_reference' => $payload['payment_reference'] ?? null, - 'is_full_payment' => !empty($payload['is_full_payment']), + 'is_full_payment' => ! empty($payload['is_full_payment']), 'school_year' => $payload['school_year'] ?? null, 'semester' => $payload['semester'] ?? null, ]; diff --git a/app/Services/Payments/PaypalPaymentService.php b/app/Services/Payments/PaypalPaymentService.php index 359d573b..ea6bae73 100644 --- a/app/Services/Payments/PaypalPaymentService.php +++ b/app/Services/Payments/PaypalPaymentService.php @@ -6,13 +6,19 @@ use App\Models\Payment; use App\Services\ApplicationUrlService; use Illuminate\Support\Facades\Cache; use Illuminate\Support\Facades\Log; +use PayPal\Api\Amount; +use PayPal\Api\Payer; +use PayPal\Api\PaymentExecution; +use PayPal\Api\RedirectUrls; +use PayPal\Api\Transaction; +use PayPal\Auth\OAuthTokenCredential; +use PayPal\Rest\ApiContext; class PaypalPaymentService { public function __construct( private ApplicationUrlService $urls, - ) { - } + ) {} public function getRedirectUrl(): string { @@ -24,11 +30,11 @@ class PaypalPaymentService public function createPayment(int $paymentId): array { $payment = Payment::query()->find($paymentId); - if (!$payment) { + if (! $payment) { throw new \RuntimeException('Payment not found.'); } - if (!$this->sdkAvailable()) { + if (! $this->sdkAvailable()) { return [ 'redirect_url' => $this->getRedirectUrl(), 'mode' => 'hosted', @@ -39,23 +45,23 @@ class PaypalPaymentService try { $apiContext = $this->buildApiContext(); - $payer = new \PayPal\Api\Payer(); + $payer = new Payer; $payer->setPaymentMethod('paypal'); - $amount = new \PayPal\Api\Amount(); + $amount = new Amount; $amount->setCurrency('USD')->setTotal($payment->balance ?? $payment->total_amount ?? 0); - $transaction = new \PayPal\Api\Transaction(); + $transaction = new Transaction; $transaction->setAmount($amount) ->setDescription('Payment for school fees') ->setInvoiceNumber(uniqid('inv_', true)); - $paypalPayment = new \PayPal\Api\Payment(); + $paypalPayment = new \PayPal\Api\Payment; $paypalPayment->setIntent('sale') ->setPayer($payer) ->setTransactions([$transaction]); - $redirectUrls = new \PayPal\Api\RedirectUrls(); + $redirectUrls = new RedirectUrls; $redirectUrls->setReturnUrl($this->urls->paypalExecuteReturnUrl()) ->setCancelUrl($this->urls->paypalCancelUrl()); @@ -71,14 +77,14 @@ class PaypalPaymentService 'payment_id' => $paymentId, ]; } catch (\Throwable $e) { - Log::error('PayPal create payment failed: ' . $e->getMessage()); + Log::error('PayPal create payment failed: '.$e->getMessage()); throw new \RuntimeException('Unable to create PayPal payment.'); } } public function executePayment(string $payerId, ?string $paypalPaymentId = null, ?int $paymentId = null): void { - if (!$this->sdkAvailable()) { + if (! $this->sdkAvailable()) { throw new \RuntimeException('PayPal SDK not installed.'); } @@ -89,13 +95,13 @@ class PaypalPaymentService $apiContext = $this->buildApiContext(); $payment = \PayPal\Api\Payment::get($paypalId, $apiContext); - $execution = new \PayPal\Api\PaymentExecution(); + $execution = new PaymentExecution; $execution->setPayerId($payerId); $payment->execute($execution, $apiContext); $internalPaymentId = $paymentId; - if (!$internalPaymentId && $paypalId !== '') { + if (! $internalPaymentId && $paypalId !== '') { $internalPaymentId = (int) Cache::get($this->cacheKey($paypalId)); } if ($internalPaymentId) { @@ -113,18 +119,18 @@ class PaypalPaymentService private function sdkAvailable(): bool { - return class_exists(\PayPal\Rest\ApiContext::class) - && class_exists(\PayPal\Auth\OAuthTokenCredential::class); + return class_exists(ApiContext::class) + && class_exists(OAuthTokenCredential::class); } - private function buildApiContext(): \PayPal\Rest\ApiContext + private function buildApiContext(): ApiContext { $clientId = (string) (config('services.paypal.client_id') ?: env('PAYPAL_CLIENT_ID')); $secret = (string) (config('services.paypal.secret') ?: env('PAYPAL_SECRET')); $mode = (string) (config('services.paypal.mode') ?: env('PAYPAL_MODE') ?: 'sandbox'); - $apiContext = new \PayPal\Rest\ApiContext( - new \PayPal\Auth\OAuthTokenCredential($clientId, $secret) + $apiContext = new ApiContext( + new OAuthTokenCredential($clientId, $secret) ); $apiContext->setConfig([ @@ -137,6 +143,6 @@ class PaypalPaymentService private function cacheKey(string $paypalPaymentId): string { - return 'paypal_payment:' . $paypalPaymentId; + return 'paypal_payment:'.$paypalPaymentId; } } diff --git a/app/Services/Payments/PaypalPaymentSyncService.php b/app/Services/Payments/PaypalPaymentSyncService.php index 01b9bcbf..65a1266a 100644 --- a/app/Services/Payments/PaypalPaymentSyncService.php +++ b/app/Services/Payments/PaypalPaymentSyncService.php @@ -5,8 +5,8 @@ namespace App\Services\Payments; use App\Models\Configuration; use App\Models\Enrollment; use App\Models\Invoice; -use App\Models\PayPalPayment; use App\Models\Payment; +use App\Models\PayPalPayment; use App\Models\Student; use App\Models\User; use App\Services\EmailService; @@ -14,9 +14,7 @@ use Illuminate\Support\Facades\Log; class PaypalPaymentSyncService { - public function __construct(private EmailService $emailService) - { - } + public function __construct(private EmailService $emailService) {} public function sync(bool $dryRun = false, bool $reportOnly = false): array { @@ -36,15 +34,16 @@ class PaypalPaymentSyncService $failed = []; foreach ($entries as $entry) { - if (!$reportOnly) { + if (! $reportOnly) { $entry->sync_attempts = (int) $entry->sync_attempts + 1; $entry->save(); } $user = User::query()->where('school_id', $entry->parent_school_id)->first(); - if (!$user) { + if (! $user) { $failed[] = (string) $entry->transaction_id; Log::error('PayPal sync: no user for parent_school_id', ['parent_school_id' => $entry->parent_school_id]); + continue; } @@ -54,11 +53,12 @@ class PaypalPaymentSyncService ->latest('created_at') ->first(); - if (!$reportOnly && !$dryRun) { + if (! $reportOnly && ! $dryRun) { if ($invoice) { $ok = $this->applyPayment($invoice, $entry->amount, (string) $entry->transaction_id, $schoolYear, $semester, $entry->created_at); - if (!$ok) { + if (! $ok) { $failed[] = (string) $entry->transaction_id; + continue; } } else { @@ -106,7 +106,7 @@ class PaypalPaymentSyncService $invoice->status = 'Paid'; } - if (!$invoice->save()) { + if (! $invoice->save()) { return false; } @@ -135,7 +135,7 @@ class PaypalPaymentSyncService private function updateEnrollmentStatusIfPaid(int $invoiceId, string $schoolYear): void { $invoice = Invoice::query()->find($invoiceId); - if (!$invoice || (float) $invoice->balance > 0) { + if (! $invoice || (float) $invoice->balance > 0) { return; } @@ -155,15 +155,16 @@ class PaypalPaymentSyncService { if ($processed === 0 && empty($failed)) { Log::info("[{$mode}] No PayPal sync updates."); + return; } - $subject = "[{$mode}] PayPal Sync Report - " . now()->format('Y-m-d H:i'); + $subject = "[{$mode}] PayPal Sync Report - ".now()->format('Y-m-d H:i'); $body = "PayPal Sync Mode: {$mode}\n\n"; $body .= "{$processed} PayPal payments processed.\n\n"; - if (!empty($failed)) { - $body .= count($failed) . " failed transactions:\n" . implode("\n", $failed); + if (! empty($failed)) { + $body .= count($failed)." failed transactions:\n".implode("\n", $failed); } else { $body .= "No failed transactions.\n"; } diff --git a/app/Services/Payments/PaypalTransactionsService.php b/app/Services/Payments/PaypalTransactionsService.php index 096678a1..9e0fd78e 100644 --- a/app/Services/Payments/PaypalTransactionsService.php +++ b/app/Services/Payments/PaypalTransactionsService.php @@ -12,11 +12,11 @@ class PaypalTransactionsService if ($keyword) { $query->where(function ($q) use ($keyword) { - $q->where('transaction_id', 'like', '%' . $keyword . '%') - ->orWhere('payer_email', 'like', '%' . $keyword . '%') - ->orWhere('event_type', 'like', '%' . $keyword . '%') - ->orWhere('order_id', 'like', '%' . $keyword . '%') - ->orWhere('parent_school_id', 'like', '%' . $keyword . '%'); + $q->where('transaction_id', 'like', '%'.$keyword.'%') + ->orWhere('payer_email', 'like', '%'.$keyword.'%') + ->orWhere('event_type', 'like', '%'.$keyword.'%') + ->orWhere('order_id', 'like', '%'.$keyword.'%') + ->orWhere('parent_school_id', 'like', '%'.$keyword.'%'); }); } @@ -29,11 +29,11 @@ class PaypalTransactionsService if ($keyword) { $query->where(function ($q) use ($keyword) { - $q->where('transaction_id', 'like', '%' . $keyword . '%') - ->orWhere('payer_email', 'like', '%' . $keyword . '%') - ->orWhere('event_type', 'like', '%' . $keyword . '%') - ->orWhere('order_id', 'like', '%' . $keyword . '%') - ->orWhere('parent_school_id', 'like', '%' . $keyword . '%'); + $q->where('transaction_id', 'like', '%'.$keyword.'%') + ->orWhere('payer_email', 'like', '%'.$keyword.'%') + ->orWhere('event_type', 'like', '%'.$keyword.'%') + ->orWhere('order_id', 'like', '%'.$keyword.'%') + ->orWhere('parent_school_id', 'like', '%'.$keyword.'%'); }); } diff --git a/app/Services/Policy/PolicyContentService.php b/app/Services/Policy/PolicyContentService.php index 365b1e0f..4d7cee33 100644 --- a/app/Services/Policy/PolicyContentService.php +++ b/app/Services/Policy/PolicyContentService.php @@ -14,15 +14,16 @@ class PolicyContentService public function getPolicy(string $type): array { $type = strtolower(trim($type)); - if (!isset(self::TYPES[$type])) { + if (! isset(self::TYPES[$type])) { throw new \InvalidArgumentException('Unsupported policy type.'); } $filename = self::TYPES[$type]; - $path = resource_path('policies/' . $filename); + $path = resource_path('policies/'.$filename); - if (!is_file($path)) { + if (! is_file($path)) { Log::warning('Policy file missing.', ['type' => $type, 'path' => $path]); + return [ 'type' => $type, 'title' => $this->titleFor($type), diff --git a/app/Services/Preferences/PreferencesCommandService.php b/app/Services/Preferences/PreferencesCommandService.php index ff629909..11ac3e57 100644 --- a/app/Services/Preferences/PreferencesCommandService.php +++ b/app/Services/Preferences/PreferencesCommandService.php @@ -7,9 +7,7 @@ use Illuminate\Support\Facades\DB; class PreferencesCommandService { - public function __construct(private PreferencesOptionsService $options) - { - } + public function __construct(private PreferencesOptionsService $options) {} public function upsert(int $userId, array $payload): Preferences { @@ -22,6 +20,7 @@ class PreferencesCommandService if ($existing) { $existing->fill($data); $existing->save(); + return $existing->refresh(); } diff --git a/app/Services/Preferences/PreferencesOptionsService.php b/app/Services/Preferences/PreferencesOptionsService.php index 09cf286f..9b4fee2c 100644 --- a/app/Services/Preferences/PreferencesOptionsService.php +++ b/app/Services/Preferences/PreferencesOptionsService.php @@ -28,21 +28,23 @@ class PreferencesOptionsService public function normalizeStyle(?string $style): ?string { - if (!$style) { + if (! $style) { return null; } $style = trim($style); + return in_array($style, $this->styleOptions(), true) ? $style : null; } public function normalizeMenu(?string $menu): ?string { - if (!$menu) { + if (! $menu) { return null; } $menu = trim($menu); + return in_array($menu, $this->menuOptions(), true) ? $menu : null; } } diff --git a/app/Services/Preferences/PreferencesQueryService.php b/app/Services/Preferences/PreferencesQueryService.php index bd546226..34b7ed74 100644 --- a/app/Services/Preferences/PreferencesQueryService.php +++ b/app/Services/Preferences/PreferencesQueryService.php @@ -7,16 +7,14 @@ use Illuminate\Contracts\Pagination\LengthAwarePaginator; class PreferencesQueryService { - public function __construct(private PreferencesOptionsService $options) - { - } + public function __construct(private PreferencesOptionsService $options) {} public function getForUser(int $userId): array { $row = Preferences::query()->where('user_id', $userId)->first(); $base = $this->options->defaultPreferences(); - if (!$row) { + if (! $row) { return [ 'preferences' => $base, 'options' => $this->optionsPayload(), @@ -35,12 +33,12 @@ class PreferencesQueryService ->leftJoin('users', 'users.id', '=', 'user_preferences.user_id') ->select('user_preferences.*', 'users.firstname', 'users.lastname', 'users.email'); - if (!empty($filters['user_id'])) { + if (! empty($filters['user_id'])) { $query->where('user_preferences.user_id', (int) $filters['user_id']); } - if (!empty($filters['q'])) { - $term = '%' . strtolower(trim((string) $filters['q'])) . '%'; + if (! empty($filters['q'])) { + $term = '%'.strtolower(trim((string) $filters['q'])).'%'; $query->where(function ($q) use ($term) { $q->whereRaw('LOWER(users.firstname) LIKE ?', [$term]) ->orWhereRaw('LOWER(users.lastname) LIKE ?', [$term]) @@ -53,7 +51,7 @@ class PreferencesQueryService $allowedSorts = ['updated_at', 'created_at', 'user_id']; if (in_array($sortBy, $allowedSorts, true)) { - $query->orderBy('user_preferences.' . $sortBy, $sortDir); + $query->orderBy('user_preferences.'.$sortBy, $sortDir); } else { $query->orderBy('user_preferences.updated_at', 'desc'); } diff --git a/app/Services/PrintRequests/PrintRequestsPortalService.php b/app/Services/PrintRequests/PrintRequestsPortalService.php index 504a6ddb..a1b23838 100644 --- a/app/Services/PrintRequests/PrintRequestsPortalService.php +++ b/app/Services/PrintRequests/PrintRequestsPortalService.php @@ -2,6 +2,7 @@ namespace App\Services\PrintRequests; +use App\Controllers\PrintRequests; use App\Models\AdminNotificationSubject; use App\Models\ClassSection; use App\Models\Configuration; @@ -16,7 +17,7 @@ use Illuminate\Support\Facades\Schema; use Illuminate\Support\Str; /** - * legacy {@see \App\Controllers\PrintRequests} parity (SPA JSON + uploads). + * legacy {@see PrintRequests} parity (SPA JSON + uploads). */ class PrintRequestsPortalService { @@ -165,7 +166,7 @@ class PrintRequestsPortalService } /** - * @param array $requestData merged row after update for notifications + * @param array $requestData merged row after update for notifications */ public function notifyAdminsForPrintRequest(int $printRequestId, array $requestData, string $event = 'created'): void { @@ -323,7 +324,7 @@ class PrintRequestsPortalService } /** - * @param array $row + * @param array $row * @return array */ public function normalizePrintRequestRow(array $row): array diff --git a/app/Services/Promotions/LevelProgressionService.php b/app/Services/Promotions/LevelProgressionService.php index 60b42182..c85e75f7 100644 --- a/app/Services/Promotions/LevelProgressionService.php +++ b/app/Services/Promotions/LevelProgressionService.php @@ -5,6 +5,7 @@ namespace App\Services\Promotions; use App\Models\ClassSection; use App\Models\LevelProgression; use App\Models\SchoolClass; +use App\Services\School\AccountEventService; use Illuminate\Support\Collection; /** @@ -12,7 +13,7 @@ use Illuminate\Support\Collection; * `level_progressions` table (plan section 10). * * Falls back to `class_id + 1` only if no mapping is found, matching - * the legacy behaviour in {@see \App\Services\School\AccountEventService}. + * the legacy behaviour in {@see AccountEventService}. */ class LevelProgressionService { @@ -23,15 +24,15 @@ class LevelProgressionService public const DEFAULT_MAP = [ ['current' => 'KG1', 'next' => 'KG2', 'order' => 10, 'terminal' => false], ['current' => 'KG2', 'next' => 'Grade 1', 'order' => 20, 'terminal' => false], - ['current' => 'Grade 1','next' => 'Grade 2', 'order' => 30, 'terminal' => false], - ['current' => 'Grade 2','next' => 'Grade 3', 'order' => 40, 'terminal' => false], - ['current' => 'Grade 3','next' => 'Grade 4', 'order' => 50, 'terminal' => false], - ['current' => 'Grade 4','next' => 'Grade 5', 'order' => 60, 'terminal' => false], - ['current' => 'Grade 5','next' => 'Grade 6', 'order' => 70, 'terminal' => false], - ['current' => 'Grade 6','next' => 'Grade 7', 'order' => 80, 'terminal' => false], - ['current' => 'Grade 7','next' => 'Grade 8', 'order' => 90, 'terminal' => false], - ['current' => 'Grade 8','next' => 'Grade 9', 'order' => 100, 'terminal' => false], - ['current' => 'Grade 9','next' => 'Youth', 'order' => 110, 'terminal' => false], + ['current' => 'Grade 1', 'next' => 'Grade 2', 'order' => 30, 'terminal' => false], + ['current' => 'Grade 2', 'next' => 'Grade 3', 'order' => 40, 'terminal' => false], + ['current' => 'Grade 3', 'next' => 'Grade 4', 'order' => 50, 'terminal' => false], + ['current' => 'Grade 4', 'next' => 'Grade 5', 'order' => 60, 'terminal' => false], + ['current' => 'Grade 5', 'next' => 'Grade 6', 'order' => 70, 'terminal' => false], + ['current' => 'Grade 6', 'next' => 'Grade 7', 'order' => 80, 'terminal' => false], + ['current' => 'Grade 7', 'next' => 'Grade 8', 'order' => 90, 'terminal' => false], + ['current' => 'Grade 8', 'next' => 'Grade 9', 'order' => 100, 'terminal' => false], + ['current' => 'Grade 9', 'next' => 'Youth', 'order' => 110, 'terminal' => false], ['current' => 'Youth', 'next' => null, 'order' => 120, 'terminal' => true], ]; @@ -77,7 +78,7 @@ class LevelProgressionService } $current = SchoolClass::query()->find($currentClassId); - if (!$current) { + if (! $current) { return null; } $currentName = (string) ($current->class_name ?? ''); @@ -118,9 +119,10 @@ class LevelProgressionService return null; } $classId = ClassSection::getClassId($classSectionId); - if (!$classId) { + if (! $classId) { return null; } + return $this->resolveByCurrentClassId((int) $classId); } @@ -181,6 +183,7 @@ class LevelProgressionService if ($existing) { $existing->fill($data); $existing->save(); + return $existing; } diff --git a/app/Services/Promotions/Placement/BalancedSectionPlacementService.php b/app/Services/Promotions/Placement/BalancedSectionPlacementService.php index 9e193d74..0bb8e3d6 100644 --- a/app/Services/Promotions/Placement/BalancedSectionPlacementService.php +++ b/app/Services/Promotions/Placement/BalancedSectionPlacementService.php @@ -45,6 +45,7 @@ class BalancedSectionPlacementService return $scoreB <=> $scoreA; } $name = strcmp((string) ($a['student_name'] ?? ''), (string) ($b['student_name'] ?? '')); + return $name !== 0 ? $name : ((int) $a['student_id'] <=> (int) $b['student_id']); }); } @@ -79,6 +80,7 @@ class BalancedSectionPlacementService for ($i = 1; $i <= $sectionCount; $i++) { $sizes[$i] = $base + ($i <= $remainder ? 1 : 0); } + return $sizes; } @@ -98,6 +100,7 @@ class BalancedSectionPlacementService if ($cmp !== 0) { return $cmp; } + return $a['section_index'] <=> $b['section_index']; }); diff --git a/app/Services/Promotions/Placement/PlacementPoolBuilder.php b/app/Services/Promotions/Placement/PlacementPoolBuilder.php index 03755292..9e5818fe 100644 --- a/app/Services/Promotions/Placement/PlacementPoolBuilder.php +++ b/app/Services/Promotions/Placement/PlacementPoolBuilder.php @@ -7,9 +7,7 @@ use Illuminate\Support\Facades\DB; class PlacementPoolBuilder { - public function __construct(private ScoreBandClassifier $bands) - { - } + public function __construct(private ScoreBandClassifier $bands) {} public function build(string $fromSchoolYear, string $toSchoolYear, ?int $toGradeLevelId = null): array { @@ -38,34 +36,39 @@ class PlacementPoolBuilder $studentId = (int) $row->student_id; if ((int) ($row->is_active ?? 1) === 0) { $exceptions['withdrawn_or_inactive_student'][] = $studentId; + continue; } if ($this->alreadyPlaced($studentId, $toSchoolYear)) { $exceptions['duplicate_target_year_placement'][] = $studentId; + continue; } - if (!$this->isPromotedDecision((string) ($row->decision ?? ''))) { + if (! $this->isPromotedDecision((string) ($row->decision ?? ''))) { $bucket = $row->decision === null ? 'missing_student_decision' : 'decision_conflicts_with_enrollment_request'; $exceptions[$bucket][] = $studentId; + continue; } $score = $row->year_score !== null ? (float) $row->year_score : ($row->final_average !== null ? (float) $row->final_average : null); if ($score === null) { $exceptions['missing_student_decision'][] = $studentId; + continue; } try { $band = $this->bands->fromScore($score, true); } catch (\RuntimeException) { $exceptions['failed_retained'][] = $studentId; + continue; } $pool[] = [ 'student_id' => $studentId, 'student_type' => 'returning', - 'student_name' => trim((string) $row->lastname . ', ' . (string) $row->firstname), + 'student_name' => trim((string) $row->lastname.', '.(string) $row->firstname), 'source_decision_id' => $row->decision_id ? (int) $row->decision_id : null, 'source_enrollment_id' => $row->enrollment_id ? (int) $row->enrollment_id : null, 'final_score' => $score, @@ -98,17 +101,19 @@ class PlacementPoolBuilder $studentId = (int) $row->student_id; if ((int) ($row->is_active ?? 1) === 0) { $exceptions['withdrawn_or_inactive_student'][] = $studentId; + continue; } if ($this->alreadyPlaced($studentId, $toSchoolYear)) { $exceptions['duplicate_target_year_placement'][] = $studentId; + continue; } $pool[] = [ 'student_id' => $studentId, 'student_type' => 'new', - 'student_name' => trim((string) $row->lastname . ', ' . (string) $row->firstname), + 'student_name' => trim((string) $row->lastname.', '.(string) $row->firstname), 'source_decision_id' => null, 'source_enrollment_id' => (int) $row->enrollment_id, 'final_score' => null, @@ -177,6 +182,7 @@ class PlacementPoolBuilder private function isPromotedDecision(string $decision): bool { $normalized = strtolower(trim($decision)); + return in_array($normalized, ['passed', 'pass', 'promoted', 'promote', 'eligible_to_continue'], true); } } diff --git a/app/Services/Promotions/Placement/PromotionSectionCapacityService.php b/app/Services/Promotions/Placement/PromotionSectionCapacityService.php index 62770bd9..412caf47 100644 --- a/app/Services/Promotions/Placement/PromotionSectionCapacityService.php +++ b/app/Services/Promotions/Placement/PromotionSectionCapacityService.php @@ -9,6 +9,7 @@ use RuntimeException; class PromotionSectionCapacityService { public const CONFIG_KEY = 'promotion.section_capacity'; + public const DEFAULT_CAPACITY = 20; public function capacity(): array @@ -23,7 +24,7 @@ class PromotionSectionCapacityService 'fallback' => self::DEFAULT_CAPACITY, ]); $value = self::DEFAULT_CAPACITY; - } elseif (!ctype_digit((string) $raw)) { + } elseif (! ctype_digit((string) $raw)) { throw new RuntimeException('promotion.section_capacity must be a positive integer.'); } else { $value = (int) $raw; diff --git a/app/Services/Promotions/Placement/SectionPlacementPreviewService.php b/app/Services/Promotions/Placement/SectionPlacementPreviewService.php index daa402ac..555cfea9 100644 --- a/app/Services/Promotions/Placement/SectionPlacementPreviewService.php +++ b/app/Services/Promotions/Placement/SectionPlacementPreviewService.php @@ -14,8 +14,7 @@ class SectionPlacementPreviewService private PromotionSectionCapacityService $capacityService, private PlacementPoolBuilder $poolBuilder, private BalancedSectionPlacementService $placementService - ) { - } + ) {} public function createDraft( string $fromSchoolYear, @@ -119,12 +118,12 @@ class SectionPlacementPreviewService ->where('is_event_only', 0) ->exists(); if ($duplicate) { - throw new RuntimeException('Student ' . $studentId . ' already has a target-year class placement.'); + throw new RuntimeException('Student '.$studentId.' already has a target-year class placement.'); } $sectionId = $sectionMap[(int) $student->planned_section_index] ?? null; if ($sectionId === null) { - throw new RuntimeException('Missing target section for planned section ' . $student->planned_section_index . '.'); + throw new RuntimeException('Missing target section for planned section '.$student->planned_section_index.'.'); } DB::table('student_class')->insert([ @@ -133,7 +132,7 @@ class SectionPlacementPreviewService 'is_event_only' => 0, 'semester' => $semester, 'school_year' => $batch->to_school_year, - 'description' => 'Finalized from section placement batch #' . $batch->getKey(), + 'description' => 'Finalized from section placement batch #'.$batch->getKey(), 'created_at' => now(), 'updated_at' => now(), 'updated_by' => $userId, @@ -154,7 +153,7 @@ class SectionPlacementPreviewService private function ensureSections(SectionPlacementBatch $batch, string $semester): array { - if (!$batch->to_grade_level_id) { + if (! $batch->to_grade_level_id) { throw new RuntimeException('to_grade_level_id is required to finalize placement sections.'); } @@ -176,6 +175,7 @@ class SectionPlacementPreviewService if ($existing) { $map[$i] = (int) $existing; + continue; } @@ -198,7 +198,8 @@ class SectionPlacementPreviewService private function sectionName(int $classId, string $suffix, string $schoolYear): string { $className = DB::table('classes')->where('id', $classId)->value('class_name'); - $base = $className ? (string) $className : ('Class ' . $classId); - return $base . '-' . $suffix . ' ' . $schoolYear; + $base = $className ? (string) $className : ('Class '.$classId); + + return $base.'-'.$suffix.' '.$schoolYear; } } diff --git a/app/Services/Promotions/PromotionEligibilityService.php b/app/Services/Promotions/PromotionEligibilityService.php index 29845c89..3d2d169e 100644 --- a/app/Services/Promotions/PromotionEligibilityService.php +++ b/app/Services/Promotions/PromotionEligibilityService.php @@ -24,13 +24,12 @@ class PromotionEligibilityService private LevelProgressionService $progression, private PromotionStatusService $statusService, private PromotionAuditService $audit - ) { - } + ) {} /** * Evaluate eligibility for a single student. * - * @param int|null $userId actor id for audit + * @param int|null $userId actor id for audit */ public function evaluateStudent( int $studentId, @@ -38,7 +37,7 @@ class PromotionEligibilityService ?int $userId = null ): ?StudentPromotionRecord { $student = Student::query()->find($studentId); - if (!$student) { + if (! $student) { return null; } @@ -83,7 +82,7 @@ class PromotionEligibilityService $summary = $this->emptySummary(); foreach ($studentIds as $studentId) { $student = Student::query()->find($studentId); - if (!$student) { + if (! $student) { continue; } try { @@ -132,7 +131,7 @@ class PromotionEligibilityService $summary = $this->emptySummary(); foreach ($studentIds as $studentId) { $student = Student::query()->find($studentId); - if (!$student) { + if (! $student) { continue; } try { @@ -214,7 +213,7 @@ class PromotionEligibilityService ->first(); $isNew = $existing === null; - $record = $existing ?: new StudentPromotionRecord(); + $record = $existing ?: new StudentPromotionRecord; $record->fill([ 'student_id' => $studentId, 'parent_id' => (int) ($student->parent_id ?? 0) ?: null, @@ -288,7 +287,7 @@ class PromotionEligibilityService ->orderByDesc('id') ->first(); - if (!$decision) { + if (! $decision) { return [ 'passed' => null, 'final_average' => null, @@ -332,14 +331,14 @@ class PromotionEligibilityService return [ 'passed' => null, 'final_average' => $score, - 'notes' => 'student_decisions has unrecognized decision: ' . $rawDecision, + 'notes' => 'student_decisions has unrecognized decision: '.$rawDecision, 'has_data' => false, ]; } private function resolveStatusFromEvaluation(?bool $passed, bool $terminal, bool $hasData): string { - if (!$hasData) { + if (! $hasData) { return StudentPromotionRecord::STATUS_ON_HOLD; } if ($passed === false) { @@ -348,6 +347,7 @@ class PromotionEligibilityService if ($terminal) { return StudentPromotionRecord::STATUS_GRADUATED; } + return StudentPromotionRecord::STATUS_AWAITING_PARENT; } @@ -380,15 +380,17 @@ class PromotionEligibilityService private function resolveCurrentSchoolYear(): ?string { $year = (string) (Configuration::getConfigValueByKey('school_year') ?? ''); + return $year !== '' ? $year : null; } private function nextSchoolYear(string $current): ?string { - if (!preg_match('/^(\d{4})-(\d{4})$/', $current, $m)) { + if (! preg_match('/^(\d{4})-(\d{4})$/', $current, $m)) { return null; } - return ((int) $m[1] + 1) . '-' . ((int) $m[2] + 1); + + return ((int) $m[1] + 1).'-'.((int) $m[2] + 1); } private function passThreshold(): float @@ -397,6 +399,7 @@ class PromotionEligibilityService if ($configured !== null && is_numeric($configured)) { return (float) $configured; } + return self::DEFAULT_PASS_THRESHOLD; } } diff --git a/app/Services/Promotions/PromotionEnrollmentService.php b/app/Services/Promotions/PromotionEnrollmentService.php index b6a3c54c..2ad07f09 100644 --- a/app/Services/Promotions/PromotionEnrollmentService.php +++ b/app/Services/Promotions/PromotionEnrollmentService.php @@ -28,8 +28,7 @@ class PromotionEnrollmentService public function __construct( private PromotionStatusService $statusService, private PromotionAuditService $audit - ) { - } + ) {} /** * Lists actionable promotion records for a parent (the parent @@ -54,9 +53,9 @@ class PromotionEnrollmentService $studentIds = $records->pluck('student_id')->unique()->all(); /** @var EloquentCollection $studentsCollection */ - $studentsCollection = !empty($studentIds) + $studentsCollection = ! empty($studentIds) ? Student::query()->whereIn('id', $studentIds)->get() - : new EloquentCollection(); + : new EloquentCollection; $studentsById = $studentsCollection->keyBy('id'); $payload = $records->map(function (StudentPromotionRecord $record) use ($studentsById) { @@ -80,7 +79,7 @@ class PromotionEnrollmentService $this->assertActionable($record); return DB::transaction(function () use ($record, $parentId, $userId) { - if (!$record->enrollment_started_at) { + if (! $record->enrollment_started_at) { $record->enrollment_started_at = now(); } $record->enrollment_status = StudentPromotionRecord::ENROLLMENT_IN_PROGRESS; @@ -117,7 +116,7 @@ class PromotionEnrollmentService $sanitized = []; foreach ($steps as $field => $value) { - if (!in_array($field, self::ALLOWED_STEPS, true)) { + if (! in_array($field, self::ALLOWED_STEPS, true)) { continue; } $sanitized[$field] = (bool) $value; @@ -139,7 +138,7 @@ class PromotionEnrollmentService return $record; } - if (!$record->enrollment_started_at) { + if (! $record->enrollment_started_at) { $record->enrollment_started_at = now(); } $record->enrollment_status = StudentPromotionRecord::ENROLLMENT_IN_PROGRESS; @@ -185,7 +184,7 @@ class PromotionEnrollmentService $this->assertParentOwns($record, $parentId); $this->assertActionable($record); - if (!$record->enrollmentChecklistComplete()) { + if (! $record->enrollmentChecklistComplete()) { throw new RuntimeException('Enrollment checklist is incomplete.'); } @@ -294,10 +293,12 @@ class PromotionEnrollmentService if ($existing) { $existing->fill($payload); $existing->save(); + return (int) $existing->getKey(); } $created = Enrollment::query()->create($payload); + return $created ? (int) $created->getKey() : null; } @@ -307,7 +308,7 @@ class PromotionEnrollmentService 'promotion_id' => (int) $record->getKey(), 'student_id' => (int) $record->student_id, 'student_name' => $student - ? trim((string) $student->firstname . ' ' . (string) $student->lastname) + ? trim((string) $student->firstname.' '.(string) $student->lastname) : null, 'current_level' => $record->current_level_name, 'promoted_level' => $record->promoted_level_name, @@ -343,12 +344,13 @@ class PromotionEnrollmentService true ); }); - if (!$actionable) { + if (! $actionable) { return null; } $level = $actionable->promoted_level_name ?: 'the next level'; $deadline = $actionable->enrollment_deadline?->toDateString(); - $deadlineText = $deadline ? 'by ' . $deadline : 'as soon as possible'; + $deadlineText = $deadline ? 'by '.$deadline : 'as soon as possible'; + return sprintf( 'Your child is eligible for promotion to %s. Please complete enrollment for the new school year %s.', $level, @@ -372,7 +374,7 @@ class PromotionEnrollmentService private function assertActionable(StudentPromotionRecord $record): void { $actionable = StudentPromotionRecord::parentActionableStatuses(); - if (!in_array($record->promotion_status, $actionable, true)) { + if (! in_array($record->promotion_status, $actionable, true)) { throw new RuntimeException(sprintf( 'Promotion record is not actionable in status "%s".', $record->promotion_status @@ -383,9 +385,10 @@ class PromotionEnrollmentService private function guessNextSchoolYear(): ?string { $current = (string) (Configuration::getConfigValueByKey('school_year') ?? ''); - if (!preg_match('/^(\d{4})-(\d{4})$/', $current, $m)) { + if (! preg_match('/^(\d{4})-(\d{4})$/', $current, $m)) { return null; } - return ((int) $m[1] + 1) . '-' . ((int) $m[2] + 1); + + return ((int) $m[1] + 1).'-'.((int) $m[2] + 1); } } diff --git a/app/Services/Promotions/PromotionQueryService.php b/app/Services/Promotions/PromotionQueryService.php index 8cd3eb31..c4b99455 100644 --- a/app/Services/Promotions/PromotionQueryService.php +++ b/app/Services/Promotions/PromotionQueryService.php @@ -42,7 +42,6 @@ class PromotionQueryService * parent_action_required?: bool, * per_page?: int|null, * } $filters - * * @return array{ * data: array>, * total: int, @@ -68,12 +67,12 @@ class PromotionQueryService $studentIds = $records->pluck('student_id')->unique()->values()->all(); $parentIds = $records->pluck('parent_id')->filter()->unique()->values()->all(); - $studentsById = !empty($studentIds) + $studentsById = ! empty($studentIds) ? Student::query()->whereIn('id', $studentIds)->get()->keyBy('id') - : new EloquentCollection(); - $parentsById = !empty($parentIds) + : new EloquentCollection; + $parentsById = ! empty($parentIds) ? User::query()->whereIn('id', $parentIds)->get()->keyBy('id') - : new EloquentCollection(); + : new EloquentCollection; $rows = $records->map(function (StudentPromotionRecord $record) use ($studentsById, $parentsById) { return $this->presentAdminRow( @@ -117,6 +116,7 @@ class PromotionQueryService foreach ($rows as $row) { $counts[(string) $row->promotion_status] = (int) $row->total; } + return $counts; } @@ -133,34 +133,34 @@ class PromotionQueryService } /** - * @param Builder $query + * @param Builder $query */ private function applyFilters(Builder $query, array $filters): void { - if (!empty($filters['status'])) { + if (! empty($filters['status'])) { $statuses = is_array($filters['status']) ? $filters['status'] : [$filters['status']]; $statuses = array_values(array_filter($statuses, static fn ($s) => is_string($s) && $s !== '')); - if (!empty($statuses)) { + if (! empty($statuses)) { $query->whereIn('promotion_status', $statuses); } } - if (!empty($filters['parent_action_required'])) { + if (! empty($filters['parent_action_required'])) { $query->whereIn('promotion_status', StudentPromotionRecord::parentActionableStatuses()); } - if (!empty($filters['next_school_year'])) { + if (! empty($filters['next_school_year'])) { $query->where('next_school_year', $filters['next_school_year']); } - if (!empty($filters['current_school_year'])) { + if (! empty($filters['current_school_year'])) { $query->where('current_school_year', $filters['current_school_year']); } - if (!empty($filters['parent_id'])) { + if (! empty($filters['parent_id'])) { $query->where('parent_id', (int) $filters['parent_id']); } - if (!empty($filters['student_id'])) { + if (! empty($filters['student_id'])) { $query->where('student_id', (int) $filters['student_id']); } - if (!empty($filters['search'])) { - $search = '%' . strtolower((string) $filters['search']) . '%'; + if (! empty($filters['search'])) { + $search = '%'.strtolower((string) $filters['search']).'%'; $query->whereIn('student_id', function ($sub) use ($search) { $sub->select('id') ->from('students') @@ -182,12 +182,12 @@ class PromotionQueryService 'promotion_id' => (int) $record->getKey(), 'student_id' => (int) $record->student_id, 'student_name' => $student - ? trim((string) $student->firstname . ' ' . (string) $student->lastname) + ? trim((string) $student->firstname.' '.(string) $student->lastname) : null, 'school_id' => $student->school_id ?? null, 'parent_id' => $record->parent_id ? (int) $record->parent_id : null, 'parent_name' => $parent - ? trim((string) $parent->firstname . ' ' . (string) $parent->lastname) + ? trim((string) $parent->firstname.' '.(string) $parent->lastname) : null, 'parent_email' => $parent->email ?? null, 'current_school_year' => $record->current_school_year, diff --git a/app/Services/Promotions/PromotionReminderService.php b/app/Services/Promotions/PromotionReminderService.php index 5227946b..64e030b9 100644 --- a/app/Services/Promotions/PromotionReminderService.php +++ b/app/Services/Promotions/PromotionReminderService.php @@ -19,7 +19,7 @@ use Illuminate\Support\Facades\Log; */ class PromotionReminderService { - /** @var (callable(int $userId, string $title, string $body, array $channels):void)|null */ + /** @var (callable(int, string, string, array):void)|null */ private $dispatcher; public function __construct( @@ -40,7 +40,7 @@ class PromotionReminderService ?string $message = null, array $channels = ['in_app', 'email'] ): PromotionReminderLog { - if (!in_array($reminderType, PromotionReminderLog::ALLOWED_TYPES, true)) { + if (! in_array($reminderType, PromotionReminderLog::ALLOWED_TYPES, true)) { $reminderType = PromotionReminderLog::TYPE_MANUAL; } @@ -53,7 +53,7 @@ class PromotionReminderService '%s is eligible for promotion to %s.%s Please complete enrollment as soon as possible.', $studentName ?: 'Your child', $level, - $deadline ? ' Deadline: ' . $deadline . '.' : '' + $deadline ? ' Deadline: '.$deadline.'.' : '' ); $finalSubject = $subject ?: $defaultSubject; @@ -83,7 +83,7 @@ class PromotionReminderService $this->dispatch((int) $record->parent_id, $finalSubject, $finalMessage, $channels); } - if (!$record->parent_notified_at) { + if (! $record->parent_notified_at) { $record->parent_notified_at = now(); $record->save(); $this->audit->logParentNotified($record, $userId, $reminderType); @@ -118,6 +118,7 @@ class PromotionReminderService $type = $this->resolveReminderType($record, $now); if ($type === null) { $skipped++; + continue; } $alreadySent = PromotionReminderLog::query() @@ -126,6 +127,7 @@ class PromotionReminderService ->exists(); if ($alreadySent) { $skipped++; + continue; } try { @@ -177,22 +179,24 @@ class PromotionReminderService if ($elapsedSeconds * 2 >= $totalSeconds) { return PromotionReminderLog::TYPE_HALFWAY; } + return PromotionReminderLog::TYPE_FIRST; } private function studentName(StudentPromotionRecord $record): ?string { $student = Student::query()->find($record->student_id); - if (!$student) { + if (! $student) { return null; } - $name = trim((string) ($student->firstname ?? '') . ' ' . (string) ($student->lastname ?? '')); + $name = trim((string) ($student->firstname ?? '').' '.(string) ($student->lastname ?? '')); + return $name !== '' ? $name : null; } private function dispatch(int $userId, string $subject, string $message, array $channels): void { - if (!$this->dispatcher) { + if (! $this->dispatcher) { return; } try { diff --git a/app/Services/Promotions/PromotionStatusService.php b/app/Services/Promotions/PromotionStatusService.php index 6399a451..1b61de3d 100644 --- a/app/Services/Promotions/PromotionStatusService.php +++ b/app/Services/Promotions/PromotionStatusService.php @@ -15,9 +15,7 @@ use RuntimeException; */ class PromotionStatusService { - public function __construct(private PromotionAuditService $audit) - { - } + public function __construct(private PromotionAuditService $audit) {} /** * Map of allowed transitions from current → set of next statuses. @@ -80,10 +78,11 @@ class PromotionStatusService if ($from === $to) { return true; } - if (!in_array($to, StudentPromotionRecord::ALL_STATUSES, true)) { + if (! in_array($to, StudentPromotionRecord::ALL_STATUSES, true)) { return false; } $allowed = self::ALLOWED_TRANSITIONS[$from] ?? []; + return in_array($to, $allowed, true); } @@ -98,7 +97,7 @@ class PromotionStatusService ?int $userId = null, ?string $notes = null ): StudentPromotionRecord { - if (!in_array($newStatus, StudentPromotionRecord::ALL_STATUSES, true)) { + if (! in_array($newStatus, StudentPromotionRecord::ALL_STATUSES, true)) { throw new InvalidArgumentException(sprintf('Unknown promotion status "%s".', $newStatus)); } @@ -106,7 +105,7 @@ class PromotionStatusService if ($oldStatus === $newStatus) { return $record; } - if (!$this->canTransition($oldStatus, $newStatus)) { + if (! $this->canTransition($oldStatus, $newStatus)) { throw new RuntimeException(sprintf( 'Transition from "%s" to "%s" is not allowed.', $oldStatus, @@ -135,7 +134,7 @@ class PromotionStatusService ?int $userId = null, ?string $notes = null ): StudentPromotionRecord { - if (!in_array($newStatus, StudentPromotionRecord::ALL_STATUSES, true)) { + if (! in_array($newStatus, StudentPromotionRecord::ALL_STATUSES, true)) { throw new InvalidArgumentException(sprintf('Unknown promotion status "%s".', $newStatus)); } diff --git a/app/Services/PublicWinners/PublicWinnersPortalService.php b/app/Services/PublicWinners/PublicWinnersPortalService.php index 95954a7c..31484b33 100644 --- a/app/Services/PublicWinners/PublicWinnersPortalService.php +++ b/app/Services/PublicWinners/PublicWinnersPortalService.php @@ -2,12 +2,14 @@ namespace App\Services\PublicWinners; +use App\Controllers\WinnersController; use App\Models\ClassSection; use App\Models\Competition; +use Carbon\CarbonInterface; use Illuminate\Support\Facades\DB; /** - * legacy {@see \App\Controllers\WinnersController} — public published winners pages. + * legacy {@see WinnersController} — public published winners pages. */ class PublicWinnersPortalService { @@ -104,7 +106,7 @@ class PublicWinnersPortalService { $arr = $c->toArray(); foreach ($arr as $key => $value) { - if ($value instanceof \Carbon\CarbonInterface) { + if ($value instanceof CarbonInterface) { $arr[$key] = $value->format('Y-m-d H:i:s'); } } diff --git a/app/Services/PurchaseOrders/PurchaseOrderCreateService.php b/app/Services/PurchaseOrders/PurchaseOrderCreateService.php index c7776451..1729b15c 100644 --- a/app/Services/PurchaseOrders/PurchaseOrderCreateService.php +++ b/app/Services/PurchaseOrders/PurchaseOrderCreateService.php @@ -12,7 +12,7 @@ class PurchaseOrderCreateService public function create(array $payload): PurchaseOrder { $itemsPayload = $payload['items'] ?? []; - if (empty($itemsPayload) || !is_array($itemsPayload)) { + if (empty($itemsPayload) || ! is_array($itemsPayload)) { throw new RuntimeException('Add at least one line item.'); } diff --git a/app/Services/PurchaseOrders/PurchaseOrderQueryService.php b/app/Services/PurchaseOrders/PurchaseOrderQueryService.php index 7bdab684..22145d57 100644 --- a/app/Services/PurchaseOrders/PurchaseOrderQueryService.php +++ b/app/Services/PurchaseOrders/PurchaseOrderQueryService.php @@ -45,7 +45,7 @@ class PurchaseOrderQueryService ->where('purchase_orders.id', $id) ->first(); - if (!$po) { + if (! $po) { return null; } diff --git a/app/Services/PurchaseOrders/PurchaseOrderReceiveService.php b/app/Services/PurchaseOrders/PurchaseOrderReceiveService.php index 9dec9681..deba8571 100644 --- a/app/Services/PurchaseOrders/PurchaseOrderReceiveService.php +++ b/app/Services/PurchaseOrders/PurchaseOrderReceiveService.php @@ -14,13 +14,12 @@ class PurchaseOrderReceiveService { public function __construct( private InventoryMovementService $inventoryMovementService - ) { - } + ) {} public function receive(int $poId, array $received, string $issuedBy): array { $po = PurchaseOrder::query()->find($poId); - if (!$po || in_array($po->status, [PurchaseOrder::STATUS_CANCELED, PurchaseOrder::STATUS_RECEIVED], true)) { + if (! $po || in_array($po->status, [PurchaseOrder::STATUS_CANCELED, PurchaseOrder::STATUS_RECEIVED], true)) { throw new RuntimeException('PO not receivable.'); } @@ -43,8 +42,9 @@ class PurchaseOrderReceiveService ->where('id', $itemId) ->first(); - if (!$poItem) { + if (! $poItem) { $completed = false; + continue; } @@ -67,7 +67,7 @@ class PurchaseOrderReceiveService 'supply_id' => $supply->id, 'type' => 'in', 'quantity' => $toReceive, - 'ref' => 'PO ' . ($po->po_number ?? $po->id), + 'ref' => 'PO '.($po->po_number ?? $po->id), 'issued_to' => 'Inventory', 'issued_by' => $issuedBy, 'notes' => 'Received against PO', @@ -83,7 +83,7 @@ class PurchaseOrderReceiveService qtyChange: $toReceive, type: 'in', reason: 'Purchase order received', - note: 'PO ' . ($po->po_number ?? $po->id), + note: 'PO '.($po->po_number ?? $po->id), performedBy: $performedBy ); } diff --git a/app/Services/PurchaseOrders/PurchaseOrderStatusService.php b/app/Services/PurchaseOrders/PurchaseOrderStatusService.php index 3a417589..5eeae0d0 100644 --- a/app/Services/PurchaseOrders/PurchaseOrderStatusService.php +++ b/app/Services/PurchaseOrders/PurchaseOrderStatusService.php @@ -10,7 +10,7 @@ class PurchaseOrderStatusService public function cancel(int $poId): PurchaseOrder { $po = PurchaseOrder::query()->find($poId); - if (!$po || $po->status === PurchaseOrder::STATUS_RECEIVED) { + if (! $po || $po->status === PurchaseOrder::STATUS_RECEIVED) { throw new RuntimeException('Cannot cancel this PO.'); } diff --git a/app/Services/Refunds/RefundDecisionService.php b/app/Services/Refunds/RefundDecisionService.php index c57baddf..75808864 100644 --- a/app/Services/Refunds/RefundDecisionService.php +++ b/app/Services/Refunds/RefundDecisionService.php @@ -9,11 +9,11 @@ class RefundDecisionService public function updateDecision(int $refundId, string $status, string $reason, int $actorId): array { $refund = Refund::query()->find($refundId); - if (!$refund) { + if (! $refund) { return ['ok' => false, 'message' => 'Refund not found.']; } - if (!in_array($status, [Refund::STATUS_APPROVED, Refund::STATUS_REJECTED], true)) { + if (! in_array($status, [Refund::STATUS_APPROVED, Refund::STATUS_REJECTED], true)) { return ['ok' => false, 'message' => 'Invalid refund status.']; } @@ -46,7 +46,7 @@ class RefundDecisionService public function cancel(int $refundId, int $actorId): array { $refund = Refund::query()->find($refundId); - if (!$refund) { + if (! $refund) { return ['ok' => false, 'message' => 'Refund not found.']; } diff --git a/app/Services/Refunds/RefundInvoiceAdjustmentService.php b/app/Services/Refunds/RefundInvoiceAdjustmentService.php index ecccc8ae..c5dd0345 100644 --- a/app/Services/Refunds/RefundInvoiceAdjustmentService.php +++ b/app/Services/Refunds/RefundInvoiceAdjustmentService.php @@ -8,9 +8,7 @@ use App\Services\Discounts\DiscountInvoiceService; class RefundInvoiceAdjustmentService { - public function __construct(private DiscountInvoiceService $invoiceService) - { - } + public function __construct(private DiscountInvoiceService $invoiceService) {} public function applyBookChargeAndUpdateInvoice( Invoice $invoice, @@ -23,7 +21,7 @@ class RefundInvoiceAdjustmentService $bookChargeDesc = 'Book fee retained for withdrawals before the refund deadline.'; $bookPrice = (float) ($refundDetails['book_price'] ?? 0.0); - $eligible = !empty($refundDetails['eligible']); + $eligible = ! empty($refundDetails['eligible']); $bookChargeDelta = 0.0; $bookChargeAction = 'none'; diff --git a/app/Services/Refunds/RefundOverpaymentService.php b/app/Services/Refunds/RefundOverpaymentService.php index bd349a52..0daafbdd 100644 --- a/app/Services/Refunds/RefundOverpaymentService.php +++ b/app/Services/Refunds/RefundOverpaymentService.php @@ -15,8 +15,7 @@ class RefundOverpaymentService public function __construct( private RefundNotificationService $notifications, private ApplicationUrlService $urls, - ) { - } + ) {} public function recalculate(bool $triggerEvents = false, ?string $invoiceNumber = null, ?int $actorId = null): array { @@ -77,7 +76,7 @@ class RefundOverpaymentService private function recalculateForInvoice(string $invoiceNumber, bool $triggerEvents, ?int $actorId): array { $invoice = Invoice::query()->where('invoice_number', $invoiceNumber)->first(); - if (!$invoice) { + if (! $invoice) { return ['ok' => false, 'message' => 'Invoice not found.']; } @@ -121,6 +120,7 @@ class RefundOverpaymentService 'updated_at' => utc_now(), 'updated_by' => $actorId, ]); + return ['ok' => true, 'message' => 'Overpayment updated for invoice.']; } @@ -209,6 +209,7 @@ class RefundOverpaymentService 'updated_by' => $actorId, ]); $updated[] = (int) $openRow->id; + continue; } @@ -229,6 +230,7 @@ class RefundOverpaymentService 'updated_by' => $actorId, ]); $updated[] = (int) $parentLevelOpen->id; + continue; } @@ -239,7 +241,7 @@ class RefundOverpaymentService ->orderByDesc('id') ->first(); - if (!$existing || $existing->status === Refund::STATUS_PAID) { + if (! $existing || $existing->status === Refund::STATUS_PAID) { $refund = Refund::query()->create([ 'parent_id' => $pid, 'school_year' => $schoolYear, diff --git a/app/Services/Refunds/RefundPayoutService.php b/app/Services/Refunds/RefundPayoutService.php index 33a687c1..0a6ec0e3 100644 --- a/app/Services/Refunds/RefundPayoutService.php +++ b/app/Services/Refunds/RefundPayoutService.php @@ -14,11 +14,11 @@ class RefundPayoutService public function recordPayment(int $refundId, array $payload, int $actorId): array { $refund = Refund::query()->find($refundId); - if (!$refund) { + if (! $refund) { return ['ok' => false, 'message' => 'Refund not found.']; } - if (!in_array($refund->status, [Refund::STATUS_APPROVED, Refund::STATUS_PARTIAL], true)) { + if (! in_array($refund->status, [Refund::STATUS_APPROVED, Refund::STATUS_PARTIAL], true)) { return ['ok' => false, 'message' => 'Refund must be Approved or Partial to pay.']; } @@ -126,6 +126,7 @@ class RefundPayoutService return $latest ? (int) $latest->id : null; } catch (\Throwable $e) { Log::warning('Refund invoice assignment failed.', ['error' => $e->getMessage()]); + return null; } } diff --git a/app/Services/Refunds/RefundPolicyService.php b/app/Services/Refunds/RefundPolicyService.php index 2e0f4757..62aa28c8 100644 --- a/app/Services/Refunds/RefundPolicyService.php +++ b/app/Services/Refunds/RefundPolicyService.php @@ -14,8 +14,7 @@ class RefundPolicyService public function __construct( private FeeRefundDetailService $refundDetails, private RefundInvoiceAdjustmentService $invoiceAdjustments - ) { - } + ) {} public function processWithdrawalRefund( int $parentId, @@ -39,7 +38,7 @@ class RefundPolicyService ->orderByDesc('created_at') ->first(); - if (!$invoice) { + if (! $invoice) { return ['ok' => false, 'error' => 'No invoice found for refund processing.']; } @@ -116,14 +115,14 @@ class RefundPolicyService $refundAmount = (float) ($details['refund_amount'] ?? 0.0); $debitAmount = (float) ($details['debit_amount'] ?? 0.0); - $eligible = !empty($details['eligible']); + $eligible = ! empty($details['eligible']); $status = $refundAmount > 0 ? Refund::STATUS_PENDING : Refund::STATUS_REJECTED; $reason = null; if ($refundAmount <= 0) { - if (!$eligible) { + if (! $eligible) { $reason = 'No refund due to late withdrawal.'; - } elseif (!empty($details['full_discount'])) { + } elseif (! empty($details['full_discount'])) { $reason = 'No refund due to full discount.'; } elseif ($debitAmount > 0) { $reason = 'No refund due; book fee balance remains.'; @@ -189,7 +188,7 @@ class RefundPolicyService 'updated_by' => $actorId, ]); - if (!$created) { + if (! $created) { return ['ok' => false, 'error' => 'Failed to create refund record.']; } diff --git a/app/Services/Refunds/RefundQueryService.php b/app/Services/Refunds/RefundQueryService.php index cf3fb261..1289e832 100644 --- a/app/Services/Refunds/RefundQueryService.php +++ b/app/Services/Refunds/RefundQueryService.php @@ -16,7 +16,7 @@ class RefundQueryService $sortDir = strtolower((string) ($filters['sort_dir'] ?? 'desc')) === 'asc' ? 'asc' : 'desc'; $allowedSorts = ['created_at', 'updated_at', 'refund_amount', 'status', 'parent_id', 'invoice_id']; - if (!in_array($sortBy, $allowedSorts, true)) { + if (! in_array($sortBy, $allowedSorts, true)) { $sortBy = 'created_at'; } @@ -29,25 +29,25 @@ class RefundQueryService ->leftJoin('users as parents', 'parents.id', '=', 'refunds.parent_id') ->leftJoin('users as approvers', 'approvers.id', '=', 'refunds.approved_by'); - if (!empty($filters['status'])) { + if (! empty($filters['status'])) { $query->where('refunds.status', $filters['status']); } - if (!empty($filters['request'])) { + if (! empty($filters['request'])) { $query->where('refunds.request', $filters['request']); } - if (!empty($filters['parent_id'])) { + if (! empty($filters['parent_id'])) { $query->where('refunds.parent_id', (int) $filters['parent_id']); } - if (!empty($filters['invoice_id'])) { + if (! empty($filters['invoice_id'])) { $query->where('refunds.invoice_id', (int) $filters['invoice_id']); } - if (!empty($filters['school_year'])) { + if (! empty($filters['school_year'])) { $query->where('refunds.school_year', (string) $filters['school_year']); } - if (!empty($filters['semester'])) { + if (! empty($filters['semester'])) { $query->where('refunds.semester', (string) $filters['semester']); } - if (!empty($filters['q'])) { + if (! empty($filters['q'])) { $term = trim((string) $filters['q']); $query->where(function ($where) use ($term) { $where->where('refunds.reason', 'like', "%{$term}%") @@ -57,7 +57,7 @@ class RefundQueryService }); } - $paginator = $query->orderBy('refunds.' . $sortBy, $sortDir) + $paginator = $query->orderBy('refunds.'.$sortBy, $sortDir) ->paginate($perPage, ['*'], 'page', $page); return [ diff --git a/app/Services/Refunds/RefundRequestService.php b/app/Services/Refunds/RefundRequestService.php index fbc37bb9..aeb3aaad 100644 --- a/app/Services/Refunds/RefundRequestService.php +++ b/app/Services/Refunds/RefundRequestService.php @@ -18,8 +18,7 @@ class RefundRequestService private RefundSummaryService $summaryService, private RefundNotificationService $notifications, private ApplicationUrlService $urls, - ) { - } + ) {} public function requestRefund(array $payload, int $actorId): array { @@ -30,12 +29,12 @@ class RefundRequestService $paymentId = isset($payload['payment_id']) ? (int) $payload['payment_id'] : null; $reason = $payload['reason'] ?? null; - if (!in_array($requestType, self::REQUEST_TYPES, true)) { + if (! in_array($requestType, self::REQUEST_TYPES, true)) { return ['ok' => false, 'message' => 'Invalid refund request type.']; } $parent = User::query()->find($parentId); - if (!$parent) { + if (! $parent) { return ['ok' => false, 'message' => 'Parent not found.']; } @@ -101,18 +100,19 @@ class RefundRequestService 'status' => Refund::STATUS_PENDING, 'reason' => $reason, 'request' => $requestType, - 'note' => $paymentId ? ('duplicate-of-payment#' . $paymentId) : null, + 'note' => $paymentId ? ('duplicate-of-payment#'.$paymentId) : null, 'requested_at' => utc_now(), 'created_at' => utc_now(), 'updated_at' => utc_now(), 'updated_by' => $actorId, ]); - if (!$refund) { + if (! $refund) { Log::error('Failed to create refund request.', [ 'parent_id' => $parentId, 'type' => $requestType, ]); + return ['ok' => false, 'message' => 'Failed to create refund request.']; } diff --git a/app/Services/Reimbursements/ReimbursementBatchAssignmentService.php b/app/Services/Reimbursements/ReimbursementBatchAssignmentService.php index a56d19fb..29cbeec6 100644 --- a/app/Services/Reimbursements/ReimbursementBatchAssignmentService.php +++ b/app/Services/Reimbursements/ReimbursementBatchAssignmentService.php @@ -9,9 +9,7 @@ use RuntimeException; class ReimbursementBatchAssignmentService { - public function __construct(private ReimbursementLookupService $lookupService) - { - } + public function __construct(private ReimbursementLookupService $lookupService) {} public function updateAssignment( int $expenseId, @@ -35,7 +33,7 @@ class ReimbursementBatchAssignmentService if ($batchId <= 0) { if ($activeItem) { $activeItem->update(['unassigned_at' => $now]); - if (!$reimbursementId && !empty($activeItem->reimbursement_id)) { + if (! $reimbursementId && ! empty($activeItem->reimbursement_id)) { $reimbursementId = (int) $activeItem->reimbursement_id; } } @@ -48,7 +46,7 @@ class ReimbursementBatchAssignmentService } $batch = ReimbursementBatch::query()->find($batchId); - if (!$batch || strtolower((string) $batch->status) !== ReimbursementBatch::STATUS_OPEN) { + if (! $batch || strtolower((string) $batch->status) !== ReimbursementBatch::STATUS_OPEN) { throw new RuntimeException('Batch not found or already closed.'); } @@ -63,7 +61,7 @@ class ReimbursementBatchAssignmentService } if ($activeSameBatch) { - if (!$reimbursementId) { + if (! $reimbursementId) { $reimbursementId = $activeItem->reimbursement_id ? (int) $activeItem->reimbursement_id : $this->lookupService->findReimbursementIdForExpense($expenseId); @@ -85,12 +83,12 @@ class ReimbursementBatchAssignmentService if ($activeItem) { $activeItem->update(['unassigned_at' => $now]); - if (!$reimbursementId && !empty($activeItem->reimbursement_id)) { + if (! $reimbursementId && ! empty($activeItem->reimbursement_id)) { $reimbursementId = (int) $activeItem->reimbursement_id; } } - if (!$reimbursementId) { + if (! $reimbursementId) { $reimbursementId = $this->lookupService->findReimbursementIdForExpense($expenseId); } @@ -105,7 +103,7 @@ class ReimbursementBatchAssignmentService 'semester' => $semester, ]); } catch (\Throwable $e) { - Log::error('Failed to assign expense to batch: ' . $e->getMessage(), [ + Log::error('Failed to assign expense to batch: '.$e->getMessage(), [ 'expense_id' => $expenseId, 'batch_id' => $batchId, ]); diff --git a/app/Services/Reimbursements/ReimbursementBatchService.php b/app/Services/Reimbursements/ReimbursementBatchService.php index 698db538..23829d16 100644 --- a/app/Services/Reimbursements/ReimbursementBatchService.php +++ b/app/Services/Reimbursements/ReimbursementBatchService.php @@ -12,9 +12,7 @@ use RuntimeException; class ReimbursementBatchService { - public function __construct(private ReimbursementLookupService $lookupService) - { - } + public function __construct(private ReimbursementLookupService $lookupService) {} public function createBatch(?string $title, int $userId, string $schoolYear, string $semester): array { @@ -31,7 +29,7 @@ class ReimbursementBatchService 'yearly_batch_number' => $sequence, ]); - $label = trim((string) $batch->title) !== '' ? (string) $batch->title : ('Batch #' . $sequence); + $label = trim((string) $batch->title) !== '' ? (string) $batch->title : ('Batch #'.$sequence); if (trim((string) $batch->title) === '') { $batch->title = $label; $batch->save(); @@ -47,7 +45,7 @@ class ReimbursementBatchService public function lockBatch(int $batchId, int $userId, string $schoolYear, string $semester): void { $batch = ReimbursementBatch::query()->find($batchId); - if (!$batch || strtolower((string) $batch->status) !== ReimbursementBatch::STATUS_OPEN) { + if (! $batch || strtolower((string) $batch->status) !== ReimbursementBatch::STATUS_OPEN) { throw new RuntimeException('Batch not found or already closed.'); } @@ -60,7 +58,7 @@ class ReimbursementBatchService $adminIds = array_values(array_unique($adminRows->pluck('admin_id')->map(fn ($id) => (int) $id)->all())); $requiredAdmins = array_values(array_filter($adminIds, static fn ($adminId) => $adminId > 0)); - if (!empty($requiredAdmins)) { + if (! empty($requiredAdmins)) { $uploadedAdminIds = ReimbursementBatchAdminFile::query() ->where('batch_id', $batchId) ->whereIn('admin_id', $requiredAdmins) @@ -71,7 +69,7 @@ class ReimbursementBatchService ->all(); foreach ($requiredAdmins as $adminId) { - if (!in_array($adminId, $uploadedAdminIds, true)) { + if (! in_array($adminId, $uploadedAdminIds, true)) { throw new RuntimeException('All admin sections must have a check file before submitting the batch.'); } } @@ -94,7 +92,7 @@ class ReimbursementBatchService $reimbId = $item->batch_reimb_id ?: ($item->expense_reimb_id ?: $this->lookupService->findReimbursementIdForExpense($expenseId)); - if (!$reimbId) { + if (! $reimbId) { $payload = [ 'expense_id' => $expenseId, 'amount' => (float) ($item->amount ?? 0), @@ -122,7 +120,7 @@ class ReimbursementBatchService ]); Expense::query()->where('id', $expenseId)->update(['reimbursement_id' => $reimbId]); - if (!empty($item->batch_item_id)) { + if (! empty($item->batch_item_id)) { ReimbursementBatchItem::query()->where('id', (int) $item->batch_item_id)->update([ 'reimbursement_id' => $reimbId, ]); diff --git a/app/Services/Reimbursements/ReimbursementDonationService.php b/app/Services/Reimbursements/ReimbursementDonationService.php index 6413fee3..26d25327 100644 --- a/app/Services/Reimbursements/ReimbursementDonationService.php +++ b/app/Services/Reimbursements/ReimbursementDonationService.php @@ -17,11 +17,11 @@ class ReimbursementDonationService } $expense = Expense::query()->find($expenseId); - if (!$expense) { + if (! $expense) { throw new RuntimeException('Expense not found.'); } - if (!empty($expense->reimbursement_id)) { + if (! empty($expense->reimbursement_id)) { throw new RuntimeException('Expense is already tied to a reimbursement.'); } @@ -46,7 +46,7 @@ class ReimbursementDonationService DB::commit(); } catch (\Throwable $e) { DB::rollBack(); - Log::error('Failed to mark donation: ' . $e->getMessage(), ['expense_id' => $expenseId]); + Log::error('Failed to mark donation: '.$e->getMessage(), ['expense_id' => $expenseId]); throw new RuntimeException('Unable to mark donation right now.'); } } diff --git a/app/Services/Reimbursements/ReimbursementEmailService.php b/app/Services/Reimbursements/ReimbursementEmailService.php index 31adbcf6..dd8ec02d 100644 --- a/app/Services/Reimbursements/ReimbursementEmailService.php +++ b/app/Services/Reimbursements/ReimbursementEmailService.php @@ -11,9 +11,7 @@ use ZipArchive; class ReimbursementEmailService { - public function __construct(private ReimbursementRecipientService $recipients) - { - } + public function __construct(private ReimbursementRecipientService $recipients) {} public function sendBatchEmail( int $batchId, @@ -23,14 +21,14 @@ class ReimbursementEmailService array $checkIds ): bool { $batch = ReimbursementBatch::query()->find($batchId); - if (!$batch) { + if (! $batch) { throw new \RuntimeException('Requested batch was not found.'); } - $receiptRows = !empty($receiptIds) ? $this->fetchBatchReceiptRows($batchId, $receiptIds) : []; + $receiptRows = ! empty($receiptIds) ? $this->fetchBatchReceiptRows($batchId, $receiptIds) : []; $checkRows = []; - if (!empty($checkIds)) { + if (! empty($checkIds)) { $checkRows = ReimbursementBatchAdminFile::query() ->select('id as file_id', 'admin_id', 'filename', 'original_filename', 'uploaded_at') ->where('batch_id', $batchId) @@ -40,29 +38,29 @@ class ReimbursementEmailService $adminLookup = []; foreach ($this->recipients->adminRoster() as $admin) { - $adminLookup[(int) ($admin['id'] ?? 0)] = trim((string) (($admin['firstname'] ?? '') . ' ' . ($admin['lastname'] ?? ''))); + $adminLookup[(int) ($admin['id'] ?? 0)] = trim((string) (($admin['firstname'] ?? '').' '.($admin['lastname'] ?? ''))); } foreach ($checkRows as &$row) { $aid = (int) ($row['admin_id'] ?? 0); - $row['admin'] = $adminLookup[$aid] ?? ($aid > 0 ? 'Admin #' . $aid : 'Admin'); + $row['admin'] = $adminLookup[$aid] ?? ($aid > 0 ? 'Admin #'.$aid : 'Admin'); } unset($row); } $tempDir = storage_path('tmp'); - if (!is_dir($tempDir)) { + if (! is_dir($tempDir)) { mkdir($tempDir, 0755, true); } $cleanupPaths = []; - $batchLabel = trim((string) ($batch->title ?? '')) ?: ('Batch #' . ((int) ($batch->yearly_batch_number ?? $batchId))); + $batchLabel = trim((string) ($batch->title ?? '')) ?: ('Batch #'.((int) ($batch->yearly_batch_number ?? $batchId))); $batchFileLabel = $this->sanitizeAttachmentComponent($batchLabel); $csvPath = tempnam($tempDir, 'batch_csv_'); $cleanupPaths[] = $csvPath; $csvHandle = fopen($csvPath, 'w'); - if (!$csvHandle) { + if (! $csvHandle) { if (is_file($csvPath)) { @unlink($csvPath); } @@ -74,31 +72,31 @@ class ReimbursementEmailService $this->writeBatchCsvSummary($csvHandle, $receiptRows, $batchClosedDate); fclose($csvHandle); - $csvAttachmentName = $batchFileLabel . '_summary.csv'; + $csvAttachmentName = $batchFileLabel.'_summary.csv'; $receiptsArchivePath = null; $receiptZipCreated = false; - if (!empty($receiptRows)) { - $archivePath = $tempDir . '/batch_receipts_' . $batchId . '_' . date('Ymd_His') . '_' . bin2hex(random_bytes(4)) . '.zip'; - $zip = new ZipArchive(); + if (! empty($receiptRows)) { + $archivePath = $tempDir.'/batch_receipts_'.$batchId.'_'.date('Ymd_His').'_'.bin2hex(random_bytes(4)).'.zip'; + $zip = new ZipArchive; $added = 0; $usedNames = []; if ($zip->open($archivePath, ZipArchive::CREATE | ZipArchive::OVERWRITE) === true) { foreach ($receiptRows as $row) { - $sourcePath = storage_path('uploads/receipts/' . basename((string) ($row['receipt_filename'] ?? ''))); - if (!is_file($sourcePath)) { + $sourcePath = storage_path('uploads/receipts/'.basename((string) ($row['receipt_filename'] ?? ''))); + if (! is_file($sourcePath)) { continue; } $firstName = $row['purchaser_firstname'] ?? ''; - if (trim($firstName) === '' && !empty($row['purchaser_lastname'] ?? '')) { + if (trim($firstName) === '' && ! empty($row['purchaser_lastname'] ?? '')) { $firstName = $row['purchaser_lastname']; } $firstName = $this->sanitizeAttachmentComponent($firstName); $vendorPart = $this->sanitizeAttachmentComponent($row['retailor'] ?? ''); $datePart = $this->formatAttachmentDate($row['purchase_date'] ?? ''); $extension = pathinfo($sourcePath, PATHINFO_EXTENSION); - $extPart = $extension !== '' ? ('.' . strtolower($extension)) : ''; - $baseName = $firstName . '_' . $vendorPart . '_' . $datePart; + $extPart = $extension !== '' ? ('.'.strtolower($extension)) : ''; + $baseName = $firstName.'_'.$vendorPart.'_'.$datePart; $entryName = $this->uniqueZipEntryName($baseName, $extPart, $usedNames); if ($zip->addFile($sourcePath, $entryName)) { $added++; @@ -117,21 +115,21 @@ class ReimbursementEmailService $checksArchivePath = null; $checksAdded = 0; - if (!empty($checkRows)) { - $archivePath = $tempDir . '/batch_checks_' . $batchId . '_' . date('Ymd_His') . '_' . bin2hex(random_bytes(4)) . '.zip'; - $zip = new ZipArchive(); + if (! empty($checkRows)) { + $archivePath = $tempDir.'/batch_checks_'.$batchId.'_'.date('Ymd_His').'_'.bin2hex(random_bytes(4)).'.zip'; + $zip = new ZipArchive; $usedNames = []; if ($zip->open($archivePath, ZipArchive::CREATE | ZipArchive::OVERWRITE) === true) { foreach ($checkRows as $row) { - $sourcePath = storage_path('uploads/reimbursements/' . basename((string) ($row['filename'] ?? ''))); - if (!is_file($sourcePath)) { + $sourcePath = storage_path('uploads/reimbursements/'.basename((string) ($row['filename'] ?? ''))); + if (! is_file($sourcePath)) { continue; } $adminLabel = $row['admin'] ?? 'Admin'; $firstToken = strtok($adminLabel, ' '); $namePart = $this->sanitizeAttachmentComponent($firstToken ?: $adminLabel); $extension = pathinfo($sourcePath, PATHINFO_EXTENSION); - $extPart = $extension !== '' ? ('.' . strtolower($extension)) : ''; + $extPart = $extension !== '' ? ('.'.strtolower($extension)) : ''; $entryName = $this->uniqueZipEntryName($namePart, $extPart, $usedNames); if ($zip->addFile($sourcePath, $entryName)) { $checksAdded++; @@ -155,14 +153,14 @@ class ReimbursementEmailService $attachmentSummary[] = 'Check Scans'; } - $subject = 'Reimbursement Batch Export: ' . $batchLabel; + $subject = 'Reimbursement Batch Export: '.$batchLabel; $bodyParts = [ - '

Please find attached the export for ' . htmlspecialchars($batchLabel, ENT_QUOTES, 'UTF-8') . '.

', + '

Please find attached the export for '.htmlspecialchars($batchLabel, ENT_QUOTES, 'UTF-8').'.

', ]; if (trim($message) !== '') { - $bodyParts[] = '

Message:
' . nl2br(htmlspecialchars($message, ENT_QUOTES, 'UTF-8')) . '

'; + $bodyParts[] = '

Message:
'.nl2br(htmlspecialchars($message, ENT_QUOTES, 'UTF-8')).'

'; } - $bodyParts[] = '

Attachments: ' . htmlspecialchars(implode(', ', $attachmentSummary), ENT_QUOTES, 'UTF-8') . '.

'; + $bodyParts[] = '

Attachments: '.htmlspecialchars(implode(', ', $attachmentSummary), ENT_QUOTES, 'UTF-8').'.

'; $bodyParts[] = '

Regards,
Al Rahma Sunday School

'; $htmlBody = implode("\n", $bodyParts); @@ -170,10 +168,10 @@ class ReimbursementEmailService ['path' => $csvPath, 'name' => $csvAttachmentName], ]; if ($receiptsArchivePath) { - $attachments[] = ['path' => $receiptsArchivePath, 'name' => $batchFileLabel . '_receipts.zip']; + $attachments[] = ['path' => $receiptsArchivePath, 'name' => $batchFileLabel.'_receipts.zip']; } if ($checksArchivePath) { - $attachments[] = ['path' => $checksArchivePath, 'name' => $batchFileLabel . '_checks.zip']; + $attachments[] = ['path' => $checksArchivePath, 'name' => $batchFileLabel.'_checks.zip']; } $sender = $this->resolveSender('general'); @@ -181,11 +179,11 @@ class ReimbursementEmailService try { Mail::html($htmlBody, function ($message) use ($recipientEmail, $subject, $sender, $attachments) { $message->to($recipientEmail)->subject($subject); - if (!empty($sender['email'])) { + if (! empty($sender['email'])) { $message->from($sender['email'], $sender['name']); } foreach ($attachments as $file) { - if (!empty($file['path']) && is_file($file['path'])) { + if (! empty($file['path']) && is_file($file['path'])) { $message->attach($file['path'], ['as' => $file['name'] ?? basename($file['path'])]); } } @@ -193,7 +191,8 @@ class ReimbursementEmailService return true; } catch (\Throwable $e) { - Log::error('Failed to send batch export email: ' . $e->getMessage()); + Log::error('Failed to send batch export email: '.$e->getMessage()); + return false; } finally { foreach ($cleanupPaths as $path) { @@ -212,7 +211,7 @@ class ReimbursementEmailService ->leftJoin('users as u', 'u.id', '=', 'e.purchased_by') ->where('bi.batch_id', $batchId); - if (!empty($receiptIds)) { + if (! empty($receiptIds)) { $builder->whereIn('e.id', $receiptIds); } @@ -230,7 +229,7 @@ class ReimbursementEmailService 'purchaser_lastname' => $row->purchaser_lastname ?? null, ]; }, $rows), static function ($row) { - return !empty($row['receipt_filename']); + return ! empty($row['receipt_filename']); })); } @@ -247,7 +246,7 @@ class ReimbursementEmailService { $receiptTotalsByRecipient = []; foreach ($receiptRows as $row) { - $recipientName = trim(($row['purchaser_firstname'] ?? '') . ' ' . ($row['purchaser_lastname'] ?? '')); + $recipientName = trim(($row['purchaser_firstname'] ?? '').' '.($row['purchaser_lastname'] ?? '')); if ($recipientName === '') { $recipientName = 'Unknown'; } @@ -271,7 +270,7 @@ class ReimbursementEmailService implode(' | ', $descriptionParts), ]); } - if (!empty($receiptTotalsByRecipient)) { + if (! empty($receiptTotalsByRecipient)) { fputcsv($handle, []); fputcsv($handle, ['Totals by Recipient', '', '', '', '', '', '', '']); foreach ($receiptTotalsByRecipient as $recipient => $total) { @@ -294,42 +293,46 @@ class ReimbursementEmailService { $clean = preg_replace('/[^A-Za-z0-9]+/', '_', trim((string) $value)); $clean = trim($clean, '_'); + return $clean === '' ? 'unknown' : $clean; } private function formatAttachmentDate(?string $value): string { - if (!$value) { + if (! $value) { return 'unknown'; } $timestamp = strtotime($value); if ($timestamp === false) { return 'unknown'; } + return date('Ymd', $timestamp); } private function uniqueZipEntryName(string $base, string $extension, array &$used): string { - $candidate = $base . $extension; + $candidate = $base.$extension; $counter = 1; while (isset($used[$candidate])) { - $candidate = $base . '_' . $counter . $extension; + $candidate = $base.'_'.$counter.$extension; $counter++; } $used[$candidate] = true; + return $candidate; } private function formatCsvDate(?string $value): string { - if (!$value) { + if (! $value) { return ''; } $timestamp = strtotime($value); if ($timestamp === false) { return ''; } + return date('m-d-Y', $timestamp); } @@ -341,6 +344,7 @@ class ReimbursementEmailService if (is_array($senders) && isset($senders[$fromKey])) { $info = $senders[$fromKey]; + return [ 'name' => (string) ($info['name'] ?? 'Sender'), 'email' => (string) ($info['email'] ?? ''), diff --git a/app/Services/Reimbursements/ReimbursementExportService.php b/app/Services/Reimbursements/ReimbursementExportService.php index f2c4d8f6..7a297cc7 100644 --- a/app/Services/Reimbursements/ReimbursementExportService.php +++ b/app/Services/Reimbursements/ReimbursementExportService.php @@ -8,9 +8,7 @@ use RuntimeException; class ReimbursementExportService { - public function __construct(private ReimbursementRecipientService $recipients) - { - } + public function __construct(private ReimbursementRecipientService $recipients) {} public function buildUnderProcessingCsv(): array { @@ -27,12 +25,12 @@ class ReimbursementExportService foreach ($records as $row) { $personId = (int) ($row->purchased_by ?? 0); - $name = trim(($row->purchaser_firstname ?? '') . ' ' . ($row->purchaser_lastname ?? '')); + $name = trim(($row->purchaser_firstname ?? '').' '.($row->purchaser_lastname ?? '')); if ($name === '') { $name = $row->purchaser_firstname ?? $row->purchaser_lastname ?? 'Unknown'; } - $key = $personId > 0 ? 'id_' . $personId : 'name_' . strtolower($name); - if (!isset($grouped[$key])) { + $key = $personId > 0 ? 'id_'.$personId : 'name_'.strtolower($name); + if (! isset($grouped[$key])) { $grouped[$key] = [ 'name' => $name ?: 'Unknown', 'total' => 0.0, @@ -45,12 +43,12 @@ class ReimbursementExportService $grouped[$key]['total'] += $amount; $vendor = trim((string) ($row->retailor ?? '')); - if ($vendor !== '' && !in_array($vendor, $grouped[$key]['vendors'], true)) { + if ($vendor !== '' && ! in_array($vendor, $grouped[$key]['vendors'], true)) { $grouped[$key]['vendors'][] = $vendor; } $desc = trim((string) ($row->description ?? '')); - if ($desc !== '' && !in_array($desc, $grouped[$key]['descriptions'], true)) { + if ($desc !== '' && ! in_array($desc, $grouped[$key]['descriptions'], true)) { $grouped[$key]['descriptions'][] = $desc; } } @@ -78,16 +76,16 @@ class ReimbursementExportService ->select('reimbursements.*', 'u.firstname as reimb_firstname', 'u.lastname as reimb_lastname') ->leftJoin('users as u', 'u.id', '=', 'reimbursements.reimbursed_to'); - if (!empty($filters['semester'])) { + if (! empty($filters['semester'])) { $builder->where('reimbursements.semester', $filters['semester']); } - if (!empty($filters['status'])) { + if (! empty($filters['status'])) { $builder->where('reimbursements.status', $filters['status']); } - if (!empty($filters['user_id'])) { + if (! empty($filters['user_id'])) { $builder->where('reimbursements.reimbursed_to', $filters['user_id']); } - if (!empty($filters['school_year'])) { + if (! empty($filters['school_year'])) { $builder->where('reimbursements.school_year', $filters['school_year']); } @@ -96,8 +94,8 @@ class ReimbursementExportService $rows[] = ['Amount', 'Reimbursed To', 'Status', 'Method', 'Check #', 'School Year', 'Semester', 'Date']; foreach ($records as $row) { - $hasName = trim(($row->reimb_firstname ?? '') . ' ' . ($row->reimb_lastname ?? '')) !== ''; - if (!$hasName) { + $hasName = trim(($row->reimb_firstname ?? '').' '.($row->reimb_lastname ?? '')) !== ''; + if (! $hasName) { $label = $this->recipients->specialRecipientLabel($row->reimbursed_to ?? null); if ($label !== null) { $row->reimb_firstname = $label; @@ -107,7 +105,7 @@ class ReimbursementExportService $rows[] = [ $row->amount, - trim(($row->reimb_firstname ?? '') . ' ' . ($row->reimb_lastname ?? '')), + trim(($row->reimb_firstname ?? '').' '.($row->reimb_lastname ?? '')), $row->status, $row->reimbursement_method ?? '-', $row->check_number ?? '-', @@ -126,11 +124,11 @@ class ReimbursementExportService public function buildBatchCsv(int $batchId): array { $batch = ReimbursementBatch::query()->find($batchId); - if (!$batch) { + if (! $batch) { throw new RuntimeException('Batch not found.'); } - $batchLabel = trim((string) ($batch->title ?? '')) ?: ('Batch #' . $batchId); + $batchLabel = trim((string) ($batch->title ?? '')) ?: ('Batch #'.$batchId); $receiptRows = $this->fetchBatchReceiptRows($batchId); $rows = []; @@ -151,14 +149,14 @@ class ReimbursementExportService ->leftJoin('users as u', 'u.id', '=', 'e.purchased_by') ->where('bi.batch_id', $batchId); - if (!empty($receiptIds)) { + if (! empty($receiptIds)) { $builder->whereIn('e.id', $receiptIds); } $rows = $builder->get()->toArray(); return array_values(array_filter($rows, static function ($row) { - return !empty($row->receipt_filename); + return ! empty($row->receipt_filename); })); } @@ -175,7 +173,7 @@ class ReimbursementExportService { $receiptTotalsByRecipient = []; foreach ($receiptRows as $row) { - $recipientName = trim(($row->purchaser_firstname ?? '') . ' ' . ($row->purchaser_lastname ?? '')); + $recipientName = trim(($row->purchaser_firstname ?? '').' '.($row->purchaser_lastname ?? '')); if ($recipientName === '') { $recipientName = 'Unknown'; } @@ -200,7 +198,7 @@ class ReimbursementExportService ]; } - if (!empty($receiptTotalsByRecipient)) { + if (! empty($receiptTotalsByRecipient)) { $rows[] = []; $rows[] = ['Totals by Recipient', '', '', '', '', '', '', '']; foreach ($receiptTotalsByRecipient as $recipient => $total) { @@ -221,13 +219,14 @@ class ReimbursementExportService private function formatCsvDate(?string $value): string { - if (!$value) { + if (! $value) { return ''; } $timestamp = strtotime($value); if ($timestamp === false) { return ''; } + return date('m-d-Y', $timestamp); } } diff --git a/app/Services/Reimbursements/ReimbursementFileService.php b/app/Services/Reimbursements/ReimbursementFileService.php index a842e351..ec42d495 100644 --- a/app/Services/Reimbursements/ReimbursementFileService.php +++ b/app/Services/Reimbursements/ReimbursementFileService.php @@ -12,8 +12,7 @@ class ReimbursementFileService { public function __construct( private ApplicationUrlService $urls - ) { - } + ) {} public function receiptUrl(?string $filename): ?string { @@ -32,7 +31,7 @@ class ReimbursementFileService public function storeReceipt(?UploadedFile $file): ?string { - if (!$file || !$file->isValid()) { + if (! $file || ! $file->isValid()) { return null; } @@ -42,10 +41,10 @@ class ReimbursementFileService $dir = $this->ensureUploadDir(); $extension = $file->getClientOriginalExtension(); - $extension = $extension !== '' ? '.' . strtolower($extension) : ''; - $filename = 'reimb_' . date('Ymd_His') . '_' . Str::random(8) . $extension; + $extension = $extension !== '' ? '.'.strtolower($extension) : ''; + $filename = 'reimb_'.date('Ymd_His').'_'.Str::random(8).$extension; - if (!$file->move($dir, $filename)) { + if (! $file->move($dir, $filename)) { throw new RuntimeException('Unable to save uploaded file.'); } @@ -56,10 +55,10 @@ class ReimbursementFileService { $dir = $this->ensureUploadDir(); $extension = $file->getClientOriginalExtension(); - $extension = $extension !== '' ? '.' . strtolower($extension) : ''; - $filename = 'batch_check_' . date('Ymd_His') . '_' . Str::random(8) . $extension; + $extension = $extension !== '' ? '.'.strtolower($extension) : ''; + $filename = 'batch_check_'.date('Ymd_His').'_'.Str::random(8).$extension; - if (!$file->move($dir, $filename)) { + if (! $file->move($dir, $filename)) { throw new RuntimeException('Unable to save uploaded file.'); } @@ -68,8 +67,8 @@ class ReimbursementFileService ->where('admin_id', $adminId) ->first(); - if ($existing && !empty($existing->filename)) { - $oldPath = $dir . DIRECTORY_SEPARATOR . $existing->filename; + if ($existing && ! empty($existing->filename)) { + $oldPath = $dir.DIRECTORY_SEPARATOR.$existing->filename; if (is_file($oldPath)) { @unlink($oldPath); } @@ -102,13 +101,13 @@ class ReimbursementFileService { $dir = storage_path('uploads/reimbursements'); - if (!is_dir($dir)) { - if (!@mkdir($dir, 0755, true) && !is_dir($dir)) { + if (! is_dir($dir)) { + if (! @mkdir($dir, 0755, true) && ! is_dir($dir)) { throw new RuntimeException('Upload directory is not available.'); } } - if (!is_writable($dir)) { + if (! is_writable($dir)) { throw new RuntimeException('Upload directory is not writable.'); } diff --git a/app/Services/Reimbursements/ReimbursementQueryService.php b/app/Services/Reimbursements/ReimbursementQueryService.php index 66b2d40f..9d65a734 100644 --- a/app/Services/Reimbursements/ReimbursementQueryService.php +++ b/app/Services/Reimbursements/ReimbursementQueryService.php @@ -11,8 +11,7 @@ class ReimbursementQueryService public function __construct( private ReimbursementRecipientService $recipients, private ReimbursementFileService $files - ) { - } + ) {} public function underProcessing(): array { @@ -45,7 +44,7 @@ class ReimbursementQueryService } $receiptUrl = $this->files->expenseReceiptUrl($row->expense_receipt ?? null); - $fullName = trim(($row->purchaser_firstname ?? '') . ' ' . ($row->purchaser_lastname ?? '')) ?: 'Unknown'; + $fullName = trim(($row->purchaser_firstname ?? '').' '.($row->purchaser_lastname ?? '')) ?: 'Unknown'; $item = [ 'id' => $expenseId, @@ -96,16 +95,16 @@ class ReimbursementQueryService } $receiptUrl = $this->files->expenseReceiptUrl($row->expense_receipt ?? null); - $purchaser = trim(($row->purchaser_firstname ?? '') . ' ' . ($row->purchaser_lastname ?? '')) ?: 'Unknown'; + $purchaser = trim(($row->purchaser_firstname ?? '').' '.($row->purchaser_lastname ?? '')) ?: 'Unknown'; $yearlyNumber = (int) ($row->batch_year_number ?? 0); $batchLabel = trim((string) ($row->batch_title ?? '')); if ($batchLabel === '') { $fallbackNumber = $yearlyNumber > 0 ? $yearlyNumber : $batchId; - $batchLabel = 'Batch #' . $fallbackNumber; + $batchLabel = 'Batch #'.$fallbackNumber; } $adminId = $row->admin_id ? (int) $row->admin_id : 0; $adminName = $adminId > 0 - ? (trim(($row->admin_firstname ?? '') . ' ' . ($row->admin_lastname ?? '')) ?: 'Admin') + ? (trim(($row->admin_firstname ?? '').' '.($row->admin_lastname ?? '')) ?: 'Admin') : 'Unassigned'; $batchStatus = strtolower(trim((string) ($row->batch_status ?? ''))) ?: 'open'; @@ -127,7 +126,7 @@ class ReimbursementQueryService $itemsMap[$expenseId] = $item; - if (!isset($batchStructures[$batchId])) { + if (! isset($batchStructures[$batchId])) { $batchStructures[$batchId] = [ 'batchId' => $batchId, 'label' => $batchLabel, @@ -143,7 +142,7 @@ class ReimbursementQueryService } $slotKey = $adminId ?: 0; - if (!isset($batchStructures[$batchId]['slots'][$slotKey])) { + if (! isset($batchStructures[$batchId]['slots'][$slotKey])) { $batchStructures[$batchId]['slots'][$slotKey] = [ 'admin_id' => $adminId ?: 0, 'admin_name' => $adminId > 0 ? $adminName : 'Unassigned', @@ -155,7 +154,7 @@ class ReimbursementQueryService } $adminFileMap = []; - if (!empty($batchStructures)) { + if (! empty($batchStructures)) { $batchIds = array_keys($batchStructures); $files = ReimbursementBatchAdminFile::query() ->select('batch_id', 'admin_id', 'filename', 'original_filename') @@ -188,6 +187,7 @@ class ReimbursementQueryService $existingBatches = array_map(static function (array $batch) { $normalizedSlots = array_map(static function (array $slot) { $slot['items'] = array_values(array_map('intval', $slot['items'])); + return $slot; }, array_values($batch['slots'])); @@ -238,13 +238,13 @@ class ReimbursementQueryService ->where('b.status', 'closed') ->whereNull('bi.unassigned_at'); - if (!empty($filters['school_year'])) { + if (! empty($filters['school_year'])) { $batchQuery->where(function ($q) use ($filters) { $q->where('b.school_year', $filters['school_year']) ->orWhere('e.school_year', $filters['school_year']); }); } - if (!empty($filters['user_id'])) { + if (! empty($filters['user_id'])) { $batchQuery->where('e.purchased_by', $filters['user_id']); } @@ -255,10 +255,10 @@ class ReimbursementQueryService continue; } $labelNumber = (int) ($row->yearly_batch_number ?? $bid); - if (!isset($batchSummaries[$bid])) { + if (! isset($batchSummaries[$bid])) { $batchSummaries[$bid] = [ 'batch_id' => $bid, - 'title' => trim((string) ($row->batch_title ?? '')) ?: ('Batch #' . $labelNumber), + 'title' => trim((string) ($row->batch_title ?? '')) ?: ('Batch #'.$labelNumber), 'sequence' => $labelNumber, 'closed_at' => $row->closed_at ?? null, 'amount' => 0.0, @@ -269,7 +269,7 @@ class ReimbursementQueryService $batchSummaries[$bid]['amount'] += (float) $amount; $batchSummaries[$bid]['items'] += 1; - if (!isset($batchDetails[$bid])) { + if (! isset($batchDetails[$bid])) { $batchDetails[$bid] = [ 'title' => $batchSummaries[$bid]['title'], 'sequence' => $labelNumber, @@ -278,7 +278,7 @@ class ReimbursementQueryService 'checks' => [], ]; } - if (!isset($batchAttachments[$bid])) { + if (! isset($batchAttachments[$bid])) { $batchAttachments[$bid] = [ 'receipts' => [], 'checks' => [], @@ -289,7 +289,7 @@ class ReimbursementQueryService 'amount' => (float) $amount, 'description' => $row->description ?? '', 'vendor' => $row->retailor ?? '', - 'purchased_by' => trim(($row->purchaser_firstname ?? '') . ' ' . ($row->purchaser_lastname ?? '')), + 'purchased_by' => trim(($row->purchaser_firstname ?? '').' '.($row->purchaser_lastname ?? '')), 'receipt_url' => $this->files->expenseReceiptUrl($row->expense_receipt ?? null), ]; @@ -309,7 +309,7 @@ class ReimbursementQueryService } } - if (!empty($batchSummaries)) { + if (! empty($batchSummaries)) { $batchIds = array_keys($batchSummaries); $files = ReimbursementBatchAdminFile::query() ->select('id as file_id', 'batch_id', 'admin_id', 'filename', 'original_filename', 'uploaded_at') @@ -319,22 +319,22 @@ class ReimbursementQueryService $adminNames = $this->recipients->adminRoster(); $adminNameMap = []; foreach ($adminNames as $admin) { - $adminNameMap[(int) $admin['id']] = trim(($admin['firstname'] ?? '') . ' ' . ($admin['lastname'] ?? '')); + $adminNameMap[(int) $admin['id']] = trim(($admin['firstname'] ?? '').' '.($admin['lastname'] ?? '')); } foreach ($files as $file) { $bid = (int) ($file->batch_id ?? 0); - if ($bid <= 0 || !isset($batchDetails[$bid])) { + if ($bid <= 0 || ! isset($batchDetails[$bid])) { continue; } $aid = (int) ($file->admin_id ?? 0); $batchDetails[$bid]['checks'][] = [ - 'admin' => $adminNameMap[$aid] ?? ($aid > 0 ? 'Admin #' . $aid : 'Unassigned'), + 'admin' => $adminNameMap[$aid] ?? ($aid > 0 ? 'Admin #'.$aid : 'Unassigned'), 'filename' => $file->filename, 'original' => $file->original_filename ?? $file->filename, 'url' => $this->files->adminFileUrl($file->filename ?? '', 'inline'), ]; - if (!isset($batchAttachments[$bid])) { + if (! isset($batchAttachments[$bid])) { $batchAttachments[$bid] = [ 'receipts' => [], 'checks' => [], @@ -344,18 +344,19 @@ class ReimbursementQueryService 'file_id' => (int) ($file->file_id ?? 0), 'filename' => $file->filename, 'original_filename' => $file->original_filename ?? $file->filename, - 'admin' => $adminNameMap[$aid] ?? ($aid > 0 ? 'Admin #' . $aid : 'Unassigned'), + 'admin' => $adminNameMap[$aid] ?? ($aid > 0 ? 'Admin #'.$aid : 'Unassigned'), 'url' => $this->files->adminFileUrl($file->filename ?? '', 'inline'), 'uploaded_at' => $file->uploaded_at ?? null, ]; } } - if (!empty($batchSummaries)) { + if (! empty($batchSummaries)) { $batchSummaries = array_values($batchSummaries); usort($batchSummaries, static function ($a, $b) { $aSeq = (int) ($a['sequence'] ?? 0); $bSeq = (int) ($b['sequence'] ?? 0); + return $bSeq <=> $aSeq; }); } @@ -371,16 +372,16 @@ class ReimbursementQueryService ->leftJoin('users as u', 'u.id', '=', 'e.purchased_by') ->where('e.category', 'Donation'); - if (!empty($filters['school_year'])) { + if (! empty($filters['school_year'])) { $donationQuery->where('e.school_year', $filters['school_year']); } - if (!empty($filters['semester'])) { + if (! empty($filters['semester'])) { $donationQuery->where('e.semester', $filters['semester']); } - if (!empty($filters['user_id'])) { + if (! empty($filters['user_id'])) { $donationQuery->where('e.purchased_by', $filters['user_id']); } - if (!empty($filters['status'])) { + if (! empty($filters['status'])) { $donationQuery->where(function ($q) use ($filters) { $q->where('e.status', $filters['status']) ->orWhere('e.status', strtolower((string) $filters['status'])) @@ -394,7 +395,7 @@ class ReimbursementQueryService foreach ($donationRows as $row) { $amount = (float) ($row->expense_amount ?? 0); $donationTotal += $amount; - $purchaser = trim(($row->purchaser_firstname ?? '') . ' ' . ($row->purchaser_lastname ?? '')); + $purchaser = trim(($row->purchaser_firstname ?? '').' '.($row->purchaser_lastname ?? '')); if ($purchaser === '') { $purchaser = 'Unknown'; } @@ -428,8 +429,8 @@ class ReimbursementQueryService } $recipientId = $e['reimb_recipient_id'] ?? ($e['reimbursed_to'] ?? null); - $hasName = trim(($e['reimb_firstname'] ?? '') . ' ' . ($e['reimb_lastname'] ?? '')) !== ''; - if (!$hasName) { + $hasName = trim(($e['reimb_firstname'] ?? '').' '.($e['reimb_lastname'] ?? '')) !== ''; + if (! $hasName) { $label = $this->recipients->specialRecipientLabel($recipientId); if ($label !== null) { $e['reimb_firstname'] = $label; diff --git a/app/Services/Reimbursements/ReimbursementRecipientService.php b/app/Services/Reimbursements/ReimbursementRecipientService.php index d614b514..eeef0bed 100644 --- a/app/Services/Reimbursements/ReimbursementRecipientService.php +++ b/app/Services/Reimbursements/ReimbursementRecipientService.php @@ -29,7 +29,7 @@ class ReimbursementRecipientService if ($id <= 0 || in_array($roleName, $excludedRoles, true)) { continue; } - if (!isset($staff[$id])) { + if (! isset($staff[$id])) { $staff[$id] = [ 'id' => $id, 'firstname' => $row->firstname ?? '', @@ -39,8 +39,9 @@ class ReimbursementRecipientService } uasort($staff, static function (array $a, array $b) { - $nameA = trim(($a['firstname'] ?? '') . ' ' . ($a['lastname'] ?? '')); - $nameB = trim(($b['firstname'] ?? '') . ' ' . ($b['lastname'] ?? '')); + $nameA = trim(($a['firstname'] ?? '').' '.($a['lastname'] ?? '')); + $nameB = trim(($b['firstname'] ?? '').' '.($b['lastname'] ?? '')); + return strcasecmp($nameA, $nameB); }); @@ -55,7 +56,7 @@ class ReimbursementRecipientService public function adminRoster(): array { $admins = $this->staffUsers(); - if (!empty($admins)) { + if (! empty($admins)) { return $admins; } @@ -64,11 +65,12 @@ class ReimbursementRecipientService public function specialRecipientLabel($value): ?string { - if ($value === null || !is_numeric($value)) { + if ($value === null || ! is_numeric($value)) { return null; } $id = (int) $value; + return self::SPECIAL_RECIPIENTS[$id] ?? null; } @@ -79,7 +81,7 @@ class ReimbursementRecipientService }, $users); foreach (self::SPECIAL_RECIPIENTS as $id => $label) { - if (!in_array($id, $existingIds, true)) { + if (! in_array($id, $existingIds, true)) { $users[] = [ 'id' => $id, 'firstname' => $label, diff --git a/app/Services/Reports/ReportCards/ReportCardService.php b/app/Services/Reports/ReportCards/ReportCardService.php index b2098603..2ecc82db 100644 --- a/app/Services/Reports/ReportCards/ReportCardService.php +++ b/app/Services/Reports/ReportCards/ReportCardService.php @@ -2,10 +2,9 @@ namespace App\Services\Reports\ReportCards; -use App\Models\Calendar; use App\Models\AttendanceData; use App\Models\AttendanceRecord; -use App\Models\ClassSection; +use App\Models\Calendar; use App\Models\Configuration; use App\Models\ReportCardAcknowledgement; use App\Models\ScoreComment; @@ -13,13 +12,12 @@ use App\Models\SemesterScore; use App\Models\Student; use App\Models\StudentClass; use App\Models\TeacherClass; -use App\Models\User; -use Illuminate\Database\Query\Builder; use Illuminate\Support\Facades\DB; class ReportCardService { private string $schoolYear; + private string $semester; public function __construct() @@ -56,7 +54,7 @@ class ReportCardService ->get(); foreach ($q2->all() as $r) { $val = (string) ($r->school_year ?? ''); - if ($val !== '' && !in_array($val, $schoolYears, true)) { + if ($val !== '' && ! in_array($val, $schoolYears, true)) { $schoolYears[] = $val; } } @@ -84,7 +82,7 @@ class ReportCardService ->get(); foreach ($rs2->all() as $r) { $val = (string) ($r->semester ?? ''); - if ($val !== '' && !in_array($val, $semesters, true)) { + if ($val !== '' && ! in_array($val, $semesters, true)) { $semesters[] = $val; } } @@ -93,14 +91,14 @@ class ReportCardService $defaults = array_values(array_filter([(string) ($this->semester ?? ''), 'Fall', 'Spring'], static fn ($v) => $v !== '')); foreach ($defaults as $d) { - if (!in_array($d, $semesters, true)) { + if (! in_array($d, $semesters, true)) { $semesters[] = $d; } } $semesters = array_values(array_unique($semesters)); - if ($sem === '' && !empty($semesters)) { + if ($sem === '' && ! empty($semesters)) { $sem = $semesters[0]; - } elseif ($sem !== '' && !in_array($sem, $semesters, true)) { + } elseif ($sem !== '' && ! in_array($sem, $semesters, true)) { array_unshift($semesters, $sem); $semesters = array_values(array_unique($semesters)); } @@ -128,7 +126,7 @@ class ReportCardService ->get() ->map(fn ($r) => (array) $r) ->all(); - if (!empty($stuRows)) { + if (! empty($stuRows)) { $students = $stuRows; } } catch (\Throwable $e) { @@ -254,7 +252,7 @@ class ReportCardService $students = $this->fetchStudentsByClass($sectionId, $year); } $rosterSemester = ''; - $usedFallback = !empty($students); + $usedFallback = ! empty($students); } if (empty($students)) { @@ -301,7 +299,7 @@ class ReportCardService $scoreByStudent = []; foreach ($scoreRows as $row) { $sid = (int) ($row['student_id'] ?? 0); - if ($sid > 0 && !isset($scoreByStudent[$sid])) { + if ($sid > 0 && ! isset($scoreByStudent[$sid])) { $scoreByStudent[$sid] = $row; } } @@ -326,7 +324,7 @@ class ReportCardService ->all(); foreach ($otherRows as $row) { $sid = (int) ($row['student_id'] ?? 0); - if ($sid > 0 && !isset($firstScoreByStudent[$sid])) { + if ($sid > 0 && ! isset($firstScoreByStudent[$sid])) { $firstScoreByStudent[$sid] = $row; } } @@ -389,7 +387,7 @@ class ReportCardService ->all(); foreach ($ackRows as $row) { $sid = (int) ($row['student_id'] ?? 0); - if ($sid > 0 && !isset($ackMap[$sid])) { + if ($sid > 0 && ! isset($ackMap[$sid])) { $ackMap[$sid] = $row; } } @@ -402,7 +400,7 @@ class ReportCardService $warningCount = 0; foreach ($students as $student) { $sid = (int) ($student['id'] ?? 0); - $name = trim(((string) ($student['firstname'] ?? '')) . ' ' . ((string) ($student['lastname'] ?? ''))); + $name = trim(((string) ($student['firstname'] ?? '')).' '.((string) ($student['lastname'] ?? ''))); $missing = []; $warnings = []; @@ -417,7 +415,7 @@ class ReportCardService $semesterScore = null; $hasAllComponents = false; - if (!$score) { + if (! $score) { $missing[] = 'Score record'; } else { $attendanceScore = $score['attendance_score'] ?? null; @@ -430,11 +428,11 @@ class ReportCardService $score['project_avg'] ?? null, $score['test_avg'] ?? null, ]; - $hasAllComponents = !in_array(false, array_map($isNumeric, $components), true); + $hasAllComponents = ! in_array(false, array_map($isNumeric, $components), true); if ($isSecond) { $examScore = $score['final_exam_score'] ?? null; - if (!$isNumeric($examScore) && $isNumeric($score['midterm_exam_score'] ?? null)) { + if (! $isNumeric($examScore) && $isNumeric($score['midterm_exam_score'] ?? null)) { $examScore = $score['midterm_exam_score']; $warnings[] = 'Final exam stored as midterm'; } @@ -444,13 +442,13 @@ class ReportCardService $examScore = $score['final_exam_score'] ?? ($score['midterm_exam_score'] ?? null); } - if (!$isNumeric($examScore)) { + if (! $isNumeric($examScore)) { $missing[] = $isSecond ? 'Final exam score' : ($isFirst ? 'Midterm exam score' : 'Exam score'); } - if (!$isNumeric($attendanceScore)) { + if (! $isNumeric($attendanceScore)) { $missing[] = 'Attendance score'; } - if (!$isNumeric($ptapScore)) { + if (! $isNumeric($ptapScore)) { if ($hasAllComponents) { $warnings[] = 'PTAP computed from components'; } else { @@ -459,7 +457,7 @@ class ReportCardService } $canComputeSemester = $isNumeric($examScore) && $isNumeric($attendanceScore) && ($isNumeric($ptapScore) || $hasAllComponents); - if (!$isNumeric($semesterScore)) { + if (! $isNumeric($semesterScore)) { if ($canComputeSemester) { $warnings[] = 'Semester score computed'; } else { @@ -469,7 +467,7 @@ class ReportCardService if ($isSecond) { $firstScore = $firstScoreByStudent[$sid]['semester_score'] ?? null; - if (!$isNumeric($firstScore)) { + if (! $isNumeric($firstScore)) { $missing[] = 'First semester score'; } } @@ -482,7 +480,7 @@ class ReportCardService ? ($commentSet[$commentKey] ?? '') : ($commentSet['final'] ?? ($commentSet['midterm'] ?? '')); if (trim((string) $examComment) === '') { - $missing[] = $commentLabel . ' comment'; + $missing[] = $commentLabel.' comment'; } if (trim((string) ($commentSet['ptap'] ?? '')) === '') { $missing[] = 'PTAP comment'; @@ -495,7 +493,7 @@ class ReportCardService if ($isComplete) { $completeCount++; } - if (!empty($warnings)) { + if (! empty($warnings)) { $warningCount++; } @@ -564,7 +562,7 @@ class ReportCardService $reportDate = $this->sanitizeReportDate((string) ($params['report_date'] ?? '')); $data = $this->prepareStudentReportData($studentId, $schoolYear, $semester); - if (!$data) { + if (! $data) { return ['ok' => false, 'message' => 'Student not found or missing scores.']; } @@ -574,7 +572,7 @@ class ReportCardService $pdf = new \FPDF('P', 'mm', 'Letter'); $this->formatReportPDF($pdf, $data); - $filename = 'ReportCard_' . ($data['student']['lastname'] ?? 'student') . '.pdf'; + $filename = 'ReportCard_'.($data['student']['lastname'] ?? 'student').'.pdf'; $pdfContent = $pdf->Output('S'); @@ -596,10 +594,10 @@ class ReportCardService ->join('students as s', 's.id', '=', 'semester_scores.student_id') ->where('s.is_active', 1) ->where('semester_scores.class_section_id', $classSectionId); - if (!empty($schoolYear)) { + if (! empty($schoolYear)) { $q->where('semester_scores.school_year', $schoolYear); } - if (!empty($semester)) { + if (! empty($semester)) { $this->applySemesterFilter($q, $semester, 'semester_scores.semester'); } $scores = $q @@ -609,7 +607,7 @@ class ReportCardService ->map(fn ($r) => $r->toArray()) ->all(); - if (!$scores) { + if (! $scores) { return ['ok' => false, 'message' => 'No students found for this class section.']; } @@ -625,7 +623,7 @@ class ReportCardService } } - $filename = 'ClassReport_Section_' . $classSectionId . '.pdf'; + $filename = 'ClassReport_Section_'.$classSectionId.'.pdf'; $pdfContent = $pdf->Output('S'); return [ @@ -667,7 +665,7 @@ class ReportCardService ->orderBy('s.firstname', 'ASC') ->orderBy('s.lastname', 'ASC'); - if (!empty($schoolYear)) { + if (! empty($schoolYear)) { $query->where('student_class.school_year', $schoolYear); } @@ -683,6 +681,7 @@ class ReportCardService if ($s === 'spring' || $s === 'second' || str_contains($s, 'spring') || str_contains($s, '2')) { return 'spring'; } + return ''; } @@ -709,7 +708,7 @@ class ReportCardService private function applySemesterFilter($builder, ?string $semester, string $field = 'semester'): void { $vals = $this->semesterVariants($semester); - if (!empty($vals)) { + if (! empty($vals)) { $builder->whereIn($field, $vals); } } @@ -717,7 +716,7 @@ class ReportCardService private function applySemesterExclusion($builder, ?string $semester, string $field = 'semester'): void { $vals = $this->semesterVariants($semester); - if (!empty($vals)) { + if (! empty($vals)) { $builder->whereNotIn($field, $vals); } } @@ -761,6 +760,7 @@ class ReportCardService if ($weekday === 0) { return $now->format('Y-m-d'); } + return $now->modify('next sunday')->format('Y-m-d'); } @@ -788,13 +788,14 @@ class ReportCardService $sundays[] = $cursor->format('Y-m-d'); $cursor = $cursor->modify('+7 days'); } + return $sundays; } private function prepareStudentReportData(int $studentId, ?string $schoolYear = null, ?string $semester = null) { $student = Student::query()->find($studentId); - if (!$student || (int) ($student->is_active ?? 1) !== 1) { + if (! $student || (int) ($student->is_active ?? 1) !== 1) { return null; } @@ -815,7 +816,7 @@ class ReportCardService $this->applySemesterFilter($qb, $refSemester, 'semester'); } $score = $qb->orderBy('updated_at', 'DESC')->orderBy('id', 'DESC')->first(); - if (!$score) { + if (! $score) { return null; } $score = $score->toArray(); @@ -845,7 +846,7 @@ class ReportCardService } } - if (!$sectionId || $sectionCode <= 0) { + if (! $sectionId || $sectionCode <= 0) { try { $scRow = DB::table('student_class') ->select('class_section_id') @@ -854,7 +855,7 @@ class ReportCardService ->orderByRaw('COALESCE(updated_at, created_at) DESC') ->limit(1) ->first(); - if (!empty($scRow->class_section_id)) { + if (! empty($scRow->class_section_id)) { $sectionCode = (int) $scRow->class_section_id; $row = DB::table('classSection') ->select('id') @@ -887,7 +888,7 @@ class ReportCardService if (empty($taNames)) { $taNames = $names['ta_names']; } - if (empty($teacherNamesList) && !empty($names['teacher_names'])) { + if (empty($teacherNamesList) && ! empty($names['teacher_names'])) { $teacherNamesList = $names['teacher_names']; } } @@ -915,22 +916,22 @@ class ReportCardService if ($commentVal === '') { continue; } - if (!isset($commentMap[$typeRaw])) { + if (! isset($commentMap[$typeRaw])) { $commentMap[$typeRaw] = $commentVal; } } - if (isset($commentMap['attendance']) && !isset($score['attendance_comment'])) { + if (isset($commentMap['attendance']) && ! isset($score['attendance_comment'])) { $score['attendance_comment'] = $commentMap['attendance']; } $att = $score['attendance_score'] ?? null; $attendanceCommentVal = $commentMap['attendance'] ?? $score['attendance_comment'] ?? null; - if ((!is_string($attendanceCommentVal) || trim($attendanceCommentVal) === '') && is_numeric($att) && function_exists('attendance_comment_from_score')) { + if ((! is_string($attendanceCommentVal) || trim($attendanceCommentVal) === '') && is_numeric($att) && function_exists('attendance_comment_from_score')) { $autoAttendance = attendance_comment_from_score((float) $att, (string) ($student->firstname ?? '')); if (is_string($autoAttendance) && $autoAttendance !== '') { $commentMap['attendance'] = $autoAttendance; - if (!isset($score['attendance_comment'])) { + if (! isset($score['attendance_comment'])) { $score['attendance_comment'] = $autoAttendance; } } @@ -942,7 +943,7 @@ class ReportCardService if ($normSemester !== '') { $events = Calendar::getEventsBySchoolYearAndSemester($refYear, $normSemester); $dates = array_values(array_filter(array_map(static fn ($r) => (string) ($r['date'] ?? ''), $events))); - if (!empty($dates)) { + if (! empty($dates)) { $start = min($dates); $end = max($dates); $sundays = $this->listSundays($start, $end); @@ -967,7 +968,7 @@ class ReportCardService static fn ($r) => isset($r['total_attendance']) ? (int) $r['total_attendance'] : null, $attRows ), static fn ($v) => $v !== null)); - if (!empty($vals)) { + if (! empty($vals)) { $totalSemesterDays = max($vals); } } catch (\Throwable $e) { @@ -1024,7 +1025,8 @@ class ReportCardService $withPrefix = static function (array $row): string { $first = trim((string) ($row['firstname'] ?? '')); $last = trim((string) ($row['lastname'] ?? '')); - $name = trim($last . ', ' . $first, ' ,'); + $name = trim($last.', '.$first, ' ,'); + return $name !== '' ? $name : 'N/A'; }; $normalizePos = static function (?string $pos): string { @@ -1032,6 +1034,7 @@ class ReportCardService if ($pos === 'ta' || $pos === 'assistant') { return 'ta'; } + return 'main'; }; @@ -1065,7 +1068,7 @@ class ReportCardService $ids = [$sectionIdOrCode]; $rows = $runner($ids, (string) $schoolYear, (string) $semester); - if (!empty($rows)) { + if (! empty($rows)) { $consume($rows); } @@ -1120,6 +1123,7 @@ class ReportCardService if (is_numeric($val)) { return number_format((float) $val, 1); } + return (string) $val; }; $splitText = static function ($text, int $limit = 55): array { @@ -1129,6 +1133,7 @@ class ReportCardService } $lines = explode("\n", wordwrap($text, $limit)); $lines[] = ''; + return $lines; }; $drawLabeledCell = static function (\FPDF $pdf, string $label, string $value, float $width = 95) { @@ -1143,6 +1148,7 @@ class ReportCardService return $item; } } + return null; }; $wrapLinesToWidth = static function (\FPDF $pdf, string $text, float $width) { @@ -1153,18 +1159,19 @@ class ReportCardService if ($width <= 0) { return [$text]; } - $segments = preg_split("/\\r?\\n+/", $text); + $segments = preg_split('/\\r?\\n+/', $text); $lines = []; foreach ($segments as $seg) { $seg = trim($seg); if ($seg === '') { $lines[] = ' '; + continue; } $words = preg_split('/\\s+/u', $seg); $current = ''; foreach ($words as $word) { - $trial = trim($current === '' ? $word : ($current . ' ' . $word)); + $trial = trim($current === '' ? $word : ($current.' '.$word)); if ($pdf->GetStringWidth($trial) <= $width) { $current = $trial; } else { @@ -1178,6 +1185,7 @@ class ReportCardService $lines[] = $current; } } + return $lines; }; $draw3ColumnRow = static function (\FPDF $pdf, string $label, $score, $feedback) use ($wrapLinesToWidth) { @@ -1235,12 +1243,13 @@ class ReportCardService $seg = trim($seg); if ($seg === '') { $out[] = ' '; + continue; } $words = preg_split('/\\s+/u', $seg); $current = ''; foreach ($words as $word) { - $trial = $current === '' ? $word : ($current . ' ' . $word); + $trial = $current === '' ? $word : ($current.' '.$word); if ($pdf->GetStringWidth($trial) <= ($width - 2)) { $current = $trial; } else { @@ -1254,14 +1263,15 @@ class ReportCardService $out[] = $current; } } - return !empty($out) ? $out : [' ']; + + return ! empty($out) ? $out : [' ']; }; if ($startY !== null) { $pdf->SetY($startY); } foreach ($rows as $row) { - if (!is_array($row) || count($row) < 4) { + if (! is_array($row) || count($row) < 4) { continue; } [$labelLeft, $valueLeft, $labelRight, $valueRight] = $row; @@ -1316,6 +1326,7 @@ class ReportCardService $currentY += $rowHeight; $pdf->SetY($currentY); } + return $currentY; }; @@ -1332,7 +1343,7 @@ class ReportCardService } $teacherNamesList = $data['teacher_names'] ?? []; - if (empty($teacherNamesList) && !empty($data['teacher_name'])) { + if (empty($teacherNamesList) && ! empty($data['teacher_name'])) { $teacherNamesList = [trim((string) $data['teacher_name'])]; } $teacherNamesList = array_values(array_filter( @@ -1365,9 +1376,9 @@ class ReportCardService $schoolYear = (string) ($data['score']['school_year'] ?? ($data['score']['year'] ?? 'N/A')); if (preg_match('/^(\\d{4})-(\\d{4})$/', $schoolYear, $m)) { - $schoolYear = $m[1] . '/' . $m[2]; + $schoolYear = $m[1].'/'.$m[2]; } - $studentName = trim(($data['student']['firstname'] ?? '') . ' ' . ($data['student']['lastname'] ?? '')); + $studentName = trim(($data['student']['firstname'] ?? '').' '.($data['student']['lastname'] ?? '')); $gradeLabel = (string) ($data['grade'] ?? ($data['class_section_name'] ?? 'N/A')); $today = (string) ($data['report_date_display'] ?? date('m-d-Y')); $firstSemScore = $data['first_semester_score'] ?? null; @@ -1396,9 +1407,9 @@ class ReportCardService }; $afterInfoY = $drawInfoRows($pdf, [ - ["Teachers' Names: ", ' ' . ($teacherNames !== '' ? $teacherNames : 'N/A'), 'School Year: ', ' ' . $schoolYear], - ['Student Name: ', ' ' . ($studentName !== '' ? $studentName : 'N/A'), 'Grade: ', ' ' . ($gradeLabel !== '' ? $gradeLabel : 'N/A')], - ['Term: ', ' ' . $semFriendly, 'Date: ', ' ' . $today], + ["Teachers' Names: ", ' '.($teacherNames !== '' ? $teacherNames : 'N/A'), 'School Year: ', ' '.$schoolYear], + ['Student Name: ', ' '.($studentName !== '' ? $studentName : 'N/A'), 'Grade: ', ' '.($gradeLabel !== '' ? $gradeLabel : 'N/A')], + ['Term: ', ' '.$semFriendly, 'Date: ', ' '.$today], ]); $pdf->SetY($afterInfoY); @@ -1475,7 +1486,7 @@ class ReportCardService $pdf->Rect($x, $startY, $statW, $statH); $pdf->SetXY($x + 2, $startY + 3); $pdf->SetFont('Helvetica', 'B', 11); - $pdf->Write(5, $lbl . ' '); + $pdf->Write(5, $lbl.' '); $pdf->SetFont('Helvetica', '', 11); $pdf->Write(5, $val); } @@ -1483,7 +1494,7 @@ class ReportCardService $effectiveSecond = $secondSemScore ?? $data['score']['semester_score'] ?? $data['total_score'] ?? null; $finalScoreVal = $finalAverage; - if (!is_numeric($finalScoreVal)) { + if (! is_numeric($finalScoreVal)) { $finalScoreVal = (is_numeric($firstSemScore) && is_numeric($effectiveSecond)) ? number_format(((float) $firstSemScore + (float) $effectiveSecond) / 2, 1) : $numFmt($effectiveSecond); @@ -1498,8 +1509,8 @@ class ReportCardService $pdf->Rect($x, $y, $w, $h); $pdf->SetXY($x + $pad, $y + 3); $pdf->SetFont('Helvetica', 'B', 11); - $pdf->Write(5, $label . ' '); - $labelWidth = $pdf->GetStringWidth($label . ' '); + $pdf->Write(5, $label.' '); + $labelWidth = $pdf->GetStringWidth($label.' '); $pdf->SetFont('Helvetica', '', 12); $pdf->SetXY($x + 2 + $labelWidth, $y + 3); $pdf->Write(5, $value); @@ -1507,14 +1518,14 @@ class ReportCardService if ($semNum === 2) { $firstLabel = ' 1st Semester Grade:'; - $firstValue = $numFmt($firstSemScore) . '/100'; + $firstValue = $numFmt($firstSemScore).'/100'; $secondLabel = ' 2nd Semester Grade:'; - $secondValue = $numFmt($effectiveSecond) . '/100'; + $secondValue = $numFmt($effectiveSecond).'/100'; $drawGradeCell($pdf, $gradeX, $gradeY, $gradeCellWidth, $gradeCellHeight, $firstLabel, $firstValue, $gradePad); $drawGradeCell($pdf, $gradeX + $gradeCellWidth, $gradeY, $gradeCellWidth, $gradeCellHeight, $secondLabel, $secondValue, $gradePad); } else { $gradeLabel = ' 1st Semester Grade:'; - $gradeValue = $numFmt($effectiveSecond) . '/100'; + $gradeValue = $numFmt($effectiveSecond).'/100'; $drawGradeCell($pdf, $gradeX, $gradeY, $gradeCellWidth, $gradeCellHeight, $gradeLabel, $gradeValue, $gradePad); } $pdf->SetY($gradeY + $gradeCellHeight); @@ -1523,7 +1534,7 @@ class ReportCardService $finalScoreEndY = null; if ($semNum === 2) { $finalLabel = 'Final Score****:'; - $finalValue = (is_string($finalScoreVal) ? $finalScoreVal : $numFmt($finalScoreVal)) . '/100'; + $finalValue = (is_string($finalScoreVal) ? $finalScoreVal : $numFmt($finalScoreVal)).'/100'; $finalCellWidth = 65; $finalCellHeight = $finalScoreRowHeight; $finalX = $pdf->GetX(); @@ -1532,8 +1543,8 @@ class ReportCardService $finalPad = max(0, 2 - (4 * 0.3528)); $pdf->SetXY($finalX + $finalPad, $finalY + 3); $pdf->SetFont('Helvetica', 'B', 11); - $pdf->Write(5, ' ' . $finalLabel . ' '); - $finalLabelWidth = $pdf->GetStringWidth(' ' . $finalLabel . ' '); + $pdf->Write(5, ' '.$finalLabel.' '); + $finalLabelWidth = $pdf->GetStringWidth(' '.$finalLabel.' '); $pdf->SetFont('Helvetica', '', 12); $pdf->SetXY($finalX + 2 + $finalLabelWidth, $finalY + 3); $pdf->Write(5, $finalValue); @@ -1545,11 +1556,11 @@ class ReportCardService $examLegendLabel = ($semNum === 1) ? 'Midterm Exam' : 'Final Exam'; $legendLines = [ "(*) {$examLegendLabel} (Weight: 60% of semester score)", - "(**) PTAP - Participation, Tests, Assignments, Projects (Weight: 20%)", + '(**) PTAP - Participation, Tests, Assignments, Projects (Weight: 20%)', "(***) Attendance = ({$totalDays} - Total Absences + One Sick Day) / {$totalDays} ) x 100 (Weight: 20%)", ]; if ($semNum === 2) { - $legendLines[] = "(****) Final Score = (1st Semester Score + 2nd Semester Score) / 2"; + $legendLines[] = '(****) Final Score = (1st Semester Score + 2nd Semester Score) / 2'; } $legendLines[] = ['text' => 'If you have any questions, please contact the school via email at alrahma.isgl@gmail.com', 'bold' => true]; $lineHeight = 4; @@ -1608,6 +1619,7 @@ class ReportCardService return $path; } } + return null; } } diff --git a/app/Services/Reports/SlipPrinterConfigService.php b/app/Services/Reports/SlipPrinterConfigService.php index 3bed6d80..8700234a 100644 --- a/app/Services/Reports/SlipPrinterConfigService.php +++ b/app/Services/Reports/SlipPrinterConfigService.php @@ -20,7 +20,7 @@ class SlipPrinterConfigService if ($userId && $userId > 0) { $user = User::query()->select('firstname', 'lastname')->find($userId); if ($user) { - $full = trim(($user->firstname ?? '') . ' ' . ($user->lastname ?? '')); + $full = trim(($user->firstname ?? '').' '.($user->lastname ?? '')); if ($full !== '') { return $full; } @@ -40,6 +40,7 @@ class SlipPrinterConfigService return $val; } } + return $default; }; diff --git a/app/Services/Reports/SlipPrinterFormatterService.php b/app/Services/Reports/SlipPrinterFormatterService.php index fee957c4..b2ed7d5f 100644 --- a/app/Services/Reports/SlipPrinterFormatterService.php +++ b/app/Services/Reports/SlipPrinterFormatterService.php @@ -15,13 +15,16 @@ class SlipPrinterFormatterService } if (preg_match('/^\d{1,2}\/\d{1,2}\/\d{4}$/', $value)) { $dt = \DateTime::createFromFormat('!m/d/Y', $value); + return $dt ? $dt->format('Y-m-d') : null; } if (preg_match('/^\d{1,2}-\d{1,2}-\d{4}$/', $value)) { $dt = \DateTime::createFromFormat('!m-d-Y', $value); + return $dt ? $dt->format('Y-m-d') : null; } $ts = strtotime($value); + return $ts ? date('Y-m-d', $ts) : null; } @@ -32,6 +35,7 @@ class SlipPrinterFormatterService return null; } $ts = strtotime($value); + return $ts ? date('H:i:s', $ts) : null; } @@ -42,6 +46,7 @@ class SlipPrinterFormatterService return ''; } $ts = strtotime($value); + return $ts ? date('m/d/Y', $ts) : ''; } @@ -53,8 +58,9 @@ class SlipPrinterFormatterService } $ts = strtotime($value); if ($ts === false) { - $ts = strtotime('today ' . $value); + $ts = strtotime('today '.$value); } + return $ts ? date('h:i A', $ts) : ''; } @@ -64,17 +70,19 @@ class SlipPrinterFormatterService if (strlen($line) > $width) { return substr($line, 0, $width); } - return $line . str_repeat(' ', $width - strlen($line)); + + return $line.str_repeat(' ', $width - strlen($line)); } public function buildSlipLines(array $data, int $width): array { $lines = []; - $lines[] = $this->fitLine('Student Name: ' . (string) ($data['student_name'] ?? ''), $width); - $lines[] = $this->fitLine('Date: ' . (string) ($data['date'] ?? '') . ' Time In: ' . (string) ($data['time_in'] ?? ''), $width); - $lines[] = $this->fitLine('Grade: ' . (string) ($data['grade'] ?? ''), $width); - $lines[] = $this->fitLine('Reason: ' . (string) ($data['reason'] ?? ''), $width); - $lines[] = $this->fitLine('Admin: ' . (string) ($data['admin_name'] ?? ''), $width); + $lines[] = $this->fitLine('Student Name: '.(string) ($data['student_name'] ?? ''), $width); + $lines[] = $this->fitLine('Date: '.(string) ($data['date'] ?? '').' Time In: '.(string) ($data['time_in'] ?? ''), $width); + $lines[] = $this->fitLine('Grade: '.(string) ($data['grade'] ?? ''), $width); + $lines[] = $this->fitLine('Reason: '.(string) ($data['reason'] ?? ''), $width); + $lines[] = $this->fitLine('Admin: '.(string) ($data['admin_name'] ?? ''), $width); + return $lines; } } diff --git a/app/Services/Reports/SlipPrinterPdfService.php b/app/Services/Reports/SlipPrinterPdfService.php index e2bdd688..3c5a7380 100644 --- a/app/Services/Reports/SlipPrinterPdfService.php +++ b/app/Services/Reports/SlipPrinterPdfService.php @@ -8,9 +8,7 @@ use Dompdf\Options; class SlipPrinterPdfService { - public function __construct(private SlipPrinterConfigService $configService) - { - } + public function __construct(private SlipPrinterConfigService $configService) {} public function render(array $data, array $lines, array $options = []): array { @@ -63,7 +61,7 @@ class SlipPrinterPdfService 'h_mm' => $hMm, ]); - $optionsObj = new Options(); + $optionsObj = new Options; $optionsObj->set('isRemoteEnabled', true); $optionsObj->set('defaultFont', 'Courier'); @@ -76,7 +74,7 @@ class SlipPrinterPdfService $dompdf->setPaper([0, 0, $wPt, $hPt]); $dompdf->render(); - $filename = 'LateSlip_' . preg_replace('/[^A-Za-z0-9]+/', '_', (string) ($data['student_name'] ?? 'slip')) . '_' . date('Ymd_His') . '.pdf'; + $filename = 'LateSlip_'.preg_replace('/[^A-Za-z0-9]+/', '_', (string) ($data['student_name'] ?? 'slip')).'_'.date('Ymd_His').'.pdf'; return [ 'content' => $dompdf->output(), diff --git a/app/Services/Reports/SlipPrinterService.php b/app/Services/Reports/SlipPrinterService.php index 553e72b0..518ffc03 100644 --- a/app/Services/Reports/SlipPrinterService.php +++ b/app/Services/Reports/SlipPrinterService.php @@ -10,8 +10,7 @@ class SlipPrinterService private SlipPrinterConfigService $configService, private SlipPrinterFormatterService $formatter, private SlipPrinterPdfService $pdfService - ) { - } + ) {} public function print(array $input, ?int $userId): array { @@ -45,7 +44,7 @@ class SlipPrinterService return [ 'ok' => true, - 'text' => $header . $body . $footer, + 'text' => $header.$body.$footer, 'width' => $width, ]; } @@ -97,7 +96,7 @@ class SlipPrinterService public function reprint(int $id, ?int $userId): array { $row = LateSlipLog::query()->find($id); - if (!$row) { + if (! $row) { return ['ok' => false, 'message' => 'Slip not found.']; } @@ -173,6 +172,7 @@ class SlipPrinterService { $cfg = $this->configService->printerConfig(); $width = (int) ($cfg['chars_per_line'] ?? 48); + return $width > 0 ? $width : 48; } diff --git a/app/Services/Reports/Stickers/StickerPresetService.php b/app/Services/Reports/Stickers/StickerPresetService.php index c1a84c65..7e0093b3 100644 --- a/app/Services/Reports/Stickers/StickerPresetService.php +++ b/app/Services/Reports/Stickers/StickerPresetService.php @@ -14,9 +14,10 @@ class StickerPresetService ]; return array_map(static function (array $row): array { - $row['title'] = $row['title'] ?? ($row['label'] ?? ($row['value'] . ' mm')); + $row['title'] = $row['title'] ?? ($row['label'] ?? ($row['value'].' mm')); $row['text'] = $row['text'] ?? $row['title']; $row['per_page'] = $row['per_page'] ?? ($row['ppg'] ?? null); + return $row; }, $rows); } diff --git a/app/Services/Reports/Stickers/StickerPrintService.php b/app/Services/Reports/Stickers/StickerPrintService.php index f4b0ebf9..74278420 100644 --- a/app/Services/Reports/Stickers/StickerPrintService.php +++ b/app/Services/Reports/Stickers/StickerPrintService.php @@ -6,9 +6,7 @@ use Illuminate\Support\Facades\Log; class StickerPrintService { - public function __construct(private StickerQueryService $queryService) - { - } + public function __construct(private StickerQueryService $queryService) {} public function generate(array $payload): array { @@ -23,7 +21,7 @@ class StickerPrintService if ($studentId > 0) { $student = $this->queryService->findStudent($studentId); - if (!$student) { + if (! $student) { return ['ok' => false, 'message' => 'Student not found.']; } $students = [$student]; @@ -102,7 +100,7 @@ class StickerPrintService $minPt = 8; $hPad = 2; - $fullName = trim(($student['firstname'] ?? '') . ' ' . ($student['lastname'] ?? '')) ?: 'Student'; + $fullName = trim(($student['firstname'] ?? '').' '.($student['lastname'] ?? '')) ?: 'Student'; $bestPt = $maxPt; $textW = 0.0; @@ -158,6 +156,7 @@ class StickerPrintService for ($k = 0; $k < $qty; $k++) { $printList[] = $students[0]; } + return $printList; } @@ -221,19 +220,19 @@ class StickerPrintService || (abs($stickerWidth - 100.0) < 0.6 && abs($stickerHeight - 24.5) < 0.6); if ($isXSmallPreset) { - if (!$marginLeftProvided) { + if (! $marginLeftProvided) { $marginLeft = 4.0; - if (!$marginRightProvided) { + if (! $marginRightProvided) { $marginRight = 4.0; } } - if (!$marginRightProvided && $marginRight < 4.0) { + if (! $marginRightProvided && $marginRight < 4.0) { $marginRight = 4.0; } - if (!$gapXProvided) { + if (! $gapXProvided) { $gapX = 0.0; } - if (!$gapYProvided) { + if (! $gapYProvided) { $gapY = 0.0; } } diff --git a/app/Services/Reports/Stickers/StickerQueryService.php b/app/Services/Reports/Stickers/StickerQueryService.php index a363bacc..68a37376 100644 --- a/app/Services/Reports/Stickers/StickerQueryService.php +++ b/app/Services/Reports/Stickers/StickerQueryService.php @@ -6,9 +6,7 @@ use Illuminate\Support\Facades\DB; class StickerQueryService { - public function __construct(private StickerContextService $context) - { - } + public function __construct(private StickerContextService $context) {} public function resolveSchoolYear(?string $schoolYear): string { @@ -108,7 +106,8 @@ class StickerQueryService if ($sectionName === '') { return false; } - return !preg_match('/^(youth|kg)(?:\b|-)/i', $sectionName); + + return ! preg_match('/^(youth|kg)(?:\b|-)/i', $sectionName); })); } } diff --git a/app/Services/Roles/PermissionCrudService.php b/app/Services/Roles/PermissionCrudService.php index 6c57bdb4..5fa38fe1 100644 --- a/app/Services/Roles/PermissionCrudService.php +++ b/app/Services/Roles/PermissionCrudService.php @@ -17,7 +17,7 @@ class PermissionCrudService return Permission::query()->create($data); }); } catch (\Throwable $e) { - Log::error('Permission create failed: ' . $e->getMessage()); + Log::error('Permission create failed: '.$e->getMessage()); throw $e; } } @@ -30,10 +30,11 @@ class PermissionCrudService return DB::transaction(function () use ($permission, $data) { $permission->fill($data); $permission->save(); + return $permission; }); } catch (\Throwable $e) { - Log::error('Permission update failed: ' . $e->getMessage()); + Log::error('Permission update failed: '.$e->getMessage()); throw $e; } } @@ -45,7 +46,7 @@ class PermissionCrudService $permission->delete(); }); } catch (\Throwable $e) { - Log::error('Permission delete failed: ' . $e->getMessage()); + Log::error('Permission delete failed: '.$e->getMessage()); throw $e; } } diff --git a/app/Services/Roles/RoleAssignmentService.php b/app/Services/Roles/RoleAssignmentService.php index 8c55dd8e..6268e57f 100644 --- a/app/Services/Roles/RoleAssignmentService.php +++ b/app/Services/Roles/RoleAssignmentService.php @@ -3,7 +3,6 @@ namespace App\Services\Roles; use App\Models\Role; -use App\Models\Staff; use App\Models\User; use App\Models\UserRole; use Illuminate\Support\Facades\DB; @@ -11,9 +10,7 @@ use Illuminate\Support\Facades\Log; class RoleAssignmentService { - public function __construct(private RoleStaffService $staffService) - { - } + public function __construct(private RoleStaffService $staffService) {} /** @param int[] $roleIds */ public function assignRoles(int $userId, array $roleIds, ?int $adminId = null): array @@ -21,7 +18,7 @@ class RoleAssignmentService $roleIds = array_values(array_unique(array_filter(array_map('intval', $roleIds), fn ($id) => $id > 0))); $user = User::query()->find($userId); - if (!$user) { + if (! $user) { return ['ok' => false, 'message' => 'User not found.']; } @@ -38,7 +35,7 @@ class RoleAssignmentService } }); } catch (\Throwable $e) { - Log::error('Role assignment failed: ' . $e->getMessage()); + Log::error('Role assignment failed: '.$e->getMessage()); throw $e; } diff --git a/app/Services/Roles/RoleCrudService.php b/app/Services/Roles/RoleCrudService.php index a11b0570..4a629871 100644 --- a/app/Services/Roles/RoleCrudService.php +++ b/app/Services/Roles/RoleCrudService.php @@ -17,7 +17,7 @@ class RoleCrudService return Role::query()->create($data); }); } catch (\Throwable $e) { - Log::error('Role create failed: ' . $e->getMessage()); + Log::error('Role create failed: '.$e->getMessage()); throw $e; } } @@ -30,10 +30,11 @@ class RoleCrudService return DB::transaction(function () use ($role, $data) { $role->fill($data); $role->save(); + return $role; }); } catch (\Throwable $e) { - Log::error('Role update failed: ' . $e->getMessage()); + Log::error('Role update failed: '.$e->getMessage()); throw $e; } } @@ -45,7 +46,7 @@ class RoleCrudService $role->delete(); }); } catch (\Throwable $e) { - Log::error('Role delete failed: ' . $e->getMessage()); + Log::error('Role delete failed: '.$e->getMessage()); throw $e; } } @@ -57,7 +58,7 @@ class RoleCrudService $route = trim((string) ($payload['dashboard_route'] ?? '')); $desc = trim((string) ($payload['description'] ?? '')); $priority = (int) ($payload['priority'] ?? 0); - $isActive = !empty($payload['is_active']) ? 1 : 0; + $isActive = ! empty($payload['is_active']) ? 1 : 0; $name = strtolower($rawName); $dashboardRoute = strtolower($route); @@ -92,11 +93,11 @@ class RoleCrudService $query->where('id', '!=', $ignoreId); } - if (!$query->exists()) { + if (! $query->exists()) { return $slug; } - $slug = $baseSlug . '_' . $i; + $slug = $baseSlug.'_'.$i; $i++; if ($i > 100000) { diff --git a/app/Services/Roles/RoleDashboardService.php b/app/Services/Roles/RoleDashboardService.php index f5a6f119..74406ff6 100644 --- a/app/Services/Roles/RoleDashboardService.php +++ b/app/Services/Roles/RoleDashboardService.php @@ -10,7 +10,7 @@ class RoleDashboardService public function bestDashboardRouteFor(array $roleKeys): string { $rows = Role::findByNamesOrSlugs($roleKeys); - if (!empty($rows)) { + if (! empty($rows)) { return $rows[0]->dashboard_route ?? '/landing_page/guest_dashboard'; } diff --git a/app/Services/Roles/RolePermissionService.php b/app/Services/Roles/RolePermissionService.php index c299adc3..4d01060d 100644 --- a/app/Services/Roles/RolePermissionService.php +++ b/app/Services/Roles/RolePermissionService.php @@ -58,11 +58,11 @@ class RolePermissionService } $data = [ - 'can_create' => !empty($crudValues['create']) ? 1 : 0, - 'can_read' => !empty($crudValues['read']) ? 1 : 0, - 'can_update' => !empty($crudValues['update']) ? 1 : 0, - 'can_delete' => !empty($crudValues['delete']) ? 1 : 0, - 'can_manage' => !empty($crudValues['manage']) ? 1 : 0, + 'can_create' => ! empty($crudValues['create']) ? 1 : 0, + 'can_read' => ! empty($crudValues['read']) ? 1 : 0, + 'can_update' => ! empty($crudValues['update']) ? 1 : 0, + 'can_delete' => ! empty($crudValues['delete']) ? 1 : 0, + 'can_manage' => ! empty($crudValues['manage']) ? 1 : 0, ]; if ($existing->has($permissionId)) { @@ -79,7 +79,7 @@ class RolePermissionService } foreach ($existing as $permissionId => $row) { - if (!array_key_exists((string) $permissionId, $permissions) && !array_key_exists((int) $permissionId, $permissions)) { + if (! array_key_exists((string) $permissionId, $permissions) && ! array_key_exists((int) $permissionId, $permissions)) { RolePermission::query() ->where('role_id', $roleId) ->where('permission_id', $permissionId) @@ -88,7 +88,7 @@ class RolePermissionService } }); } catch (\Throwable $e) { - Log::error('Role permission save failed: ' . $e->getMessage()); + Log::error('Role permission save failed: '.$e->getMessage()); throw $e; } } diff --git a/app/Services/Roles/RoleQueryService.php b/app/Services/Roles/RoleQueryService.php index aba59176..b4a2c60f 100644 --- a/app/Services/Roles/RoleQueryService.php +++ b/app/Services/Roles/RoleQueryService.php @@ -40,19 +40,19 @@ class RoleQueryService 'roles.updated_at', ]); - if (!empty($filters['search'])) { + if (! empty($filters['search'])) { $search = trim((string) $filters['search']); $query->where(function (Builder $q) use ($search) { - $q->where('roles.name', 'like', '%' . $search . '%') - ->orWhere('roles.slug', 'like', '%' . $search . '%') - ->orWhere('roles.description', 'like', '%' . $search . '%'); + $q->where('roles.name', 'like', '%'.$search.'%') + ->orWhere('roles.slug', 'like', '%'.$search.'%') + ->orWhere('roles.description', 'like', '%'.$search.'%'); }); } $sortBy = (string) ($filters['sort_by'] ?? 'name'); $sortDir = strtolower((string) ($filters['sort_dir'] ?? 'asc')) === 'desc' ? 'desc' : 'asc'; $allowed = ['name', 'slug', 'priority', 'is_active', 'created_at']; - if (!in_array($sortBy, $allowed, true)) { + if (! in_array($sortBy, $allowed, true)) { $sortBy = 'name'; } @@ -80,26 +80,26 @@ class RoleQueryService ) ->groupBy('users.id', 'users.firstname', 'users.lastname', 'users.email', 'users.account_id'); - if (!empty($filters['search'])) { + if (! empty($filters['search'])) { $search = trim((string) $filters['search']); $query->where(function ($q) use ($search) { - $q->where('users.firstname', 'like', '%' . $search . '%') - ->orWhere('users.lastname', 'like', '%' . $search . '%') - ->orWhere('users.email', 'like', '%' . $search . '%') - ->orWhere('users.account_id', 'like', '%' . $search . '%'); + $q->where('users.firstname', 'like', '%'.$search.'%') + ->orWhere('users.lastname', 'like', '%'.$search.'%') + ->orWhere('users.email', 'like', '%'.$search.'%') + ->orWhere('users.account_id', 'like', '%'.$search.'%'); }); } $sortBy = (string) ($filters['sort_by'] ?? 'lastname'); $sortDir = strtolower((string) ($filters['sort_dir'] ?? 'asc')) === 'desc' ? 'desc' : 'asc'; $allowed = ['firstname', 'lastname', 'email', 'account_id']; - if (!in_array($sortBy, $allowed, true)) { + if (! in_array($sortBy, $allowed, true)) { $sortBy = 'lastname'; } $perPage = (int) ($filters['per_page'] ?? 20); $perPage = $perPage > 0 ? $perPage : 20; - return $query->orderBy('users.' . $sortBy, $sortDir)->paginate($perPage); + return $query->orderBy('users.'.$sortBy, $sortDir)->paginate($perPage); } } diff --git a/app/Services/Roles/RoleStaffService.php b/app/Services/Roles/RoleStaffService.php index 17473e0b..abf1d0d1 100644 --- a/app/Services/Roles/RoleStaffService.php +++ b/app/Services/Roles/RoleStaffService.php @@ -16,9 +16,9 @@ class RoleStaffService static fn ($role) => trim((string) $role), $newRoleNames ), - static fn ($role) => $role !== '' && !in_array(strtolower($role), $excludedRoles, true) + static fn ($role) => $role !== '' && ! in_array(strtolower($role), $excludedRoles, true) ))); - $activeRole = !empty($staffRoles) ? strtolower((string) $staffRoles[0]) : 'inactive'; + $activeRole = ! empty($staffRoles) ? strtolower((string) $staffRoles[0]) : 'inactive'; $email = $this->generateUniqueStaffEmail($user->firstname ?? '', $user->lastname ?? '', (int) $user->id); @@ -34,7 +34,7 @@ class RoleStaffService ]; $saved = Staff::upsertStaff($payload); - if (!$saved) { + if (! $saved) { Log::warning('Failed to upsert staff record.', ['user_id' => $user->id]); } } @@ -50,7 +50,7 @@ class RoleStaffService $out = ''; foreach ($roles as $role) { - $next = $out === '' ? $role : $out . ', ' . $role; + $next = $out === '' ? $role : $out.', '.$role; if (strlen($next) > $maxLength) { break; } @@ -73,18 +73,18 @@ class RoleStaffService $email = ''; for ($i = $min; $i <= $max; $i++) { - $base = substr($firstname, 0, $i) . $lastname; - $email = $base . $domain; + $base = substr($firstname, 0, $i).$lastname; + $email = $base.$domain; $exists = Staff::query() ->where('email', $email) ->where('user_id', '!=', $userId) ->exists(); - if (!$exists) { + if (! $exists) { return $email; } } - return $base . $userId . $domain; + return $base.$userId.$domain; } } diff --git a/app/Services/Roles/RoleSwitchService.php b/app/Services/Roles/RoleSwitchService.php index da2c05bf..52fdbb32 100644 --- a/app/Services/Roles/RoleSwitchService.php +++ b/app/Services/Roles/RoleSwitchService.php @@ -8,13 +8,12 @@ use Symfony\Component\HttpKernel\Exception\NotFoundHttpException; class RoleSwitchService { - public function __construct(private RoleDashboardService $dashboardService) - { - } + public function __construct(private RoleDashboardService $dashboardService) {} public function getUserRoleNames(int $userId): array { $roles = UserRole::getRolesByUserId($userId); + return array_values(array_filter(array_map(fn ($r) => (string) ($r['role_name'] ?? ''), $roles))); } diff --git a/app/Services/School/AccountEventService.php b/app/Services/School/AccountEventService.php index 36f4f24e..0cc05b98 100644 --- a/app/Services/School/AccountEventService.php +++ b/app/Services/School/AccountEventService.php @@ -20,8 +20,7 @@ class AccountEventService public function __construct( private EmailService $emailService, private UserNotificationDispatchService $notifier - ) { - } + ) {} public function newAccountAdded(array $userData): array { @@ -29,7 +28,7 @@ class AccountEventService $message = $this->renderEmail('emails/welcome_user', ['user' => $userData], $subject); $sent = false; - if (!empty($userData['email'])) { + if (! empty($userData['email'])) { $sent = $this->emailService->send( (string) $userData['email'], $subject, @@ -41,7 +40,7 @@ class AccountEventService $supportMessage = $this->renderEmail('emails/support_new_account', ['user' => $userData], $subject); $this->emailService->send( 'support@alrahmaisgl.org', - 'New Account Created: ' . trim((string) ($userData['firstname'] ?? '') . ' ' . (string) ($userData['lastname'] ?? '')), + 'New Account Created: '.trim((string) ($userData['firstname'] ?? '').' '.(string) ($userData['lastname'] ?? '')), $supportMessage, 'notifications' ); @@ -53,13 +52,14 @@ class AccountEventService public function studentRegistered(array $data): array { - if (empty($data['parents']) || !is_array($data['parents'])) { + if (empty($data['parents']) || ! is_array($data['parents'])) { Log::warning('StudentRegistered: missing parent data'); + return ['ok' => false, 'message' => 'Missing parent data.']; } - $studentFullName = trim((string) ($data['firstname'] ?? '') . ' ' . (string) ($data['lastname'] ?? '')); - if (!empty($data['parents']['user_id'])) { + $studentFullName = trim((string) ($data['firstname'] ?? '').' '.(string) ($data['lastname'] ?? '')); + if (! empty($data['parents']['user_id'])) { $title = 'Your child has been registered'; $msg = "{$studentFullName} has been successfully registered to Al Rahma Sunday School."; $this->notifier->notifyUser((int) $data['parents']['user_id'], $title, $msg, ['in_app', 'email'], 'registration', 'parent'); @@ -100,18 +100,21 @@ class AccountEventService public function userRegistered(array $user): array { $this->notifier->notifyUser((int) ($user['id'] ?? 0), 'Welcome!', 'Thanks for registering with our school system.', ['in_app', 'email'], 'notifications', 'parent'); + return ['ok' => true]; } public function userProfileUpdated(array $user): array { $this->notifier->notifyUser((int) ($user['id'] ?? 0), 'Profile Updated', 'Your profile information was successfully updated.', ['in_app'], 'notifications', 'parent'); + return ['ok' => true]; } public function userDeactivated(array $user): array { $this->notifier->notifyUser((int) ($user['id'] ?? 0), 'Account Deactivated', 'Your account has been deactivated by the administrator.', ['in_app', 'email'], 'notifications', 'parent'); + return ['ok' => true]; } @@ -120,6 +123,7 @@ class AccountEventService foreach ((array) ($data['students'] ?? []) as $student) { $this->notifier->notifyUser((int) ($student['id'] ?? 0), 'New Score Available', 'A new score has been posted.', ['in_app', 'email'], 'registration', 'student'); } + return ['ok' => true]; } @@ -136,21 +140,24 @@ class AccountEventService public function studentMarkedAbsent(array $record): array { - $msg = (string) ($record['student_name'] ?? 'Student') . ' was absent on ' . (string) ($record['date'] ?? ''); + $msg = (string) ($record['student_name'] ?? 'Student').' was absent on '.(string) ($record['date'] ?? ''); $this->notifier->notifyUser((int) ($record['parent_id'] ?? 0), 'Absence Notification', $msg, ['in_app', 'email'], 'registration', 'parent'); + return ['ok' => true]; } public function studentMarkedLate(array $record): array { - $msg = (string) ($record['student_name'] ?? 'Student') . ' was late on ' . (string) ($record['date'] ?? ''); + $msg = (string) ($record['student_name'] ?? 'Student').' was late on '.(string) ($record['date'] ?? ''); $this->notifier->notifyUser((int) ($record['parent_id'] ?? 0), 'Late Notification', $msg, ['in_app', 'email'], 'registration', 'parent'); + return ['ok' => true]; } public function paymentMissed(array $user): array { $this->notifier->notifyUser((int) ($user['id'] ?? 0), 'Payment Missed', 'You have missed a scheduled payment. Please settle your balance.', ['in_app', 'email'], 'payment', 'parent'); + return ['ok' => true]; } @@ -159,13 +166,15 @@ class AccountEventService foreach ((array) ($data['students'] ?? []) as $student) { $this->notifier->notifyUser((int) ($student['id'] ?? 0), 'Schedule Update', 'Your class schedule has been updated.', ['in_app'], 'notifications', 'student'); } + return ['ok' => true]; } public function newMessageReceived(array $data): array { - $msg = 'You have a new message from ' . (string) ($data['sender_name'] ?? ''); + $msg = 'You have a new message from '.(string) ($data['sender_name'] ?? ''); $this->notifier->notifyUser((int) ($data['recipient_id'] ?? 0), 'New Message Received', $msg, ['in_app'], 'notifications', 'parent'); + return ['ok' => true]; } @@ -180,6 +189,7 @@ class AccountEventService foreach ($users as $user) { $this->notifier->notifyUser((int) ($user['id'] ?? 0), (string) ($data['title'] ?? ''), (string) ($data['message'] ?? ''), ['in_app'], 'notifications', $targetGroup); } + return ['ok' => true]; } @@ -205,10 +215,10 @@ class AccountEventService } DB::transaction(function () use ($userData, $userId): void { - $familyCode = 'FAM-' . $userId; + $familyCode = 'FAM-'.$userId; $family = Family::query()->where('family_code', $familyCode)->first(); - if (!$family) { - $householdName = trim('Family of ' . ((string) ($userData['firstname'] ?? '') . ' ' . (string) ($userData['lastname'] ?? ''))); + if (! $family) { + $householdName = trim('Family of '.((string) ($userData['firstname'] ?? '').' '.(string) ($userData['lastname'] ?? ''))); $family = Family::query()->create([ 'family_code' => $familyCode, 'household_name' => $householdName, @@ -252,12 +262,12 @@ class AccountEventService { try { $currentYear = (string) Configuration::getConfigValueByKey('school_year'); - if (!preg_match('/^(\d{4})-(\d{4})$/', $currentYear, $m)) { + if (! preg_match('/^(\d{4})-(\d{4})$/', $currentYear, $m)) { return; } $y1 = (int) $m[1]; $y2 = (int) $m[2]; - $nextYear = ($y1 + 1) . '-' . ($y2 + 1); + $nextYear = ($y1 + 1).'-'.($y2 + 1); $rows = DB::table('students as s') ->select('s.id as student_id', 'sc.class_section_id', 'fall.semester_score as fall_score', 'spring.semester_score as spring_score') @@ -309,7 +319,7 @@ class AccountEventService ]); } } catch (\Throwable $e) { - Log::error('Promotion queue build failed: ' . $e->getMessage()); + Log::error('Promotion queue build failed: '.$e->getMessage()); } } @@ -317,6 +327,6 @@ class AccountEventService { Log::debug('Skipping Blade email template', ['view' => $viewName]); - return '

' . e($fallbackTitle) . '

'; + return '

'.e($fallbackTitle).'

'; } } diff --git a/app/Services/School/EnrollmentEventService.php b/app/Services/School/EnrollmentEventService.php index b61719cb..8ef92418 100644 --- a/app/Services/School/EnrollmentEventService.php +++ b/app/Services/School/EnrollmentEventService.php @@ -13,8 +13,7 @@ class EnrollmentEventService private EmailService $emailService, private UserNotificationDispatchService $notifier, private ApplicationUrlService $urls, - ) { - } + ) {} public function admissionUnderReview(array $parentData, array $students): array { @@ -179,7 +178,7 @@ class EnrollmentEventService 'parentName' => $parentName, 'studentName' => $studentName, 'withdrawalDate' => $parentData['withdrawal_date'] ?? ($parentData['date'] ?? ''), - 'withdrawals' => !empty($withdrawals) ? $withdrawals : null, + 'withdrawals' => ! empty($withdrawals) ? $withdrawals : null, 'portalLink' => $parentData['portalLink'] ?? $this->urls->webLoginUrl(), 'title' => 'Withdrawal Confirmed', ]; @@ -278,16 +277,16 @@ class EnrollmentEventService private function parentName(array $parentData): string { - return trim((string) ($parentData['firstname'] ?? '') . ' ' . (string) ($parentData['lastname'] ?? '')); + return trim((string) ($parentData['firstname'] ?? '').' '.(string) ($parentData['lastname'] ?? '')); } private function studentNameData(array $studentdata, array $parentData) { $names = []; foreach ($studentdata as $s) { - $nm = !empty($s['name']) + $nm = ! empty($s['name']) ? trim((string) $s['name']) - : trim((string) ($s['firstname'] ?? '') . ' ' . (string) ($s['lastname'] ?? '')); + : trim((string) ($s['firstname'] ?? '').' '.(string) ($s['lastname'] ?? '')); if ($nm !== '') { $names[] = $nm; } @@ -299,6 +298,7 @@ class EnrollmentEventService if (count($names) === 1) { return $names[0]; } + return $names; } @@ -306,9 +306,9 @@ class EnrollmentEventService { $names = []; foreach ($studentdata as $s) { - $nm = !empty($s['name']) + $nm = ! empty($s['name']) ? trim((string) $s['name']) - : trim((string) ($s['firstname'] ?? '') . ' ' . (string) ($s['lastname'] ?? '')); + : trim((string) ($s['firstname'] ?? '').' '.(string) ($s['lastname'] ?? '')); if ($nm !== '') { $names[] = $nm; } @@ -321,19 +321,20 @@ class EnrollmentEventService return $names[0]; } if (count($names) === 2) { - return $names[0] . ' and ' . $names[1]; + return $names[0].' and '.$names[1]; } $last = array_pop($names); - return implode(', ', $names) . ', and ' . $last; + + return implode(', ', $names).', and '.$last; } private function studentDetails(array $studentdata): array { $details = []; foreach ($studentdata as $s) { - $nm = !empty($s['name']) + $nm = ! empty($s['name']) ? trim((string) $s['name']) - : trim((string) ($s['firstname'] ?? '') . ' ' . (string) ($s['lastname'] ?? '')); + : trim((string) ($s['firstname'] ?? '').' '.(string) ($s['lastname'] ?? '')); if ($nm === '') { continue; } @@ -345,6 +346,7 @@ class EnrollmentEventService 'startDate' => $s['start_date'] ?? '', ]; } + return $details; } @@ -352,9 +354,9 @@ class EnrollmentEventService { $withdrawals = []; foreach ($studentdata as $s) { - $nm = !empty($s['name']) + $nm = ! empty($s['name']) ? trim((string) $s['name']) - : trim((string) ($s['firstname'] ?? '') . ' ' . (string) ($s['lastname'] ?? '')); + : trim((string) ($s['firstname'] ?? '').' '.(string) ($s['lastname'] ?? '')); if ($nm === '') { continue; } @@ -366,6 +368,7 @@ class EnrollmentEventService ]; } } + return $withdrawals; } @@ -373,9 +376,9 @@ class EnrollmentEventService { $rows = []; foreach ($studentdata as $s) { - $nm = !empty($s['name']) + $nm = ! empty($s['name']) ? trim((string) $s['name']) - : trim((string) ($s['firstname'] ?? '') . ' ' . (string) ($s['lastname'] ?? '')); + : trim((string) ($s['firstname'] ?? '').' '.(string) ($s['lastname'] ?? '')); if ($nm === '') { continue; } @@ -384,6 +387,7 @@ class EnrollmentEventService 'reason' => $s['reason'] ?? null, ]; } + return $rows; } @@ -399,13 +403,15 @@ class EnrollmentEventService { if ($to === '') { Log::warning('Enrollment event: missing recipient email', ['subject' => $subject]); + return ['ok' => false, 'message' => 'Missing recipient email.']; } $ok = $this->emailService->send($to, $subject, $html, $fromKey); - if (!$ok) { + if (! $ok) { Log::error('Enrollment event email failed', ['to' => $to, 'subject' => $subject]); } + return ['ok' => $ok]; } @@ -413,6 +419,6 @@ class EnrollmentEventService { Log::debug('Skipping Blade email template', ['view' => $viewName]); - return '

' . e($fallbackTitle) . '

'; + return '

'.e($fallbackTitle).'

'; } } diff --git a/app/Services/School/PaymentEventService.php b/app/Services/School/PaymentEventService.php index 23e84fd4..eb23383c 100644 --- a/app/Services/School/PaymentEventService.php +++ b/app/Services/School/PaymentEventService.php @@ -13,21 +13,21 @@ class PaymentEventService private EmailService $emailService, private UserNotificationDispatchService $notifier, private ApplicationUrlService $urls, - ) { - } + ) {} public function paymentReceived(array $data, array $studentdata = []): array { - $parentName = trim((string) ($data['firstname'] ?? '') . ' ' . (string) ($data['lastname'] ?? '')); + $parentName = trim((string) ($data['firstname'] ?? '').' '.(string) ($data['lastname'] ?? '')); - $fmtMoney = static fn ($v) => '$' . number_format((float) $v, 2); + $fmtMoney = static fn ($v) => '$'.number_format((float) $v, 2); $fmtDateTz = static function (?string $s): string { - if (!$s) { + if (! $s) { return ''; } $dt = new \DateTime($s, new \DateTimeZone('UTC')); $tzName = (string) (config('School')->attendance['timezone'] ?? user_timezone()); $dt->setTimezone(new \DateTimeZone($tzName ?: 'UTC')); + return $dt->format('m-d-Y h:i A'); }; @@ -94,30 +94,33 @@ class PaymentEventService public function extraCharge(array $data, array $studentdata = []): array { - $fmtMoney = static fn ($v) => '$' . number_format((float) $v, 2); + $fmtMoney = static fn ($v) => '$'.number_format((float) $v, 2); $fmtFromUtc = static function (?string $s, string $fmt = 'm-d-Y h:i A'): string { - if (!$s) { + if (! $s) { return ''; } try { $dt = new \DateTime($s, new \DateTimeZone('UTC')); $tzName = (string) (config('School')->attendance['timezone'] ?? user_timezone()); $dt->setTimezone(new \DateTimeZone($tzName ?: 'UTC')); + return $dt->format($fmt); } catch (\Throwable $e) { - Log::error('extraCharge date error: ' . $e->getMessage()); + Log::error('extraCharge date error: '.$e->getMessage()); + return ''; } }; $fmtDueLocal = static function (?string $s): string { - if (!$s) { + if (! $s) { return ''; } try { if (strlen($s) <= 10) { $tzName = (string) (config('School')->attendance['timezone'] ?? user_timezone()); $dt = \DateTime::createFromFormat('Y-m-d', $s, new \DateTimeZone($tzName ?: 'UTC')); + return $dt ? $dt->format('m-d-Y') : ''; } @@ -125,19 +128,22 @@ class PaymentEventService $dt = new \DateTime($s); $tzName = (string) (config('School')->attendance['timezone'] ?? user_timezone()); $dt->setTimezone(new \DateTimeZone($tzName ?: 'UTC')); + return $dt->format('m-d-Y h:i A'); } $tzName = (string) (config('School')->attendance['timezone'] ?? user_timezone()); $dt = new \DateTime($s, new \DateTimeZone($tzName ?: 'UTC')); + return $dt->format('m-d-Y h:i A'); } catch (\Throwable $e) { - Log::error('extraCharge due date error: ' . $e->getMessage()); + Log::error('extraCharge due date error: '.$e->getMessage()); + return ''; } }; - $parentName = trim((string) ($data['firstname'] ?? '') . ' ' . (string) ($data['lastname'] ?? '')); + $parentName = trim((string) ($data['firstname'] ?? '').' '.(string) ($data['lastname'] ?? '')); $amountSigned = (float) ($data['amount_signed'] ?? 0); $amountAbs = (float) ($data['amount_abs'] ?? abs($amountSigned)); @@ -189,6 +195,7 @@ class PaymentEventService ); $body = $this->renderEmail('emails/extra_charge_notice', $emailData, 'Account Charge Update'); + return $this->sendEmail((string) ($data['email'] ?? ''), $subject, $body, 'billing'); } @@ -204,13 +211,15 @@ class PaymentEventService { if ($to === '') { Log::warning('Payment event missing recipient email', ['subject' => $subject]); + return ['ok' => false, 'message' => 'Missing recipient email.']; } $ok = $this->emailService->send($to, $subject, $html, $fromKey); - if (!$ok) { + if (! $ok) { Log::error('Payment event email failed', ['to' => $to, 'subject' => $subject]); } + return ['ok' => $ok]; } @@ -218,6 +227,6 @@ class PaymentEventService { Log::debug('Skipping Blade email template', ['view' => $viewName]); - return '

' . e($fallbackTitle) . '

'; + return '

'.e($fallbackTitle).'

'; } } diff --git a/app/Services/SchoolIdService.php b/app/Services/SchoolIdService.php index 390f1233..32af8e4b 100644 --- a/app/Services/SchoolIdService.php +++ b/app/Services/SchoolIdService.php @@ -10,7 +10,7 @@ class SchoolIdService public function assignSchoolIdToUser(int $userId): ?string { $user = User::query()->find($userId); - if (!$user) { + if (! $user) { return null; } @@ -36,7 +36,7 @@ class SchoolIdService ->where('school_id', 'like', "STU{$year}%") ->first(); - if (!empty($row?->max_id)) { + if (! empty($row?->max_id)) { $numericPart = substr((string) $row->max_id, 5); $maxId = (int) $numericPart; } @@ -57,7 +57,7 @@ class SchoolIdService ->where('school_id', 'like', "{$year}%") ->first(); - if (!empty($row?->max_id)) { + if (! empty($row?->max_id)) { $numericPart = substr((string) $row->max_id, 2); $maxId = max($maxId, (int) $numericPart); } diff --git a/app/Services/SchoolIds/SchoolIdAssignmentService.php b/app/Services/SchoolIds/SchoolIdAssignmentService.php index c0faaba3..951bd5e5 100644 --- a/app/Services/SchoolIds/SchoolIdAssignmentService.php +++ b/app/Services/SchoolIds/SchoolIdAssignmentService.php @@ -6,9 +6,7 @@ use App\Services\SchoolIdService; class SchoolIdAssignmentService { - public function __construct(private SchoolIdService $schoolIdService) - { - } + public function __construct(private SchoolIdService $schoolIdService) {} public function assignToUser(int $userId): ?string { diff --git a/app/Services/SchoolIds/SchoolIdGenerationService.php b/app/Services/SchoolIds/SchoolIdGenerationService.php index 79a02e11..3f1be635 100644 --- a/app/Services/SchoolIds/SchoolIdGenerationService.php +++ b/app/Services/SchoolIds/SchoolIdGenerationService.php @@ -6,9 +6,7 @@ use App\Services\SchoolIdService; class SchoolIdGenerationService { - public function __construct(private SchoolIdService $schoolIdService) - { - } + public function __construct(private SchoolIdService $schoolIdService) {} public function generateStudentId(): string { diff --git a/app/Services/SchoolYears/SchoolYearClosureService.php b/app/Services/SchoolYears/SchoolYearClosureService.php index 6c10dc74..d494acbf 100644 --- a/app/Services/SchoolYears/SchoolYearClosureService.php +++ b/app/Services/SchoolYears/SchoolYearClosureService.php @@ -8,7 +8,6 @@ use App\Models\ParentAccount; use App\Models\ParentBalanceTransfer; use App\Models\SchoolYear; use App\Services\Promotions\LevelProgressionService; -use Illuminate\Support\Collection; use Illuminate\Support\Facades\DB; use Illuminate\Support\Facades\Schema; use Illuminate\Validation\ValidationException; @@ -19,8 +18,7 @@ class SchoolYearClosureService public function __construct( private SchoolYearResolver $resolver, private LevelProgressionService $progression - ) { - } + ) {} public function list(): array { @@ -235,7 +233,7 @@ class SchoolYearClosureService { $year = $this->findOrFail($id); $validation = $this->validateClose($id, $payload); - if (!$validation['can_close']) { + if (! $validation['can_close']) { throw ValidationException::withMessages(['school_year' => $validation['errors']]); } @@ -249,7 +247,7 @@ class SchoolYearClosureService $newYearPayload = $payload['new_school_year']; $promotionPreview = $this->buildPromotionPreview($year->name, $newYearPayload['name']); - $balances = !empty($payload['transfer_unpaid_balances']) + $balances = ! empty($payload['transfer_unpaid_balances']) ? $this->buildParentBalanceRows($year->name, $newYearPayload['name']) : ['rows' => [], 'summary' => ['net_balance_to_transfer' => 0]]; @@ -291,7 +289,7 @@ class SchoolYearClosureService Configuration::setConfigValueByKey('school_year', $newYear->name); $promotionCounts = $this->applyPromotions($promotionPreview['rows'], $newYear->name, $newYear->start_date, $actorId); - $balanceCounts = !empty($payload['transfer_unpaid_balances']) + $balanceCounts = ! empty($payload['transfer_unpaid_balances']) ? $this->applyBalanceTransfers($balances['rows'], $year->name, $newYear->name, $actorId) : [ 'transferred_parents' => 0, @@ -396,8 +394,9 @@ class SchoolYearClosureService $errors[] = 'New school year name is required.'; } - if (!$startDate || !$endDate) { + if (! $startDate || ! $endDate) { $errors[] = 'New school year start_date and end_date are required.'; + return $errors; } @@ -457,7 +456,7 @@ class SchoolYearClosureService ]; foreach ($tables as $mapping) { - if (!Schema::hasTable($mapping['table'])) { + if (! Schema::hasTable($mapping['table'])) { continue; } if (DB::table($mapping['table'])->where($mapping['column'], $schoolYear)->exists()) { @@ -490,7 +489,7 @@ class SchoolYearClosureService $assignmentByStudent = []; foreach ($assignments as $assignment) { $studentId = (int) $assignment->student_id; - if (!isset($assignmentByStudent[$studentId])) { + if (! isset($assignmentByStudent[$studentId])) { $assignmentByStudent[$studentId] = $assignment; } } @@ -506,7 +505,7 @@ class SchoolYearClosureService $enrollmentByStudent = []; foreach ($enrollments as $enrollment) { $studentId = (int) $enrollment->student_id; - if (!isset($enrollmentByStudent[$studentId])) { + if (! isset($enrollmentByStudent[$studentId])) { $enrollmentByStudent[$studentId] = $enrollment; } } @@ -521,7 +520,7 @@ class SchoolYearClosureService $decisionByStudent = []; foreach ($decisions as $decision) { $studentId = (int) $decision->student_id; - if (!isset($decisionByStudent[$studentId])) { + if (! isset($decisionByStudent[$studentId])) { $decisionByStudent[$studentId] = $decision; } } @@ -539,7 +538,7 @@ class SchoolYearClosureService foreach ($studentIds as $studentId) { $student = $students->get($studentId); - if (!$student) { + if (! $student) { continue; } @@ -599,7 +598,7 @@ class SchoolYearClosureService $row = [ 'student_id' => $studentId, - 'student_name' => trim((string) ($student->firstname ?? '') . ' ' . (string) ($student->lastname ?? '')), + 'student_name' => trim((string) ($student->firstname ?? '').' '.(string) ($student->lastname ?? '')), 'parent_id' => (int) ($student->parent_id ?? 0) ?: null, 'current_grade' => $progression['current_level_name'] ?? ($currentClass->class_name ?? null), 'current_class' => $section->class_section_name ?? null, @@ -645,7 +644,7 @@ class SchoolYearClosureService ? (float) $invoice->balance : (float) ($invoice->total_amount ?? 0) - (float) ($invoice->paid_amount ?? 0); - if (!isset($byParent[$parentId])) { + if (! isset($byParent[$parentId])) { $existing = ParentBalanceTransfer::query() ->where('parent_id', $parentId) ->where('from_school_year', $fromSchoolYear) @@ -737,7 +736,7 @@ class SchoolYearClosureService ->orderBy('class_section_id') ->first(); - if (!$section) { + if (! $section) { return [null, null]; } @@ -757,13 +756,13 @@ class SchoolYearClosureService foreach ($rows as $row) { $action = $row['promotion_action']; - if (!isset($counts[$action])) { + if (! isset($counts[$action])) { continue; } $counts[$action]++; - if (!in_array($action, ['promote', 'repeat'], true)) { + if (! in_array($action, ['promote', 'repeat'], true)) { $this->logAudit( $actorId, 'school_year_promotion_decision_applied', @@ -773,6 +772,7 @@ class SchoolYearClosureService null, ['action' => $action, 'new_school_year' => $newSchoolYear] ); + continue; } @@ -781,7 +781,7 @@ class SchoolYearClosureService ->where('school_year', $newSchoolYear) ->first(); - if (!$existingEnrollment) { + if (! $existingEnrollment) { DB::table('enrollments')->insert([ 'student_id' => $row['student_id'], 'class_section_id' => $row['target_class_section_id'], @@ -805,7 +805,7 @@ class SchoolYearClosureService ->where('class_section_id', $row['target_class_section_id']) ->exists(); - if (!$existingAssignment) { + if (! $existingAssignment) { DB::table('student_class')->insert([ 'student_id' => $row['student_id'], 'class_section_id' => $row['target_class_section_id'], @@ -950,7 +950,7 @@ class SchoolYearClosureService private function countPendingPayments(string $schoolYear): int { - if (!Schema::hasTable('payments')) { + if (! Schema::hasTable('payments')) { return 0; } @@ -978,7 +978,7 @@ class SchoolYearClosureService private function countDuplicateParentAccounts(): int { - if (!Schema::hasTable('parent_accounts')) { + if (! Schema::hasTable('parent_accounts')) { return 0; } @@ -992,11 +992,11 @@ class SchoolYearClosureService private function resolveParentName(int $parentId): ?string { $row = DB::table('users')->where('id', $parentId)->first(); - if (!$row) { + if (! $row) { return null; } - return trim((string) ($row->firstname ?? '') . ' ' . (string) ($row->lastname ?? '')); + return trim((string) ($row->firstname ?? '').' '.(string) ($row->lastname ?? '')); } private function resolveStudentNames(int $parentId, string $schoolYear): array @@ -1015,7 +1015,7 @@ class SchoolYearClosureService return DB::table('students') ->whereIn('id', $studentIds) ->get() - ->map(fn ($student) => trim((string) ($student->firstname ?? '') . ' ' . (string) ($student->lastname ?? ''))) + ->map(fn ($student) => trim((string) ($student->firstname ?? '').' '.(string) ($student->lastname ?? ''))) ->filter() ->values() ->all(); @@ -1023,7 +1023,7 @@ class SchoolYearClosureService private function presentSchoolYear(?SchoolYear $year): ?array { - if (!$year) { + if (! $year) { return null; } @@ -1048,7 +1048,7 @@ class SchoolYearClosureService ?array $newValue, ?array $metadata = null ): void { - if (!Schema::hasTable('audit_logs')) { + if (! Schema::hasTable('audit_logs')) { return; } diff --git a/app/Services/SchoolYears/SchoolYearContextService.php b/app/Services/SchoolYears/SchoolYearContextService.php index bbaf6f4b..3094b0d8 100644 --- a/app/Services/SchoolYears/SchoolYearContextService.php +++ b/app/Services/SchoolYears/SchoolYearContextService.php @@ -9,9 +9,7 @@ use Illuminate\Support\Facades\Schema; class SchoolYearContextService { - public function __construct(private SchoolYearResolver $resolver) - { - } + public function __construct(private SchoolYearResolver $resolver) {} public function currentSchoolYear(?string $requested = null): string { @@ -95,7 +93,7 @@ class SchoolYearContextService } foreach ($this->legacySchoolYearNames() as $name) { - if (!isset($seen[$name])) { + if (! isset($seen[$name])) { $years[] = [ 'id' => null, 'name' => $name, @@ -111,7 +109,7 @@ class SchoolYearContextService } $include = trim((string) $include); - if ($include !== '' && !isset($seen[$include])) { + if ($include !== '' && ! isset($seen[$include])) { array_unshift($years, [ 'id' => null, 'name' => $include, @@ -131,7 +129,7 @@ class SchoolYearContextService { $names = ['Fall', 'Spring']; $current = trim((string) $include); - if ($current !== '' && !in_array($current, $names, true)) { + if ($current !== '' && ! in_array($current, $names, true)) { array_unshift($names, $current); } @@ -153,7 +151,7 @@ class SchoolYearContextService $names = []; foreach ($tables as $table) { - if (!Schema::hasTable($table) || !Schema::hasColumn($table, 'school_year')) { + if (! Schema::hasTable($table) || ! Schema::hasColumn($table, 'school_year')) { continue; } diff --git a/app/Services/SchoolYears/SchoolYearResolver.php b/app/Services/SchoolYears/SchoolYearResolver.php index ffc07421..510d1f96 100644 --- a/app/Services/SchoolYears/SchoolYearResolver.php +++ b/app/Services/SchoolYears/SchoolYearResolver.php @@ -16,7 +16,7 @@ class SchoolYearResolver $existing = SchoolYear::query()->where('name', $current)->first(); if ($existing) { - if ($existing->status !== SchoolYear::STATUS_ACTIVE || !$existing->is_current) { + if ($existing->status !== SchoolYear::STATUS_ACTIVE || ! $existing->is_current) { SchoolYear::query()->where('id', '!=', $existing->id)->where('is_current', true)->update([ 'is_current' => false, 'updated_at' => now(), diff --git a/app/Services/SchoolYears/SchoolYearWriteGuard.php b/app/Services/SchoolYears/SchoolYearWriteGuard.php index c44632d7..cd86fafa 100644 --- a/app/Services/SchoolYears/SchoolYearWriteGuard.php +++ b/app/Services/SchoolYears/SchoolYearWriteGuard.php @@ -10,7 +10,7 @@ class SchoolYearWriteGuard { public function assertEditable(array $schoolYears): void { - if (!Schema::hasTable('school_years')) { + if (! Schema::hasTable('school_years')) { return; } diff --git a/app/Services/Scores/AttendanceCalculator.php b/app/Services/Scores/AttendanceCalculator.php index f97b3120..f5d45b20 100644 --- a/app/Services/Scores/AttendanceCalculator.php +++ b/app/Services/Scores/AttendanceCalculator.php @@ -14,8 +14,7 @@ class AttendanceCalculator implements ScoreCalculatorInterface private AttendanceRecord $attendanceModel, private Configuration $configModel, private CalendarEvent $calendarModel - ) { - } + ) {} public function calculate(int $studentId, string $semester, string $schoolYear, ?int $classSectionId = null): array { @@ -30,7 +29,7 @@ class AttendanceCalculator implements ScoreCalculatorInterface $events = CalendarEvent::getEventsBySchoolYearAndSemester($schoolYear, $normSemester); $noSchoolSundays = 0; foreach ($events as $event) { - if (!empty($event['no_school'])) { + if (! empty($event['no_school'])) { $date = new \DateTime($event['date']); if ($date->format('N') == 7) { if ($date->format('Y-m-d') >= $semRange[0] && $date->format('Y-m-d') <= $semRange[1]) { @@ -75,16 +74,21 @@ class AttendanceCalculator implements ScoreCalculatorInterface protected function normalizeSemester(?string $input): string { $s = strtolower(trim((string) $input)); - if ($s === 'fall' || str_contains($s, '1')) return 'fall'; - if ($s === 'spring' || str_contains($s, '2')) return 'spring'; + if ($s === 'fall' || str_contains($s, '1')) { + return 'fall'; + } + if ($s === 'spring' || str_contains($s, '2')) { + return 'spring'; + } + return ''; } protected function getSemesterRange(string $schoolYear, string $semester): ?array { $norm = $this->normalizeSemester($semester); - if ($norm === '' || !preg_match('/^(\d{4})-(\d{4})$/', trim($schoolYear), $m)) { - if ($norm === '' || !preg_match('/^(\d{4})/', trim($schoolYear), $m)) { + if ($norm === '' || ! preg_match('/^(\d{4})-(\d{4})$/', trim($schoolYear), $m)) { + if ($norm === '' || ! preg_match('/^(\d{4})/', trim($schoolYear), $m)) { return null; } $y1 = (int) $m[1]; @@ -102,12 +106,14 @@ class AttendanceCalculator implements ScoreCalculatorInterface if ($norm === 'fall') { $start = ($fallStartCfg !== '') ? sprintf('%04d-%s', $y1, date('m-d', strtotime($fallStartCfg))) : "{$y1}-09-01"; $end = ($fallEndCfg !== '') ? sprintf('%04d-%s', $y2, date('m-d', strtotime($fallEndCfg))) : "{$y2}-01-15"; + return [$start, $end]; } if ($norm === 'spring') { $start = ($springStartCfg !== '') ? sprintf('%04d-%s', $y2, date('m-d', strtotime($springStartCfg))) : "{$y2}-01-16"; $end = ($springEndCfg !== '') ? sprintf('%04d-%s', $y2, date('m-d', strtotime($springEndCfg))) : "{$y2}-06-30"; + return [$start, $end]; } @@ -127,6 +133,7 @@ class AttendanceCalculator implements ScoreCalculatorInterface $sundays++; } } + return $sundays; } @@ -139,11 +146,12 @@ class AttendanceCalculator implements ScoreCalculatorInterface $configKey = $norm === 'fall' ? 'total_semester1_days' : 'total_semester2_days'; $value = Configuration::getConfig($configKey); - if (!is_numeric($value)) { + if (! is_numeric($value)) { return null; } $length = (int) $value; + return $length > 0 ? $length : null; } } diff --git a/app/Services/Scores/ExamScoreService.php b/app/Services/Scores/ExamScoreService.php index 66746cda..f4b0a123 100644 --- a/app/Services/Scores/ExamScoreService.php +++ b/app/Services/Scores/ExamScoreService.php @@ -2,19 +2,17 @@ namespace App\Services\Scores; +use App\Models\ClassSection; use App\Models\GradingLock; use App\Models\MissingScoreOverride; use App\Models\Student; use App\Models\TeacherClass; -use App\Models\ClassSection; use App\Services\Grading\Validation\ScoreValueValidator; use Illuminate\Support\Facades\DB; class ExamScoreService { - public function __construct(private ScoreTermService $term) - { - } + public function __construct(private ScoreTermService $term) {} public function resolveClassSectionIdForTeacher(int $teacherId, ?int $requestedClassSectionId, ?string $schoolYearInput): ?int { @@ -72,7 +70,7 @@ class ExamScoreService ->where('sc.school_year', '=', $schoolYear); }) ->leftJoinSub($latestSub, 'latest', 'latest.student_id', '=', 's.id') - ->leftJoin($table . ' as ex', 'ex.id', '=', 'latest.max_id') + ->leftJoin($table.' as ex', 'ex.id', '=', 'latest.max_id') ->orderBy('s.lastname') ->orderBy('s.firstname') ->get() @@ -125,17 +123,17 @@ class ExamScoreService $count = 0; foreach ($scores as $studentId => $data) { - if (!is_numeric($studentId)) { + if (! is_numeric($studentId)) { continue; } $student = Student::query()->find((int) $studentId); - if (!$student) { + if (! $student) { continue; } $rawScore = is_array($data) ? ($data['score'] ?? null) : $data; - $validator = new ScoreValueValidator(); + $validator = new ScoreValueValidator; $normalizedScore = $validator->normalizeNullable($rawScore, ScoreValueValidator::DEFAULT_MAX_POINTS, 'Exam score'); - $missingAllowed = !empty($missingOk[$studentId]); + $missingAllowed = ! empty($missingOk[$studentId]); $existing = $builder ->where('student_id', $studentId) @@ -178,7 +176,7 @@ class ExamScoreService $schoolYearVariants = [$schoolYear]; } - $rows = DB::table($table . ' as ex') + $rows = DB::table($table.' as ex') ->select([ 'ex.*', 's.firstname as student_firstname', diff --git a/app/Services/Scores/HomeworkCalculator.php b/app/Services/Scores/HomeworkCalculator.php index 96a63d1d..18eabce4 100644 --- a/app/Services/Scores/HomeworkCalculator.php +++ b/app/Services/Scores/HomeworkCalculator.php @@ -7,13 +7,12 @@ use App\Models\Homework; class HomeworkCalculator implements ScoreCalculatorInterface { - public function __construct(private Homework $homeworkModel) - { - } + public function __construct(private Homework $homeworkModel) {} public function calculate(int $studentId, string $semester, string $schoolYear, ?int $classSectionId = null): array { $average = Homework::getAverageHomeworkScore($studentId, $semester, $schoolYear, $classSectionId); + return ['homework_avg' => $average !== null ? (float) $average : null]; } } diff --git a/app/Services/Scores/HomeworkScoreService.php b/app/Services/Scores/HomeworkScoreService.php index 13475f69..2c95a63e 100644 --- a/app/Services/Scores/HomeworkScoreService.php +++ b/app/Services/Scores/HomeworkScoreService.php @@ -2,20 +2,18 @@ namespace App\Services\Scores; +use App\Models\ClassSection; use App\Models\GradingLock; use App\Models\Homework; use App\Models\MissingScoreOverride; use App\Models\Student; use App\Models\StudentClass; use App\Models\TeacherClass; -use App\Models\ClassSection; use App\Services\Grading\Validation\ScoreValueValidator; class HomeworkScoreService { - public function __construct(private ScoreTermService $term) - { - } + public function __construct(private ScoreTermService $term) {} /** * When the client omits class_section_id (legacy teacher portal), use the teacher's assignment @@ -174,6 +172,7 @@ class HomeworkScoreService return true; } } + return false; } @@ -181,10 +180,11 @@ class HomeworkScoreService { foreach ($schoolYearVariants as $year) { $map = MissingScoreOverride::getOverridesMap($classSectionId, $semester, $year, 'homework'); - if (!empty($map)) { + if (! empty($map)) { return $map; } } + return []; } @@ -197,7 +197,7 @@ class HomeworkScoreService $studentIds = array_keys($scores); $indexSet = []; foreach ($scores as $hwData) { - if (!is_array($hwData)) { + if (! is_array($hwData)) { continue; } foreach ($hwData as $index => $_) { @@ -209,11 +209,11 @@ class HomeworkScoreService $checkedItems = []; foreach ($missingOk as $studentId => $indexes) { - if (!is_array($indexes)) { + if (! is_array($indexes)) { continue; } foreach ($indexes as $index => $flag) { - if (!is_numeric($index)) { + if (! is_numeric($index)) { continue; } $checkedItems[] = [ @@ -236,22 +236,22 @@ class HomeworkScoreService $count = 0; foreach ($scores as $studentId => $hwData) { - if (!is_numeric($studentId) || !is_array($hwData)) { + if (! is_numeric($studentId) || ! is_array($hwData)) { continue; } $student = Student::query()->find((int) $studentId); - if (!$student) { + if (! $student) { continue; } foreach ($hwData as $index => $score) { - if (!is_numeric($index)) { + if (! is_numeric($index)) { continue; } $rawScore = $score ?? null; - $validator = new ScoreValueValidator(); + $validator = new ScoreValueValidator; $normalizedScore = $validator->normalizeNullable($rawScore, ScoreValueValidator::DEFAULT_MAX_POINTS, 'Homework score'); - $missingAllowed = !empty($missingOk[$studentId][$index]); + $missingAllowed = ! empty($missingOk[$studentId][$index]); $existing = Homework::query() ->where('student_id', $studentId) @@ -306,7 +306,7 @@ class HomeworkScoreService foreach ($students as $student) { $studentRow = Student::query()->find((int) $student->student_id); - if (!$studentRow) { + if (! $studentRow) { continue; } $exists = Homework::query() diff --git a/app/Services/Scores/ParticipationScoreService.php b/app/Services/Scores/ParticipationScoreService.php index ebcbf743..53ae406e 100644 --- a/app/Services/Scores/ParticipationScoreService.php +++ b/app/Services/Scores/ParticipationScoreService.php @@ -2,20 +2,18 @@ namespace App\Services\Scores; -use App\Models\GradingLock; use App\Models\ClassSection; -use App\Services\Grading\Validation\ScoreValueValidator; +use App\Models\GradingLock; use App\Models\MissingScoreOverride; use App\Models\Participation; use App\Models\Student; use App\Models\StudentClass; use App\Models\TeacherClass; +use App\Services\Grading\Validation\ScoreValueValidator; class ParticipationScoreService { - public function __construct(private ScoreTermService $term) - { - } + public function __construct(private ScoreTermService $term) {} public function resolveClassSectionIdForTeacher(int $teacherId, ?int $requestedClassSectionId, ?string $schoolYearInput): ?int { @@ -173,22 +171,22 @@ class ParticipationScoreService $count = 0; foreach ($scores as $studentId => $data) { - if (!is_numeric($studentId)) { + if (! is_numeric($studentId)) { continue; } - if (!is_array($data)) { + if (! is_array($data)) { $data = ['score' => $data]; } $student = Student::query()->find((int) $studentId); - if (!$student) { + if (! $student) { continue; } $rawScore = $data['score'] ?? null; - $validator = new ScoreValueValidator(); + $validator = new ScoreValueValidator; $normalizedScore = $validator->normalizeNullable($rawScore, ScoreValueValidator::DEFAULT_MAX_POINTS, 'Participation score'); - $missingAllowed = !empty($missingOk[$studentId]); + $missingAllowed = ! empty($missingOk[$studentId]); $existing = Participation::query() ->where('student_id', $studentId) diff --git a/app/Services/Scores/ProjectCalculator.php b/app/Services/Scores/ProjectCalculator.php index b4c4da27..15cce2f3 100644 --- a/app/Services/Scores/ProjectCalculator.php +++ b/app/Services/Scores/ProjectCalculator.php @@ -7,13 +7,12 @@ use App\Models\Project; class ProjectCalculator implements ScoreCalculatorInterface { - public function __construct(private Project $projectModel) - { - } + public function __construct(private Project $projectModel) {} public function calculate(int $studentId, string $semester, string $schoolYear, ?int $classSectionId = null): array { $average = Project::averageScore($studentId, $semester, $schoolYear, $classSectionId); + return ['project_avg' => $average !== null ? (float) $average : null]; } } diff --git a/app/Services/Scores/ProjectScoreService.php b/app/Services/Scores/ProjectScoreService.php index 6372d968..43c2eaa0 100644 --- a/app/Services/Scores/ProjectScoreService.php +++ b/app/Services/Scores/ProjectScoreService.php @@ -2,21 +2,18 @@ namespace App\Services\Scores; +use App\Models\ClassSection; use App\Models\GradingLock; use App\Models\MissingScoreOverride; use App\Models\Project; use App\Models\Student; use App\Models\StudentClass; use App\Models\TeacherClass; -use App\Models\ClassSection; use App\Services\Grading\Validation\ScoreValueValidator; -use Illuminate\Support\Facades\DB; class ProjectScoreService { - public function __construct(private ScoreTermService $term) - { - } + public function __construct(private ScoreTermService $term) {} public function resolveClassSectionIdForTeacher(int $teacherId, ?int $requestedClassSectionId, ?string $schoolYearInput): ?int { @@ -168,7 +165,7 @@ class ProjectScoreService $studentIds = array_keys($scores); $indexSet = []; foreach ($scores as $projects) { - if (!is_array($projects)) { + if (! is_array($projects)) { continue; } foreach ($projects as $index => $_) { @@ -180,11 +177,11 @@ class ProjectScoreService $checkedItems = []; foreach ($missingOk as $studentId => $indexes) { - if (!is_array($indexes)) { + if (! is_array($indexes)) { continue; } foreach ($indexes as $index => $flag) { - if (!is_numeric($index)) { + if (! is_numeric($index)) { continue; } $checkedItems[] = [ @@ -207,21 +204,21 @@ class ProjectScoreService $count = 0; foreach ($scores as $studentId => $projects) { - if (!is_numeric($studentId) || !is_array($projects)) { + if (! is_numeric($studentId) || ! is_array($projects)) { continue; } $student = Student::query()->find((int) $studentId); - if (!$student) { + if (! $student) { continue; } foreach ($projects as $index => $score) { - if (!is_numeric($index)) { + if (! is_numeric($index)) { continue; } - $validator = new ScoreValueValidator(); + $validator = new ScoreValueValidator; $normalizedScore = $validator->normalizeNullable($score, ScoreValueValidator::DEFAULT_MAX_POINTS, 'Project score'); - $missingAllowed = !empty($missingOk[$studentId][$index]); + $missingAllowed = ! empty($missingOk[$studentId][$index]); $existing = Project::query() ->where('student_id', $studentId) @@ -275,7 +272,7 @@ class ProjectScoreService foreach ($students as $student) { $studentRow = Student::query()->find((int) $student->student_id); - if (!$studentRow) { + if (! $studentRow) { continue; } $exists = Project::query() diff --git a/app/Services/Scores/QuizCalculator.php b/app/Services/Scores/QuizCalculator.php index 63708f75..03468d3e 100644 --- a/app/Services/Scores/QuizCalculator.php +++ b/app/Services/Scores/QuizCalculator.php @@ -24,7 +24,7 @@ class QuizCalculator implements ScoreCalculatorInterface $count = 0; foreach ($rows as $row) { $score = $row->score ?? null; - if ($score === null || $score === '' || !is_numeric($score)) { + if ($score === null || $score === '' || ! is_numeric($score)) { continue; } $total += (float) $score; diff --git a/app/Services/Scores/QuizScoreService.php b/app/Services/Scores/QuizScoreService.php index 117ede14..72c52c25 100644 --- a/app/Services/Scores/QuizScoreService.php +++ b/app/Services/Scores/QuizScoreService.php @@ -2,20 +2,18 @@ namespace App\Services\Scores; +use App\Models\ClassSection; use App\Models\GradingLock; use App\Models\MissingScoreOverride; use App\Models\Quiz; use App\Models\Student; use App\Models\StudentClass; use App\Models\TeacherClass; -use App\Models\ClassSection; use App\Services\Grading\Validation\ScoreValueValidator; class QuizScoreService { - public function __construct(private ScoreTermService $term) - { - } + public function __construct(private ScoreTermService $term) {} public function resolveClassSectionIdForTeacher(int $teacherId, ?int $requestedClassSectionId, ?string $schoolYearInput): ?int { @@ -167,7 +165,7 @@ class QuizScoreService $studentIds = array_keys($scores); $indexSet = []; foreach ($scores as $quizData) { - if (!is_array($quizData)) { + if (! is_array($quizData)) { continue; } foreach ($quizData as $index => $_) { @@ -179,11 +177,11 @@ class QuizScoreService $checkedItems = []; foreach ($missingOk as $studentId => $indexes) { - if (!is_array($indexes)) { + if (! is_array($indexes)) { continue; } foreach ($indexes as $index => $flag) { - if (!is_numeric($index)) { + if (! is_numeric($index)) { continue; } $checkedItems[] = [ @@ -206,23 +204,23 @@ class QuizScoreService $count = 0; foreach ($scores as $studentId => $quizData) { - if (!is_numeric($studentId)) { + if (! is_numeric($studentId)) { continue; } - if (!is_array($quizData)) { + if (! is_array($quizData)) { $quizData = [1 => $quizData]; } $student = Student::query()->find((int) $studentId); - if (!$student) { + if (! $student) { continue; } foreach ($quizData as $quizNumber => $score) { - if (!is_numeric($quizNumber)) { + if (! is_numeric($quizNumber)) { continue; } - $validator = new ScoreValueValidator(); + $validator = new ScoreValueValidator; $normalizedScore = $validator->normalizeNullable($score, ScoreValueValidator::DEFAULT_MAX_POINTS, 'Quiz score'); - $missingAllowed = !empty($missingOk[$studentId][$quizNumber]); + $missingAllowed = ! empty($missingOk[$studentId][$quizNumber]); $existing = Quiz::query() ->where('student_id', $studentId) @@ -276,7 +274,7 @@ class QuizScoreService foreach ($students as $student) { $studentRow = Student::query()->find((int) $student->student_id); - if (!$studentRow) { + if (! $studentRow) { continue; } $exists = Quiz::query() diff --git a/app/Services/Scores/ScoreCommentService.php b/app/Services/Scores/ScoreCommentService.php index 6796f64c..b316b943 100644 --- a/app/Services/Scores/ScoreCommentService.php +++ b/app/Services/Scores/ScoreCommentService.php @@ -2,21 +2,18 @@ namespace App\Services\Scores; -use App\Models\Configuration; use App\Models\ClassSection; +use App\Models\Configuration; use App\Models\GradingLock; use App\Models\MissingScoreOverride; use App\Models\ScoreComment; use App\Models\SemesterScore; use App\Models\Student; -use App\Models\StudentClass; use Illuminate\Support\Facades\DB; class ScoreCommentService { - public function __construct(private ScoreTermService $term) - { - } + public function __construct(private ScoreTermService $term) {} public function list(?string $classSectionId, ?string $semester, ?string $schoolYear): array { @@ -33,24 +30,24 @@ class ScoreCommentService ->orderBy('s.firstname') ->distinct(); - if (!$isAllSections && $classSectionInt) { + if (! $isAllSections && $classSectionInt) { $builder->where('sc.class_section_id', $classSectionInt); } $students = $builder->get()->map(fn ($row) => (array) $row)->all(); $comments = []; - if (!empty($students)) { + if (! empty($students)) { $studentIds = array_map(static fn ($r) => (int) $r['student_id'], $students); $rawComments = ScoreComment::query() ->whereIn('student_id', $studentIds) ->whereIn('score_type', ['midterm', 'final', 'ptap', 'attendance']) ->where('semester', $activeSemester) ->where('school_year', $schoolYear) - ->when(!$isAllSections && $classSectionInt, function ($q) use ($classSectionInt) { + ->when(! $isAllSections && $classSectionInt, function ($q) use ($classSectionInt) { $q->where(function ($w) use ($classSectionInt) { $w->where('class_section_id', $classSectionInt) - ->orWhereNull('class_section_id'); + ->orWhereNull('class_section_id'); }); }) ->get(); @@ -87,7 +84,7 @@ class ScoreCommentService if ($hasComment) { continue; } - if (!function_exists('attendance_comment_from_score')) { + if (! function_exists('attendance_comment_from_score')) { continue; } $score = $attendanceScores[$sid] ?? null; @@ -112,10 +109,10 @@ class ScoreCommentService 'semester' => $activeSemester, 'schoolYear' => $schoolYear, 'classSectionId' => $isAllSections ? 'allsections' : $classSectionInt, - 'classSectionName' => (!$isAllSections && $classSectionInt) + 'classSectionName' => (! $isAllSections && $classSectionInt) ? (string) (ClassSection::query()->where('class_section_id', $classSectionInt)->value('class_section_name') ?? '') : '', - 'scoresLocked' => (!$isAllSections && $classSectionInt) + 'scoresLocked' => (! $isAllSections && $classSectionInt) ? GradingLock::isLocked($classSectionInt, $activeSemester, $schoolYear) : false, ]; @@ -143,7 +140,7 @@ class ScoreCommentService foreach ($commentTypes as $type) { $checkedItems = []; foreach ($missingOk as $studentId => $types) { - if (!is_array($types) || empty($types[$type])) { + if (! is_array($types) || empty($types[$type])) { continue; } $checkedItems[] = [ @@ -175,6 +172,7 @@ class ScoreCommentService $validationError = $this->validateComment($trimmedComment, (int) $studentId, $normalizedScoreType, $studentNames); if ($validationError !== null) { $errors[] = $validationError; + continue; } @@ -185,7 +183,7 @@ class ScoreCommentService ->where('school_year', $schoolYear) ->where(function ($q) use ($classSectionId) { $q->where('class_section_id', $classSectionId) - ->orWhereNull('class_section_id'); + ->orWhereNull('class_section_id'); }) ->first(); @@ -239,6 +237,7 @@ class ScoreCommentService $validationError = $this->validateComment($commentText, (int) $studentId, $normalizedScoreType, $studentNames); if ($validationError !== null) { $errors[] = $validationError; + continue; } } @@ -250,7 +249,7 @@ class ScoreCommentService ->where('school_year', $schoolYear) ->where(function ($q) use ($classSectionId) { $q->where('class_section_id', $classSectionId) - ->orWhereNull('class_section_id'); + ->orWhereNull('class_section_id'); }) ->first(); @@ -322,8 +321,8 @@ class ScoreCommentService return "{$firstName}'s {$scoreType} comment must be at most 400 characters."; } - $pattern = '/^' . preg_quote($firstName, '/') . '\\b/i'; - if (!preg_match($pattern, $trimmed)) { + $pattern = '/^'.preg_quote($firstName, '/').'\\b/i'; + if (! preg_match($pattern, $trimmed)) { return "{$firstName}'s {$scoreType} comment must start with the student's first name."; } @@ -333,7 +332,7 @@ class ScoreCommentService private function isReviewer(): bool { $user = auth()->user(); - if (!$user) { + if (! $user) { return false; } @@ -354,6 +353,6 @@ class ScoreCommentService explode(',', $reviewerRoles) ); - return !empty(array_intersect($currentUserRoles, $allowedRoles)); + return ! empty(array_intersect($currentUserRoles, $allowedRoles)); } } diff --git a/app/Services/Scores/ScoreDashboardService.php b/app/Services/Scores/ScoreDashboardService.php index fe4546ce..cbe417e4 100644 --- a/app/Services/Scores/ScoreDashboardService.php +++ b/app/Services/Scores/ScoreDashboardService.php @@ -3,7 +3,6 @@ namespace App\Services\Scores; use App\Models\ClassSection; -use App\Models\Configuration; use App\Models\GradingLock; use App\Models\MissingScoreOverride; use App\Models\ScoreComment; @@ -15,16 +14,14 @@ use Illuminate\Support\Facades\DB; class ScoreDashboardService { - public function __construct(private ScoreTermService $term) - { - } + public function __construct(private ScoreTermService $term) {} public function overview(int $teacherId, ?int $classSectionId, ?string $semester, ?string $schoolYear): array { $schoolYear = $this->term->schoolYear($schoolYear); $semesterLabel = $this->term->semesterLabel($semester, $semester); - if ($teacherId <= 0 && !empty($classSectionId)) { + if ($teacherId <= 0 && ! empty($classSectionId)) { $allowedIds = [(int) $classSectionId]; } else { $allowedIds = TeacherClass::query() @@ -45,7 +42,7 @@ class ScoreDashboardService $studentQuery = StudentClass::query(); if (DB::connection()->getDriverName() === 'sqlite') { - $studentQuery->whereRaw('class_section_id = ' . (int) $classSectionId); + $studentQuery->whereRaw('class_section_id = '.(int) $classSectionId); if ($schoolYear !== '') { $quoted = DB::connection()->getPdo()->quote($schoolYear); $studentQuery->whereRaw("school_year = {$quoted}"); @@ -62,9 +59,9 @@ class ScoreDashboardService ->map(fn ($v) => (int) $v) ->all(); if (empty($studentIds) && DB::connection()->getDriverName() === 'sqlite') { - $schoolYearClause = $schoolYear !== '' ? " and school_year = " . DB::connection()->getPdo()->quote($schoolYear) : ''; + $schoolYearClause = $schoolYear !== '' ? ' and school_year = '.DB::connection()->getPdo()->quote($schoolYear) : ''; $rows = DB::select( - "select student_id from student_class where class_section_id = " . (int) $classSectionId . $schoolYearClause + 'select student_id from student_class where class_section_id = '.(int) $classSectionId.$schoolYearClause ); $studentIds = array_values(array_filter(array_map( static fn ($row) => isset($row->student_id) ? (int) $row->student_id : 0, @@ -173,7 +170,7 @@ class ScoreDashboardService foreach ($commentTypes as $type) { $checkedItems = []; foreach ($missingOk as $studentId => $types) { - if (!is_array($types) || empty($types[$type])) { + if (! is_array($types) || empty($types[$type])) { continue; } $checkedItems[] = [ @@ -229,7 +226,7 @@ class ScoreDashboardService ->all(); $scores = []; - if (!empty($students)) { + if (! empty($students)) { $studentIdList = array_column($students, 'id'); $semesterScores = DB::table('semester_scores') ->whereIn('student_id', $studentIdList) diff --git a/app/Services/Scores/ScorePredictorDataService.php b/app/Services/Scores/ScorePredictorDataService.php index 66da63b8..04716959 100644 --- a/app/Services/Scores/ScorePredictorDataService.php +++ b/app/Services/Scores/ScorePredictorDataService.php @@ -56,7 +56,7 @@ class ScorePredictorDataService ->orWhere('cs.class_section_name', 'not like', 'KG%'); }); - if (!empty($classSectionId)) { + if (! empty($classSectionId)) { $builder->where('sc.class_section_id', $classSectionId); } @@ -81,7 +81,7 @@ class ScorePredictorDataService ->orWhere('cs.class_section_name', 'not like', 'KG%'); }); - if (!empty($classSectionId)) { + if (! empty($classSectionId)) { $builder->where('student_class.class_section_id', $classSectionId); } diff --git a/app/Services/Scores/ScorePredictorRiskService.php b/app/Services/Scores/ScorePredictorRiskService.php index cbb9ad1b..46382f0b 100644 --- a/app/Services/Scores/ScorePredictorRiskService.php +++ b/app/Services/Scores/ScorePredictorRiskService.php @@ -14,6 +14,7 @@ class ScorePredictorRiskService $a5 = 1.061405429; $erf = 1 - ((((($a5 * $t + $a4) * $t + $a3) * $t + $a2) * $t + $a1) * $t) * exp(-$z * $z); $sign = $z < 0 ? -1 : 1; + return 0.5 * (1 + $sign * $erf); } @@ -26,7 +27,7 @@ class ScorePredictorRiskService $springScore = is_numeric($student['spring_score'] ?? null) ? (float) $student['spring_score'] : null; $scoreParts = array_values(array_filter([$fallScore, $springScore], static fn ($v) => $v !== null)); - $finalAvg = !empty($scoreParts) ? round(array_sum($scoreParts) / count($scoreParts), 1) : null; + $finalAvg = ! empty($scoreParts) ? round(array_sum($scoreParts) / count($scoreParts), 1) : null; $waitingForSpring = ($fallScore !== null && $springScore === null); @@ -40,8 +41,8 @@ class ScorePredictorRiskService $probLow = $zLow !== null ? $this->normalCdf($zLow) : null; $riskHigh = match (true) { - $finalAvg !== null && !$waitingForSpring && $finalAvg >= $trophyThreshold => 'Achieved Trophy', - $finalAvg !== null && !$waitingForSpring => 'Unreachable', + $finalAvg !== null && ! $waitingForSpring && $finalAvg >= $trophyThreshold => 'Achieved Trophy', + $finalAvg !== null && ! $waitingForSpring => 'Unreachable', $requiredHigh !== null && $requiredHigh > 100 => 'Unreachable', is_null($probHigh) => 'New student', $probHigh >= 0.66 => 'Low', @@ -50,8 +51,8 @@ class ScorePredictorRiskService }; $riskLow = match (true) { - $finalAvg !== null && !$waitingForSpring && $finalAvg < $targetLow => 'High', - $finalAvg !== null && !$waitingForSpring => 'Low', + $finalAvg !== null && ! $waitingForSpring && $finalAvg < $targetLow => 'High', + $finalAvg !== null && ! $waitingForSpring => 'Low', $requiredLow !== null && $requiredLow > 100 => 'Unlikely to pass', is_null($probLow) => 'New student', $probLow >= 0.66 => 'High', @@ -103,19 +104,23 @@ class ScorePredictorRiskService } foreach ($bySection as $cid => $indices) { - if (!empty($hasThresholdTrophy[$cid])) { + if (! empty($hasThresholdTrophy[$cid])) { continue; } $candidates = []; foreach ($indices as $idx) { $val = $results[$idx]['final_average'] ?? null; - if (!is_numeric($val)) { + if (! is_numeric($val)) { $fall = $results[$idx]['fall_score'] ?? null; $spring = $results[$idx]['spring_score'] ?? null; $parts = []; - if (is_numeric($fall)) $parts[] = (float) $fall; - if (is_numeric($spring)) $parts[] = (float) $spring; - if (!empty($parts)) { + if (is_numeric($fall)) { + $parts[] = (float) $fall; + } + if (is_numeric($spring)) { + $parts[] = (float) $spring; + } + if (! empty($parts)) { $val = array_sum($parts) / count($parts); } } diff --git a/app/Services/Scores/ScorePredictorService.php b/app/Services/Scores/ScorePredictorService.php index 5f37be9f..7e860ab1 100644 --- a/app/Services/Scores/ScorePredictorService.php +++ b/app/Services/Scores/ScorePredictorService.php @@ -9,8 +9,7 @@ class ScorePredictorService public function __construct( private ScorePredictorDataService $dataService, private ScorePredictorRiskService $riskService - ) { - } + ) {} public function report(?string $schoolYear, ?int $classSectionId = null): array { diff --git a/app/Services/Scores/ScoreTermService.php b/app/Services/Scores/ScoreTermService.php index 1ab6eb47..f9cbf145 100644 --- a/app/Services/Scores/ScoreTermService.php +++ b/app/Services/Scores/ScoreTermService.php @@ -15,6 +15,7 @@ class ScoreTermService if ($value === 'spring' || str_contains($value, '2')) { return 'spring'; } + return ''; } diff --git a/app/Services/Scores/SemesterScoreService.php b/app/Services/Scores/SemesterScoreService.php index 5bd082f8..978e45ae 100644 --- a/app/Services/Scores/SemesterScoreService.php +++ b/app/Services/Scores/SemesterScoreService.php @@ -3,21 +3,17 @@ namespace App\Services\Scores; use App\Interfaces\ScoreCalculatorInterface; -use App\Models\Configuration; -use App\Models\FinalExam; -use App\Models\MidtermExam; -use App\Models\Participation; -use App\Models\SemesterScore; -use App\Services\Scores\AttendanceCalculator; -use App\Services\Scores\HomeworkCalculator; -use App\Services\Scores\ProjectCalculator; -use App\Services\Scores\QuizCalculator; use App\Models\AttendanceRecord; use App\Models\CalendarEvent; +use App\Models\Configuration; +use App\Models\FinalExam; use App\Models\Homework; +use App\Models\MidtermExam; +use App\Models\Participation; use App\Models\Project; -use RuntimeException; +use App\Models\SemesterScore; use App\Services\Grading\Policy\GradingPolicyResolver; +use RuntimeException; class SemesterScoreService { @@ -25,8 +21,11 @@ class SemesterScoreService private array $scoreCalculators = []; private string $semester; + private string $schoolYear; + private ?int $actorId; + private GradingPolicyResolver $policyResolver; public function __construct( @@ -38,16 +37,16 @@ class SemesterScoreService $this->semester = (string) (Configuration::getConfig('semester') ?? ''); $this->schoolYear = (string) (Configuration::getConfig('school_year') ?? ''); $this->actorId = (int) (auth()->id() ?? 0) ?: null; - $this->policyResolver = new GradingPolicyResolver(); + $this->policyResolver = new GradingPolicyResolver; $attendanceCalculator = $attendanceCalculator ?? new AttendanceCalculator( - new AttendanceRecord(), - new Configuration(), - new CalendarEvent() + new AttendanceRecord, + new Configuration, + new CalendarEvent ); - $homeworkCalculator = $homeworkCalculator ?? new HomeworkCalculator(new Homework()); - $quizCalculator = $quizCalculator ?? new QuizCalculator(); - $projectCalculator = $projectCalculator ?? new ProjectCalculator(new Project()); + $homeworkCalculator = $homeworkCalculator ?? new HomeworkCalculator(new Homework); + $quizCalculator = $quizCalculator ?? new QuizCalculator; + $projectCalculator = $projectCalculator ?? new ProjectCalculator(new Project); $this->scoreCalculators = [ AttendanceCalculator::class => $attendanceCalculator, @@ -70,7 +69,7 @@ class SemesterScoreService public function updateStudentScores(array $student, string $semester, string $schoolYear): void { foreach (['student_id', 'school_id', 'class_section_id'] as $field) { - if (!isset($student[$field])) { + if (! isset($student[$field])) { throw new RuntimeException("Missing required student field: {$field}"); } } @@ -114,7 +113,7 @@ class SemesterScoreService } } - if (!isset($scoreData['participation_score'])) { + if (! isset($scoreData['participation_score'])) { $participationScore = Participation::getParticipationScore($studentId, $semester, $schoolYear, $classSectionId); $scoreData['participation_score'] = $participationScore !== null ? (float) $participationScore : null; } @@ -140,24 +139,27 @@ class SemesterScoreService $hasTests = $avgQuiz !== null; $hasProjects = $avgProject !== null; - if (!$hasTests && !$hasProjects) { + if (! $hasTests && ! $hasProjects) { if ($avgHomework === null || $avgParticipation === null) { return null; } + return round(($avgHomework * 0.5) + ($avgParticipation * 0.5), 1); } - if (!$hasProjects) { + if (! $hasProjects) { if ($avgHomework === null || $avgParticipation === null || $avgQuiz === null) { return null; } + return round(($avgHomework * 0.4) + ($avgParticipation * 0.4) + ($avgQuiz * 0.2), 1); } - if (!$hasTests) { + if (! $hasTests) { if ($avgHomework === null || $avgParticipation === null || $avgProject === null) { return null; } + return round(($avgHomework * 0.34) + ($avgParticipation * 0.33) + ($avgProject * 0.33), 1); } @@ -188,6 +190,7 @@ class SemesterScoreService if ($ptap !== null && $attendance !== null && $out['midterm_score'] !== null) { $out['semester_score'] = round(($attendance * 0.2) + ($ptap * 0.2) + ($out['midterm_score'] * 0.6), 1); } + return $out; } @@ -197,6 +200,7 @@ class SemesterScoreService if ($ptap !== null && $attendance !== null && $out['final_score'] !== null) { $out['semester_score'] = round(($attendance * 0.2) + ($ptap * 0.2) + ($out['final_score'] * 0.6), 1); } + return $out; } diff --git a/app/Services/Security/IpBanCommandService.php b/app/Services/Security/IpBanCommandService.php index 26b92a0f..5a237752 100644 --- a/app/Services/Security/IpBanCommandService.php +++ b/app/Services/Security/IpBanCommandService.php @@ -10,7 +10,7 @@ class IpBanCommandService public function banNow(?int $id, ?string $ip, int $hours): ?IpAttempt { $row = $this->findRow($id, $ip); - if (!$row) { + if (! $row) { return null; } @@ -21,6 +21,7 @@ class IpBanCommandService $row->blocked_until = $blockedUntil; $row->attempts = $attempts; $row->save(); + return $row->refresh(); }); } @@ -28,7 +29,7 @@ class IpBanCommandService public function unbanOne(?int $id, ?string $ip): ?IpAttempt { $row = $this->findRow($id, $ip); - if (!$row) { + if (! $row) { return null; } @@ -36,6 +37,7 @@ class IpBanCommandService $row->blocked_until = null; $row->attempts = 0; $row->save(); + return $row->refresh(); }); } @@ -67,6 +69,7 @@ class IpBanCommandService if (function_exists('utc_now')) { return (string) utc_now(); } + return now('UTC')->toDateTimeString(); } @@ -76,6 +79,7 @@ class IpBanCommandService if (function_exists('utc_now')) { return (string) date('Y-m-d H:i:s', strtotime("+{$hours} hours", strtotime((string) utc_now()))); } + return now('UTC')->addHours($hours)->toDateTimeString(); } } diff --git a/app/Services/Security/IpBanQueryService.php b/app/Services/Security/IpBanQueryService.php index d3468abb..4de54ef5 100644 --- a/app/Services/Security/IpBanQueryService.php +++ b/app/Services/Security/IpBanQueryService.php @@ -16,8 +16,8 @@ class IpBanQueryService $query->where('blocked_until', '>', $this->utcNow()); } - if (!empty($filters['search'])) { - $term = '%' . trim((string) $filters['search']) . '%'; + if (! empty($filters['search'])) { + $term = '%'.trim((string) $filters['search']).'%'; $query->where('ip_address', 'like', $term); } @@ -41,6 +41,7 @@ class IpBanQueryService if (function_exists('utc_now')) { return (string) utc_now(); } + return now('UTC')->toDateTimeString(); } } diff --git a/app/Services/SemesterRangeService.php b/app/Services/SemesterRangeService.php index f13d68a3..e9dec0d5 100644 --- a/app/Services/SemesterRangeService.php +++ b/app/Services/SemesterRangeService.php @@ -7,9 +7,7 @@ use DateTimeImmutable; class SemesterRangeService { - public function __construct(private SemesterConfigService $configService) - { - } + public function __construct(private SemesterConfigService $configService) {} public function getSchoolYearRange(string $schoolYear): array { @@ -31,7 +29,7 @@ class SemesterRangeService public function getSemesterRange(string $schoolYear, string $semester): ?array { - if (!preg_match('/^(\\d{4})-(\\d{4})$/', $schoolYear, $m)) { + if (! preg_match('/^(\\d{4})-(\\d{4})$/', $schoolYear, $m)) { return null; } $y1 = (int) $m[1]; @@ -57,6 +55,7 @@ class SemesterRangeService if ($value === 'spring') { return 'Spring'; } + return ''; } @@ -103,13 +102,15 @@ class SemesterRangeService if (preg_match('/^(\\d{4})-(\\d{4})$/', $schoolYear, $m)) { $y1 = (int) $m[1]; $y2 = (int) $m[2]; - return [new DateTimeImmutable($y1 . '-09-01'), new DateTimeImmutable($y2 . '-06-30')]; + + return [new DateTimeImmutable($y1.'-09-01'), new DateTimeImmutable($y2.'-06-30')]; } $currentYear = (int) date('Y'); + return [ - new DateTimeImmutable($currentYear . '-09-01'), - new DateTimeImmutable(($currentYear + 1) . '-06-30'), + new DateTimeImmutable($currentYear.'-09-01'), + new DateTimeImmutable(($currentYear + 1).'-06-30'), ]; } } diff --git a/app/Services/Settings/ConfigurationService.php b/app/Services/Settings/ConfigurationService.php index fe1030db..966b0523 100644 --- a/app/Services/Settings/ConfigurationService.php +++ b/app/Services/Settings/ConfigurationService.php @@ -25,7 +25,7 @@ class ConfigurationService public function update(int $id, array $payload): ?Configuration { $config = Configuration::query()->find($id); - if (!$config) { + if (! $config) { return null; } @@ -40,7 +40,7 @@ class ConfigurationService public function delete(int $id): bool { $config = Configuration::query()->find($id); - if (!$config) { + if (! $config) { return false; } diff --git a/app/Services/Settings/SchoolCalendar/SchoolCalendarFormatterService.php b/app/Services/Settings/SchoolCalendar/SchoolCalendarFormatterService.php index c75ab131..b0b62602 100644 --- a/app/Services/Settings/SchoolCalendar/SchoolCalendarFormatterService.php +++ b/app/Services/Settings/SchoolCalendar/SchoolCalendarFormatterService.php @@ -101,28 +101,28 @@ class SchoolCalendarFormatterService $parentName = (string) ($row['parent_name'] ?? 'Parent'); $studentName = (string) ($row['student_name'] ?? 'Student'); $time = trim((string) ($row['time'] ?? '')); - $timeLabel = $time !== '' ? (' ' . $time) : ''; + $timeLabel = $time !== '' ? (' '.$time) : ''; $title = $audience === 'admin' ? 'Admin Meeting' : 'Parent Meeting'; $descParts = []; - $descParts[] = 'Student: ' . $studentName; - $descParts[] = 'Parent: ' . $parentName; - if (!empty($row['class_section_name'])) { - $descParts[] = 'Section: ' . $row['class_section_name']; + $descParts[] = 'Student: '.$studentName; + $descParts[] = 'Parent: '.$parentName; + if (! empty($row['class_section_name'])) { + $descParts[] = 'Section: '.$row['class_section_name']; } - $descParts[] = 'Date/Time: ' . ($row['date'] ?? '') . $timeLabel; - if (!empty($row['notes'])) { - $descParts[] = 'Notes: ' . $row['notes']; + $descParts[] = 'Date/Time: '.($row['date'] ?? '').$timeLabel; + if (! empty($row['notes'])) { + $descParts[] = 'Notes: '.$row['notes']; } $start = (string) ($row['date'] ?? ''); if ($start !== '' && $time !== '') { - $start = $start . 'T' . $time; + $start = $start.'T'.$time; } return [ - 'id' => 'pm-' . (int) ($row['id'] ?? 0), + 'id' => 'pm-'.(int) ($row['id'] ?? 0), 'title' => $title, 'start' => $start, 'description' => implode("\n", $descParts), diff --git a/app/Services/Settings/SchoolCalendar/SchoolCalendarMeetingService.php b/app/Services/Settings/SchoolCalendar/SchoolCalendarMeetingService.php index f05032d6..79c7e016 100644 --- a/app/Services/Settings/SchoolCalendar/SchoolCalendarMeetingService.php +++ b/app/Services/Settings/SchoolCalendar/SchoolCalendarMeetingService.php @@ -23,7 +23,7 @@ class SchoolCalendarMeetingService if ($audience === 'parent' && $parentUserId) { $studentIds = $this->getStudentIdsForParent($parentUserId); - if (!empty($studentIds)) { + if (! empty($studentIds)) { $builder->where(function (Builder $q) use ($parentUserId, $studentIds) { $q->where('parent_user_id', $parentUserId) ->orWhereIn('student_id', $studentIds); @@ -34,7 +34,7 @@ class SchoolCalendarMeetingService } $rows = $builder->orderBy('date', 'ASC')->get()->toArray(); - if (!empty($rows)) { + if (! empty($rows)) { return $rows; } @@ -48,7 +48,7 @@ class SchoolCalendarMeetingService if ($audience === 'parent' && $parentUserId) { $studentIds = $this->getStudentIdsForParent($parentUserId); - if (!empty($studentIds)) { + if (! empty($studentIds)) { $fallback->where(function (Builder $q) use ($parentUserId, $studentIds) { $q->where('parent_user_id', $parentUserId) ->orWhereIn('student_id', $studentIds); diff --git a/app/Services/Settings/SchoolCalendar/SchoolCalendarMutationService.php b/app/Services/Settings/SchoolCalendar/SchoolCalendarMutationService.php index a14861e1..faed63ea 100644 --- a/app/Services/Settings/SchoolCalendar/SchoolCalendarMutationService.php +++ b/app/Services/Settings/SchoolCalendar/SchoolCalendarMutationService.php @@ -11,8 +11,7 @@ class SchoolCalendarMutationService public function __construct( private SchoolCalendarContextService $context, private SchoolCalendarNotificationService $notificationService - ) { - } + ) {} public function create(array $payload, array $targets = []): array { @@ -23,7 +22,7 @@ class SchoolCalendarMutationService return CalendarEvent::query()->create($data); }); } catch (\Throwable $e) { - Log::error('SchoolCalendar create failed: ' . $e->getMessage()); + Log::error('SchoolCalendar create failed: '.$e->getMessage()); throw $e; } @@ -45,7 +44,7 @@ class SchoolCalendarMutationService $event->save(); }); } catch (\Throwable $e) { - Log::error('SchoolCalendar update failed: ' . $e->getMessage()); + Log::error('SchoolCalendar update failed: '.$e->getMessage()); throw $e; } @@ -65,7 +64,7 @@ class SchoolCalendarMutationService $event->delete(); }); } catch (\Throwable $e) { - Log::error('SchoolCalendar delete failed: ' . $e->getMessage()); + Log::error('SchoolCalendar delete failed: '.$e->getMessage()); throw $e; } } @@ -77,15 +76,15 @@ class SchoolCalendarMutationService 'description' => $payload['description'] ?? null, 'event_type' => $payload['event_type'] ?? null, 'date' => $payload['date'] ?? $payload['start'] ?? null, - 'notify_parent' => !empty($payload['notify_parent']) ? 1 : 0, - 'notify_teacher' => !empty($payload['notify_teacher']) ? 1 : 0, - 'notify_admin' => !empty($payload['notify_admin']) ? 1 : 0, - 'no_school' => !empty($payload['no_school']) ? 1 : 0, + 'notify_parent' => ! empty($payload['notify_parent']) ? 1 : 0, + 'notify_teacher' => ! empty($payload['notify_teacher']) ? 1 : 0, + 'notify_admin' => ! empty($payload['notify_admin']) ? 1 : 0, + 'no_school' => ! empty($payload['no_school']) ? 1 : 0, 'school_year' => $payload['school_year'] ?? $this->context->defaultSchoolYear(), 'semester' => $payload['semester'] ?? $this->context->defaultSemester(), ]; - if (!CalendarEvent::supportsEventType()) { + if (! CalendarEvent::supportsEventType()) { unset($data['event_type']); } diff --git a/app/Services/Settings/SchoolCalendar/SchoolCalendarNotificationService.php b/app/Services/Settings/SchoolCalendar/SchoolCalendarNotificationService.php index d51eba72..a62ae428 100644 --- a/app/Services/Settings/SchoolCalendar/SchoolCalendarNotificationService.php +++ b/app/Services/Settings/SchoolCalendar/SchoolCalendarNotificationService.php @@ -13,8 +13,7 @@ class SchoolCalendarNotificationService public function __construct( private EmailService $emailService, private ApplicationUrlService $urls, - ) { - } + ) {} public function notify(array $event, array $targets): array { @@ -27,7 +26,7 @@ class SchoolCalendarNotificationService $results = []; foreach ($targets as $group => $enabled) { - if (!$enabled) { + if (! $enabled) { continue; } @@ -35,12 +34,13 @@ class SchoolCalendarNotificationService if (empty($emails)) { Log::warning("SchoolCalendar: no recipient emails for {$group}."); $results[$group] = null; + continue; } $to = $emails[0] ?? ''; $ok = $this->emailService->send($to, $subject, $body, 'general', [], $emails); - if (!$ok) { + if (! $ok) { Log::warning("SchoolCalendar: failed to send calendar notification to {$group}."); } $results[$group] = $ok; @@ -52,8 +52,9 @@ class SchoolCalendarNotificationService private function buildSubject(array $event): string { $title = trim((string) ($event['title'] ?? 'School Calendar Update')); - $typeSegment = !empty($event['event_type']) ? ' (' . $event['event_type'] . ')' : ''; - return 'School Calendar: ' . $title . $typeSegment; + $typeSegment = ! empty($event['event_type']) ? ' ('.$event['event_type'].')' : ''; + + return 'School Calendar: '.$title.$typeSegment; } private function buildBody(array $event): string @@ -63,12 +64,12 @@ class SchoolCalendarNotificationService $date = (string) ($event['date'] ?? ''); $description = trim((string) ($event['description'] ?? '')); - $body = '

' . e($title) . '

'; - $body .= '

Date: ' . e($date) . '

'; + $body = '

'.e($title).'

'; + $body .= '

Date: '.e($date).'

'; if ($description !== '') { - $body .= '

' . nl2br(e($description)) . '

'; + $body .= '

'.nl2br(e($description)).'

'; } - $body .= '

View calendar

'; + $body .= '

View calendar

'; return $body; } @@ -88,18 +89,19 @@ class SchoolCalendarNotificationService $emails = []; foreach ($rows as $row) { $email = trim((string) ($row['email'] ?? '')); - if ($email === '' || !filter_var($email, FILTER_VALIDATE_EMAIL)) { + if ($email === '' || ! filter_var($email, FILTER_VALIDATE_EMAIL)) { continue; } $emails[$email] = true; } + return array_values(array_keys($emails)); } private function fetchAdminRows(): array { $excluded = ['guest', 'teacher', 'teacher_assistant', 'parent']; - $escaped = array_map(static fn ($value) => "'" . addslashes($value) . "'", $excluded); + $escaped = array_map(static fn ($value) => "'".addslashes($value)."'", $excluded); $excludedList = implode(', ', $escaped); $builder = DB::table('users as u') diff --git a/app/Services/Settings/SchoolCalendar/SchoolCalendarQueryService.php b/app/Services/Settings/SchoolCalendar/SchoolCalendarQueryService.php index 9688cc42..0090ef9f 100644 --- a/app/Services/Settings/SchoolCalendar/SchoolCalendarQueryService.php +++ b/app/Services/Settings/SchoolCalendar/SchoolCalendarQueryService.php @@ -3,7 +3,6 @@ namespace App\Services\Settings\SchoolCalendar; use App\Models\CalendarEvent; -use Illuminate\Database\Eloquent\Builder; use Illuminate\Database\Eloquent\Collection; class SchoolCalendarQueryService @@ -36,7 +35,7 @@ class SchoolCalendarQueryService public function filterEventsForAudience(Collection $events, ?string $audience): Collection { $audience = $audience !== null ? strtolower(trim($audience)) : null; - if (!in_array($audience, ['parent', 'teacher', 'admin'], true)) { + if (! in_array($audience, ['parent', 'teacher', 'admin'], true)) { return $events; } @@ -51,7 +50,7 @@ class SchoolCalendarQueryService return true; } - if (!$hasAny) { + if (! $hasAny) { return true; } diff --git a/app/Services/Settings/SettingsService.php b/app/Services/Settings/SettingsService.php index 045ae1a4..6ed06c95 100644 --- a/app/Services/Settings/SettingsService.php +++ b/app/Services/Settings/SettingsService.php @@ -32,7 +32,7 @@ class SettingsService $updates['timezone'] = $payload['timezone']; } - if (!empty($updates)) { + if (! empty($updates)) { $updates['updated_by'] = $userId; $settings->fill($updates); $settings->save(); diff --git a/app/Services/Staff/StaffCommandService.php b/app/Services/Staff/StaffCommandService.php index 9aab50c5..edd3fd4d 100644 --- a/app/Services/Staff/StaffCommandService.php +++ b/app/Services/Staff/StaffCommandService.php @@ -10,9 +10,7 @@ use RuntimeException; class StaffCommandService { - public function __construct(private GlobalConfigService $configService) - { - } + public function __construct(private GlobalConfigService $configService) {} public function create(array $payload): Staff { @@ -21,7 +19,7 @@ class StaffCommandService $roleName = (string) ($payload['role_name'] ?? ''); $activeRole = strtolower($roleName); - if (!empty($payload['status']) && strtolower((string) $payload['status']) === 'inactive') { + if (! empty($payload['status']) && strtolower((string) $payload['status']) === 'inactive') { $activeRole = 'inactive'; } @@ -40,7 +38,7 @@ class StaffCommandService 'updated_at' => now(), ]); - if (!$staff) { + if (! $staff) { throw new RuntimeException('Failed to create staff member.'); } @@ -61,13 +59,13 @@ class StaffCommandService if (array_key_exists('role_name', $updates)) { $updates['active_role'] = strtolower((string) $updates['role_name']); } - if (!empty($payload['status']) && strtolower((string) $payload['status']) === 'inactive') { + if (! empty($payload['status']) && strtolower((string) $payload['status']) === 'inactive') { $updates['active_role'] = 'inactive'; } $updates['updated_at'] = now(); - if (!empty($updates)) { + if (! empty($updates)) { $staff->fill($updates); $staff->save(); } @@ -83,7 +81,7 @@ class StaffCommandService private function resolveUserId(array $payload): int { - if (!empty($payload['user_id'])) { + if (! empty($payload['user_id'])) { return (int) $payload['user_id']; } diff --git a/app/Services/Staff/StaffQueryService.php b/app/Services/Staff/StaffQueryService.php index 5e465d95..29db117b 100644 --- a/app/Services/Staff/StaffQueryService.php +++ b/app/Services/Staff/StaffQueryService.php @@ -6,14 +6,11 @@ use App\Models\Staff; use App\Models\TeacherClass; use App\Models\User; use App\Services\System\GlobalConfigService; -use Illuminate\Contracts\Pagination\LengthAwarePaginator; use Illuminate\Support\Facades\DB; class StaffQueryService { - public function __construct(private GlobalConfigService $configService) - { - } + public function __construct(private GlobalConfigService $configService) {} public function paginate(array $filters, int $page, int $perPage): array { @@ -22,7 +19,7 @@ class StaffQueryService $query = Staff::query() ->whereNotIn(DB::raw('LOWER(active_role)'), $excluded); - if (!empty($filters['role'])) { + if (! empty($filters['role'])) { $role = strtolower(trim((string) $filters['role'])); $query->whereRaw('LOWER(active_role) = ?', [$role]); } @@ -47,7 +44,7 @@ class StaffQueryService if (in_array($role, ['teacher', 'teacher_assistant'], true)) { $tid = (int) ($staff->user_id ?? 0); $labels = $assignByTeacher[$tid] ?? []; - if (!empty($labels)) { + if (! empty($labels)) { $staff->class_section = implode(', ', array_unique($labels)); $staff->verification_issue = false; } else { @@ -101,10 +98,10 @@ class StaffQueryService continue; } $pos = strtolower((string) ($row->position ?? '')); - if (!in_array($pos, ['main', 'ta'], true)) { + if (! in_array($pos, ['main', 'ta'], true)) { continue; } - $assignByTeacher[$tid][] = $name . ' (' . $pos . ')'; + $assignByTeacher[$tid][] = $name.' ('.$pos.')'; } return $assignByTeacher; diff --git a/app/Services/Staff/StaffTimeOffLinkService.php b/app/Services/Staff/StaffTimeOffLinkService.php index c1bd8719..d66f9ea8 100644 --- a/app/Services/Staff/StaffTimeOffLinkService.php +++ b/app/Services/Staff/StaffTimeOffLinkService.php @@ -5,9 +5,11 @@ namespace App\Services\Staff; class StaffTimeOffLinkService { private const TOKEN_CONTEXT = 'timeoff_notify'; + private const DEFAULT_TTL = 1209600; private string $secret; + private int $ttl; public function __construct(?string $secret = null, ?int $ttlSeconds = null) @@ -30,12 +32,12 @@ class StaffTimeOffLinkService public function parseToken(?string $token): ?array { - if (!is_string($token) || $token === '') { + if (! is_string($token) || $token === '') { return null; } $payload = $this->jwtDecode($token, $this->secret); - if (!is_array($payload)) { + if (! is_array($payload)) { return null; } @@ -77,17 +79,17 @@ class StaffTimeOffLinkService $payload = json_decode($this->base64UrlDecode($payload64), true); $signature = $this->base64UrlDecode($signature64); - if (!is_array($header) || !is_array($payload)) { + if (! is_array($header) || ! is_array($payload)) { return null; } - $signingInput = $header64 . '.' . $payload64; + $signingInput = $header64.'.'.$payload64; if (($header['alg'] ?? '') !== 'HS256') { return null; } $expected = hash_hmac('sha256', $signingInput, $secret, true); - if (!hash_equals($expected, $signature)) { + if (! hash_equals($expected, $signature)) { return null; } @@ -105,6 +107,7 @@ class StaffTimeOffLinkService if ($padding > 0) { $data .= str_repeat('=', 4 - $padding); } + return base64_decode(strtr($data, '-_', '+/')) ?: ''; } } diff --git a/app/Services/Students/StudentAssignmentService.php b/app/Services/Students/StudentAssignmentService.php index c4ff20e2..a7461e44 100644 --- a/app/Services/Students/StudentAssignmentService.php +++ b/app/Services/Students/StudentAssignmentService.php @@ -11,9 +11,7 @@ use Illuminate\Support\Facades\Schema; class StudentAssignmentService { - public function __construct(private StudentConfigService $configService) - { - } + public function __construct(private StudentConfigService $configService) {} public function assignClasses(int $studentId, array $classSectionIds, bool $isEventOnly, ?int $userId = null): array { @@ -27,7 +25,7 @@ class StudentAssignmentService } $student = Student::query()->find($studentId); - if (!$student) { + if (! $student) { return ['ok' => false, 'message' => 'Student not found.']; } @@ -47,7 +45,7 @@ class StudentAssignmentService } $missing = array_diff($classSectionIds, array_keys($sectionMap)); - if (!empty($missing)) { + if (! empty($missing)) { return ['ok' => false, 'message' => 'One or more selected classes do not exist.']; } @@ -105,7 +103,7 @@ class StudentAssignmentService } } - if (!$isEventOnly) { + if (! $isEventOnly) { $enrollment = Enrollment::query() ->where('student_id', $studentId) ->where('school_year', $schoolYear) @@ -143,12 +141,12 @@ class StudentAssignmentService $displayNames = []; foreach ($classSectionIds as $classSectionId) { $section = $sectionMap[$classSectionId] ?? null; - if (!$section) { + if (! $section) { continue; } $name = (string) ($section['class_section_name'] ?? ''); if ($name === '') { - $name = 'Section #' . $classSectionId; + $name = 'Section #'.$classSectionId; } $displayNames[] = $name; } @@ -180,7 +178,7 @@ class StudentAssignmentService ->where('school_year', $schoolYear) ->first(); - if (!$row) { + if (! $row) { return ['ok' => false, 'message' => 'Assignment not found for this student/class.']; } @@ -212,7 +210,7 @@ class StudentAssignmentService ->first(); if ($enrollment) { - $newClassId = !empty($remainingIds) ? $remainingIds[0] : null; + $newClassId = ! empty($remainingIds) ? $remainingIds[0] : null; if ((int) $enrollment->class_section_id === $classSectionId || $newClassId !== null) { $enrollment->update([ 'class_section_id' => $newClassId, @@ -298,7 +296,7 @@ class StudentAssignmentService $payload['updated_by'] = $modifiedBy; } - $results[$table . '_updated'] = DB::table($table) + $results[$table.'_updated'] = DB::table($table) ->where('student_id', $studentId) ->where('semester', $semester) ->where('school_year', $schoolYear) @@ -312,6 +310,7 @@ class StudentAssignmentService { $values = array_map('intval', $ids); $values = array_values(array_unique(array_filter($values, static fn ($v) => $v > 0))); + return $values; } } diff --git a/app/Services/Students/StudentAutoDistributionService.php b/app/Services/Students/StudentAutoDistributionService.php index 0f857b60..92b4857f 100644 --- a/app/Services/Students/StudentAutoDistributionService.php +++ b/app/Services/Students/StudentAutoDistributionService.php @@ -9,9 +9,7 @@ use Illuminate\Support\Facades\DB; class StudentAutoDistributionService { - public function __construct(private StudentConfigService $configService) - { - } + public function __construct(private StudentConfigService $configService) {} public function promotionTotals(?string $schoolYear = null): array { @@ -30,6 +28,7 @@ class StudentAutoDistributionService $name = strtolower($nameRaw); if ($name === 'kg' || $name === 'youth') { $wanted[] = $row; + continue; } if (ctype_digit($name)) { @@ -167,6 +166,7 @@ class StudentAutoDistributionService $bestIdx = $idx; } } + return $bestIdx; }; diff --git a/app/Services/Students/StudentDirectoryService.php b/app/Services/Students/StudentDirectoryService.php index 1367ba70..27343327 100644 --- a/app/Services/Students/StudentDirectoryService.php +++ b/app/Services/Students/StudentDirectoryService.php @@ -10,9 +10,7 @@ use Illuminate\Support\Facades\DB; class StudentDirectoryService { - public function __construct(private StudentConfigService $configService) - { - } + public function __construct(private StudentConfigService $configService) {} public function assignmentData(?string $schoolYear = null): array { @@ -55,7 +53,7 @@ class StudentDirectoryService $studentId = (int) ($student['id'] ?? 0); $sectionNames = StudentClass::getClassSectionsByStudentIdWithFlags($studentId, $schoolYear, true); $sectionIds = StudentClass::getClassSectionIdsByStudentId($studentId, $schoolYear); - $sectionDisplay = !empty($sectionNames) ? implode(', ', $sectionNames) : ''; + $sectionDisplay = ! empty($sectionNames) ? implode(', ', $sectionNames) : ''; $parentId = (int) ($student['parent_id'] ?? 0); $emergencyInfo = $parentId > 0 @@ -64,7 +62,7 @@ class StudentDirectoryService $studentData[] = [ 'student_id' => $studentId, - 'name' => trim(($student['firstname'] ?? '') . ' ' . ($student['lastname'] ?? '')), + 'name' => trim(($student['firstname'] ?? '').' '.($student['lastname'] ?? '')), 'age' => $student['age'] ?? 'N/A', 'email' => $emergencyInfo['email'] ?? '', 'phone' => $emergencyInfo['cellphone'] ?? '', @@ -149,9 +147,10 @@ class StudentDirectoryService $names = $classMap[$studentId] ?? []; $names = array_values(array_unique(array_filter($names))); $student['class_sections'] = $names; - $student['class_section_name'] = !empty($names) ? implode(', ', $names) : 'No class assigned'; + $student['class_section_name'] = ! empty($names) ? implode(', ', $names) : 'No class assigned'; } unset($student); + return $students; }; @@ -188,7 +187,7 @@ class StudentDirectoryService 'school_year', ]) ->when($selectedYear !== '', fn ($q) => $q->where('school_year', $selectedYear)) - ->when(!empty($parentIds), fn ($q) => $q->whereIn('parent_id', $parentIds)) + ->when(! empty($parentIds), fn ($q) => $q->whereIn('parent_id', $parentIds)) ->orderBy('lastname') ->orderBy('firstname') ->get() diff --git a/app/Services/Students/StudentProfileService.php b/app/Services/Students/StudentProfileService.php index 975e895a..c5495f62 100644 --- a/app/Services/Students/StudentProfileService.php +++ b/app/Services/Students/StudentProfileService.php @@ -12,7 +12,7 @@ class StudentProfileService public function updateStudent(int $studentId, array $payload): array { $student = Student::query()->find($studentId); - if (!$student) { + if (! $student) { return ['ok' => false, 'message' => 'Student not found.']; } @@ -43,7 +43,7 @@ class StudentProfileService $shouldSyncAllergies = array_key_exists('allergies', $payload); DB::transaction(function () use ($student, $studentData, $conditions, $allergies): void { - if (!empty($studentData)) { + if (! empty($studentData)) { $student->update($studentData); } @@ -84,7 +84,7 @@ class StudentProfileService private function syncHealthList(int $studentId, string $modelClass, string $field, array $newValues): void { - $model = new $modelClass(); + $model = new $modelClass; $rows = $model->newQuery()->where('student_id', $studentId)->get(['id', $field])->toArray(); $current = []; @@ -111,14 +111,14 @@ class StudentProfileService $toDeleteKeys = array_diff(array_keys($current), array_keys($incoming)); $toInsertKeys = array_diff(array_keys($incoming), array_keys($current)); - if (!empty($toDeleteKeys)) { + if (! empty($toDeleteKeys)) { $ids = array_map(fn ($key) => $byId[$key], $toDeleteKeys); - if (!empty($ids)) { + if (! empty($ids)) { $model->newQuery()->whereIn('id', $ids)->delete(); } } - if (!empty($toInsertKeys)) { + if (! empty($toInsertKeys)) { $batch = []; foreach ($toInsertKeys as $key) { $batch[] = [ @@ -126,7 +126,7 @@ class StudentProfileService $field => $incoming[$key], ]; } - if (!empty($batch)) { + if (! empty($batch)) { $model->newQuery()->insert($batch); } } diff --git a/app/Services/Students/StudentScoreCardService.php b/app/Services/Students/StudentScoreCardService.php index 96379ae7..1400758e 100644 --- a/app/Services/Students/StudentScoreCardService.php +++ b/app/Services/Students/StudentScoreCardService.php @@ -146,7 +146,7 @@ class StudentScoreCardService ->where('id', $studentId) ->first(); - if (!$student) { + if (! $student) { return ['ok' => false, 'message' => 'Student not found.']; } @@ -177,6 +177,7 @@ class StudentScoreCardService if ($ay !== $by) { return strcmp($by, $ay); } + return strcmp((string) ($a['semester'] ?? ''), (string) ($b['semester'] ?? '')); }); @@ -184,7 +185,7 @@ class StudentScoreCardService foreach ($rows as $row) { $year = (string) ($row['school_year'] ?? ''); $score = $row['semester_score'] ?? null; - if ($year === '' || !is_numeric($score)) { + if ($year === '' || ! is_numeric($score)) { continue; } $yearScoreMap[$year][] = (float) $score; @@ -224,14 +225,14 @@ class StudentScoreCardService if ($commentVal === '') { continue; } - $key = $year . '|' . $semester; + $key = $year.'|'.$semester; $commentMap[$key][$type] = $commentVal; } foreach ($rows as &$row) { $year = (string) ($row['school_year'] ?? ''); $semester = strtolower(trim((string) ($row['semester'] ?? ''))); - $row['comments'] = $commentMap[$year . '|' . $semester] ?? []; + $row['comments'] = $commentMap[$year.'|'.$semester] ?? []; $row['year_score'] = $yearAvg[$year] ?? null; } unset($row); diff --git a/app/Services/Students/StudentStatusService.php b/app/Services/Students/StudentStatusService.php index 86b272f6..51faa77e 100644 --- a/app/Services/Students/StudentStatusService.php +++ b/app/Services/Students/StudentStatusService.php @@ -8,9 +8,7 @@ use App\Models\StudentClass; class StudentStatusService { - public function __construct(private StudentConfigService $configService) - { - } + public function __construct(private StudentConfigService $configService) {} public function setActive(int $studentId, bool $isActive, ?int $userId = null): array { @@ -19,7 +17,7 @@ class StudentStatusService $schoolYear = (string) ($context['school_year'] ?? ''); $student = Student::query()->find($studentId); - if (!$student) { + if (! $student) { return ['ok' => false, 'message' => 'Student not found.']; } @@ -36,7 +34,7 @@ class StudentStatusService ->when($schoolYear !== '', fn ($q) => $q->where('school_year', $schoolYear)) ->first(); - if (!$hasCurrentClass) { + if (! $hasCurrentClass) { $lastClass = StudentClass::query() ->where('student_id', $studentId) ->orderByDesc('updated_at') @@ -59,7 +57,7 @@ class StudentStatusService $classLabel = ClassSection::getClassSectionNameBySectionId($restoreClassId); if ($classLabel) { - $message .= ' Class assignment restored to ' . $classLabel . '.'; + $message .= ' Class assignment restored to '.$classLabel.'.'; } } else { $message .= ' No class assignment found for the current term.'; diff --git a/app/Services/Subjects/SubjectCurriculumService.php b/app/Services/Subjects/SubjectCurriculumService.php index 232c2748..4b4f9f88 100644 --- a/app/Services/Subjects/SubjectCurriculumService.php +++ b/app/Services/Subjects/SubjectCurriculumService.php @@ -80,7 +80,7 @@ class SubjectCurriculumService public function update(int $id, array $payload): ?SubjectCurriculum { $entry = SubjectCurriculum::query()->find($id); - if (!$entry) { + if (! $entry) { return null; } @@ -104,7 +104,7 @@ class SubjectCurriculumService public function delete(int $id): bool { $entry = SubjectCurriculum::query()->find($id); - if (!$entry) { + if (! $entry) { return false; } diff --git a/app/Services/Support/ContactMessageService.php b/app/Services/Support/ContactMessageService.php index 4ca3882d..6a205c05 100644 --- a/app/Services/Support/ContactMessageService.php +++ b/app/Services/Support/ContactMessageService.php @@ -7,9 +7,7 @@ use Illuminate\Support\Facades\Log; class ContactMessageService { - public function __construct(private EmailDispatchService $emailService) - { - } + public function __construct(private EmailDispatchService $emailService) {} public function send(array $payload): array { @@ -20,17 +18,17 @@ class ContactMessageService $formatted = "

From: {$name} ({$email})

"; $formatted .= "

Subject: {$subject}

"; - $formatted .= '

' . nl2br($message) . '

'; + $formatted .= '

'.nl2br($message).'

'; $recipient = (string) config('support.contact_email', 'support@alrahmaisgl.org'); $emailSent = false; - if (!app()->runningUnitTests()) { + if (! app()->runningUnitTests()) { $emailSent = $this->emailService->send($recipient, $subject, $formatted, null, $email, $name); } - if (!$emailSent) { - Log::warning('Contact email failed for ' . $email); + if (! $emailSent) { + Log::warning('Contact email failed for '.$email); } return [ diff --git a/app/Services/Support/SupportRequestService.php b/app/Services/Support/SupportRequestService.php index a70e90cf..e5bafee0 100644 --- a/app/Services/Support/SupportRequestService.php +++ b/app/Services/Support/SupportRequestService.php @@ -14,20 +14,19 @@ class SupportRequestService public function __construct( private GlobalConfigService $configService, private EmailDispatchService $emailService - ) { - } + ) {} public function list(int $userId, array $filters, int $page, int $perPage, bool $asAdmin = false): LengthAwarePaginator { $query = SupportRequest::query()->with('user'); - if (!$asAdmin) { + if (! $asAdmin) { $query->where('user_id', $userId); - } elseif (!empty($filters['user_id'])) { + } elseif (! empty($filters['user_id'])) { $query->where('user_id', (int) $filters['user_id']); } - if (!empty($filters['status'])) { + if (! empty($filters['status'])) { $query->where('status', $filters['status']); } @@ -55,7 +54,7 @@ class SupportRequestService $recipient = (string) config('support.contact_email', 'support@alrahmaisgl.org'); $emailSent = false; - if (!app()->runningUnitTests()) { + if (! app()->runningUnitTests()) { $emailSent = $this->emailService->send( $recipient, (string) $payload['subject'], @@ -66,8 +65,8 @@ class SupportRequestService ); } - if (!$emailSent) { - Log::warning('Support email failed for user ' . $userId); + if (! $emailSent) { + Log::warning('Support email failed for user '.$userId); } return [ diff --git a/app/Services/System/CleanupSchedulerService.php b/app/Services/System/CleanupSchedulerService.php index 31e299e7..18d616bd 100644 --- a/app/Services/System/CleanupSchedulerService.php +++ b/app/Services/System/CleanupSchedulerService.php @@ -10,7 +10,7 @@ class CleanupSchedulerService public function shouldRun(string $cacheKey, int $minutes): bool { $lastRun = Cache::get($cacheKey); - if (!$lastRun) { + if (! $lastRun) { return true; } diff --git a/app/Services/System/ConfigUpdateService.php b/app/Services/System/ConfigUpdateService.php index 163abd26..8aaa3647 100644 --- a/app/Services/System/ConfigUpdateService.php +++ b/app/Services/System/ConfigUpdateService.php @@ -24,31 +24,34 @@ class ConfigUpdateService public function runTask(string $task, bool $dry, bool $force, DateTimeZone $tz): array { - if (!isset($this->tasks[$task])) { + if (! isset($this->tasks[$task])) { return ['ok' => false, 'message' => 'Unknown task.']; } - $lockFile = storage_path('app/locks/config_update_' . $task . '.lock'); - if (!is_dir(dirname($lockFile))) { + $lockFile = storage_path('app/locks/config_update_'.$task.'.lock'); + if (! is_dir(dirname($lockFile))) { @mkdir(dirname($lockFile), 0775, true); } $fp = @fopen($lockFile, 'c+'); - if (!$fp) { + if (! $fp) { return ['ok' => false, 'message' => 'Unable to open lock file.']; } - if (!$force && !flock($fp, LOCK_EX | LOCK_NB)) { + if (! $force && ! flock($fp, LOCK_EX | LOCK_NB)) { fclose($fp); + return ['ok' => false, 'message' => 'Task is already running.']; } try { $method = $this->tasks[$task]; $ok = (bool) $this->{$method}($dry, $force, $tz); + return ['ok' => $ok, 'task' => $task]; } catch (\Throwable $e) { - Log::error('ConfigUpdate task failed: ' . $e->getMessage()); + Log::error('ConfigUpdate task failed: '.$e->getMessage()); + return ['ok' => false, 'message' => $e->getMessage()]; } finally { try { @@ -74,11 +77,12 @@ class ConfigUpdateService { $now = new DateTimeImmutable('now', $tz); $isJune1 = $now->format('n-j') === '6-1'; - if (!$isJune1 && !$force) { + if (! $isJune1 && ! $force) { return true; } - $value = $now->format('Y') . '-12-31'; + $value = $now->format('Y').'-12-31'; + return $dry ? true : Configuration::setConfigValueByKey('date_age_reference', $value); } @@ -86,7 +90,7 @@ class ConfigUpdateService { $now = new DateTimeImmutable('now', $tz); $isFeb1 = $now->format('n-j') === '2-1'; - if (!$isFeb1 && !$force) { + if (! $isFeb1 && ! $force) { return true; } @@ -97,7 +101,7 @@ class ConfigUpdateService { $now = new DateTimeImmutable('now', $tz); $isJun1 = $now->format('n-j') === '6-1'; - if (!$isJun1 && !$force) { + if (! $isJun1 && ! $force) { return true; } diff --git a/app/Services/System/HealthCheckService.php b/app/Services/System/HealthCheckService.php index de744997..c5e32806 100644 --- a/app/Services/System/HealthCheckService.php +++ b/app/Services/System/HealthCheckService.php @@ -12,10 +12,10 @@ class HealthCheckService $uploadsBase = storage_path('app/uploads'); $paths = [ 'uploads' => $uploadsBase, - 'uploads/reimbursements' => $uploadsBase . DIRECTORY_SEPARATOR . 'reimbursements', - 'uploads/receipts' => $uploadsBase . DIRECTORY_SEPARATOR . 'receipts', - 'uploads/checks' => $uploadsBase . DIRECTORY_SEPARATOR . 'checks', - 'uploads/early_dismissal_signatures' => $uploadsBase . DIRECTORY_SEPARATOR . 'early_dismissal_signatures', + 'uploads/reimbursements' => $uploadsBase.DIRECTORY_SEPARATOR.'reimbursements', + 'uploads/receipts' => $uploadsBase.DIRECTORY_SEPARATOR.'receipts', + 'uploads/checks' => $uploadsBase.DIRECTORY_SEPARATOR.'checks', + 'uploads/early_dismissal_signatures' => $uploadsBase.DIRECTORY_SEPARATOR.'early_dismissal_signatures', ]; $pathsStatus = []; @@ -48,10 +48,10 @@ class HealthCheckService }, true); $okDb = true; - if ($dbChecks['user_preferences_exists'] && !$dbChecks['user_preferences_has_timezone']) { + if ($dbChecks['user_preferences_exists'] && ! $dbChecks['user_preferences_has_timezone']) { $okDb = false; } - if ($dbChecks['settings_exists'] && !$dbChecks['settings_has_timezone']) { + if ($dbChecks['settings_exists'] && ! $dbChecks['settings_has_timezone']) { $okDb = false; } diff --git a/app/Services/System/TimeService.php b/app/Services/System/TimeService.php index 2a90a6ed..62eae8f6 100644 --- a/app/Services/System/TimeService.php +++ b/app/Services/System/TimeService.php @@ -50,7 +50,7 @@ class TimeService public function toUtc($value, ?string $fromTz = null, string $format = 'Y-m-d H:i:s'): ?string { $dt = $this->parse($value, $fromTz); - if (!$dt) { + if (! $dt) { return null; } @@ -60,7 +60,7 @@ class TimeService public function toLocal($value, ?string $sourceTz = null, ?string $targetTz = null, string $format = 'Y-m-d H:i:s'): ?string { $dt = $this->parse($value, $sourceTz); - if (!$dt) { + if (! $dt) { return null; } @@ -70,7 +70,7 @@ class TimeService public function formatLocal($value, string $format = 'Y-m-d', ?string $sourceTz = null): string { $dt = $this->parse($value, $sourceTz); - if (!$dt) { + if (! $dt) { return ''; } diff --git a/app/Services/Teachers/TeacherAbsenceService.php b/app/Services/Teachers/TeacherAbsenceService.php index 43817f7c..cc96011d 100644 --- a/app/Services/Teachers/TeacherAbsenceService.php +++ b/app/Services/Teachers/TeacherAbsenceService.php @@ -2,12 +2,12 @@ namespace App\Services\Teachers; -use App\Services\ApplicationUrlService; -use App\Services\Staff\StaffTimeOffLinkService; use App\Models\StaffAttendance; use App\Models\TeacherClass; use App\Models\User; +use App\Services\ApplicationUrlService; use App\Services\SemesterRangeService; +use App\Services\Staff\StaffTimeOffLinkService; use Carbon\Carbon; use Illuminate\Support\Facades\Mail; @@ -18,8 +18,7 @@ class TeacherAbsenceService private SemesterRangeService $semesterRangeService, private StaffTimeOffLinkService $staffTimeOffLinkService, private ApplicationUrlService $urls, - ) { - } + ) {} public function formData(int $userId): array { @@ -29,7 +28,7 @@ class TeacherAbsenceService $teacher = User::query()->find($userId); $displayName = $teacher - ? trim(($teacher->firstname ?? '') . ' ' . ($teacher->lastname ?? '')) + ? trim(($teacher->firstname ?? '').' '.($teacher->lastname ?? '')) : 'Teacher'; $existing = StaffAttendance::query() @@ -76,7 +75,7 @@ class TeacherAbsenceService } $reasonBase = $reasonType !== '' ? strtolower($reasonType) : ''; - $reason = $reasonBase !== '' ? ($reasonBase . ': ' . $reasonText) : $reasonText; + $reason = $reasonBase !== '' ? ($reasonBase.': '.$reasonText) : $reasonText; $allowedSet = array_fill_keys($this->allowedAbsenceDates($schoolYear), true); $roleName = User::getUserRoleName($userId) ?: null; @@ -97,10 +96,12 @@ class TeacherAbsenceService $parsed = Carbon::createFromFormat('Y-m-d', $date); if ($parsed->format('Y-m-d') !== $date || empty($allowedSet[$date])) { $invalid[] = $date; + continue; } } catch (\Throwable) { $invalid[] = $date; + continue; } @@ -126,10 +127,10 @@ class TeacherAbsenceService } } - if (!empty($invalid)) { + if (! empty($invalid)) { return [ 'ok' => false, - 'message' => 'Invalid dates: ' . implode(', ', $invalid), + 'message' => 'Invalid dates: '.implode(', ', $invalid), 'status' => 422, ]; } @@ -147,7 +148,7 @@ class TeacherAbsenceService return [ 'ok' => true, - 'message' => $saved . ' day(s) saved as absent.', + 'message' => $saved.' day(s) saved as absent.', 'saved' => $saved, 'dates' => $savedDates, 'status' => 200, @@ -211,10 +212,10 @@ class TeacherAbsenceService ): void { try { $user = User::query()->find($userId); - $fullName = trim(($user->firstname ?? '') . ' ' . ($user->lastname ?? '')) ?: 'Teacher'; + $fullName = trim(($user->firstname ?? '').' '.($user->lastname ?? '')) ?: 'Teacher'; $userEmail = $user->email ?? ''; $role = $roleName ?: 'teacher'; - $dateList = !empty($savedDates) ? implode(', ', $savedDates) : implode(', ', $dates); + $dateList = ! empty($savedDates) ? implode(', ', $savedDates) : implode(', ', $dates); $subject = sprintf( 'TimeOff Request: %s (%s) — %s', @@ -227,21 +228,21 @@ class TeacherAbsenceService $assignedText = $this->formatAssignedClasses($userId, $schoolYear, $semester); $body = '
' - . '

New TimeOff Request

' - . '

A staff time-off request was submitted from the teacher portal.

' - . '' - . '' - . '' - . '' - . '' - . '' - . '' - . '' - . '' - . '' - . '' - . '
Name' . e($fullName) . '
Email' . e($userEmail) . '
Role' . e((string) $role) . '
Semester' . e($semester) . '
School Year' . e($schoolYear) . '
Reason Type' . e($reasonType ?: '-') . '
Reason' . e($reasonText) . '
Dates' . e($dateList ?: '-') . '
Assigned Class(es)' . e($assignedText) . '
Submitted At' . e($submittedAt) . '
' - . '
'; + .'

New TimeOff Request

' + .'

A staff time-off request was submitted from the teacher portal.

' + .'' + .'' + .'' + .'' + .'' + .'' + .'' + .'' + .'' + .'' + .'' + .'
Name'.e($fullName).'
Email'.e($userEmail).'
Role'.e((string) $role).'
Semester'.e($semester).'
School Year'.e($schoolYear).'
Reason Type'.e($reasonType ?: '-').'
Reason'.e($reasonText).'
Dates'.e($dateList ?: '-').'
Assigned Class(es)'.e($assignedText).'
Submitted At'.e($submittedAt).'
' + .''; $token = $this->staffTimeOffLinkService->createToken([ 'uid' => $userId, @@ -256,9 +257,9 @@ class TeacherAbsenceService ]); $notifyUrl = $this->urls->timeoffNotifyUrl($token); - $body .= '

Click Send confirmation email to ' - . e($fullName) - . ' so the staff member is notified automatically. This link expires in 14 days.

'; + $body .= '

Click Send confirmation email to ' + .e($fullName) + .' so the staff member is notified automatically. This link expires in 14 days.

'; $principalEmail = env('PRINCIPAL_EMAIL', 'principal@alrahmaisgl.org'); @@ -283,10 +284,10 @@ class TeacherAbsenceService $name = (string) ($assignment['class_section_name'] ?? ''); $role = (string) ($assignment['teacher_role'] ?? ''); if ($name !== '') { - $parts[] = $role !== '' ? ($name . ' (' . $role . ')') : $name; + $parts[] = $role !== '' ? ($name.' ('.$role.')') : $name; } } - return !empty($parts) ? implode(', ', $parts) : 'No assigned class'; + return ! empty($parts) ? implode(', ', $parts) : 'No assigned class'; } } diff --git a/app/Services/Teachers/TeacherAssignmentService.php b/app/Services/Teachers/TeacherAssignmentService.php index 91595428..d5890e14 100644 --- a/app/Services/Teachers/TeacherAssignmentService.php +++ b/app/Services/Teachers/TeacherAssignmentService.php @@ -8,9 +8,7 @@ use App\Models\User; class TeacherAssignmentService { - public function __construct(private TeacherConfigService $configService) - { - } + public function __construct(private TeacherConfigService $configService) {} public function listAssignments(?string $schoolYear = null): array { @@ -53,7 +51,7 @@ class TeacherAssignmentService } $role = strtolower((string) ($assign['position'] ?? '')); - if (!in_array($role, ['main', 'ta'], true)) { + if (! in_array($role, ['main', 'ta'], true)) { continue; } @@ -73,9 +71,9 @@ class TeacherAssignmentService continue; } $assigned = $assignmentMap[$tid] ?? ['main' => [], 'ta' => []]; - $name = trim(($teacher['firstname'] ?? '') . ' ' . ($teacher['lastname'] ?? '')); + $name = trim(($teacher['firstname'] ?? '').' '.($teacher['lastname'] ?? '')); if ($name === '') { - $name = 'User#' . $tid; + $name = 'User#'.$tid; } $teacherData[] = [ @@ -170,7 +168,7 @@ class TeacherAssignmentService $semester = trim((string) ($input['semester'] ?? $context['semester'] ?? '')); $schoolYear = trim((string) ($input['school_year'] ?? $context['school_year'] ?? '')); - if ($teacherId <= 0 || $classSectionId <= 0 || !in_array($position, ['main', 'ta'], true) || $schoolYear === '' || $semester === '') { + if ($teacherId <= 0 || $classSectionId <= 0 || ! in_array($position, ['main', 'ta'], true) || $schoolYear === '' || $semester === '') { return ['ok' => false, 'message' => 'Missing or invalid data for deletion.']; } @@ -182,12 +180,12 @@ class TeacherAssignmentService ->where('position', $position) ->first(); - if (!$assignment) { + if (! $assignment) { return ['ok' => false, 'message' => 'Assignment not found.']; } $assignment->delete(); - return ['ok' => true, 'message' => ucfirst($position) . ' assignment removed successfully.']; + return ['ok' => true, 'message' => ucfirst($position).' assignment removed successfully.']; } } diff --git a/app/Services/Teachers/TeacherDashboardService.php b/app/Services/Teachers/TeacherDashboardService.php index ee297c0d..fd677060 100644 --- a/app/Services/Teachers/TeacherDashboardService.php +++ b/app/Services/Teachers/TeacherDashboardService.php @@ -11,9 +11,7 @@ use Illuminate\Support\Facades\DB; class TeacherDashboardService { - public function __construct(private TeacherConfigService $configService) - { - } + public function __construct(private TeacherConfigService $configService) {} public function classView(int $userId, ?int $requestedClassSectionId = null): array { @@ -29,7 +27,7 @@ class TeacherDashboardService ->map(fn ($r) => strtolower((string) $r)) ->all(); - if (!array_intersect($roles, ['teacher', 'teacher_assistant'])) { + if (! array_intersect($roles, ['teacher', 'teacher_assistant'])) { throw new \RuntimeException('Access denied.'); } @@ -50,24 +48,24 @@ class TeacherDashboardService $activeClassSectionId = null; if ($requestedClassSectionId && in_array($requestedClassSectionId, $classSectionIds, true)) { $activeClassSectionId = $requestedClassSectionId; - } elseif (!empty($classSectionIds)) { + } elseif (! empty($classSectionIds)) { $activeClassSectionId = $classSectionIds[0]; } $studentsBySection = []; - if (!empty($classSectionIds)) { + if (! empty($classSectionIds)) { $students = StudentClass::getStudentsByClassSectionIds($classSectionIds); - if (!empty($students)) { + if (! empty($students)) { $studentIds = array_values(array_unique(array_map( static fn (array $row) => (int) ($row['student_id'] ?? 0), $students ))); $studentIds = array_values(array_filter($studentIds)); - $conditionsByStudent = !empty($studentIds) + $conditionsByStudent = ! empty($studentIds) ? StudentMedicalCondition::getMedicalByStudentIds($studentIds) : []; - $allergiesByStudent = !empty($studentIds) + $allergiesByStudent = ! empty($studentIds) ? StudentAllergy::getAllergiesByStudentIds($studentIds) : []; @@ -89,7 +87,7 @@ class TeacherDashboardService } $assignedNames = []; - if (!empty($classSectionIds)) { + if (! empty($classSectionIds)) { $rows = DB::table('teacher_class as tc') ->select('tc.class_section_id', 'tc.position', 'u.firstname', 'u.lastname') ->join('users as u', 'u.id', '=', 'tc.teacher_id') @@ -103,7 +101,7 @@ class TeacherDashboardService foreach ($rows as $row) { $sid = (int) ($row->class_section_id ?? 0); $pos = strtolower((string) ($row->position ?? '')); - $name = trim(($row->firstname ?? '') . ' ' . ($row->lastname ?? '')); + $name = trim(($row->firstname ?? '').' '.($row->lastname ?? '')); if ($sid === 0 || $name === '') { continue; } diff --git a/app/Services/Ui/UiStyleService.php b/app/Services/Ui/UiStyleService.php index 76483dcf..ddbff6fc 100644 --- a/app/Services/Ui/UiStyleService.php +++ b/app/Services/Ui/UiStyleService.php @@ -13,10 +13,12 @@ class UiStyleService $prefs = Preferences::query()->where('user_id', $userId)->first(); if ($prefs) { $prefs->fill($updates)->save(); + return $prefs->fresh(); } $updates['user_id'] = $userId; + return Preferences::query()->create($updates); } @@ -59,6 +61,7 @@ class UiStyleService private function sanitizePaletteKey(?string $value): ?string { $value = trim((string) $value); + return $value === '' ? null : $value; } @@ -68,6 +71,7 @@ class UiStyleService if (in_array($mode, ['light', 'dark', 'auto'], true)) { return $mode; } + return 'auto'; } @@ -78,17 +82,19 @@ class UiStyleService return null; } if ($h[0] !== '#') { - $h = '#' . $h; + $h = '#'.$h; } if (preg_match('/^#([0-9a-fA-F]{3})$/', $h, $m)) { $r = $m[1][0]; $g = $m[1][1]; $b = $m[1][2]; - return strtoupper('#' . $r . $r . $g . $g . $b . $b); + + return strtoupper('#'.$r.$r.$g.$g.$b.$b); } if (preg_match('/^#([0-9a-fA-F]{6})$/', $h)) { return strtoupper($h); } + return null; } } diff --git a/app/Services/Users/DeleteUnverifiedUserService.php b/app/Services/Users/DeleteUnverifiedUserService.php index 6070640f..08558522 100644 --- a/app/Services/Users/DeleteUnverifiedUserService.php +++ b/app/Services/Users/DeleteUnverifiedUserService.php @@ -9,14 +9,12 @@ use Illuminate\Support\Facades\Log; class DeleteUnverifiedUserService { - public function __construct(private AccountEventService $accountEvents) - { - } + public function __construct(private AccountEventService $accountEvents) {} public function deleteAfterTimeout(int $userId, int $timeoutSeconds = 86400): array { $user = User::query()->find($userId); - if (!$user) { + if (! $user) { return ['ok' => false, 'reason' => 'not_found']; } @@ -25,7 +23,7 @@ class DeleteUnverifiedUserService } $lastActivity = $user->updated_at ?? $user->created_at; - if (!$lastActivity) { + if (! $lastActivity) { return ['ok' => false, 'reason' => 'no_timestamp']; } diff --git a/app/Services/Users/InactiveUserCleanupService.php b/app/Services/Users/InactiveUserCleanupService.php index ccbbac8d..335b78c6 100644 --- a/app/Services/Users/InactiveUserCleanupService.php +++ b/app/Services/Users/InactiveUserCleanupService.php @@ -21,6 +21,7 @@ class InactiveUserCleanupService if (empty($users)) { $orphans = $this->purgeOrphanedUserRoles(); + return [ 'deleted_users' => 0, 'deleted_parents' => 0, diff --git a/app/Services/Users/UserEventService.php b/app/Services/Users/UserEventService.php index d3c706ef..8c8a1cb6 100644 --- a/app/Services/Users/UserEventService.php +++ b/app/Services/Users/UserEventService.php @@ -7,24 +7,23 @@ use Illuminate\Support\Facades\Log; class UserEventService { - public function __construct(private EmailService $emailService) - { - } + public function __construct(private EmailService $emailService) {} public function handleNewAccountAdded(array $userData): bool { $email = (string) ($userData['email'] ?? ''); if ($email === '') { Log::warning('UserEventService: missing email for welcome message.'); + return false; } - $name = trim((string) (($userData['firstname'] ?? '') . ' ' . ($userData['lastname'] ?? ''))); + $name = trim((string) (($userData['firstname'] ?? '').' '.($userData['lastname'] ?? ''))); $safeName = e($name !== '' ? $name : 'there'); $subject = 'Welcome to Al Rahma Sunday School!'; - $message = '

Hello ' . $safeName . ',

' - . '

Your account has been created. Welcome to Al Rahma Sunday School.

'; + $message = '

Hello '.$safeName.',

' + .'

Your account has been created. Welcome to Al Rahma Sunday School.

'; return $this->emailService->send($email, $subject, $message, 'registration'); } diff --git a/app/Services/Users/UserListService.php b/app/Services/Users/UserListService.php index 918773cc..b3490b88 100644 --- a/app/Services/Users/UserListService.php +++ b/app/Services/Users/UserListService.php @@ -40,11 +40,11 @@ class UserListService $parentType = $guardianMap[$userId] ?? ''; $isSecond = false; - if ($userId > 0 && !empty($secondByUserId[$userId])) { + if ($userId > 0 && ! empty($secondByUserId[$userId])) { $isSecond = true; } else { $email = trim(strtolower((string) $user->email)); - if ($email !== '' && !empty($secondByEmail[$email])) { + if ($email !== '' && ! empty($secondByEmail[$email])) { $isSecond = true; } } @@ -91,7 +91,7 @@ class UserListService private function guardianTypeMap(): array { - if (!Schema::hasTable('family_guardians')) { + if (! Schema::hasTable('family_guardians')) { return []; } @@ -119,7 +119,7 @@ class UserListService private function secondParentMaps(): array { - if (!Schema::hasTable('parents')) { + if (! Schema::hasTable('parents')) { return [[], []]; } diff --git a/app/Services/Users/UserManagementService.php b/app/Services/Users/UserManagementService.php index 2c3911c2..f7cdb33a 100644 --- a/app/Services/Users/UserManagementService.php +++ b/app/Services/Users/UserManagementService.php @@ -12,9 +12,7 @@ use Illuminate\Support\Facades\Validator; class UserManagementService { - public function __construct(private UserEventService $eventService) - { - } + public function __construct(private UserEventService $eventService) {} public function create(array $payload, ?int $actorId = null): array { @@ -45,7 +43,7 @@ class UserManagementService public function update(int $userId, array $payload): array { $user = User::query()->find($userId); - if (!$user) { + if (! $user) { return ['ok' => false, 'message' => 'User not found.']; } @@ -67,7 +65,7 @@ class UserManagementService public function delete(int $userId): array { $user = User::query()->find($userId); - if (!$user) { + if (! $user) { return ['ok' => false, 'message' => 'User not found.']; } @@ -87,11 +85,11 @@ class UserManagementService { $actorId = $actorId ?? (auth()->id() ?? null); - if (!User::query()->whereKey($userId)->exists()) { + if (! User::query()->whereKey($userId)->exists()) { return false; } - if (!Role::query()->whereKey($roleId)->exists()) { + if (! Role::query()->whereKey($roleId)->exists()) { return false; } @@ -133,10 +131,10 @@ class UserManagementService 'accept_school_policy' => ['required'], ]; - $isParent = !empty($data['is_parent']); - $noSecondParent = !empty($data['no_second_parent_info']); + $isParent = ! empty($data['is_parent']); + $noSecondParent = ! empty($data['no_second_parent_info']); - if ($isParent && !$noSecondParent) { + if ($isParent && ! $noSecondParent) { $rules += [ 'second_firstname' => ['required', 'regex:/^[a-zA-Z\\s\\-]+$/', 'min:2', 'max:30'], 'second_lastname' => ['required', 'regex:/^[a-zA-Z\\s\\-]+$/', 'min:2', 'max:30'], @@ -215,7 +213,7 @@ class UserManagementService ]; foreach ($map as $field) { - if (!array_key_exists($field, $payload)) { + if (! array_key_exists($field, $payload)) { continue; } @@ -265,6 +263,7 @@ class UserManagementService if ($value === 'female') { return 'Female'; } + return null; } @@ -274,6 +273,7 @@ class UserManagementService if ($value === '') { return ''; } + return ucfirst($value); } diff --git a/app/Services/Whatsapp/WhatsappContactService.php b/app/Services/Whatsapp/WhatsappContactService.php index 5075d8ab..9ee94188 100644 --- a/app/Services/Whatsapp/WhatsappContactService.php +++ b/app/Services/Whatsapp/WhatsappContactService.php @@ -8,9 +8,7 @@ use Illuminate\Support\Facades\Log; class WhatsappContactService { - public function __construct(private WhatsappContextService $context) - { - } + public function __construct(private WhatsappContextService $context) {} public function listParentContacts(?string $schoolYear, ?string $semester): array { @@ -18,7 +16,7 @@ class WhatsappContactService $semester = trim((string) $semester); $builder = DB::table('parents as sp'); - $builder->select(" + $builder->select(' sp.id AS parents_row_id, sp.firstparent_id AS firstparent_id, sp.secondparent_firstname AS sp_firstname, @@ -34,7 +32,7 @@ class WhatsappContactService u.cellphone AS u_phone, u.school_year AS u_school_year, u.semester AS u_semester - "); + '); $builder->leftJoin('users as u', 'u.id', '=', 'sp.firstparent_id'); if ($schoolYear !== '') { @@ -49,7 +47,7 @@ class WhatsappContactService $contacts = []; foreach ($rows as $row) { $r = (array) $row; - if (!empty($r['u_id'])) { + if (! empty($r['u_id'])) { $contacts[] = [ 'firstname' => (string) ($r['u_firstname'] ?? ''), 'lastname' => (string) ($r['u_lastname'] ?? ''), @@ -59,7 +57,7 @@ class WhatsappContactService ]; } - $hasSecond = !empty($r['sp_firstname']) || !empty($r['sp_lastname']) || !empty($r['sp_email']) || !empty($r['sp_phone']); + $hasSecond = ! empty($r['sp_firstname']) || ! empty($r['sp_lastname']) || ! empty($r['sp_email']) || ! empty($r['sp_phone']); if ($hasSecond) { $contacts[] = [ 'firstname' => (string) ($r['sp_firstname'] ?? ''), @@ -72,8 +70,9 @@ class WhatsappContactService } $contacts = array_values(array_filter($contacts, function ($c) { - $name = trim(($c['firstname'] ?? '') . ($c['lastname'] ?? '')); - $contact = trim(($c['phone'] ?? '') . ($c['email'] ?? '')); + $name = trim(($c['firstname'] ?? '').($c['lastname'] ?? '')); + $contact = trim(($c['phone'] ?? '').($c['email'] ?? '')); + return $name !== '' || $contact !== ''; })); @@ -90,6 +89,7 @@ class WhatsappContactService } $ta = ($a['type'] ?? '') === 'Primary Parent' ? 0 : 1; $tb = ($b['type'] ?? '') === 'Primary Parent' ? 0 : 1; + return $ta <=> $tb; }); @@ -115,7 +115,7 @@ class WhatsappContactService )))); $namesById = []; - if (!empty($sectionIds)) { + if (! empty($sectionIds)) { $nameRows = DB::table('classSection') ->select('class_section_id', 'class_section_name') ->whereIn('class_section_id', $sectionIds) @@ -130,13 +130,13 @@ class WhatsappContactService $classSections = []; foreach ($sections as $s) { $sid = (int) ($s->class_section_id ?? 0); - if (!$sid) { + if (! $sid) { continue; } $classSections[$sid] = [ 'class_section_id' => $sid, - 'class_section_name' => $namesById[$sid] ?? ('Section ' . $sid), + 'class_section_name' => $namesById[$sid] ?? ('Section '.$sid), 'semester' => $semester, 'school_year' => (string) ($s->school_year ?? ''), 'description' => '', @@ -178,11 +178,11 @@ class WhatsappContactService $r = (array) $row; $sid = (int) ($r['class_section_id'] ?? 0); $pid = (int) ($r['primary_id'] ?? 0); - if (!$sid || !$pid || !isset($classSections[$sid])) { + if (! $sid || ! $pid || ! isset($classSections[$sid])) { continue; } - $key = $sid . ':' . $pid; + $key = $sid.':'.$pid; if (isset($seen[$key])) { continue; } @@ -191,11 +191,11 @@ class WhatsappContactService $classSections[$sid]['parents'][] = [ 'class_section_id' => $sid, 'primary_id' => $pid, - 'primary_name' => trim(($r['primary_lastname'] ?? '') . ', ' . ($r['primary_firstname'] ?? '')), + 'primary_name' => trim(($r['primary_lastname'] ?? '').', '.($r['primary_firstname'] ?? '')), 'primary_phone' => (string) ($r['primary_phone'] ?? ''), 'primary_email' => (string) ($r['primary_email'] ?? ''), 'second_id' => (int) ($r['second_id'] ?? 0), - 'second_name' => trim(($r['second_lastname'] ?? '') . ', ' . ($r['second_firstname'] ?? '')), + 'second_name' => trim(($r['second_lastname'] ?? '').', '.($r['second_firstname'] ?? '')), 'second_phone' => (string) ($r['second_phone'] ?? ''), 'second_email' => (string) ($r['second_email'] ?? ''), 'primary_member' => null, @@ -225,7 +225,7 @@ class WhatsappContactService } unset($section); } catch (\Throwable $e) { - Log::warning('WhatsApp membership lookup failed: ' . $e->getMessage()); + Log::warning('WhatsApp membership lookup failed: '.$e->getMessage()); } try { @@ -239,10 +239,10 @@ class WhatsappContactService foreach ($teachers as $row) { $r = (array) $row; $sid = (int) ($r['class_section_id'] ?? 0); - if (!isset($classSections[$sid])) { + if (! isset($classSections[$sid])) { continue; } - $name = trim(($r['lastname'] ?? '') . ', ' . ($r['firstname'] ?? '')); + $name = trim(($r['lastname'] ?? '').', '.($r['firstname'] ?? '')); $pos = strtolower((string) ($r['position'] ?? 'main')); if ($pos === 'ta' || $pos === 'assistant') { $classSections[$sid]['teacher_assistants'][] = $name; @@ -251,7 +251,7 @@ class WhatsappContactService } } } catch (\Throwable $e) { - Log::warning('WhatsApp teacher list lookup failed: ' . $e->getMessage()); + Log::warning('WhatsApp teacher list lookup failed: '.$e->getMessage()); } foreach ($classSections as &$section) { diff --git a/app/Services/Whatsapp/WhatsappContextService.php b/app/Services/Whatsapp/WhatsappContextService.php index de77a5cc..37a8db4d 100644 --- a/app/Services/Whatsapp/WhatsappContextService.php +++ b/app/Services/Whatsapp/WhatsappContextService.php @@ -47,11 +47,11 @@ class WhatsappContextService } $end = $start + 1; - $canonical = $start . '-' . $end; + $canonical = $start.'-'.$end; $aliases = [ $canonical, - $start . '/' . $end, + $start.'/'.$end, sprintf('%d-%02d', $start, $end % 100), (string) $start, ]; diff --git a/app/Services/Whatsapp/WhatsappInviteBundleService.php b/app/Services/Whatsapp/WhatsappInviteBundleService.php index 9e9e8de9..e7050066 100644 --- a/app/Services/Whatsapp/WhatsappInviteBundleService.php +++ b/app/Services/Whatsapp/WhatsappInviteBundleService.php @@ -13,12 +13,12 @@ class WhatsappInviteBundleService $isPrimary = User::query()->whereKey($pickedId)->exists(); $primaryId = $pickedId; - if (!$isPrimary) { + if (! $isPrimary) { $map = ParentModel::query() ->select('firstparent_id', 'id') ->where('id', $pickedId) ->first(); - if (!$map || !$map->firstparent_id) { + if (! $map || ! $map->firstparent_id) { return null; } $primaryId = (int) $map->firstparent_id; @@ -28,7 +28,7 @@ class WhatsappInviteBundleService ->select('id', 'firstname', 'lastname', 'email') ->whereKey($primaryId) ->first(); - if (!$primary) { + if (! $primary) { return null; } @@ -50,7 +50,7 @@ class WhatsappInviteBundleService $linksSet = []; foreach ($sectionRows as $row) { $cid = (int) ($row->class_section_id ?? 0); - if (!$cid) { + if (! $cid) { continue; } $link = (string) ($linkBySection[$cid]['invite_link'] ?? ''); @@ -60,7 +60,7 @@ class WhatsappInviteBundleService $sections[] = [ 'class_section_id' => $cid, - 'class_section_name' => (string) ($linkBySection[$cid]['class_section_name'] ?? ('Section ' . $cid)), + 'class_section_name' => (string) ($linkBySection[$cid]['class_section_name'] ?? ('Section '.$cid)), 'invite_link' => $link, ]; $linksSet[$link] = true; @@ -71,10 +71,10 @@ class WhatsappInviteBundleService } $emails = []; - if (!empty($primary->email)) { + if (! empty($primary->email)) { $emails[$primary->email] = true; } - if (!empty($secondary?->secondparent_email)) { + if (! empty($secondary?->secondparent_email)) { $emails[$secondary->secondparent_email] = true; } if (empty($emails)) { @@ -89,7 +89,7 @@ class WhatsappInviteBundleService 'email' => (string) ($primary->email ?? ''), 'source' => 'users', ], - 'secondary_parents' => $secondary && !empty($secondary->id) ? [[ + 'secondary_parents' => $secondary && ! empty($secondary->id) ? [[ 'id' => (int) $secondary->id, 'firstname' => (string) ($secondary->secondparent_firstname ?? ''), 'lastname' => (string) ($secondary->secondparent_lastname ?? ''), @@ -186,15 +186,15 @@ class WhatsappInviteBundleService $bundles = []; foreach ($parentIds as $pid) { $profile = $profiles[$pid] ?? null; - if (!$profile) { + if (! $profile) { continue; } $emails = []; - if (!empty($profile['email'])) { + if (! empty($profile['email'])) { $emails[$profile['email']] = true; } - if (!empty($secondaryByPrimary[$pid]['email'])) { + if (! empty($secondaryByPrimary[$pid]['email'])) { $emails[$secondaryByPrimary[$pid]['email']] = true; } if (empty($emails)) { @@ -203,10 +203,10 @@ class WhatsappInviteBundleService $bundles[] = [ 'parent' => $profile, - 'secondary_parents' => !empty($secondaryByPrimary[$pid]['id']) ? [$secondaryByPrimary[$pid]] : [], + 'secondary_parents' => ! empty($secondaryByPrimary[$pid]['id']) ? [$secondaryByPrimary[$pid]] : [], 'sections' => [[ 'class_section_id' => $classSectionId, - 'class_section_name' => (string) ($linkBySection[$classSectionId]['class_section_name'] ?? ('Section ' . $classSectionId)), + 'class_section_name' => (string) ($linkBySection[$classSectionId]['class_section_name'] ?? ('Section '.$classSectionId)), 'invite_link' => $link, ]], 'links' => [$link], @@ -232,7 +232,7 @@ class WhatsappInviteBundleService $bundles = []; foreach ($sectionRows as $row) { $cid = (int) ($row->class_section_id ?? 0); - if (!$cid) { + if (! $cid) { continue; } @@ -255,6 +255,7 @@ class WhatsappInviteBundleService $out[$e] = true; } } + return array_keys($out); }; @@ -264,6 +265,7 @@ class WhatsappInviteBundleService $k = is_array($v) ? md5(json_encode($v)) : (string) $v; $map[$k] = $v; } + return array_values($map); }; @@ -273,10 +275,10 @@ class WhatsappInviteBundleService if (is_array($row)) { $id = $row['class_section_id'] ?? null; $name = $row['class_section_name'] ?? null; - $key = $id !== null ? ('id:' . (int) $id) : ('name:' . (string) $name); + $key = $id !== null ? ('id:'.(int) $id) : ('name:'.(string) $name); $byKey[$key] = $row; } else { - $byKey['name:' . (string) $row] = $row; + $byKey['name:'.(string) $row] = $row; } }; @@ -295,11 +297,11 @@ class WhatsappInviteBundleService $add = static function (&$index, $item) { if (is_array($item)) { $key = isset($item['class_section_id']) - ? 'id:' . (int) $item['class_section_id'] - : ('url:' . (string) ($item['invite_link'] ?? json_encode($item))); + ? 'id:'.(int) $item['class_section_id'] + : ('url:'.(string) ($item['invite_link'] ?? json_encode($item))); $index[$key] = $item; } else { - $index['url:' . (string) $item] = $item; + $index['url:'.(string) $item] = $item; } }; @@ -318,9 +320,9 @@ class WhatsappInviteBundleService $parent = $bundle['parent'] ?? []; $pid = (int) ($parent['id'] ?? 0); $primaryEmails = $normalizeEmails((array) ($bundle['emails'] ?? [])); - $groupKey = $pid > 0 ? ('pid:' . $pid) : ('mail:' . implode(',', $primaryEmails)); + $groupKey = $pid > 0 ? ('pid:'.$pid) : ('mail:'.implode(',', $primaryEmails)); - if (!isset($grouped[$groupKey])) { + if (! isset($grouped[$groupKey])) { $grouped[$groupKey] = [ 'parent' => $parent, 'emails' => $primaryEmails, @@ -330,10 +332,11 @@ class WhatsappInviteBundleService 'items' => (array) ($bundle['items'] ?? []), 'secondary_parents' => (array) ($bundle['secondary_parents'] ?? []), ]; + continue; } - $grouped[$groupKey]['parent'] = !empty($parent) ? $parent : $grouped[$groupKey]['parent']; + $grouped[$groupKey]['parent'] = ! empty($parent) ? $parent : $grouped[$groupKey]['parent']; $grouped[$groupKey]['emails'] = $normalizeEmails(array_merge($grouped[$groupKey]['emails'], $primaryEmails)); $grouped[$groupKey]['sections'] = $mergeSections($grouped[$groupKey]['sections'], (array) ($bundle['sections'] ?? [])); $grouped[$groupKey]['links'] = $mergeLinks($grouped[$groupKey]['links'], (array) ($bundle['links'] ?? [])); diff --git a/app/Services/Whatsapp/WhatsappInviteEmailService.php b/app/Services/Whatsapp/WhatsappInviteEmailService.php index b70f42d8..18911451 100644 --- a/app/Services/Whatsapp/WhatsappInviteEmailService.php +++ b/app/Services/Whatsapp/WhatsappInviteEmailService.php @@ -8,9 +8,7 @@ use Illuminate\Support\Facades\Log; class WhatsappInviteEmailService { - public function __construct(private EmailService $emailService) - { - } + public function __construct(private EmailService $emailService) {} public function send(array $payload): array { @@ -18,12 +16,13 @@ class WhatsappInviteEmailService $cc = array_values(array_unique(array_filter((array) ($payload['cc_emails'] ?? [])))); $cc = array_values(array_diff($cc, $to)); - if (empty($to) && !empty($cc)) { + if (empty($to) && ! empty($cc)) { $to = $cc; $cc = []; } if (empty($to)) { Log::warning('WhatsappInvite: no recipients after normalization.'); + return ['ok' => false, 'message' => 'No recipients provided.']; } @@ -34,6 +33,7 @@ class WhatsappInviteEmailService $items = $this->buildItems($sections, $links); if (empty($items)) { Log::warning('WhatsappInvite: no invite items built.'); + return ['ok' => false, 'message' => 'No invite links found.']; } @@ -77,12 +77,12 @@ class WhatsappInviteEmailService foreach ($sections as $section) { $sid = isset($section['class_section_id']) ? (int) $section['class_section_id'] : 0; - $name = trim((string) ($section['class_section_name'] ?? ($sid ? ('Section ' . $sid) : 'Class'))); + $name = trim((string) ($section['class_section_name'] ?? ($sid ? ('Section '.$sid) : 'Class'))); $url = trim((string) ($section['invite_link'] ?? '')); if ($url === '') { continue; } - $key = $sid . '|' . $url; + $key = $sid.'|'.$url; if (isset($seen[$key])) { continue; } @@ -96,20 +96,20 @@ class WhatsappInviteEmailService ]; } - if (empty($items) && !empty($links)) { + if (empty($items) && ! empty($links)) { foreach ($links as $i => $url) { $url = trim((string) $url); if ($url === '') { continue; } - $key = '0|' . $url; + $key = '0|'.$url; if (isset($seen[$key])) { continue; } $seen[$key] = true; $items[] = [ 'class_section_id' => 0, - 'class_section_name' => 'Class Link ' . ($i + 1), + 'class_section_name' => 'Class Link '.($i + 1), 'invite_link' => $url, 'qr_src' => $this->generateQrImage($url), ]; @@ -126,21 +126,23 @@ class WhatsappInviteEmailService } $dir = storage_path('app/qrcodes'); - if (!is_dir($dir)) { - if (!@mkdir($dir, 0775, true) && !is_dir($dir)) { + if (! is_dir($dir)) { + if (! @mkdir($dir, 0775, true) && ! is_dir($dir)) { Log::error('QR: cannot create dir', ['dir' => $dir]); + return $this->qrFallback($inviteLink); } } - if (!is_writable($dir)) { + if (! is_writable($dir)) { Log::error('QR: dir not writable', ['dir' => $dir]); + return $this->qrFallback($inviteLink); } - $filename = 'wa_' . md5($inviteLink) . '.png'; - $path = $dir . DIRECTORY_SEPARATOR . $filename; + $filename = 'wa_'.md5($inviteLink).'.png'; + $path = $dir.DIRECTORY_SEPARATOR.$filename; - if (!is_file($path)) { + if (! is_file($path)) { $v5BuilderClass = '\\Endroid\\QrCode\\Builder\\Builder'; $v5WriterClass = '\\Endroid\\QrCode\\Writer\\PngWriter'; $v5Encoding = '\\Endroid\\QrCode\\Encoding\\Encoding'; @@ -157,7 +159,7 @@ class WhatsappInviteEmailService if (class_exists($v5BuilderClass) && class_exists($v5WriterClass)) { try { $builder = $v5BuilderClass::create() - ->writer(new $v5WriterClass()) + ->writer(new $v5WriterClass) ->data($inviteLink); if (class_exists($v5Encoding)) { $builder->encoding(new $v5Encoding('UTF-8')); @@ -165,11 +167,11 @@ class WhatsappInviteEmailService $result = $builder->size(500)->margin(2)->build(); $result->saveToFile($path); } catch (\Throwable $e) { - $lastErr = 'Endroid v5: ' . $e->getMessage(); + $lastErr = 'Endroid v5: '.$e->getMessage(); } } - if (!is_file($path) && class_exists($writerClass)) { + if (! is_file($path) && class_exists($writerClass)) { try { if (class_exists($qrClass) && method_exists($qrClass, 'create')) { $qr = $qrClass::create($inviteLink); @@ -183,7 +185,7 @@ class WhatsappInviteEmailService if (method_exists($qr, 'setMargin')) { $qr->setMargin(2); } - $writer = new $writerClass(); + $writer = new $writerClass; $result = $writer->write($qr); if (method_exists($result, 'saveToFile')) { $result->saveToFile($path); @@ -193,21 +195,21 @@ class WhatsappInviteEmailService } } } catch (\Throwable $e) { - $lastErr = 'Endroid v3/v4: ' . $e->getMessage(); + $lastErr = 'Endroid v3/v4: '.$e->getMessage(); } } - if (!is_file($path) && class_exists($chlQRCode) && class_exists($chlQROptions)) { + if (! is_file($path) && class_exists($chlQRCode) && class_exists($chlQROptions)) { try { $optsArr = [ 'version' => 5, - 'outputType' => constant($chlQRCode . '::OUTPUT_IMAGE_PNG'), + 'outputType' => constant($chlQRCode.'::OUTPUT_IMAGE_PNG'), 'scale' => 6, 'imageBase64' => false, 'quietzoneSize' => 2, ]; - if (class_exists($chlQRMatrix) && defined($chlQRMatrix . '::ECC_H')) { - $optsArr['eccLevel'] = constant($chlQRMatrix . '::ECC_H'); + if (class_exists($chlQRMatrix) && defined($chlQRMatrix.'::ECC_H')) { + $optsArr['eccLevel'] = constant($chlQRMatrix.'::ECC_H'); } $opts = new $chlQROptions($optsArr); @@ -215,11 +217,11 @@ class WhatsappInviteEmailService $png = $qrcode->render($inviteLink); file_put_contents($path, $png); } catch (\Throwable $e) { - $lastErr = 'chillerlan: ' . $e->getMessage(); + $lastErr = 'chillerlan: '.$e->getMessage(); } } - if (!is_file($path) && $lastErr) { + if (! is_file($path) && $lastErr) { Log::error('QR generation failed', ['error' => $lastErr]); } } @@ -227,7 +229,7 @@ class WhatsappInviteEmailService if (is_file($path)) { $data = @file_get_contents($path); if ($data !== false) { - return 'data:image/png;base64,' . base64_encode($data); + return 'data:image/png;base64,'.base64_encode($data); } } @@ -237,6 +239,7 @@ class WhatsappInviteEmailService private function qrFallback(string $inviteLink): string { $link = rawurlencode($inviteLink); - return 'https://api.qrserver.com/v1/create-qr-code/?size=260x260&qzone=2&data=' . $link; + + return 'https://api.qrserver.com/v1/create-qr-code/?size=260x260&qzone=2&data='.$link; } } diff --git a/app/Services/Whatsapp/WhatsappInviteNotificationService.php b/app/Services/Whatsapp/WhatsappInviteNotificationService.php index 6396bdc2..91183220 100644 --- a/app/Services/Whatsapp/WhatsappInviteNotificationService.php +++ b/app/Services/Whatsapp/WhatsappInviteNotificationService.php @@ -14,6 +14,7 @@ class WhatsappInviteNotificationService $listenerCount = Event::hasListeners(WhatsappInvitesSend::class) ? 1 : 0; if ($listenerCount === 0) { Log::warning('WhatsappInvitesSend has no listeners registered.'); + return [ 'ok' => false, 'message' => 'No event listener registered for WhatsappInvitesSend.', @@ -29,6 +30,7 @@ class WhatsappInviteNotificationService $out[$e] = true; } } + return array_keys($out); }; @@ -47,9 +49,9 @@ class WhatsappInviteNotificationService $to = $primaryEmails; $cc = []; - if (!empty($primaryEmails) && !empty($secondaryEmails)) { + if (! empty($primaryEmails) && ! empty($secondaryEmails)) { $cc = array_values(array_diff($secondaryEmails, $primaryEmails)); - } elseif (empty($primaryEmails) && !empty($secondaryEmails)) { + } elseif (empty($primaryEmails) && ! empty($secondaryEmails)) { $to = $secondaryEmails; } @@ -57,6 +59,7 @@ class WhatsappInviteNotificationService Log::warning('Skipping WhatsApp invite: no recipient email(s).', [ 'parent_id' => $bundle['parent']['id'] ?? null, ]); + continue; } @@ -73,7 +76,7 @@ class WhatsappInviteNotificationService $triggered++; } catch (\Throwable $e) { - Log::error('WhatsappInvitesSend dispatch failed: ' . $e->getMessage()); + Log::error('WhatsappInvitesSend dispatch failed: '.$e->getMessage()); } } @@ -87,12 +90,12 @@ class WhatsappInviteNotificationService { $collected = []; - if (!empty($bundle['emails_secondary'])) { + if (! empty($bundle['emails_secondary'])) { $collected = array_merge($collected, (array) $bundle['emails_secondary']); } - if (!empty($bundle['secondary_parents'])) { + if (! empty($bundle['secondary_parents'])) { foreach ((array) $bundle['secondary_parents'] as $sp) { - if (!empty($sp['email'])) { + if (! empty($sp['email'])) { $collected[] = $sp['email']; } } @@ -100,11 +103,11 @@ class WhatsappInviteNotificationService $parent = $bundle['parent'] ?? []; foreach (['secondary_email', 'secondparent_email', 'sec_email', 'email_secondary'] as $key) { - if (!empty($parent[$key])) { + if (! empty($parent[$key])) { $collected[] = $parent[$key]; } } - if (!empty($parent['secondparent']['email'])) { + if (! empty($parent['secondparent']['email'])) { $collected[] = $parent['secondparent']['email']; } @@ -116,12 +119,12 @@ class WhatsappInviteNotificationService ->where('firstparent_id', $primaryId) ->orderByDesc('updated_at') ->first(); - if (!empty($row?->secondparent_email)) { + if (! empty($row?->secondparent_email)) { $collected[] = $row->secondparent_email; } } } catch (\Throwable $e) { - Log::warning('Secondary email DB lookup failed: ' . $e->getMessage()); + Log::warning('Secondary email DB lookup failed: '.$e->getMessage()); } return $normalize($collected); diff --git a/app/Services/Whatsapp/WhatsappInviteService.php b/app/Services/Whatsapp/WhatsappInviteService.php index 16357587..d9324092 100644 --- a/app/Services/Whatsapp/WhatsappInviteService.php +++ b/app/Services/Whatsapp/WhatsappInviteService.php @@ -10,8 +10,7 @@ class WhatsappInviteService private WhatsappContextService $context, private WhatsappInviteBundleService $bundleService, private WhatsappInviteNotificationService $notificationService - ) { - } + ) {} public function send(array $payload): array { @@ -25,7 +24,7 @@ class WhatsappInviteService $linkBySection = []; foreach ($links as $link) { $cid = (int) ($link->class_section_id ?? 0); - if ($cid && !isset($linkBySection[$cid])) { + if ($cid && ! isset($linkBySection[$cid])) { $linkBySection[$cid] = $link->toArray(); } } @@ -38,7 +37,7 @@ class WhatsappInviteService continue; } $bundle = $this->bundleService->bundleForParent($pickedId, $linkBySection, $schoolYear, $semester); - if ($bundle && !empty($bundle['emails']) && !empty($bundle['links'])) { + if ($bundle && ! empty($bundle['emails']) && ! empty($bundle['links'])) { $bundles[] = $bundle; } } @@ -46,14 +45,14 @@ class WhatsappInviteService $classSectionId = (int) ($payload['class_section_id'] ?? 0); $classBundles = $this->bundleService->bundlesForClass($classSectionId, $linkBySection, $schoolYear, $semester); foreach ($classBundles as $bundle) { - if (!empty($bundle['emails']) && !empty($bundle['links'])) { + if (! empty($bundle['emails']) && ! empty($bundle['links'])) { $bundles[] = $bundle; } } } elseif ($mode === 'all') { $allBundles = $this->bundleService->bundlesForAll($linkBySection, $schoolYear, $semester); foreach ($allBundles as $bundle) { - if (!empty($bundle['emails']) && !empty($bundle['links'])) { + if (! empty($bundle['emails']) && ! empty($bundle['links'])) { $bundles[] = $bundle; } } @@ -70,7 +69,7 @@ class WhatsappInviteService $consolidated = $this->bundleService->consolidateBundles($bundles); $result = $this->notificationService->dispatch($consolidated, $schoolYear, $semester); - if (!($result['ok'] ?? false)) { + if (! ($result['ok'] ?? false)) { return $result + ['ok' => false]; } diff --git a/app/Services/Whatsapp/WhatsappLinkService.php b/app/Services/Whatsapp/WhatsappLinkService.php index 03e5f727..e9983575 100644 --- a/app/Services/Whatsapp/WhatsappLinkService.php +++ b/app/Services/Whatsapp/WhatsappLinkService.php @@ -9,9 +9,7 @@ use Illuminate\Database\Eloquent\ModelNotFoundException; class WhatsappLinkService { - public function __construct(private WhatsappContextService $context) - { - } + public function __construct(private WhatsappContextService $context) {} public function paginate(array $filters): LengthAwarePaginator { @@ -29,7 +27,7 @@ class WhatsappLinkService } } - if (!empty($filters['class_section_id'])) { + if (! empty($filters['class_section_id'])) { $query->forSection((int) $filters['class_section_id']); } @@ -37,11 +35,11 @@ class WhatsappLinkService $query->where('active', (int) (bool) $filters['active']); } - if (!empty($filters['search'])) { + if (! empty($filters['search'])) { $search = trim((string) $filters['search']); $query->where(function ($q) use ($search) { - $q->where('class_section_name', 'like', '%' . $search . '%') - ->orWhere('invite_link', 'like', '%' . $search . '%') + $q->where('class_section_name', 'like', '%'.$search.'%') + ->orWhere('invite_link', 'like', '%'.$search.'%') ->orWhere('class_section_id', $search); }); } @@ -50,7 +48,7 @@ class WhatsappLinkService $sortDir = strtolower((string) ($filters['sort_dir'] ?? 'desc')) === 'asc' ? 'asc' : 'desc'; $allowedSorts = ['class_section_id', 'class_section_name', 'school_year', 'semester', 'active', 'updated_at']; - if (!in_array($sortBy, $allowedSorts, true)) { + if (! in_array($sortBy, $allowedSorts, true)) { $sortBy = 'updated_at'; } @@ -63,7 +61,7 @@ class WhatsappLinkService public function findOrFail(int $id): WhatsappGroupLink { $link = WhatsappGroupLink::query()->find($id); - if (!$link) { + if (! $link) { throw new ModelNotFoundException('WhatsApp group link not found.'); } @@ -83,7 +81,7 @@ class WhatsappLinkService $link = WhatsappGroupLink::upsertLinkForSection( $sectionId, - $sectionName !== '' ? $sectionName : ('Section ' . $sectionId), + $sectionName !== '' ? $sectionName : ('Section '.$sectionId), $schoolYear, $semester, (string) ($payload['invite_link'] ?? ''), @@ -99,7 +97,7 @@ class WhatsappLinkService if (array_key_exists('class_section_name', $payload)) { $name = trim((string) ($payload['class_section_name'] ?? '')); - if ($name === '' && !empty($payload['class_section_id'])) { + if ($name === '' && ! empty($payload['class_section_id'])) { $name = $this->resolveSectionName((int) $payload['class_section_id']); } $payload['class_section_name'] = $name; @@ -120,6 +118,7 @@ class WhatsappLinkService private function resolveSectionName(int $sectionId): string { $name = ClassSection::getClassSectionNameBySectionId($sectionId); - return $name !== null && $name !== '' ? $name : ('Section ' . $sectionId); + + return $name !== null && $name !== '' ? $name : ('Section '.$sectionId); } } diff --git a/app/Services/Whatsapp/WhatsappMembershipService.php b/app/Services/Whatsapp/WhatsappMembershipService.php index cf6189e4..301e2770 100644 --- a/app/Services/Whatsapp/WhatsappMembershipService.php +++ b/app/Services/Whatsapp/WhatsappMembershipService.php @@ -11,8 +11,9 @@ class WhatsappMembershipService { public function updateMembership(array $payload, ?int $verifiedBy = null): array { - if (!Schema::hasTable('whatsapp_group_memberships')) { + if (! Schema::hasTable('whatsapp_group_memberships')) { Log::error('WhatsApp membership table missing.'); + return [ 'ok' => false, 'message' => 'Membership table missing. Please run migrations.', @@ -56,7 +57,7 @@ class WhatsappMembershipService (bool) $primaryMember, $verifiedBy ); - $saved[] = 'primary=' . $primaryId . ':' . ($primaryMember ? '1' : '0'); + $saved[] = 'primary='.$primaryId.':'.($primaryMember ? '1' : '0'); if ($id <= 0) { Log::warning('Failed to upsert primary membership.', [ 'class_section_id' => $classSectionId, @@ -75,7 +76,7 @@ class WhatsappMembershipService (bool) $secondMember, $verifiedBy ); - $saved[] = 'second=' . $secondId . ':' . ($secondMember ? '1' : '0'); + $saved[] = 'second='.$secondId.':'.($secondMember ? '1' : '0'); if ($id <= 0) { Log::warning('Failed to upsert second membership.', [ 'class_section_id' => $classSectionId, diff --git a/app/Support/MailHtml.php b/app/Support/MailHtml.php index df06e07f..ee7ea41d 100644 --- a/app/Support/MailHtml.php +++ b/app/Support/MailHtml.php @@ -12,12 +12,12 @@ final class MailHtml $t = htmlspecialchars($title, ENT_QUOTES, 'UTF-8'); return '' - . '' - . ''.$t.'' - . '' - . '
' + .'' + .''.$t.'' + .'' + .'
' .$bodyInnerHtml - . '
'; + .'
'; } /** Late slip PDF body (Dompdf). */ diff --git a/app/Support/SessionTimeoutConfig.php b/app/Support/SessionTimeoutConfig.php index f8cce002..b7b6ff16 100644 --- a/app/Support/SessionTimeoutConfig.php +++ b/app/Support/SessionTimeoutConfig.php @@ -5,6 +5,8 @@ namespace App\Support; class SessionTimeoutConfig { public const TIMEOUT_DURATION = 1200; + public const WARNING_THRESHOLD = 900; + public const CHECK_INTERVAL = 60; } diff --git a/app/ThirdParty/fpdf/font/courier.php b/app/ThirdParty/fpdf/font/courier.php index bc8478eb..09219989 100644 --- a/app/ThirdParty/fpdf/font/courier.php +++ b/app/ThirdParty/fpdf/font/courier.php @@ -1,10 +1,11 @@ array(0,128),128=>8364,130=>8218,131=>402,132=>8222,133=>8230,134=>array(8224,2),136=>710,137=>8240,138=>352,139=>8249,140=>338,142=>381,145=>array(8216,2),147=>array(8220,2),149=>8226,150=>array(8211,2),152=>732,153=>8482,154=>353,155=>8250,156=>339,158=>382,159=>376,160=>array(160,96)); -?> +$uv = [0 => [0, 128], 128 => 8364, 130 => 8218, 131 => 402, 132 => 8222, 133 => 8230, 134 => [8224, 2], 136 => 710, 137 => 8240, 138 => 352, 139 => 8249, 140 => 338, 142 => 381, 145 => [8216, 2], 147 => [8220, 2], 149 => 8226, 150 => [8211, 2], 152 => 732, 153 => 8482, 154 => 353, 155 => 8250, 156 => 339, 158 => 382, 159 => 376, 160 => [160, 96]]; diff --git a/app/ThirdParty/fpdf/font/courierb.php b/app/ThirdParty/fpdf/font/courierb.php index 97ecd701..33bbd222 100644 --- a/app/ThirdParty/fpdf/font/courierb.php +++ b/app/ThirdParty/fpdf/font/courierb.php @@ -1,10 +1,11 @@ array(0,128),128=>8364,130=>8218,131=>402,132=>8222,133=>8230,134=>array(8224,2),136=>710,137=>8240,138=>352,139=>8249,140=>338,142=>381,145=>array(8216,2),147=>array(8220,2),149=>8226,150=>array(8211,2),152=>732,153=>8482,154=>353,155=>8250,156=>339,158=>382,159=>376,160=>array(160,96)); -?> +$uv = [0 => [0, 128], 128 => 8364, 130 => 8218, 131 => 402, 132 => 8222, 133 => 8230, 134 => [8224, 2], 136 => 710, 137 => 8240, 138 => 352, 139 => 8249, 140 => 338, 142 => 381, 145 => [8216, 2], 147 => [8220, 2], 149 => 8226, 150 => [8211, 2], 152 => 732, 153 => 8482, 154 => 353, 155 => 8250, 156 => 339, 158 => 382, 159 => 376, 160 => [160, 96]]; diff --git a/app/ThirdParty/fpdf/font/courierbi.php b/app/ThirdParty/fpdf/font/courierbi.php index c4bfff88..c9763421 100644 --- a/app/ThirdParty/fpdf/font/courierbi.php +++ b/app/ThirdParty/fpdf/font/courierbi.php @@ -1,10 +1,11 @@ array(0,128),128=>8364,130=>8218,131=>402,132=>8222,133=>8230,134=>array(8224,2),136=>710,137=>8240,138=>352,139=>8249,140=>338,142=>381,145=>array(8216,2),147=>array(8220,2),149=>8226,150=>array(8211,2),152=>732,153=>8482,154=>353,155=>8250,156=>339,158=>382,159=>376,160=>array(160,96)); -?> +$uv = [0 => [0, 128], 128 => 8364, 130 => 8218, 131 => 402, 132 => 8222, 133 => 8230, 134 => [8224, 2], 136 => 710, 137 => 8240, 138 => 352, 139 => 8249, 140 => 338, 142 => 381, 145 => [8216, 2], 147 => [8220, 2], 149 => 8226, 150 => [8211, 2], 152 => 732, 153 => 8482, 154 => 353, 155 => 8250, 156 => 339, 158 => 382, 159 => 376, 160 => [160, 96]]; diff --git a/app/ThirdParty/fpdf/font/courieri.php b/app/ThirdParty/fpdf/font/courieri.php index 015a15ad..ba94e54b 100644 --- a/app/ThirdParty/fpdf/font/courieri.php +++ b/app/ThirdParty/fpdf/font/courieri.php @@ -1,10 +1,11 @@ array(0,128),128=>8364,130=>8218,131=>402,132=>8222,133=>8230,134=>array(8224,2),136=>710,137=>8240,138=>352,139=>8249,140=>338,142=>381,145=>array(8216,2),147=>array(8220,2),149=>8226,150=>array(8211,2),152=>732,153=>8482,154=>353,155=>8250,156=>339,158=>382,159=>376,160=>array(160,96)); -?> +$uv = [0 => [0, 128], 128 => 8364, 130 => 8218, 131 => 402, 132 => 8222, 133 => 8230, 134 => [8224, 2], 136 => 710, 137 => 8240, 138 => 352, 139 => 8249, 140 => 338, 142 => 381, 145 => [8216, 2], 147 => [8220, 2], 149 => 8226, 150 => [8211, 2], 152 => 732, 153 => 8482, 154 => 353, 155 => 8250, 156 => 339, 158 => 382, 159 => 376, 160 => [160, 96]]; diff --git a/app/ThirdParty/fpdf/font/helvetica.php b/app/ThirdParty/fpdf/font/helvetica.php index 927759b9..3dcf5e5d 100644 --- a/app/ThirdParty/fpdf/font/helvetica.php +++ b/app/ThirdParty/fpdf/font/helvetica.php @@ -1,21 +1,21 @@ 278,chr(1)=>278,chr(2)=>278,chr(3)=>278,chr(4)=>278,chr(5)=>278,chr(6)=>278,chr(7)=>278,chr(8)=>278,chr(9)=>278,chr(10)=>278,chr(11)=>278,chr(12)=>278,chr(13)=>278,chr(14)=>278,chr(15)=>278,chr(16)=>278,chr(17)=>278,chr(18)=>278,chr(19)=>278,chr(20)=>278,chr(21)=>278, - chr(22)=>278,chr(23)=>278,chr(24)=>278,chr(25)=>278,chr(26)=>278,chr(27)=>278,chr(28)=>278,chr(29)=>278,chr(30)=>278,chr(31)=>278,' '=>278,'!'=>278,'"'=>355,'#'=>556,'$'=>556,'%'=>889,'&'=>667,'\''=>191,'('=>333,')'=>333,'*'=>389,'+'=>584, - ','=>278,'-'=>333,'.'=>278,'/'=>278,'0'=>556,'1'=>556,'2'=>556,'3'=>556,'4'=>556,'5'=>556,'6'=>556,'7'=>556,'8'=>556,'9'=>556,':'=>278,';'=>278,'<'=>584,'='=>584,'>'=>584,'?'=>556,'@'=>1015,'A'=>667, - 'B'=>667,'C'=>722,'D'=>722,'E'=>667,'F'=>611,'G'=>778,'H'=>722,'I'=>278,'J'=>500,'K'=>667,'L'=>556,'M'=>833,'N'=>722,'O'=>778,'P'=>667,'Q'=>778,'R'=>722,'S'=>667,'T'=>611,'U'=>722,'V'=>667,'W'=>944, - 'X'=>667,'Y'=>667,'Z'=>611,'['=>278,'\\'=>278,']'=>278,'^'=>469,'_'=>556,'`'=>333,'a'=>556,'b'=>556,'c'=>500,'d'=>556,'e'=>556,'f'=>278,'g'=>556,'h'=>556,'i'=>222,'j'=>222,'k'=>500,'l'=>222,'m'=>833, - 'n'=>556,'o'=>556,'p'=>556,'q'=>556,'r'=>333,'s'=>500,'t'=>278,'u'=>556,'v'=>500,'w'=>722,'x'=>500,'y'=>500,'z'=>500,'{'=>334,'|'=>260,'}'=>334,'~'=>584,chr(127)=>350,chr(128)=>556,chr(129)=>350,chr(130)=>222,chr(131)=>556, - chr(132)=>333,chr(133)=>1000,chr(134)=>556,chr(135)=>556,chr(136)=>333,chr(137)=>1000,chr(138)=>667,chr(139)=>333,chr(140)=>1000,chr(141)=>350,chr(142)=>611,chr(143)=>350,chr(144)=>350,chr(145)=>222,chr(146)=>222,chr(147)=>333,chr(148)=>333,chr(149)=>350,chr(150)=>556,chr(151)=>1000,chr(152)=>333,chr(153)=>1000, - chr(154)=>500,chr(155)=>333,chr(156)=>944,chr(157)=>350,chr(158)=>500,chr(159)=>667,chr(160)=>278,chr(161)=>333,chr(162)=>556,chr(163)=>556,chr(164)=>556,chr(165)=>556,chr(166)=>260,chr(167)=>556,chr(168)=>333,chr(169)=>737,chr(170)=>370,chr(171)=>556,chr(172)=>584,chr(173)=>333,chr(174)=>737,chr(175)=>333, - chr(176)=>400,chr(177)=>584,chr(178)=>333,chr(179)=>333,chr(180)=>333,chr(181)=>556,chr(182)=>537,chr(183)=>278,chr(184)=>333,chr(185)=>333,chr(186)=>365,chr(187)=>556,chr(188)=>834,chr(189)=>834,chr(190)=>834,chr(191)=>611,chr(192)=>667,chr(193)=>667,chr(194)=>667,chr(195)=>667,chr(196)=>667,chr(197)=>667, - chr(198)=>1000,chr(199)=>722,chr(200)=>667,chr(201)=>667,chr(202)=>667,chr(203)=>667,chr(204)=>278,chr(205)=>278,chr(206)=>278,chr(207)=>278,chr(208)=>722,chr(209)=>722,chr(210)=>778,chr(211)=>778,chr(212)=>778,chr(213)=>778,chr(214)=>778,chr(215)=>584,chr(216)=>778,chr(217)=>722,chr(218)=>722,chr(219)=>722, - chr(220)=>722,chr(221)=>667,chr(222)=>667,chr(223)=>611,chr(224)=>556,chr(225)=>556,chr(226)=>556,chr(227)=>556,chr(228)=>556,chr(229)=>556,chr(230)=>889,chr(231)=>500,chr(232)=>556,chr(233)=>556,chr(234)=>556,chr(235)=>556,chr(236)=>278,chr(237)=>278,chr(238)=>278,chr(239)=>278,chr(240)=>556,chr(241)=>556, - chr(242)=>556,chr(243)=>556,chr(244)=>556,chr(245)=>556,chr(246)=>556,chr(247)=>584,chr(248)=>611,chr(249)=>556,chr(250)=>556,chr(251)=>556,chr(252)=>556,chr(253)=>500,chr(254)=>556,chr(255)=>500); +$cw = [ + chr(0) => 278, chr(1) => 278, chr(2) => 278, chr(3) => 278, chr(4) => 278, chr(5) => 278, chr(6) => 278, chr(7) => 278, chr(8) => 278, chr(9) => 278, chr(10) => 278, chr(11) => 278, chr(12) => 278, chr(13) => 278, chr(14) => 278, chr(15) => 278, chr(16) => 278, chr(17) => 278, chr(18) => 278, chr(19) => 278, chr(20) => 278, chr(21) => 278, + chr(22) => 278, chr(23) => 278, chr(24) => 278, chr(25) => 278, chr(26) => 278, chr(27) => 278, chr(28) => 278, chr(29) => 278, chr(30) => 278, chr(31) => 278, ' ' => 278, '!' => 278, '"' => 355, '#' => 556, '$' => 556, '%' => 889, '&' => 667, '\'' => 191, '(' => 333, ')' => 333, '*' => 389, '+' => 584, + ',' => 278, '-' => 333, '.' => 278, '/' => 278, '0' => 556, '1' => 556, '2' => 556, '3' => 556, '4' => 556, '5' => 556, '6' => 556, '7' => 556, '8' => 556, '9' => 556, ':' => 278, ';' => 278, '<' => 584, '=' => 584, '>' => 584, '?' => 556, '@' => 1015, 'A' => 667, + 'B' => 667, 'C' => 722, 'D' => 722, 'E' => 667, 'F' => 611, 'G' => 778, 'H' => 722, 'I' => 278, 'J' => 500, 'K' => 667, 'L' => 556, 'M' => 833, 'N' => 722, 'O' => 778, 'P' => 667, 'Q' => 778, 'R' => 722, 'S' => 667, 'T' => 611, 'U' => 722, 'V' => 667, 'W' => 944, + 'X' => 667, 'Y' => 667, 'Z' => 611, '[' => 278, '\\' => 278, ']' => 278, '^' => 469, '_' => 556, '`' => 333, 'a' => 556, 'b' => 556, 'c' => 500, 'd' => 556, 'e' => 556, 'f' => 278, 'g' => 556, 'h' => 556, 'i' => 222, 'j' => 222, 'k' => 500, 'l' => 222, 'm' => 833, + 'n' => 556, 'o' => 556, 'p' => 556, 'q' => 556, 'r' => 333, 's' => 500, 't' => 278, 'u' => 556, 'v' => 500, 'w' => 722, 'x' => 500, 'y' => 500, 'z' => 500, '{' => 334, '|' => 260, '}' => 334, '~' => 584, chr(127) => 350, chr(128) => 556, chr(129) => 350, chr(130) => 222, chr(131) => 556, + chr(132) => 333, chr(133) => 1000, chr(134) => 556, chr(135) => 556, chr(136) => 333, chr(137) => 1000, chr(138) => 667, chr(139) => 333, chr(140) => 1000, chr(141) => 350, chr(142) => 611, chr(143) => 350, chr(144) => 350, chr(145) => 222, chr(146) => 222, chr(147) => 333, chr(148) => 333, chr(149) => 350, chr(150) => 556, chr(151) => 1000, chr(152) => 333, chr(153) => 1000, + chr(154) => 500, chr(155) => 333, chr(156) => 944, chr(157) => 350, chr(158) => 500, chr(159) => 667, chr(160) => 278, chr(161) => 333, chr(162) => 556, chr(163) => 556, chr(164) => 556, chr(165) => 556, chr(166) => 260, chr(167) => 556, chr(168) => 333, chr(169) => 737, chr(170) => 370, chr(171) => 556, chr(172) => 584, chr(173) => 333, chr(174) => 737, chr(175) => 333, + chr(176) => 400, chr(177) => 584, chr(178) => 333, chr(179) => 333, chr(180) => 333, chr(181) => 556, chr(182) => 537, chr(183) => 278, chr(184) => 333, chr(185) => 333, chr(186) => 365, chr(187) => 556, chr(188) => 834, chr(189) => 834, chr(190) => 834, chr(191) => 611, chr(192) => 667, chr(193) => 667, chr(194) => 667, chr(195) => 667, chr(196) => 667, chr(197) => 667, + chr(198) => 1000, chr(199) => 722, chr(200) => 667, chr(201) => 667, chr(202) => 667, chr(203) => 667, chr(204) => 278, chr(205) => 278, chr(206) => 278, chr(207) => 278, chr(208) => 722, chr(209) => 722, chr(210) => 778, chr(211) => 778, chr(212) => 778, chr(213) => 778, chr(214) => 778, chr(215) => 584, chr(216) => 778, chr(217) => 722, chr(218) => 722, chr(219) => 722, + chr(220) => 722, chr(221) => 667, chr(222) => 667, chr(223) => 611, chr(224) => 556, chr(225) => 556, chr(226) => 556, chr(227) => 556, chr(228) => 556, chr(229) => 556, chr(230) => 889, chr(231) => 500, chr(232) => 556, chr(233) => 556, chr(234) => 556, chr(235) => 556, chr(236) => 278, chr(237) => 278, chr(238) => 278, chr(239) => 278, chr(240) => 556, chr(241) => 556, + chr(242) => 556, chr(243) => 556, chr(244) => 556, chr(245) => 556, chr(246) => 556, chr(247) => 584, chr(248) => 611, chr(249) => 556, chr(250) => 556, chr(251) => 556, chr(252) => 556, chr(253) => 500, chr(254) => 556, chr(255) => 500]; $enc = 'cp1252'; -$uv = array(0=>array(0,128),128=>8364,130=>8218,131=>402,132=>8222,133=>8230,134=>array(8224,2),136=>710,137=>8240,138=>352,139=>8249,140=>338,142=>381,145=>array(8216,2),147=>array(8220,2),149=>8226,150=>array(8211,2),152=>732,153=>8482,154=>353,155=>8250,156=>339,158=>382,159=>376,160=>array(160,96)); -?> +$uv = [0 => [0, 128], 128 => 8364, 130 => 8218, 131 => 402, 132 => 8222, 133 => 8230, 134 => [8224, 2], 136 => 710, 137 => 8240, 138 => 352, 139 => 8249, 140 => 338, 142 => 381, 145 => [8216, 2], 147 => [8220, 2], 149 => 8226, 150 => [8211, 2], 152 => 732, 153 => 8482, 154 => 353, 155 => 8250, 156 => 339, 158 => 382, 159 => 376, 160 => [160, 96]]; diff --git a/app/ThirdParty/fpdf/font/helveticab.php b/app/ThirdParty/fpdf/font/helveticab.php index bcd73672..3a9e482f 100644 --- a/app/ThirdParty/fpdf/font/helveticab.php +++ b/app/ThirdParty/fpdf/font/helveticab.php @@ -1,21 +1,21 @@ 278,chr(1)=>278,chr(2)=>278,chr(3)=>278,chr(4)=>278,chr(5)=>278,chr(6)=>278,chr(7)=>278,chr(8)=>278,chr(9)=>278,chr(10)=>278,chr(11)=>278,chr(12)=>278,chr(13)=>278,chr(14)=>278,chr(15)=>278,chr(16)=>278,chr(17)=>278,chr(18)=>278,chr(19)=>278,chr(20)=>278,chr(21)=>278, - chr(22)=>278,chr(23)=>278,chr(24)=>278,chr(25)=>278,chr(26)=>278,chr(27)=>278,chr(28)=>278,chr(29)=>278,chr(30)=>278,chr(31)=>278,' '=>278,'!'=>333,'"'=>474,'#'=>556,'$'=>556,'%'=>889,'&'=>722,'\''=>238,'('=>333,')'=>333,'*'=>389,'+'=>584, - ','=>278,'-'=>333,'.'=>278,'/'=>278,'0'=>556,'1'=>556,'2'=>556,'3'=>556,'4'=>556,'5'=>556,'6'=>556,'7'=>556,'8'=>556,'9'=>556,':'=>333,';'=>333,'<'=>584,'='=>584,'>'=>584,'?'=>611,'@'=>975,'A'=>722, - 'B'=>722,'C'=>722,'D'=>722,'E'=>667,'F'=>611,'G'=>778,'H'=>722,'I'=>278,'J'=>556,'K'=>722,'L'=>611,'M'=>833,'N'=>722,'O'=>778,'P'=>667,'Q'=>778,'R'=>722,'S'=>667,'T'=>611,'U'=>722,'V'=>667,'W'=>944, - 'X'=>667,'Y'=>667,'Z'=>611,'['=>333,'\\'=>278,']'=>333,'^'=>584,'_'=>556,'`'=>333,'a'=>556,'b'=>611,'c'=>556,'d'=>611,'e'=>556,'f'=>333,'g'=>611,'h'=>611,'i'=>278,'j'=>278,'k'=>556,'l'=>278,'m'=>889, - 'n'=>611,'o'=>611,'p'=>611,'q'=>611,'r'=>389,'s'=>556,'t'=>333,'u'=>611,'v'=>556,'w'=>778,'x'=>556,'y'=>556,'z'=>500,'{'=>389,'|'=>280,'}'=>389,'~'=>584,chr(127)=>350,chr(128)=>556,chr(129)=>350,chr(130)=>278,chr(131)=>556, - chr(132)=>500,chr(133)=>1000,chr(134)=>556,chr(135)=>556,chr(136)=>333,chr(137)=>1000,chr(138)=>667,chr(139)=>333,chr(140)=>1000,chr(141)=>350,chr(142)=>611,chr(143)=>350,chr(144)=>350,chr(145)=>278,chr(146)=>278,chr(147)=>500,chr(148)=>500,chr(149)=>350,chr(150)=>556,chr(151)=>1000,chr(152)=>333,chr(153)=>1000, - chr(154)=>556,chr(155)=>333,chr(156)=>944,chr(157)=>350,chr(158)=>500,chr(159)=>667,chr(160)=>278,chr(161)=>333,chr(162)=>556,chr(163)=>556,chr(164)=>556,chr(165)=>556,chr(166)=>280,chr(167)=>556,chr(168)=>333,chr(169)=>737,chr(170)=>370,chr(171)=>556,chr(172)=>584,chr(173)=>333,chr(174)=>737,chr(175)=>333, - chr(176)=>400,chr(177)=>584,chr(178)=>333,chr(179)=>333,chr(180)=>333,chr(181)=>611,chr(182)=>556,chr(183)=>278,chr(184)=>333,chr(185)=>333,chr(186)=>365,chr(187)=>556,chr(188)=>834,chr(189)=>834,chr(190)=>834,chr(191)=>611,chr(192)=>722,chr(193)=>722,chr(194)=>722,chr(195)=>722,chr(196)=>722,chr(197)=>722, - chr(198)=>1000,chr(199)=>722,chr(200)=>667,chr(201)=>667,chr(202)=>667,chr(203)=>667,chr(204)=>278,chr(205)=>278,chr(206)=>278,chr(207)=>278,chr(208)=>722,chr(209)=>722,chr(210)=>778,chr(211)=>778,chr(212)=>778,chr(213)=>778,chr(214)=>778,chr(215)=>584,chr(216)=>778,chr(217)=>722,chr(218)=>722,chr(219)=>722, - chr(220)=>722,chr(221)=>667,chr(222)=>667,chr(223)=>611,chr(224)=>556,chr(225)=>556,chr(226)=>556,chr(227)=>556,chr(228)=>556,chr(229)=>556,chr(230)=>889,chr(231)=>556,chr(232)=>556,chr(233)=>556,chr(234)=>556,chr(235)=>556,chr(236)=>278,chr(237)=>278,chr(238)=>278,chr(239)=>278,chr(240)=>611,chr(241)=>611, - chr(242)=>611,chr(243)=>611,chr(244)=>611,chr(245)=>611,chr(246)=>611,chr(247)=>584,chr(248)=>611,chr(249)=>611,chr(250)=>611,chr(251)=>611,chr(252)=>611,chr(253)=>556,chr(254)=>611,chr(255)=>556); +$cw = [ + chr(0) => 278, chr(1) => 278, chr(2) => 278, chr(3) => 278, chr(4) => 278, chr(5) => 278, chr(6) => 278, chr(7) => 278, chr(8) => 278, chr(9) => 278, chr(10) => 278, chr(11) => 278, chr(12) => 278, chr(13) => 278, chr(14) => 278, chr(15) => 278, chr(16) => 278, chr(17) => 278, chr(18) => 278, chr(19) => 278, chr(20) => 278, chr(21) => 278, + chr(22) => 278, chr(23) => 278, chr(24) => 278, chr(25) => 278, chr(26) => 278, chr(27) => 278, chr(28) => 278, chr(29) => 278, chr(30) => 278, chr(31) => 278, ' ' => 278, '!' => 333, '"' => 474, '#' => 556, '$' => 556, '%' => 889, '&' => 722, '\'' => 238, '(' => 333, ')' => 333, '*' => 389, '+' => 584, + ',' => 278, '-' => 333, '.' => 278, '/' => 278, '0' => 556, '1' => 556, '2' => 556, '3' => 556, '4' => 556, '5' => 556, '6' => 556, '7' => 556, '8' => 556, '9' => 556, ':' => 333, ';' => 333, '<' => 584, '=' => 584, '>' => 584, '?' => 611, '@' => 975, 'A' => 722, + 'B' => 722, 'C' => 722, 'D' => 722, 'E' => 667, 'F' => 611, 'G' => 778, 'H' => 722, 'I' => 278, 'J' => 556, 'K' => 722, 'L' => 611, 'M' => 833, 'N' => 722, 'O' => 778, 'P' => 667, 'Q' => 778, 'R' => 722, 'S' => 667, 'T' => 611, 'U' => 722, 'V' => 667, 'W' => 944, + 'X' => 667, 'Y' => 667, 'Z' => 611, '[' => 333, '\\' => 278, ']' => 333, '^' => 584, '_' => 556, '`' => 333, 'a' => 556, 'b' => 611, 'c' => 556, 'd' => 611, 'e' => 556, 'f' => 333, 'g' => 611, 'h' => 611, 'i' => 278, 'j' => 278, 'k' => 556, 'l' => 278, 'm' => 889, + 'n' => 611, 'o' => 611, 'p' => 611, 'q' => 611, 'r' => 389, 's' => 556, 't' => 333, 'u' => 611, 'v' => 556, 'w' => 778, 'x' => 556, 'y' => 556, 'z' => 500, '{' => 389, '|' => 280, '}' => 389, '~' => 584, chr(127) => 350, chr(128) => 556, chr(129) => 350, chr(130) => 278, chr(131) => 556, + chr(132) => 500, chr(133) => 1000, chr(134) => 556, chr(135) => 556, chr(136) => 333, chr(137) => 1000, chr(138) => 667, chr(139) => 333, chr(140) => 1000, chr(141) => 350, chr(142) => 611, chr(143) => 350, chr(144) => 350, chr(145) => 278, chr(146) => 278, chr(147) => 500, chr(148) => 500, chr(149) => 350, chr(150) => 556, chr(151) => 1000, chr(152) => 333, chr(153) => 1000, + chr(154) => 556, chr(155) => 333, chr(156) => 944, chr(157) => 350, chr(158) => 500, chr(159) => 667, chr(160) => 278, chr(161) => 333, chr(162) => 556, chr(163) => 556, chr(164) => 556, chr(165) => 556, chr(166) => 280, chr(167) => 556, chr(168) => 333, chr(169) => 737, chr(170) => 370, chr(171) => 556, chr(172) => 584, chr(173) => 333, chr(174) => 737, chr(175) => 333, + chr(176) => 400, chr(177) => 584, chr(178) => 333, chr(179) => 333, chr(180) => 333, chr(181) => 611, chr(182) => 556, chr(183) => 278, chr(184) => 333, chr(185) => 333, chr(186) => 365, chr(187) => 556, chr(188) => 834, chr(189) => 834, chr(190) => 834, chr(191) => 611, chr(192) => 722, chr(193) => 722, chr(194) => 722, chr(195) => 722, chr(196) => 722, chr(197) => 722, + chr(198) => 1000, chr(199) => 722, chr(200) => 667, chr(201) => 667, chr(202) => 667, chr(203) => 667, chr(204) => 278, chr(205) => 278, chr(206) => 278, chr(207) => 278, chr(208) => 722, chr(209) => 722, chr(210) => 778, chr(211) => 778, chr(212) => 778, chr(213) => 778, chr(214) => 778, chr(215) => 584, chr(216) => 778, chr(217) => 722, chr(218) => 722, chr(219) => 722, + chr(220) => 722, chr(221) => 667, chr(222) => 667, chr(223) => 611, chr(224) => 556, chr(225) => 556, chr(226) => 556, chr(227) => 556, chr(228) => 556, chr(229) => 556, chr(230) => 889, chr(231) => 556, chr(232) => 556, chr(233) => 556, chr(234) => 556, chr(235) => 556, chr(236) => 278, chr(237) => 278, chr(238) => 278, chr(239) => 278, chr(240) => 611, chr(241) => 611, + chr(242) => 611, chr(243) => 611, chr(244) => 611, chr(245) => 611, chr(246) => 611, chr(247) => 584, chr(248) => 611, chr(249) => 611, chr(250) => 611, chr(251) => 611, chr(252) => 611, chr(253) => 556, chr(254) => 611, chr(255) => 556]; $enc = 'cp1252'; -$uv = array(0=>array(0,128),128=>8364,130=>8218,131=>402,132=>8222,133=>8230,134=>array(8224,2),136=>710,137=>8240,138=>352,139=>8249,140=>338,142=>381,145=>array(8216,2),147=>array(8220,2),149=>8226,150=>array(8211,2),152=>732,153=>8482,154=>353,155=>8250,156=>339,158=>382,159=>376,160=>array(160,96)); -?> +$uv = [0 => [0, 128], 128 => 8364, 130 => 8218, 131 => 402, 132 => 8222, 133 => 8230, 134 => [8224, 2], 136 => 710, 137 => 8240, 138 => 352, 139 => 8249, 140 => 338, 142 => 381, 145 => [8216, 2], 147 => [8220, 2], 149 => 8226, 150 => [8211, 2], 152 => 732, 153 => 8482, 154 => 353, 155 => 8250, 156 => 339, 158 => 382, 159 => 376, 160 => [160, 96]]; diff --git a/app/ThirdParty/fpdf/font/helveticabi.php b/app/ThirdParty/fpdf/font/helveticabi.php index 0243cde4..943017fb 100644 --- a/app/ThirdParty/fpdf/font/helveticabi.php +++ b/app/ThirdParty/fpdf/font/helveticabi.php @@ -1,21 +1,21 @@ 278,chr(1)=>278,chr(2)=>278,chr(3)=>278,chr(4)=>278,chr(5)=>278,chr(6)=>278,chr(7)=>278,chr(8)=>278,chr(9)=>278,chr(10)=>278,chr(11)=>278,chr(12)=>278,chr(13)=>278,chr(14)=>278,chr(15)=>278,chr(16)=>278,chr(17)=>278,chr(18)=>278,chr(19)=>278,chr(20)=>278,chr(21)=>278, - chr(22)=>278,chr(23)=>278,chr(24)=>278,chr(25)=>278,chr(26)=>278,chr(27)=>278,chr(28)=>278,chr(29)=>278,chr(30)=>278,chr(31)=>278,' '=>278,'!'=>333,'"'=>474,'#'=>556,'$'=>556,'%'=>889,'&'=>722,'\''=>238,'('=>333,')'=>333,'*'=>389,'+'=>584, - ','=>278,'-'=>333,'.'=>278,'/'=>278,'0'=>556,'1'=>556,'2'=>556,'3'=>556,'4'=>556,'5'=>556,'6'=>556,'7'=>556,'8'=>556,'9'=>556,':'=>333,';'=>333,'<'=>584,'='=>584,'>'=>584,'?'=>611,'@'=>975,'A'=>722, - 'B'=>722,'C'=>722,'D'=>722,'E'=>667,'F'=>611,'G'=>778,'H'=>722,'I'=>278,'J'=>556,'K'=>722,'L'=>611,'M'=>833,'N'=>722,'O'=>778,'P'=>667,'Q'=>778,'R'=>722,'S'=>667,'T'=>611,'U'=>722,'V'=>667,'W'=>944, - 'X'=>667,'Y'=>667,'Z'=>611,'['=>333,'\\'=>278,']'=>333,'^'=>584,'_'=>556,'`'=>333,'a'=>556,'b'=>611,'c'=>556,'d'=>611,'e'=>556,'f'=>333,'g'=>611,'h'=>611,'i'=>278,'j'=>278,'k'=>556,'l'=>278,'m'=>889, - 'n'=>611,'o'=>611,'p'=>611,'q'=>611,'r'=>389,'s'=>556,'t'=>333,'u'=>611,'v'=>556,'w'=>778,'x'=>556,'y'=>556,'z'=>500,'{'=>389,'|'=>280,'}'=>389,'~'=>584,chr(127)=>350,chr(128)=>556,chr(129)=>350,chr(130)=>278,chr(131)=>556, - chr(132)=>500,chr(133)=>1000,chr(134)=>556,chr(135)=>556,chr(136)=>333,chr(137)=>1000,chr(138)=>667,chr(139)=>333,chr(140)=>1000,chr(141)=>350,chr(142)=>611,chr(143)=>350,chr(144)=>350,chr(145)=>278,chr(146)=>278,chr(147)=>500,chr(148)=>500,chr(149)=>350,chr(150)=>556,chr(151)=>1000,chr(152)=>333,chr(153)=>1000, - chr(154)=>556,chr(155)=>333,chr(156)=>944,chr(157)=>350,chr(158)=>500,chr(159)=>667,chr(160)=>278,chr(161)=>333,chr(162)=>556,chr(163)=>556,chr(164)=>556,chr(165)=>556,chr(166)=>280,chr(167)=>556,chr(168)=>333,chr(169)=>737,chr(170)=>370,chr(171)=>556,chr(172)=>584,chr(173)=>333,chr(174)=>737,chr(175)=>333, - chr(176)=>400,chr(177)=>584,chr(178)=>333,chr(179)=>333,chr(180)=>333,chr(181)=>611,chr(182)=>556,chr(183)=>278,chr(184)=>333,chr(185)=>333,chr(186)=>365,chr(187)=>556,chr(188)=>834,chr(189)=>834,chr(190)=>834,chr(191)=>611,chr(192)=>722,chr(193)=>722,chr(194)=>722,chr(195)=>722,chr(196)=>722,chr(197)=>722, - chr(198)=>1000,chr(199)=>722,chr(200)=>667,chr(201)=>667,chr(202)=>667,chr(203)=>667,chr(204)=>278,chr(205)=>278,chr(206)=>278,chr(207)=>278,chr(208)=>722,chr(209)=>722,chr(210)=>778,chr(211)=>778,chr(212)=>778,chr(213)=>778,chr(214)=>778,chr(215)=>584,chr(216)=>778,chr(217)=>722,chr(218)=>722,chr(219)=>722, - chr(220)=>722,chr(221)=>667,chr(222)=>667,chr(223)=>611,chr(224)=>556,chr(225)=>556,chr(226)=>556,chr(227)=>556,chr(228)=>556,chr(229)=>556,chr(230)=>889,chr(231)=>556,chr(232)=>556,chr(233)=>556,chr(234)=>556,chr(235)=>556,chr(236)=>278,chr(237)=>278,chr(238)=>278,chr(239)=>278,chr(240)=>611,chr(241)=>611, - chr(242)=>611,chr(243)=>611,chr(244)=>611,chr(245)=>611,chr(246)=>611,chr(247)=>584,chr(248)=>611,chr(249)=>611,chr(250)=>611,chr(251)=>611,chr(252)=>611,chr(253)=>556,chr(254)=>611,chr(255)=>556); +$cw = [ + chr(0) => 278, chr(1) => 278, chr(2) => 278, chr(3) => 278, chr(4) => 278, chr(5) => 278, chr(6) => 278, chr(7) => 278, chr(8) => 278, chr(9) => 278, chr(10) => 278, chr(11) => 278, chr(12) => 278, chr(13) => 278, chr(14) => 278, chr(15) => 278, chr(16) => 278, chr(17) => 278, chr(18) => 278, chr(19) => 278, chr(20) => 278, chr(21) => 278, + chr(22) => 278, chr(23) => 278, chr(24) => 278, chr(25) => 278, chr(26) => 278, chr(27) => 278, chr(28) => 278, chr(29) => 278, chr(30) => 278, chr(31) => 278, ' ' => 278, '!' => 333, '"' => 474, '#' => 556, '$' => 556, '%' => 889, '&' => 722, '\'' => 238, '(' => 333, ')' => 333, '*' => 389, '+' => 584, + ',' => 278, '-' => 333, '.' => 278, '/' => 278, '0' => 556, '1' => 556, '2' => 556, '3' => 556, '4' => 556, '5' => 556, '6' => 556, '7' => 556, '8' => 556, '9' => 556, ':' => 333, ';' => 333, '<' => 584, '=' => 584, '>' => 584, '?' => 611, '@' => 975, 'A' => 722, + 'B' => 722, 'C' => 722, 'D' => 722, 'E' => 667, 'F' => 611, 'G' => 778, 'H' => 722, 'I' => 278, 'J' => 556, 'K' => 722, 'L' => 611, 'M' => 833, 'N' => 722, 'O' => 778, 'P' => 667, 'Q' => 778, 'R' => 722, 'S' => 667, 'T' => 611, 'U' => 722, 'V' => 667, 'W' => 944, + 'X' => 667, 'Y' => 667, 'Z' => 611, '[' => 333, '\\' => 278, ']' => 333, '^' => 584, '_' => 556, '`' => 333, 'a' => 556, 'b' => 611, 'c' => 556, 'd' => 611, 'e' => 556, 'f' => 333, 'g' => 611, 'h' => 611, 'i' => 278, 'j' => 278, 'k' => 556, 'l' => 278, 'm' => 889, + 'n' => 611, 'o' => 611, 'p' => 611, 'q' => 611, 'r' => 389, 's' => 556, 't' => 333, 'u' => 611, 'v' => 556, 'w' => 778, 'x' => 556, 'y' => 556, 'z' => 500, '{' => 389, '|' => 280, '}' => 389, '~' => 584, chr(127) => 350, chr(128) => 556, chr(129) => 350, chr(130) => 278, chr(131) => 556, + chr(132) => 500, chr(133) => 1000, chr(134) => 556, chr(135) => 556, chr(136) => 333, chr(137) => 1000, chr(138) => 667, chr(139) => 333, chr(140) => 1000, chr(141) => 350, chr(142) => 611, chr(143) => 350, chr(144) => 350, chr(145) => 278, chr(146) => 278, chr(147) => 500, chr(148) => 500, chr(149) => 350, chr(150) => 556, chr(151) => 1000, chr(152) => 333, chr(153) => 1000, + chr(154) => 556, chr(155) => 333, chr(156) => 944, chr(157) => 350, chr(158) => 500, chr(159) => 667, chr(160) => 278, chr(161) => 333, chr(162) => 556, chr(163) => 556, chr(164) => 556, chr(165) => 556, chr(166) => 280, chr(167) => 556, chr(168) => 333, chr(169) => 737, chr(170) => 370, chr(171) => 556, chr(172) => 584, chr(173) => 333, chr(174) => 737, chr(175) => 333, + chr(176) => 400, chr(177) => 584, chr(178) => 333, chr(179) => 333, chr(180) => 333, chr(181) => 611, chr(182) => 556, chr(183) => 278, chr(184) => 333, chr(185) => 333, chr(186) => 365, chr(187) => 556, chr(188) => 834, chr(189) => 834, chr(190) => 834, chr(191) => 611, chr(192) => 722, chr(193) => 722, chr(194) => 722, chr(195) => 722, chr(196) => 722, chr(197) => 722, + chr(198) => 1000, chr(199) => 722, chr(200) => 667, chr(201) => 667, chr(202) => 667, chr(203) => 667, chr(204) => 278, chr(205) => 278, chr(206) => 278, chr(207) => 278, chr(208) => 722, chr(209) => 722, chr(210) => 778, chr(211) => 778, chr(212) => 778, chr(213) => 778, chr(214) => 778, chr(215) => 584, chr(216) => 778, chr(217) => 722, chr(218) => 722, chr(219) => 722, + chr(220) => 722, chr(221) => 667, chr(222) => 667, chr(223) => 611, chr(224) => 556, chr(225) => 556, chr(226) => 556, chr(227) => 556, chr(228) => 556, chr(229) => 556, chr(230) => 889, chr(231) => 556, chr(232) => 556, chr(233) => 556, chr(234) => 556, chr(235) => 556, chr(236) => 278, chr(237) => 278, chr(238) => 278, chr(239) => 278, chr(240) => 611, chr(241) => 611, + chr(242) => 611, chr(243) => 611, chr(244) => 611, chr(245) => 611, chr(246) => 611, chr(247) => 584, chr(248) => 611, chr(249) => 611, chr(250) => 611, chr(251) => 611, chr(252) => 611, chr(253) => 556, chr(254) => 611, chr(255) => 556]; $enc = 'cp1252'; -$uv = array(0=>array(0,128),128=>8364,130=>8218,131=>402,132=>8222,133=>8230,134=>array(8224,2),136=>710,137=>8240,138=>352,139=>8249,140=>338,142=>381,145=>array(8216,2),147=>array(8220,2),149=>8226,150=>array(8211,2),152=>732,153=>8482,154=>353,155=>8250,156=>339,158=>382,159=>376,160=>array(160,96)); -?> +$uv = [0 => [0, 128], 128 => 8364, 130 => 8218, 131 => 402, 132 => 8222, 133 => 8230, 134 => [8224, 2], 136 => 710, 137 => 8240, 138 => 352, 139 => 8249, 140 => 338, 142 => 381, 145 => [8216, 2], 147 => [8220, 2], 149 => 8226, 150 => [8211, 2], 152 => 732, 153 => 8482, 154 => 353, 155 => 8250, 156 => 339, 158 => 382, 159 => 376, 160 => [160, 96]]; diff --git a/app/ThirdParty/fpdf/font/helveticai.php b/app/ThirdParty/fpdf/font/helveticai.php index 06ec735b..5b05ec1a 100644 --- a/app/ThirdParty/fpdf/font/helveticai.php +++ b/app/ThirdParty/fpdf/font/helveticai.php @@ -1,21 +1,21 @@ 278,chr(1)=>278,chr(2)=>278,chr(3)=>278,chr(4)=>278,chr(5)=>278,chr(6)=>278,chr(7)=>278,chr(8)=>278,chr(9)=>278,chr(10)=>278,chr(11)=>278,chr(12)=>278,chr(13)=>278,chr(14)=>278,chr(15)=>278,chr(16)=>278,chr(17)=>278,chr(18)=>278,chr(19)=>278,chr(20)=>278,chr(21)=>278, - chr(22)=>278,chr(23)=>278,chr(24)=>278,chr(25)=>278,chr(26)=>278,chr(27)=>278,chr(28)=>278,chr(29)=>278,chr(30)=>278,chr(31)=>278,' '=>278,'!'=>278,'"'=>355,'#'=>556,'$'=>556,'%'=>889,'&'=>667,'\''=>191,'('=>333,')'=>333,'*'=>389,'+'=>584, - ','=>278,'-'=>333,'.'=>278,'/'=>278,'0'=>556,'1'=>556,'2'=>556,'3'=>556,'4'=>556,'5'=>556,'6'=>556,'7'=>556,'8'=>556,'9'=>556,':'=>278,';'=>278,'<'=>584,'='=>584,'>'=>584,'?'=>556,'@'=>1015,'A'=>667, - 'B'=>667,'C'=>722,'D'=>722,'E'=>667,'F'=>611,'G'=>778,'H'=>722,'I'=>278,'J'=>500,'K'=>667,'L'=>556,'M'=>833,'N'=>722,'O'=>778,'P'=>667,'Q'=>778,'R'=>722,'S'=>667,'T'=>611,'U'=>722,'V'=>667,'W'=>944, - 'X'=>667,'Y'=>667,'Z'=>611,'['=>278,'\\'=>278,']'=>278,'^'=>469,'_'=>556,'`'=>333,'a'=>556,'b'=>556,'c'=>500,'d'=>556,'e'=>556,'f'=>278,'g'=>556,'h'=>556,'i'=>222,'j'=>222,'k'=>500,'l'=>222,'m'=>833, - 'n'=>556,'o'=>556,'p'=>556,'q'=>556,'r'=>333,'s'=>500,'t'=>278,'u'=>556,'v'=>500,'w'=>722,'x'=>500,'y'=>500,'z'=>500,'{'=>334,'|'=>260,'}'=>334,'~'=>584,chr(127)=>350,chr(128)=>556,chr(129)=>350,chr(130)=>222,chr(131)=>556, - chr(132)=>333,chr(133)=>1000,chr(134)=>556,chr(135)=>556,chr(136)=>333,chr(137)=>1000,chr(138)=>667,chr(139)=>333,chr(140)=>1000,chr(141)=>350,chr(142)=>611,chr(143)=>350,chr(144)=>350,chr(145)=>222,chr(146)=>222,chr(147)=>333,chr(148)=>333,chr(149)=>350,chr(150)=>556,chr(151)=>1000,chr(152)=>333,chr(153)=>1000, - chr(154)=>500,chr(155)=>333,chr(156)=>944,chr(157)=>350,chr(158)=>500,chr(159)=>667,chr(160)=>278,chr(161)=>333,chr(162)=>556,chr(163)=>556,chr(164)=>556,chr(165)=>556,chr(166)=>260,chr(167)=>556,chr(168)=>333,chr(169)=>737,chr(170)=>370,chr(171)=>556,chr(172)=>584,chr(173)=>333,chr(174)=>737,chr(175)=>333, - chr(176)=>400,chr(177)=>584,chr(178)=>333,chr(179)=>333,chr(180)=>333,chr(181)=>556,chr(182)=>537,chr(183)=>278,chr(184)=>333,chr(185)=>333,chr(186)=>365,chr(187)=>556,chr(188)=>834,chr(189)=>834,chr(190)=>834,chr(191)=>611,chr(192)=>667,chr(193)=>667,chr(194)=>667,chr(195)=>667,chr(196)=>667,chr(197)=>667, - chr(198)=>1000,chr(199)=>722,chr(200)=>667,chr(201)=>667,chr(202)=>667,chr(203)=>667,chr(204)=>278,chr(205)=>278,chr(206)=>278,chr(207)=>278,chr(208)=>722,chr(209)=>722,chr(210)=>778,chr(211)=>778,chr(212)=>778,chr(213)=>778,chr(214)=>778,chr(215)=>584,chr(216)=>778,chr(217)=>722,chr(218)=>722,chr(219)=>722, - chr(220)=>722,chr(221)=>667,chr(222)=>667,chr(223)=>611,chr(224)=>556,chr(225)=>556,chr(226)=>556,chr(227)=>556,chr(228)=>556,chr(229)=>556,chr(230)=>889,chr(231)=>500,chr(232)=>556,chr(233)=>556,chr(234)=>556,chr(235)=>556,chr(236)=>278,chr(237)=>278,chr(238)=>278,chr(239)=>278,chr(240)=>556,chr(241)=>556, - chr(242)=>556,chr(243)=>556,chr(244)=>556,chr(245)=>556,chr(246)=>556,chr(247)=>584,chr(248)=>611,chr(249)=>556,chr(250)=>556,chr(251)=>556,chr(252)=>556,chr(253)=>500,chr(254)=>556,chr(255)=>500); +$cw = [ + chr(0) => 278, chr(1) => 278, chr(2) => 278, chr(3) => 278, chr(4) => 278, chr(5) => 278, chr(6) => 278, chr(7) => 278, chr(8) => 278, chr(9) => 278, chr(10) => 278, chr(11) => 278, chr(12) => 278, chr(13) => 278, chr(14) => 278, chr(15) => 278, chr(16) => 278, chr(17) => 278, chr(18) => 278, chr(19) => 278, chr(20) => 278, chr(21) => 278, + chr(22) => 278, chr(23) => 278, chr(24) => 278, chr(25) => 278, chr(26) => 278, chr(27) => 278, chr(28) => 278, chr(29) => 278, chr(30) => 278, chr(31) => 278, ' ' => 278, '!' => 278, '"' => 355, '#' => 556, '$' => 556, '%' => 889, '&' => 667, '\'' => 191, '(' => 333, ')' => 333, '*' => 389, '+' => 584, + ',' => 278, '-' => 333, '.' => 278, '/' => 278, '0' => 556, '1' => 556, '2' => 556, '3' => 556, '4' => 556, '5' => 556, '6' => 556, '7' => 556, '8' => 556, '9' => 556, ':' => 278, ';' => 278, '<' => 584, '=' => 584, '>' => 584, '?' => 556, '@' => 1015, 'A' => 667, + 'B' => 667, 'C' => 722, 'D' => 722, 'E' => 667, 'F' => 611, 'G' => 778, 'H' => 722, 'I' => 278, 'J' => 500, 'K' => 667, 'L' => 556, 'M' => 833, 'N' => 722, 'O' => 778, 'P' => 667, 'Q' => 778, 'R' => 722, 'S' => 667, 'T' => 611, 'U' => 722, 'V' => 667, 'W' => 944, + 'X' => 667, 'Y' => 667, 'Z' => 611, '[' => 278, '\\' => 278, ']' => 278, '^' => 469, '_' => 556, '`' => 333, 'a' => 556, 'b' => 556, 'c' => 500, 'd' => 556, 'e' => 556, 'f' => 278, 'g' => 556, 'h' => 556, 'i' => 222, 'j' => 222, 'k' => 500, 'l' => 222, 'm' => 833, + 'n' => 556, 'o' => 556, 'p' => 556, 'q' => 556, 'r' => 333, 's' => 500, 't' => 278, 'u' => 556, 'v' => 500, 'w' => 722, 'x' => 500, 'y' => 500, 'z' => 500, '{' => 334, '|' => 260, '}' => 334, '~' => 584, chr(127) => 350, chr(128) => 556, chr(129) => 350, chr(130) => 222, chr(131) => 556, + chr(132) => 333, chr(133) => 1000, chr(134) => 556, chr(135) => 556, chr(136) => 333, chr(137) => 1000, chr(138) => 667, chr(139) => 333, chr(140) => 1000, chr(141) => 350, chr(142) => 611, chr(143) => 350, chr(144) => 350, chr(145) => 222, chr(146) => 222, chr(147) => 333, chr(148) => 333, chr(149) => 350, chr(150) => 556, chr(151) => 1000, chr(152) => 333, chr(153) => 1000, + chr(154) => 500, chr(155) => 333, chr(156) => 944, chr(157) => 350, chr(158) => 500, chr(159) => 667, chr(160) => 278, chr(161) => 333, chr(162) => 556, chr(163) => 556, chr(164) => 556, chr(165) => 556, chr(166) => 260, chr(167) => 556, chr(168) => 333, chr(169) => 737, chr(170) => 370, chr(171) => 556, chr(172) => 584, chr(173) => 333, chr(174) => 737, chr(175) => 333, + chr(176) => 400, chr(177) => 584, chr(178) => 333, chr(179) => 333, chr(180) => 333, chr(181) => 556, chr(182) => 537, chr(183) => 278, chr(184) => 333, chr(185) => 333, chr(186) => 365, chr(187) => 556, chr(188) => 834, chr(189) => 834, chr(190) => 834, chr(191) => 611, chr(192) => 667, chr(193) => 667, chr(194) => 667, chr(195) => 667, chr(196) => 667, chr(197) => 667, + chr(198) => 1000, chr(199) => 722, chr(200) => 667, chr(201) => 667, chr(202) => 667, chr(203) => 667, chr(204) => 278, chr(205) => 278, chr(206) => 278, chr(207) => 278, chr(208) => 722, chr(209) => 722, chr(210) => 778, chr(211) => 778, chr(212) => 778, chr(213) => 778, chr(214) => 778, chr(215) => 584, chr(216) => 778, chr(217) => 722, chr(218) => 722, chr(219) => 722, + chr(220) => 722, chr(221) => 667, chr(222) => 667, chr(223) => 611, chr(224) => 556, chr(225) => 556, chr(226) => 556, chr(227) => 556, chr(228) => 556, chr(229) => 556, chr(230) => 889, chr(231) => 500, chr(232) => 556, chr(233) => 556, chr(234) => 556, chr(235) => 556, chr(236) => 278, chr(237) => 278, chr(238) => 278, chr(239) => 278, chr(240) => 556, chr(241) => 556, + chr(242) => 556, chr(243) => 556, chr(244) => 556, chr(245) => 556, chr(246) => 556, chr(247) => 584, chr(248) => 611, chr(249) => 556, chr(250) => 556, chr(251) => 556, chr(252) => 556, chr(253) => 500, chr(254) => 556, chr(255) => 500]; $enc = 'cp1252'; -$uv = array(0=>array(0,128),128=>8364,130=>8218,131=>402,132=>8222,133=>8230,134=>array(8224,2),136=>710,137=>8240,138=>352,139=>8249,140=>338,142=>381,145=>array(8216,2),147=>array(8220,2),149=>8226,150=>array(8211,2),152=>732,153=>8482,154=>353,155=>8250,156=>339,158=>382,159=>376,160=>array(160,96)); -?> +$uv = [0 => [0, 128], 128 => 8364, 130 => 8218, 131 => 402, 132 => 8222, 133 => 8230, 134 => [8224, 2], 136 => 710, 137 => 8240, 138 => 352, 139 => 8249, 140 => 338, 142 => 381, 145 => [8216, 2], 147 => [8220, 2], 149 => 8226, 150 => [8211, 2], 152 => 732, 153 => 8482, 154 => 353, 155 => 8250, 156 => 339, 158 => 382, 159 => 376, 160 => [160, 96]]; diff --git a/app/ThirdParty/fpdf/font/symbol.php b/app/ThirdParty/fpdf/font/symbol.php index f8f0c330..8580057f 100644 --- a/app/ThirdParty/fpdf/font/symbol.php +++ b/app/ThirdParty/fpdf/font/symbol.php @@ -1,20 +1,20 @@ 250,chr(1)=>250,chr(2)=>250,chr(3)=>250,chr(4)=>250,chr(5)=>250,chr(6)=>250,chr(7)=>250,chr(8)=>250,chr(9)=>250,chr(10)=>250,chr(11)=>250,chr(12)=>250,chr(13)=>250,chr(14)=>250,chr(15)=>250,chr(16)=>250,chr(17)=>250,chr(18)=>250,chr(19)=>250,chr(20)=>250,chr(21)=>250, - chr(22)=>250,chr(23)=>250,chr(24)=>250,chr(25)=>250,chr(26)=>250,chr(27)=>250,chr(28)=>250,chr(29)=>250,chr(30)=>250,chr(31)=>250,' '=>250,'!'=>333,'"'=>713,'#'=>500,'$'=>549,'%'=>833,'&'=>778,'\''=>439,'('=>333,')'=>333,'*'=>500,'+'=>549, - ','=>250,'-'=>549,'.'=>250,'/'=>278,'0'=>500,'1'=>500,'2'=>500,'3'=>500,'4'=>500,'5'=>500,'6'=>500,'7'=>500,'8'=>500,'9'=>500,':'=>278,';'=>278,'<'=>549,'='=>549,'>'=>549,'?'=>444,'@'=>549,'A'=>722, - 'B'=>667,'C'=>722,'D'=>612,'E'=>611,'F'=>763,'G'=>603,'H'=>722,'I'=>333,'J'=>631,'K'=>722,'L'=>686,'M'=>889,'N'=>722,'O'=>722,'P'=>768,'Q'=>741,'R'=>556,'S'=>592,'T'=>611,'U'=>690,'V'=>439,'W'=>768, - 'X'=>645,'Y'=>795,'Z'=>611,'['=>333,'\\'=>863,']'=>333,'^'=>658,'_'=>500,'`'=>500,'a'=>631,'b'=>549,'c'=>549,'d'=>494,'e'=>439,'f'=>521,'g'=>411,'h'=>603,'i'=>329,'j'=>603,'k'=>549,'l'=>549,'m'=>576, - 'n'=>521,'o'=>549,'p'=>549,'q'=>521,'r'=>549,'s'=>603,'t'=>439,'u'=>576,'v'=>713,'w'=>686,'x'=>493,'y'=>686,'z'=>494,'{'=>480,'|'=>200,'}'=>480,'~'=>549,chr(127)=>0,chr(128)=>0,chr(129)=>0,chr(130)=>0,chr(131)=>0, - chr(132)=>0,chr(133)=>0,chr(134)=>0,chr(135)=>0,chr(136)=>0,chr(137)=>0,chr(138)=>0,chr(139)=>0,chr(140)=>0,chr(141)=>0,chr(142)=>0,chr(143)=>0,chr(144)=>0,chr(145)=>0,chr(146)=>0,chr(147)=>0,chr(148)=>0,chr(149)=>0,chr(150)=>0,chr(151)=>0,chr(152)=>0,chr(153)=>0, - chr(154)=>0,chr(155)=>0,chr(156)=>0,chr(157)=>0,chr(158)=>0,chr(159)=>0,chr(160)=>750,chr(161)=>620,chr(162)=>247,chr(163)=>549,chr(164)=>167,chr(165)=>713,chr(166)=>500,chr(167)=>753,chr(168)=>753,chr(169)=>753,chr(170)=>753,chr(171)=>1042,chr(172)=>987,chr(173)=>603,chr(174)=>987,chr(175)=>603, - chr(176)=>400,chr(177)=>549,chr(178)=>411,chr(179)=>549,chr(180)=>549,chr(181)=>713,chr(182)=>494,chr(183)=>460,chr(184)=>549,chr(185)=>549,chr(186)=>549,chr(187)=>549,chr(188)=>1000,chr(189)=>603,chr(190)=>1000,chr(191)=>658,chr(192)=>823,chr(193)=>686,chr(194)=>795,chr(195)=>987,chr(196)=>768,chr(197)=>768, - chr(198)=>823,chr(199)=>768,chr(200)=>768,chr(201)=>713,chr(202)=>713,chr(203)=>713,chr(204)=>713,chr(205)=>713,chr(206)=>713,chr(207)=>713,chr(208)=>768,chr(209)=>713,chr(210)=>790,chr(211)=>790,chr(212)=>890,chr(213)=>823,chr(214)=>549,chr(215)=>250,chr(216)=>713,chr(217)=>603,chr(218)=>603,chr(219)=>1042, - chr(220)=>987,chr(221)=>603,chr(222)=>987,chr(223)=>603,chr(224)=>494,chr(225)=>329,chr(226)=>790,chr(227)=>790,chr(228)=>786,chr(229)=>713,chr(230)=>384,chr(231)=>384,chr(232)=>384,chr(233)=>384,chr(234)=>384,chr(235)=>384,chr(236)=>494,chr(237)=>494,chr(238)=>494,chr(239)=>494,chr(240)=>0,chr(241)=>329, - chr(242)=>274,chr(243)=>686,chr(244)=>686,chr(245)=>686,chr(246)=>384,chr(247)=>384,chr(248)=>384,chr(249)=>384,chr(250)=>384,chr(251)=>384,chr(252)=>494,chr(253)=>494,chr(254)=>494,chr(255)=>0); -$uv = array(32=>160,33=>33,34=>8704,35=>35,36=>8707,37=>array(37,2),39=>8715,40=>array(40,2),42=>8727,43=>array(43,2),45=>8722,46=>array(46,18),64=>8773,65=>array(913,2),67=>935,68=>array(916,2),70=>934,71=>915,72=>919,73=>921,74=>977,75=>array(922,4),79=>array(927,2),81=>920,82=>929,83=>array(931,3),86=>962,87=>937,88=>926,89=>936,90=>918,91=>91,92=>8756,93=>93,94=>8869,95=>95,96=>63717,97=>array(945,2),99=>967,100=>array(948,2),102=>966,103=>947,104=>951,105=>953,106=>981,107=>array(954,4),111=>array(959,2),113=>952,114=>961,115=>array(963,3),118=>982,119=>969,120=>958,121=>968,122=>950,123=>array(123,3),126=>8764,160=>8364,161=>978,162=>8242,163=>8804,164=>8725,165=>8734,166=>402,167=>9827,168=>9830,169=>9829,170=>9824,171=>8596,172=>array(8592,4),176=>array(176,2),178=>8243,179=>8805,180=>215,181=>8733,182=>8706,183=>8226,184=>247,185=>array(8800,2),187=>8776,188=>8230,189=>array(63718,2),191=>8629,192=>8501,193=>8465,194=>8476,195=>8472,196=>8855,197=>8853,198=>8709,199=>array(8745,2),201=>8835,202=>8839,203=>8836,204=>8834,205=>8838,206=>array(8712,2),208=>8736,209=>8711,210=>63194,211=>63193,212=>63195,213=>8719,214=>8730,215=>8901,216=>172,217=>array(8743,2),219=>8660,220=>array(8656,4),224=>9674,225=>9001,226=>array(63720,3),229=>8721,230=>array(63723,10),241=>9002,242=>8747,243=>8992,244=>63733,245=>8993,246=>array(63734,9)); -?> +$cw = [ + chr(0) => 250, chr(1) => 250, chr(2) => 250, chr(3) => 250, chr(4) => 250, chr(5) => 250, chr(6) => 250, chr(7) => 250, chr(8) => 250, chr(9) => 250, chr(10) => 250, chr(11) => 250, chr(12) => 250, chr(13) => 250, chr(14) => 250, chr(15) => 250, chr(16) => 250, chr(17) => 250, chr(18) => 250, chr(19) => 250, chr(20) => 250, chr(21) => 250, + chr(22) => 250, chr(23) => 250, chr(24) => 250, chr(25) => 250, chr(26) => 250, chr(27) => 250, chr(28) => 250, chr(29) => 250, chr(30) => 250, chr(31) => 250, ' ' => 250, '!' => 333, '"' => 713, '#' => 500, '$' => 549, '%' => 833, '&' => 778, '\'' => 439, '(' => 333, ')' => 333, '*' => 500, '+' => 549, + ',' => 250, '-' => 549, '.' => 250, '/' => 278, '0' => 500, '1' => 500, '2' => 500, '3' => 500, '4' => 500, '5' => 500, '6' => 500, '7' => 500, '8' => 500, '9' => 500, ':' => 278, ';' => 278, '<' => 549, '=' => 549, '>' => 549, '?' => 444, '@' => 549, 'A' => 722, + 'B' => 667, 'C' => 722, 'D' => 612, 'E' => 611, 'F' => 763, 'G' => 603, 'H' => 722, 'I' => 333, 'J' => 631, 'K' => 722, 'L' => 686, 'M' => 889, 'N' => 722, 'O' => 722, 'P' => 768, 'Q' => 741, 'R' => 556, 'S' => 592, 'T' => 611, 'U' => 690, 'V' => 439, 'W' => 768, + 'X' => 645, 'Y' => 795, 'Z' => 611, '[' => 333, '\\' => 863, ']' => 333, '^' => 658, '_' => 500, '`' => 500, 'a' => 631, 'b' => 549, 'c' => 549, 'd' => 494, 'e' => 439, 'f' => 521, 'g' => 411, 'h' => 603, 'i' => 329, 'j' => 603, 'k' => 549, 'l' => 549, 'm' => 576, + 'n' => 521, 'o' => 549, 'p' => 549, 'q' => 521, 'r' => 549, 's' => 603, 't' => 439, 'u' => 576, 'v' => 713, 'w' => 686, 'x' => 493, 'y' => 686, 'z' => 494, '{' => 480, '|' => 200, '}' => 480, '~' => 549, chr(127) => 0, chr(128) => 0, chr(129) => 0, chr(130) => 0, chr(131) => 0, + chr(132) => 0, chr(133) => 0, chr(134) => 0, chr(135) => 0, chr(136) => 0, chr(137) => 0, chr(138) => 0, chr(139) => 0, chr(140) => 0, chr(141) => 0, chr(142) => 0, chr(143) => 0, chr(144) => 0, chr(145) => 0, chr(146) => 0, chr(147) => 0, chr(148) => 0, chr(149) => 0, chr(150) => 0, chr(151) => 0, chr(152) => 0, chr(153) => 0, + chr(154) => 0, chr(155) => 0, chr(156) => 0, chr(157) => 0, chr(158) => 0, chr(159) => 0, chr(160) => 750, chr(161) => 620, chr(162) => 247, chr(163) => 549, chr(164) => 167, chr(165) => 713, chr(166) => 500, chr(167) => 753, chr(168) => 753, chr(169) => 753, chr(170) => 753, chr(171) => 1042, chr(172) => 987, chr(173) => 603, chr(174) => 987, chr(175) => 603, + chr(176) => 400, chr(177) => 549, chr(178) => 411, chr(179) => 549, chr(180) => 549, chr(181) => 713, chr(182) => 494, chr(183) => 460, chr(184) => 549, chr(185) => 549, chr(186) => 549, chr(187) => 549, chr(188) => 1000, chr(189) => 603, chr(190) => 1000, chr(191) => 658, chr(192) => 823, chr(193) => 686, chr(194) => 795, chr(195) => 987, chr(196) => 768, chr(197) => 768, + chr(198) => 823, chr(199) => 768, chr(200) => 768, chr(201) => 713, chr(202) => 713, chr(203) => 713, chr(204) => 713, chr(205) => 713, chr(206) => 713, chr(207) => 713, chr(208) => 768, chr(209) => 713, chr(210) => 790, chr(211) => 790, chr(212) => 890, chr(213) => 823, chr(214) => 549, chr(215) => 250, chr(216) => 713, chr(217) => 603, chr(218) => 603, chr(219) => 1042, + chr(220) => 987, chr(221) => 603, chr(222) => 987, chr(223) => 603, chr(224) => 494, chr(225) => 329, chr(226) => 790, chr(227) => 790, chr(228) => 786, chr(229) => 713, chr(230) => 384, chr(231) => 384, chr(232) => 384, chr(233) => 384, chr(234) => 384, chr(235) => 384, chr(236) => 494, chr(237) => 494, chr(238) => 494, chr(239) => 494, chr(240) => 0, chr(241) => 329, + chr(242) => 274, chr(243) => 686, chr(244) => 686, chr(245) => 686, chr(246) => 384, chr(247) => 384, chr(248) => 384, chr(249) => 384, chr(250) => 384, chr(251) => 384, chr(252) => 494, chr(253) => 494, chr(254) => 494, chr(255) => 0]; +$uv = [32 => 160, 33 => 33, 34 => 8704, 35 => 35, 36 => 8707, 37 => [37, 2], 39 => 8715, 40 => [40, 2], 42 => 8727, 43 => [43, 2], 45 => 8722, 46 => [46, 18], 64 => 8773, 65 => [913, 2], 67 => 935, 68 => [916, 2], 70 => 934, 71 => 915, 72 => 919, 73 => 921, 74 => 977, 75 => [922, 4], 79 => [927, 2], 81 => 920, 82 => 929, 83 => [931, 3], 86 => 962, 87 => 937, 88 => 926, 89 => 936, 90 => 918, 91 => 91, 92 => 8756, 93 => 93, 94 => 8869, 95 => 95, 96 => 63717, 97 => [945, 2], 99 => 967, 100 => [948, 2], 102 => 966, 103 => 947, 104 => 951, 105 => 953, 106 => 981, 107 => [954, 4], 111 => [959, 2], 113 => 952, 114 => 961, 115 => [963, 3], 118 => 982, 119 => 969, 120 => 958, 121 => 968, 122 => 950, 123 => [123, 3], 126 => 8764, 160 => 8364, 161 => 978, 162 => 8242, 163 => 8804, 164 => 8725, 165 => 8734, 166 => 402, 167 => 9827, 168 => 9830, 169 => 9829, 170 => 9824, 171 => 8596, 172 => [8592, 4], 176 => [176, 2], 178 => 8243, 179 => 8805, 180 => 215, 181 => 8733, 182 => 8706, 183 => 8226, 184 => 247, 185 => [8800, 2], 187 => 8776, 188 => 8230, 189 => [63718, 2], 191 => 8629, 192 => 8501, 193 => 8465, 194 => 8476, 195 => 8472, 196 => 8855, 197 => 8853, 198 => 8709, 199 => [8745, 2], 201 => 8835, 202 => 8839, 203 => 8836, 204 => 8834, 205 => 8838, 206 => [8712, 2], 208 => 8736, 209 => 8711, 210 => 63194, 211 => 63193, 212 => 63195, 213 => 8719, 214 => 8730, 215 => 8901, 216 => 172, 217 => [8743, 2], 219 => 8660, 220 => [8656, 4], 224 => 9674, 225 => 9001, 226 => [63720, 3], 229 => 8721, 230 => [63723, 10], 241 => 9002, 242 => 8747, 243 => 8992, 244 => 63733, 245 => 8993, 246 => [63734, 9]]; diff --git a/app/ThirdParty/fpdf/font/times.php b/app/ThirdParty/fpdf/font/times.php index 81f2a8b4..68170a03 100644 --- a/app/ThirdParty/fpdf/font/times.php +++ b/app/ThirdParty/fpdf/font/times.php @@ -1,21 +1,21 @@ 250,chr(1)=>250,chr(2)=>250,chr(3)=>250,chr(4)=>250,chr(5)=>250,chr(6)=>250,chr(7)=>250,chr(8)=>250,chr(9)=>250,chr(10)=>250,chr(11)=>250,chr(12)=>250,chr(13)=>250,chr(14)=>250,chr(15)=>250,chr(16)=>250,chr(17)=>250,chr(18)=>250,chr(19)=>250,chr(20)=>250,chr(21)=>250, - chr(22)=>250,chr(23)=>250,chr(24)=>250,chr(25)=>250,chr(26)=>250,chr(27)=>250,chr(28)=>250,chr(29)=>250,chr(30)=>250,chr(31)=>250,' '=>250,'!'=>333,'"'=>408,'#'=>500,'$'=>500,'%'=>833,'&'=>778,'\''=>180,'('=>333,')'=>333,'*'=>500,'+'=>564, - ','=>250,'-'=>333,'.'=>250,'/'=>278,'0'=>500,'1'=>500,'2'=>500,'3'=>500,'4'=>500,'5'=>500,'6'=>500,'7'=>500,'8'=>500,'9'=>500,':'=>278,';'=>278,'<'=>564,'='=>564,'>'=>564,'?'=>444,'@'=>921,'A'=>722, - 'B'=>667,'C'=>667,'D'=>722,'E'=>611,'F'=>556,'G'=>722,'H'=>722,'I'=>333,'J'=>389,'K'=>722,'L'=>611,'M'=>889,'N'=>722,'O'=>722,'P'=>556,'Q'=>722,'R'=>667,'S'=>556,'T'=>611,'U'=>722,'V'=>722,'W'=>944, - 'X'=>722,'Y'=>722,'Z'=>611,'['=>333,'\\'=>278,']'=>333,'^'=>469,'_'=>500,'`'=>333,'a'=>444,'b'=>500,'c'=>444,'d'=>500,'e'=>444,'f'=>333,'g'=>500,'h'=>500,'i'=>278,'j'=>278,'k'=>500,'l'=>278,'m'=>778, - 'n'=>500,'o'=>500,'p'=>500,'q'=>500,'r'=>333,'s'=>389,'t'=>278,'u'=>500,'v'=>500,'w'=>722,'x'=>500,'y'=>500,'z'=>444,'{'=>480,'|'=>200,'}'=>480,'~'=>541,chr(127)=>350,chr(128)=>500,chr(129)=>350,chr(130)=>333,chr(131)=>500, - chr(132)=>444,chr(133)=>1000,chr(134)=>500,chr(135)=>500,chr(136)=>333,chr(137)=>1000,chr(138)=>556,chr(139)=>333,chr(140)=>889,chr(141)=>350,chr(142)=>611,chr(143)=>350,chr(144)=>350,chr(145)=>333,chr(146)=>333,chr(147)=>444,chr(148)=>444,chr(149)=>350,chr(150)=>500,chr(151)=>1000,chr(152)=>333,chr(153)=>980, - chr(154)=>389,chr(155)=>333,chr(156)=>722,chr(157)=>350,chr(158)=>444,chr(159)=>722,chr(160)=>250,chr(161)=>333,chr(162)=>500,chr(163)=>500,chr(164)=>500,chr(165)=>500,chr(166)=>200,chr(167)=>500,chr(168)=>333,chr(169)=>760,chr(170)=>276,chr(171)=>500,chr(172)=>564,chr(173)=>333,chr(174)=>760,chr(175)=>333, - chr(176)=>400,chr(177)=>564,chr(178)=>300,chr(179)=>300,chr(180)=>333,chr(181)=>500,chr(182)=>453,chr(183)=>250,chr(184)=>333,chr(185)=>300,chr(186)=>310,chr(187)=>500,chr(188)=>750,chr(189)=>750,chr(190)=>750,chr(191)=>444,chr(192)=>722,chr(193)=>722,chr(194)=>722,chr(195)=>722,chr(196)=>722,chr(197)=>722, - chr(198)=>889,chr(199)=>667,chr(200)=>611,chr(201)=>611,chr(202)=>611,chr(203)=>611,chr(204)=>333,chr(205)=>333,chr(206)=>333,chr(207)=>333,chr(208)=>722,chr(209)=>722,chr(210)=>722,chr(211)=>722,chr(212)=>722,chr(213)=>722,chr(214)=>722,chr(215)=>564,chr(216)=>722,chr(217)=>722,chr(218)=>722,chr(219)=>722, - chr(220)=>722,chr(221)=>722,chr(222)=>556,chr(223)=>500,chr(224)=>444,chr(225)=>444,chr(226)=>444,chr(227)=>444,chr(228)=>444,chr(229)=>444,chr(230)=>667,chr(231)=>444,chr(232)=>444,chr(233)=>444,chr(234)=>444,chr(235)=>444,chr(236)=>278,chr(237)=>278,chr(238)=>278,chr(239)=>278,chr(240)=>500,chr(241)=>500, - chr(242)=>500,chr(243)=>500,chr(244)=>500,chr(245)=>500,chr(246)=>500,chr(247)=>564,chr(248)=>500,chr(249)=>500,chr(250)=>500,chr(251)=>500,chr(252)=>500,chr(253)=>500,chr(254)=>500,chr(255)=>500); +$cw = [ + chr(0) => 250, chr(1) => 250, chr(2) => 250, chr(3) => 250, chr(4) => 250, chr(5) => 250, chr(6) => 250, chr(7) => 250, chr(8) => 250, chr(9) => 250, chr(10) => 250, chr(11) => 250, chr(12) => 250, chr(13) => 250, chr(14) => 250, chr(15) => 250, chr(16) => 250, chr(17) => 250, chr(18) => 250, chr(19) => 250, chr(20) => 250, chr(21) => 250, + chr(22) => 250, chr(23) => 250, chr(24) => 250, chr(25) => 250, chr(26) => 250, chr(27) => 250, chr(28) => 250, chr(29) => 250, chr(30) => 250, chr(31) => 250, ' ' => 250, '!' => 333, '"' => 408, '#' => 500, '$' => 500, '%' => 833, '&' => 778, '\'' => 180, '(' => 333, ')' => 333, '*' => 500, '+' => 564, + ',' => 250, '-' => 333, '.' => 250, '/' => 278, '0' => 500, '1' => 500, '2' => 500, '3' => 500, '4' => 500, '5' => 500, '6' => 500, '7' => 500, '8' => 500, '9' => 500, ':' => 278, ';' => 278, '<' => 564, '=' => 564, '>' => 564, '?' => 444, '@' => 921, 'A' => 722, + 'B' => 667, 'C' => 667, 'D' => 722, 'E' => 611, 'F' => 556, 'G' => 722, 'H' => 722, 'I' => 333, 'J' => 389, 'K' => 722, 'L' => 611, 'M' => 889, 'N' => 722, 'O' => 722, 'P' => 556, 'Q' => 722, 'R' => 667, 'S' => 556, 'T' => 611, 'U' => 722, 'V' => 722, 'W' => 944, + 'X' => 722, 'Y' => 722, 'Z' => 611, '[' => 333, '\\' => 278, ']' => 333, '^' => 469, '_' => 500, '`' => 333, 'a' => 444, 'b' => 500, 'c' => 444, 'd' => 500, 'e' => 444, 'f' => 333, 'g' => 500, 'h' => 500, 'i' => 278, 'j' => 278, 'k' => 500, 'l' => 278, 'm' => 778, + 'n' => 500, 'o' => 500, 'p' => 500, 'q' => 500, 'r' => 333, 's' => 389, 't' => 278, 'u' => 500, 'v' => 500, 'w' => 722, 'x' => 500, 'y' => 500, 'z' => 444, '{' => 480, '|' => 200, '}' => 480, '~' => 541, chr(127) => 350, chr(128) => 500, chr(129) => 350, chr(130) => 333, chr(131) => 500, + chr(132) => 444, chr(133) => 1000, chr(134) => 500, chr(135) => 500, chr(136) => 333, chr(137) => 1000, chr(138) => 556, chr(139) => 333, chr(140) => 889, chr(141) => 350, chr(142) => 611, chr(143) => 350, chr(144) => 350, chr(145) => 333, chr(146) => 333, chr(147) => 444, chr(148) => 444, chr(149) => 350, chr(150) => 500, chr(151) => 1000, chr(152) => 333, chr(153) => 980, + chr(154) => 389, chr(155) => 333, chr(156) => 722, chr(157) => 350, chr(158) => 444, chr(159) => 722, chr(160) => 250, chr(161) => 333, chr(162) => 500, chr(163) => 500, chr(164) => 500, chr(165) => 500, chr(166) => 200, chr(167) => 500, chr(168) => 333, chr(169) => 760, chr(170) => 276, chr(171) => 500, chr(172) => 564, chr(173) => 333, chr(174) => 760, chr(175) => 333, + chr(176) => 400, chr(177) => 564, chr(178) => 300, chr(179) => 300, chr(180) => 333, chr(181) => 500, chr(182) => 453, chr(183) => 250, chr(184) => 333, chr(185) => 300, chr(186) => 310, chr(187) => 500, chr(188) => 750, chr(189) => 750, chr(190) => 750, chr(191) => 444, chr(192) => 722, chr(193) => 722, chr(194) => 722, chr(195) => 722, chr(196) => 722, chr(197) => 722, + chr(198) => 889, chr(199) => 667, chr(200) => 611, chr(201) => 611, chr(202) => 611, chr(203) => 611, chr(204) => 333, chr(205) => 333, chr(206) => 333, chr(207) => 333, chr(208) => 722, chr(209) => 722, chr(210) => 722, chr(211) => 722, chr(212) => 722, chr(213) => 722, chr(214) => 722, chr(215) => 564, chr(216) => 722, chr(217) => 722, chr(218) => 722, chr(219) => 722, + chr(220) => 722, chr(221) => 722, chr(222) => 556, chr(223) => 500, chr(224) => 444, chr(225) => 444, chr(226) => 444, chr(227) => 444, chr(228) => 444, chr(229) => 444, chr(230) => 667, chr(231) => 444, chr(232) => 444, chr(233) => 444, chr(234) => 444, chr(235) => 444, chr(236) => 278, chr(237) => 278, chr(238) => 278, chr(239) => 278, chr(240) => 500, chr(241) => 500, + chr(242) => 500, chr(243) => 500, chr(244) => 500, chr(245) => 500, chr(246) => 500, chr(247) => 564, chr(248) => 500, chr(249) => 500, chr(250) => 500, chr(251) => 500, chr(252) => 500, chr(253) => 500, chr(254) => 500, chr(255) => 500]; $enc = 'cp1252'; -$uv = array(0=>array(0,128),128=>8364,130=>8218,131=>402,132=>8222,133=>8230,134=>array(8224,2),136=>710,137=>8240,138=>352,139=>8249,140=>338,142=>381,145=>array(8216,2),147=>array(8220,2),149=>8226,150=>array(8211,2),152=>732,153=>8482,154=>353,155=>8250,156=>339,158=>382,159=>376,160=>array(160,96)); -?> +$uv = [0 => [0, 128], 128 => 8364, 130 => 8218, 131 => 402, 132 => 8222, 133 => 8230, 134 => [8224, 2], 136 => 710, 137 => 8240, 138 => 352, 139 => 8249, 140 => 338, 142 => 381, 145 => [8216, 2], 147 => [8220, 2], 149 => 8226, 150 => [8211, 2], 152 => 732, 153 => 8482, 154 => 353, 155 => 8250, 156 => 339, 158 => 382, 159 => 376, 160 => [160, 96]]; diff --git a/app/ThirdParty/fpdf/font/timesb.php b/app/ThirdParty/fpdf/font/timesb.php index 7db704fd..3f689ac9 100644 --- a/app/ThirdParty/fpdf/font/timesb.php +++ b/app/ThirdParty/fpdf/font/timesb.php @@ -1,21 +1,21 @@ 250,chr(1)=>250,chr(2)=>250,chr(3)=>250,chr(4)=>250,chr(5)=>250,chr(6)=>250,chr(7)=>250,chr(8)=>250,chr(9)=>250,chr(10)=>250,chr(11)=>250,chr(12)=>250,chr(13)=>250,chr(14)=>250,chr(15)=>250,chr(16)=>250,chr(17)=>250,chr(18)=>250,chr(19)=>250,chr(20)=>250,chr(21)=>250, - chr(22)=>250,chr(23)=>250,chr(24)=>250,chr(25)=>250,chr(26)=>250,chr(27)=>250,chr(28)=>250,chr(29)=>250,chr(30)=>250,chr(31)=>250,' '=>250,'!'=>333,'"'=>555,'#'=>500,'$'=>500,'%'=>1000,'&'=>833,'\''=>278,'('=>333,')'=>333,'*'=>500,'+'=>570, - ','=>250,'-'=>333,'.'=>250,'/'=>278,'0'=>500,'1'=>500,'2'=>500,'3'=>500,'4'=>500,'5'=>500,'6'=>500,'7'=>500,'8'=>500,'9'=>500,':'=>333,';'=>333,'<'=>570,'='=>570,'>'=>570,'?'=>500,'@'=>930,'A'=>722, - 'B'=>667,'C'=>722,'D'=>722,'E'=>667,'F'=>611,'G'=>778,'H'=>778,'I'=>389,'J'=>500,'K'=>778,'L'=>667,'M'=>944,'N'=>722,'O'=>778,'P'=>611,'Q'=>778,'R'=>722,'S'=>556,'T'=>667,'U'=>722,'V'=>722,'W'=>1000, - 'X'=>722,'Y'=>722,'Z'=>667,'['=>333,'\\'=>278,']'=>333,'^'=>581,'_'=>500,'`'=>333,'a'=>500,'b'=>556,'c'=>444,'d'=>556,'e'=>444,'f'=>333,'g'=>500,'h'=>556,'i'=>278,'j'=>333,'k'=>556,'l'=>278,'m'=>833, - 'n'=>556,'o'=>500,'p'=>556,'q'=>556,'r'=>444,'s'=>389,'t'=>333,'u'=>556,'v'=>500,'w'=>722,'x'=>500,'y'=>500,'z'=>444,'{'=>394,'|'=>220,'}'=>394,'~'=>520,chr(127)=>350,chr(128)=>500,chr(129)=>350,chr(130)=>333,chr(131)=>500, - chr(132)=>500,chr(133)=>1000,chr(134)=>500,chr(135)=>500,chr(136)=>333,chr(137)=>1000,chr(138)=>556,chr(139)=>333,chr(140)=>1000,chr(141)=>350,chr(142)=>667,chr(143)=>350,chr(144)=>350,chr(145)=>333,chr(146)=>333,chr(147)=>500,chr(148)=>500,chr(149)=>350,chr(150)=>500,chr(151)=>1000,chr(152)=>333,chr(153)=>1000, - chr(154)=>389,chr(155)=>333,chr(156)=>722,chr(157)=>350,chr(158)=>444,chr(159)=>722,chr(160)=>250,chr(161)=>333,chr(162)=>500,chr(163)=>500,chr(164)=>500,chr(165)=>500,chr(166)=>220,chr(167)=>500,chr(168)=>333,chr(169)=>747,chr(170)=>300,chr(171)=>500,chr(172)=>570,chr(173)=>333,chr(174)=>747,chr(175)=>333, - chr(176)=>400,chr(177)=>570,chr(178)=>300,chr(179)=>300,chr(180)=>333,chr(181)=>556,chr(182)=>540,chr(183)=>250,chr(184)=>333,chr(185)=>300,chr(186)=>330,chr(187)=>500,chr(188)=>750,chr(189)=>750,chr(190)=>750,chr(191)=>500,chr(192)=>722,chr(193)=>722,chr(194)=>722,chr(195)=>722,chr(196)=>722,chr(197)=>722, - chr(198)=>1000,chr(199)=>722,chr(200)=>667,chr(201)=>667,chr(202)=>667,chr(203)=>667,chr(204)=>389,chr(205)=>389,chr(206)=>389,chr(207)=>389,chr(208)=>722,chr(209)=>722,chr(210)=>778,chr(211)=>778,chr(212)=>778,chr(213)=>778,chr(214)=>778,chr(215)=>570,chr(216)=>778,chr(217)=>722,chr(218)=>722,chr(219)=>722, - chr(220)=>722,chr(221)=>722,chr(222)=>611,chr(223)=>556,chr(224)=>500,chr(225)=>500,chr(226)=>500,chr(227)=>500,chr(228)=>500,chr(229)=>500,chr(230)=>722,chr(231)=>444,chr(232)=>444,chr(233)=>444,chr(234)=>444,chr(235)=>444,chr(236)=>278,chr(237)=>278,chr(238)=>278,chr(239)=>278,chr(240)=>500,chr(241)=>556, - chr(242)=>500,chr(243)=>500,chr(244)=>500,chr(245)=>500,chr(246)=>500,chr(247)=>570,chr(248)=>500,chr(249)=>556,chr(250)=>556,chr(251)=>556,chr(252)=>556,chr(253)=>500,chr(254)=>556,chr(255)=>500); +$cw = [ + chr(0) => 250, chr(1) => 250, chr(2) => 250, chr(3) => 250, chr(4) => 250, chr(5) => 250, chr(6) => 250, chr(7) => 250, chr(8) => 250, chr(9) => 250, chr(10) => 250, chr(11) => 250, chr(12) => 250, chr(13) => 250, chr(14) => 250, chr(15) => 250, chr(16) => 250, chr(17) => 250, chr(18) => 250, chr(19) => 250, chr(20) => 250, chr(21) => 250, + chr(22) => 250, chr(23) => 250, chr(24) => 250, chr(25) => 250, chr(26) => 250, chr(27) => 250, chr(28) => 250, chr(29) => 250, chr(30) => 250, chr(31) => 250, ' ' => 250, '!' => 333, '"' => 555, '#' => 500, '$' => 500, '%' => 1000, '&' => 833, '\'' => 278, '(' => 333, ')' => 333, '*' => 500, '+' => 570, + ',' => 250, '-' => 333, '.' => 250, '/' => 278, '0' => 500, '1' => 500, '2' => 500, '3' => 500, '4' => 500, '5' => 500, '6' => 500, '7' => 500, '8' => 500, '9' => 500, ':' => 333, ';' => 333, '<' => 570, '=' => 570, '>' => 570, '?' => 500, '@' => 930, 'A' => 722, + 'B' => 667, 'C' => 722, 'D' => 722, 'E' => 667, 'F' => 611, 'G' => 778, 'H' => 778, 'I' => 389, 'J' => 500, 'K' => 778, 'L' => 667, 'M' => 944, 'N' => 722, 'O' => 778, 'P' => 611, 'Q' => 778, 'R' => 722, 'S' => 556, 'T' => 667, 'U' => 722, 'V' => 722, 'W' => 1000, + 'X' => 722, 'Y' => 722, 'Z' => 667, '[' => 333, '\\' => 278, ']' => 333, '^' => 581, '_' => 500, '`' => 333, 'a' => 500, 'b' => 556, 'c' => 444, 'd' => 556, 'e' => 444, 'f' => 333, 'g' => 500, 'h' => 556, 'i' => 278, 'j' => 333, 'k' => 556, 'l' => 278, 'm' => 833, + 'n' => 556, 'o' => 500, 'p' => 556, 'q' => 556, 'r' => 444, 's' => 389, 't' => 333, 'u' => 556, 'v' => 500, 'w' => 722, 'x' => 500, 'y' => 500, 'z' => 444, '{' => 394, '|' => 220, '}' => 394, '~' => 520, chr(127) => 350, chr(128) => 500, chr(129) => 350, chr(130) => 333, chr(131) => 500, + chr(132) => 500, chr(133) => 1000, chr(134) => 500, chr(135) => 500, chr(136) => 333, chr(137) => 1000, chr(138) => 556, chr(139) => 333, chr(140) => 1000, chr(141) => 350, chr(142) => 667, chr(143) => 350, chr(144) => 350, chr(145) => 333, chr(146) => 333, chr(147) => 500, chr(148) => 500, chr(149) => 350, chr(150) => 500, chr(151) => 1000, chr(152) => 333, chr(153) => 1000, + chr(154) => 389, chr(155) => 333, chr(156) => 722, chr(157) => 350, chr(158) => 444, chr(159) => 722, chr(160) => 250, chr(161) => 333, chr(162) => 500, chr(163) => 500, chr(164) => 500, chr(165) => 500, chr(166) => 220, chr(167) => 500, chr(168) => 333, chr(169) => 747, chr(170) => 300, chr(171) => 500, chr(172) => 570, chr(173) => 333, chr(174) => 747, chr(175) => 333, + chr(176) => 400, chr(177) => 570, chr(178) => 300, chr(179) => 300, chr(180) => 333, chr(181) => 556, chr(182) => 540, chr(183) => 250, chr(184) => 333, chr(185) => 300, chr(186) => 330, chr(187) => 500, chr(188) => 750, chr(189) => 750, chr(190) => 750, chr(191) => 500, chr(192) => 722, chr(193) => 722, chr(194) => 722, chr(195) => 722, chr(196) => 722, chr(197) => 722, + chr(198) => 1000, chr(199) => 722, chr(200) => 667, chr(201) => 667, chr(202) => 667, chr(203) => 667, chr(204) => 389, chr(205) => 389, chr(206) => 389, chr(207) => 389, chr(208) => 722, chr(209) => 722, chr(210) => 778, chr(211) => 778, chr(212) => 778, chr(213) => 778, chr(214) => 778, chr(215) => 570, chr(216) => 778, chr(217) => 722, chr(218) => 722, chr(219) => 722, + chr(220) => 722, chr(221) => 722, chr(222) => 611, chr(223) => 556, chr(224) => 500, chr(225) => 500, chr(226) => 500, chr(227) => 500, chr(228) => 500, chr(229) => 500, chr(230) => 722, chr(231) => 444, chr(232) => 444, chr(233) => 444, chr(234) => 444, chr(235) => 444, chr(236) => 278, chr(237) => 278, chr(238) => 278, chr(239) => 278, chr(240) => 500, chr(241) => 556, + chr(242) => 500, chr(243) => 500, chr(244) => 500, chr(245) => 500, chr(246) => 500, chr(247) => 570, chr(248) => 500, chr(249) => 556, chr(250) => 556, chr(251) => 556, chr(252) => 556, chr(253) => 500, chr(254) => 556, chr(255) => 500]; $enc = 'cp1252'; -$uv = array(0=>array(0,128),128=>8364,130=>8218,131=>402,132=>8222,133=>8230,134=>array(8224,2),136=>710,137=>8240,138=>352,139=>8249,140=>338,142=>381,145=>array(8216,2),147=>array(8220,2),149=>8226,150=>array(8211,2),152=>732,153=>8482,154=>353,155=>8250,156=>339,158=>382,159=>376,160=>array(160,96)); -?> +$uv = [0 => [0, 128], 128 => 8364, 130 => 8218, 131 => 402, 132 => 8222, 133 => 8230, 134 => [8224, 2], 136 => 710, 137 => 8240, 138 => 352, 139 => 8249, 140 => 338, 142 => 381, 145 => [8216, 2], 147 => [8220, 2], 149 => 8226, 150 => [8211, 2], 152 => 732, 153 => 8482, 154 => 353, 155 => 8250, 156 => 339, 158 => 382, 159 => 376, 160 => [160, 96]]; diff --git a/app/ThirdParty/fpdf/font/timesbi.php b/app/ThirdParty/fpdf/font/timesbi.php index 089f21ab..2edef0e7 100644 --- a/app/ThirdParty/fpdf/font/timesbi.php +++ b/app/ThirdParty/fpdf/font/timesbi.php @@ -1,21 +1,21 @@ 250,chr(1)=>250,chr(2)=>250,chr(3)=>250,chr(4)=>250,chr(5)=>250,chr(6)=>250,chr(7)=>250,chr(8)=>250,chr(9)=>250,chr(10)=>250,chr(11)=>250,chr(12)=>250,chr(13)=>250,chr(14)=>250,chr(15)=>250,chr(16)=>250,chr(17)=>250,chr(18)=>250,chr(19)=>250,chr(20)=>250,chr(21)=>250, - chr(22)=>250,chr(23)=>250,chr(24)=>250,chr(25)=>250,chr(26)=>250,chr(27)=>250,chr(28)=>250,chr(29)=>250,chr(30)=>250,chr(31)=>250,' '=>250,'!'=>389,'"'=>555,'#'=>500,'$'=>500,'%'=>833,'&'=>778,'\''=>278,'('=>333,')'=>333,'*'=>500,'+'=>570, - ','=>250,'-'=>333,'.'=>250,'/'=>278,'0'=>500,'1'=>500,'2'=>500,'3'=>500,'4'=>500,'5'=>500,'6'=>500,'7'=>500,'8'=>500,'9'=>500,':'=>333,';'=>333,'<'=>570,'='=>570,'>'=>570,'?'=>500,'@'=>832,'A'=>667, - 'B'=>667,'C'=>667,'D'=>722,'E'=>667,'F'=>667,'G'=>722,'H'=>778,'I'=>389,'J'=>500,'K'=>667,'L'=>611,'M'=>889,'N'=>722,'O'=>722,'P'=>611,'Q'=>722,'R'=>667,'S'=>556,'T'=>611,'U'=>722,'V'=>667,'W'=>889, - 'X'=>667,'Y'=>611,'Z'=>611,'['=>333,'\\'=>278,']'=>333,'^'=>570,'_'=>500,'`'=>333,'a'=>500,'b'=>500,'c'=>444,'d'=>500,'e'=>444,'f'=>333,'g'=>500,'h'=>556,'i'=>278,'j'=>278,'k'=>500,'l'=>278,'m'=>778, - 'n'=>556,'o'=>500,'p'=>500,'q'=>500,'r'=>389,'s'=>389,'t'=>278,'u'=>556,'v'=>444,'w'=>667,'x'=>500,'y'=>444,'z'=>389,'{'=>348,'|'=>220,'}'=>348,'~'=>570,chr(127)=>350,chr(128)=>500,chr(129)=>350,chr(130)=>333,chr(131)=>500, - chr(132)=>500,chr(133)=>1000,chr(134)=>500,chr(135)=>500,chr(136)=>333,chr(137)=>1000,chr(138)=>556,chr(139)=>333,chr(140)=>944,chr(141)=>350,chr(142)=>611,chr(143)=>350,chr(144)=>350,chr(145)=>333,chr(146)=>333,chr(147)=>500,chr(148)=>500,chr(149)=>350,chr(150)=>500,chr(151)=>1000,chr(152)=>333,chr(153)=>1000, - chr(154)=>389,chr(155)=>333,chr(156)=>722,chr(157)=>350,chr(158)=>389,chr(159)=>611,chr(160)=>250,chr(161)=>389,chr(162)=>500,chr(163)=>500,chr(164)=>500,chr(165)=>500,chr(166)=>220,chr(167)=>500,chr(168)=>333,chr(169)=>747,chr(170)=>266,chr(171)=>500,chr(172)=>606,chr(173)=>333,chr(174)=>747,chr(175)=>333, - chr(176)=>400,chr(177)=>570,chr(178)=>300,chr(179)=>300,chr(180)=>333,chr(181)=>576,chr(182)=>500,chr(183)=>250,chr(184)=>333,chr(185)=>300,chr(186)=>300,chr(187)=>500,chr(188)=>750,chr(189)=>750,chr(190)=>750,chr(191)=>500,chr(192)=>667,chr(193)=>667,chr(194)=>667,chr(195)=>667,chr(196)=>667,chr(197)=>667, - chr(198)=>944,chr(199)=>667,chr(200)=>667,chr(201)=>667,chr(202)=>667,chr(203)=>667,chr(204)=>389,chr(205)=>389,chr(206)=>389,chr(207)=>389,chr(208)=>722,chr(209)=>722,chr(210)=>722,chr(211)=>722,chr(212)=>722,chr(213)=>722,chr(214)=>722,chr(215)=>570,chr(216)=>722,chr(217)=>722,chr(218)=>722,chr(219)=>722, - chr(220)=>722,chr(221)=>611,chr(222)=>611,chr(223)=>500,chr(224)=>500,chr(225)=>500,chr(226)=>500,chr(227)=>500,chr(228)=>500,chr(229)=>500,chr(230)=>722,chr(231)=>444,chr(232)=>444,chr(233)=>444,chr(234)=>444,chr(235)=>444,chr(236)=>278,chr(237)=>278,chr(238)=>278,chr(239)=>278,chr(240)=>500,chr(241)=>556, - chr(242)=>500,chr(243)=>500,chr(244)=>500,chr(245)=>500,chr(246)=>500,chr(247)=>570,chr(248)=>500,chr(249)=>556,chr(250)=>556,chr(251)=>556,chr(252)=>556,chr(253)=>444,chr(254)=>500,chr(255)=>444); +$cw = [ + chr(0) => 250, chr(1) => 250, chr(2) => 250, chr(3) => 250, chr(4) => 250, chr(5) => 250, chr(6) => 250, chr(7) => 250, chr(8) => 250, chr(9) => 250, chr(10) => 250, chr(11) => 250, chr(12) => 250, chr(13) => 250, chr(14) => 250, chr(15) => 250, chr(16) => 250, chr(17) => 250, chr(18) => 250, chr(19) => 250, chr(20) => 250, chr(21) => 250, + chr(22) => 250, chr(23) => 250, chr(24) => 250, chr(25) => 250, chr(26) => 250, chr(27) => 250, chr(28) => 250, chr(29) => 250, chr(30) => 250, chr(31) => 250, ' ' => 250, '!' => 389, '"' => 555, '#' => 500, '$' => 500, '%' => 833, '&' => 778, '\'' => 278, '(' => 333, ')' => 333, '*' => 500, '+' => 570, + ',' => 250, '-' => 333, '.' => 250, '/' => 278, '0' => 500, '1' => 500, '2' => 500, '3' => 500, '4' => 500, '5' => 500, '6' => 500, '7' => 500, '8' => 500, '9' => 500, ':' => 333, ';' => 333, '<' => 570, '=' => 570, '>' => 570, '?' => 500, '@' => 832, 'A' => 667, + 'B' => 667, 'C' => 667, 'D' => 722, 'E' => 667, 'F' => 667, 'G' => 722, 'H' => 778, 'I' => 389, 'J' => 500, 'K' => 667, 'L' => 611, 'M' => 889, 'N' => 722, 'O' => 722, 'P' => 611, 'Q' => 722, 'R' => 667, 'S' => 556, 'T' => 611, 'U' => 722, 'V' => 667, 'W' => 889, + 'X' => 667, 'Y' => 611, 'Z' => 611, '[' => 333, '\\' => 278, ']' => 333, '^' => 570, '_' => 500, '`' => 333, 'a' => 500, 'b' => 500, 'c' => 444, 'd' => 500, 'e' => 444, 'f' => 333, 'g' => 500, 'h' => 556, 'i' => 278, 'j' => 278, 'k' => 500, 'l' => 278, 'm' => 778, + 'n' => 556, 'o' => 500, 'p' => 500, 'q' => 500, 'r' => 389, 's' => 389, 't' => 278, 'u' => 556, 'v' => 444, 'w' => 667, 'x' => 500, 'y' => 444, 'z' => 389, '{' => 348, '|' => 220, '}' => 348, '~' => 570, chr(127) => 350, chr(128) => 500, chr(129) => 350, chr(130) => 333, chr(131) => 500, + chr(132) => 500, chr(133) => 1000, chr(134) => 500, chr(135) => 500, chr(136) => 333, chr(137) => 1000, chr(138) => 556, chr(139) => 333, chr(140) => 944, chr(141) => 350, chr(142) => 611, chr(143) => 350, chr(144) => 350, chr(145) => 333, chr(146) => 333, chr(147) => 500, chr(148) => 500, chr(149) => 350, chr(150) => 500, chr(151) => 1000, chr(152) => 333, chr(153) => 1000, + chr(154) => 389, chr(155) => 333, chr(156) => 722, chr(157) => 350, chr(158) => 389, chr(159) => 611, chr(160) => 250, chr(161) => 389, chr(162) => 500, chr(163) => 500, chr(164) => 500, chr(165) => 500, chr(166) => 220, chr(167) => 500, chr(168) => 333, chr(169) => 747, chr(170) => 266, chr(171) => 500, chr(172) => 606, chr(173) => 333, chr(174) => 747, chr(175) => 333, + chr(176) => 400, chr(177) => 570, chr(178) => 300, chr(179) => 300, chr(180) => 333, chr(181) => 576, chr(182) => 500, chr(183) => 250, chr(184) => 333, chr(185) => 300, chr(186) => 300, chr(187) => 500, chr(188) => 750, chr(189) => 750, chr(190) => 750, chr(191) => 500, chr(192) => 667, chr(193) => 667, chr(194) => 667, chr(195) => 667, chr(196) => 667, chr(197) => 667, + chr(198) => 944, chr(199) => 667, chr(200) => 667, chr(201) => 667, chr(202) => 667, chr(203) => 667, chr(204) => 389, chr(205) => 389, chr(206) => 389, chr(207) => 389, chr(208) => 722, chr(209) => 722, chr(210) => 722, chr(211) => 722, chr(212) => 722, chr(213) => 722, chr(214) => 722, chr(215) => 570, chr(216) => 722, chr(217) => 722, chr(218) => 722, chr(219) => 722, + chr(220) => 722, chr(221) => 611, chr(222) => 611, chr(223) => 500, chr(224) => 500, chr(225) => 500, chr(226) => 500, chr(227) => 500, chr(228) => 500, chr(229) => 500, chr(230) => 722, chr(231) => 444, chr(232) => 444, chr(233) => 444, chr(234) => 444, chr(235) => 444, chr(236) => 278, chr(237) => 278, chr(238) => 278, chr(239) => 278, chr(240) => 500, chr(241) => 556, + chr(242) => 500, chr(243) => 500, chr(244) => 500, chr(245) => 500, chr(246) => 500, chr(247) => 570, chr(248) => 500, chr(249) => 556, chr(250) => 556, chr(251) => 556, chr(252) => 556, chr(253) => 444, chr(254) => 500, chr(255) => 444]; $enc = 'cp1252'; -$uv = array(0=>array(0,128),128=>8364,130=>8218,131=>402,132=>8222,133=>8230,134=>array(8224,2),136=>710,137=>8240,138=>352,139=>8249,140=>338,142=>381,145=>array(8216,2),147=>array(8220,2),149=>8226,150=>array(8211,2),152=>732,153=>8482,154=>353,155=>8250,156=>339,158=>382,159=>376,160=>array(160,96)); -?> +$uv = [0 => [0, 128], 128 => 8364, 130 => 8218, 131 => 402, 132 => 8222, 133 => 8230, 134 => [8224, 2], 136 => 710, 137 => 8240, 138 => 352, 139 => 8249, 140 => 338, 142 => 381, 145 => [8216, 2], 147 => [8220, 2], 149 => 8226, 150 => [8211, 2], 152 => 732, 153 => 8482, 154 => 353, 155 => 8250, 156 => 339, 158 => 382, 159 => 376, 160 => [160, 96]]; diff --git a/app/ThirdParty/fpdf/font/timesi.php b/app/ThirdParty/fpdf/font/timesi.php index f958b5b6..e26de456 100644 --- a/app/ThirdParty/fpdf/font/timesi.php +++ b/app/ThirdParty/fpdf/font/timesi.php @@ -1,21 +1,21 @@ 250,chr(1)=>250,chr(2)=>250,chr(3)=>250,chr(4)=>250,chr(5)=>250,chr(6)=>250,chr(7)=>250,chr(8)=>250,chr(9)=>250,chr(10)=>250,chr(11)=>250,chr(12)=>250,chr(13)=>250,chr(14)=>250,chr(15)=>250,chr(16)=>250,chr(17)=>250,chr(18)=>250,chr(19)=>250,chr(20)=>250,chr(21)=>250, - chr(22)=>250,chr(23)=>250,chr(24)=>250,chr(25)=>250,chr(26)=>250,chr(27)=>250,chr(28)=>250,chr(29)=>250,chr(30)=>250,chr(31)=>250,' '=>250,'!'=>333,'"'=>420,'#'=>500,'$'=>500,'%'=>833,'&'=>778,'\''=>214,'('=>333,')'=>333,'*'=>500,'+'=>675, - ','=>250,'-'=>333,'.'=>250,'/'=>278,'0'=>500,'1'=>500,'2'=>500,'3'=>500,'4'=>500,'5'=>500,'6'=>500,'7'=>500,'8'=>500,'9'=>500,':'=>333,';'=>333,'<'=>675,'='=>675,'>'=>675,'?'=>500,'@'=>920,'A'=>611, - 'B'=>611,'C'=>667,'D'=>722,'E'=>611,'F'=>611,'G'=>722,'H'=>722,'I'=>333,'J'=>444,'K'=>667,'L'=>556,'M'=>833,'N'=>667,'O'=>722,'P'=>611,'Q'=>722,'R'=>611,'S'=>500,'T'=>556,'U'=>722,'V'=>611,'W'=>833, - 'X'=>611,'Y'=>556,'Z'=>556,'['=>389,'\\'=>278,']'=>389,'^'=>422,'_'=>500,'`'=>333,'a'=>500,'b'=>500,'c'=>444,'d'=>500,'e'=>444,'f'=>278,'g'=>500,'h'=>500,'i'=>278,'j'=>278,'k'=>444,'l'=>278,'m'=>722, - 'n'=>500,'o'=>500,'p'=>500,'q'=>500,'r'=>389,'s'=>389,'t'=>278,'u'=>500,'v'=>444,'w'=>667,'x'=>444,'y'=>444,'z'=>389,'{'=>400,'|'=>275,'}'=>400,'~'=>541,chr(127)=>350,chr(128)=>500,chr(129)=>350,chr(130)=>333,chr(131)=>500, - chr(132)=>556,chr(133)=>889,chr(134)=>500,chr(135)=>500,chr(136)=>333,chr(137)=>1000,chr(138)=>500,chr(139)=>333,chr(140)=>944,chr(141)=>350,chr(142)=>556,chr(143)=>350,chr(144)=>350,chr(145)=>333,chr(146)=>333,chr(147)=>556,chr(148)=>556,chr(149)=>350,chr(150)=>500,chr(151)=>889,chr(152)=>333,chr(153)=>980, - chr(154)=>389,chr(155)=>333,chr(156)=>667,chr(157)=>350,chr(158)=>389,chr(159)=>556,chr(160)=>250,chr(161)=>389,chr(162)=>500,chr(163)=>500,chr(164)=>500,chr(165)=>500,chr(166)=>275,chr(167)=>500,chr(168)=>333,chr(169)=>760,chr(170)=>276,chr(171)=>500,chr(172)=>675,chr(173)=>333,chr(174)=>760,chr(175)=>333, - chr(176)=>400,chr(177)=>675,chr(178)=>300,chr(179)=>300,chr(180)=>333,chr(181)=>500,chr(182)=>523,chr(183)=>250,chr(184)=>333,chr(185)=>300,chr(186)=>310,chr(187)=>500,chr(188)=>750,chr(189)=>750,chr(190)=>750,chr(191)=>500,chr(192)=>611,chr(193)=>611,chr(194)=>611,chr(195)=>611,chr(196)=>611,chr(197)=>611, - chr(198)=>889,chr(199)=>667,chr(200)=>611,chr(201)=>611,chr(202)=>611,chr(203)=>611,chr(204)=>333,chr(205)=>333,chr(206)=>333,chr(207)=>333,chr(208)=>722,chr(209)=>667,chr(210)=>722,chr(211)=>722,chr(212)=>722,chr(213)=>722,chr(214)=>722,chr(215)=>675,chr(216)=>722,chr(217)=>722,chr(218)=>722,chr(219)=>722, - chr(220)=>722,chr(221)=>556,chr(222)=>611,chr(223)=>500,chr(224)=>500,chr(225)=>500,chr(226)=>500,chr(227)=>500,chr(228)=>500,chr(229)=>500,chr(230)=>667,chr(231)=>444,chr(232)=>444,chr(233)=>444,chr(234)=>444,chr(235)=>444,chr(236)=>278,chr(237)=>278,chr(238)=>278,chr(239)=>278,chr(240)=>500,chr(241)=>500, - chr(242)=>500,chr(243)=>500,chr(244)=>500,chr(245)=>500,chr(246)=>500,chr(247)=>675,chr(248)=>500,chr(249)=>500,chr(250)=>500,chr(251)=>500,chr(252)=>500,chr(253)=>444,chr(254)=>500,chr(255)=>444); +$cw = [ + chr(0) => 250, chr(1) => 250, chr(2) => 250, chr(3) => 250, chr(4) => 250, chr(5) => 250, chr(6) => 250, chr(7) => 250, chr(8) => 250, chr(9) => 250, chr(10) => 250, chr(11) => 250, chr(12) => 250, chr(13) => 250, chr(14) => 250, chr(15) => 250, chr(16) => 250, chr(17) => 250, chr(18) => 250, chr(19) => 250, chr(20) => 250, chr(21) => 250, + chr(22) => 250, chr(23) => 250, chr(24) => 250, chr(25) => 250, chr(26) => 250, chr(27) => 250, chr(28) => 250, chr(29) => 250, chr(30) => 250, chr(31) => 250, ' ' => 250, '!' => 333, '"' => 420, '#' => 500, '$' => 500, '%' => 833, '&' => 778, '\'' => 214, '(' => 333, ')' => 333, '*' => 500, '+' => 675, + ',' => 250, '-' => 333, '.' => 250, '/' => 278, '0' => 500, '1' => 500, '2' => 500, '3' => 500, '4' => 500, '5' => 500, '6' => 500, '7' => 500, '8' => 500, '9' => 500, ':' => 333, ';' => 333, '<' => 675, '=' => 675, '>' => 675, '?' => 500, '@' => 920, 'A' => 611, + 'B' => 611, 'C' => 667, 'D' => 722, 'E' => 611, 'F' => 611, 'G' => 722, 'H' => 722, 'I' => 333, 'J' => 444, 'K' => 667, 'L' => 556, 'M' => 833, 'N' => 667, 'O' => 722, 'P' => 611, 'Q' => 722, 'R' => 611, 'S' => 500, 'T' => 556, 'U' => 722, 'V' => 611, 'W' => 833, + 'X' => 611, 'Y' => 556, 'Z' => 556, '[' => 389, '\\' => 278, ']' => 389, '^' => 422, '_' => 500, '`' => 333, 'a' => 500, 'b' => 500, 'c' => 444, 'd' => 500, 'e' => 444, 'f' => 278, 'g' => 500, 'h' => 500, 'i' => 278, 'j' => 278, 'k' => 444, 'l' => 278, 'm' => 722, + 'n' => 500, 'o' => 500, 'p' => 500, 'q' => 500, 'r' => 389, 's' => 389, 't' => 278, 'u' => 500, 'v' => 444, 'w' => 667, 'x' => 444, 'y' => 444, 'z' => 389, '{' => 400, '|' => 275, '}' => 400, '~' => 541, chr(127) => 350, chr(128) => 500, chr(129) => 350, chr(130) => 333, chr(131) => 500, + chr(132) => 556, chr(133) => 889, chr(134) => 500, chr(135) => 500, chr(136) => 333, chr(137) => 1000, chr(138) => 500, chr(139) => 333, chr(140) => 944, chr(141) => 350, chr(142) => 556, chr(143) => 350, chr(144) => 350, chr(145) => 333, chr(146) => 333, chr(147) => 556, chr(148) => 556, chr(149) => 350, chr(150) => 500, chr(151) => 889, chr(152) => 333, chr(153) => 980, + chr(154) => 389, chr(155) => 333, chr(156) => 667, chr(157) => 350, chr(158) => 389, chr(159) => 556, chr(160) => 250, chr(161) => 389, chr(162) => 500, chr(163) => 500, chr(164) => 500, chr(165) => 500, chr(166) => 275, chr(167) => 500, chr(168) => 333, chr(169) => 760, chr(170) => 276, chr(171) => 500, chr(172) => 675, chr(173) => 333, chr(174) => 760, chr(175) => 333, + chr(176) => 400, chr(177) => 675, chr(178) => 300, chr(179) => 300, chr(180) => 333, chr(181) => 500, chr(182) => 523, chr(183) => 250, chr(184) => 333, chr(185) => 300, chr(186) => 310, chr(187) => 500, chr(188) => 750, chr(189) => 750, chr(190) => 750, chr(191) => 500, chr(192) => 611, chr(193) => 611, chr(194) => 611, chr(195) => 611, chr(196) => 611, chr(197) => 611, + chr(198) => 889, chr(199) => 667, chr(200) => 611, chr(201) => 611, chr(202) => 611, chr(203) => 611, chr(204) => 333, chr(205) => 333, chr(206) => 333, chr(207) => 333, chr(208) => 722, chr(209) => 667, chr(210) => 722, chr(211) => 722, chr(212) => 722, chr(213) => 722, chr(214) => 722, chr(215) => 675, chr(216) => 722, chr(217) => 722, chr(218) => 722, chr(219) => 722, + chr(220) => 722, chr(221) => 556, chr(222) => 611, chr(223) => 500, chr(224) => 500, chr(225) => 500, chr(226) => 500, chr(227) => 500, chr(228) => 500, chr(229) => 500, chr(230) => 667, chr(231) => 444, chr(232) => 444, chr(233) => 444, chr(234) => 444, chr(235) => 444, chr(236) => 278, chr(237) => 278, chr(238) => 278, chr(239) => 278, chr(240) => 500, chr(241) => 500, + chr(242) => 500, chr(243) => 500, chr(244) => 500, chr(245) => 500, chr(246) => 500, chr(247) => 675, chr(248) => 500, chr(249) => 500, chr(250) => 500, chr(251) => 500, chr(252) => 500, chr(253) => 444, chr(254) => 500, chr(255) => 444]; $enc = 'cp1252'; -$uv = array(0=>array(0,128),128=>8364,130=>8218,131=>402,132=>8222,133=>8230,134=>array(8224,2),136=>710,137=>8240,138=>352,139=>8249,140=>338,142=>381,145=>array(8216,2),147=>array(8220,2),149=>8226,150=>array(8211,2),152=>732,153=>8482,154=>353,155=>8250,156=>339,158=>382,159=>376,160=>array(160,96)); -?> +$uv = [0 => [0, 128], 128 => 8364, 130 => 8218, 131 => 402, 132 => 8222, 133 => 8230, 134 => [8224, 2], 136 => 710, 137 => 8240, 138 => 352, 139 => 8249, 140 => 338, 142 => 381, 145 => [8216, 2], 147 => [8220, 2], 149 => 8226, 150 => [8211, 2], 152 => 732, 153 => 8482, 154 => 353, 155 => 8250, 156 => 339, 158 => 382, 159 => 376, 160 => [160, 96]]; diff --git a/app/ThirdParty/fpdf/font/zapfdingbats.php b/app/ThirdParty/fpdf/font/zapfdingbats.php index 7c2cb5e4..d236518f 100644 --- a/app/ThirdParty/fpdf/font/zapfdingbats.php +++ b/app/ThirdParty/fpdf/font/zapfdingbats.php @@ -1,20 +1,20 @@ 0,chr(1)=>0,chr(2)=>0,chr(3)=>0,chr(4)=>0,chr(5)=>0,chr(6)=>0,chr(7)=>0,chr(8)=>0,chr(9)=>0,chr(10)=>0,chr(11)=>0,chr(12)=>0,chr(13)=>0,chr(14)=>0,chr(15)=>0,chr(16)=>0,chr(17)=>0,chr(18)=>0,chr(19)=>0,chr(20)=>0,chr(21)=>0, - chr(22)=>0,chr(23)=>0,chr(24)=>0,chr(25)=>0,chr(26)=>0,chr(27)=>0,chr(28)=>0,chr(29)=>0,chr(30)=>0,chr(31)=>0,' '=>278,'!'=>974,'"'=>961,'#'=>974,'$'=>980,'%'=>719,'&'=>789,'\''=>790,'('=>791,')'=>690,'*'=>960,'+'=>939, - ','=>549,'-'=>855,'.'=>911,'/'=>933,'0'=>911,'1'=>945,'2'=>974,'3'=>755,'4'=>846,'5'=>762,'6'=>761,'7'=>571,'8'=>677,'9'=>763,':'=>760,';'=>759,'<'=>754,'='=>494,'>'=>552,'?'=>537,'@'=>577,'A'=>692, - 'B'=>786,'C'=>788,'D'=>788,'E'=>790,'F'=>793,'G'=>794,'H'=>816,'I'=>823,'J'=>789,'K'=>841,'L'=>823,'M'=>833,'N'=>816,'O'=>831,'P'=>923,'Q'=>744,'R'=>723,'S'=>749,'T'=>790,'U'=>792,'V'=>695,'W'=>776, - 'X'=>768,'Y'=>792,'Z'=>759,'['=>707,'\\'=>708,']'=>682,'^'=>701,'_'=>826,'`'=>815,'a'=>789,'b'=>789,'c'=>707,'d'=>687,'e'=>696,'f'=>689,'g'=>786,'h'=>787,'i'=>713,'j'=>791,'k'=>785,'l'=>791,'m'=>873, - 'n'=>761,'o'=>762,'p'=>762,'q'=>759,'r'=>759,'s'=>892,'t'=>892,'u'=>788,'v'=>784,'w'=>438,'x'=>138,'y'=>277,'z'=>415,'{'=>392,'|'=>392,'}'=>668,'~'=>668,chr(127)=>0,chr(128)=>390,chr(129)=>390,chr(130)=>317,chr(131)=>317, - chr(132)=>276,chr(133)=>276,chr(134)=>509,chr(135)=>509,chr(136)=>410,chr(137)=>410,chr(138)=>234,chr(139)=>234,chr(140)=>334,chr(141)=>334,chr(142)=>0,chr(143)=>0,chr(144)=>0,chr(145)=>0,chr(146)=>0,chr(147)=>0,chr(148)=>0,chr(149)=>0,chr(150)=>0,chr(151)=>0,chr(152)=>0,chr(153)=>0, - chr(154)=>0,chr(155)=>0,chr(156)=>0,chr(157)=>0,chr(158)=>0,chr(159)=>0,chr(160)=>0,chr(161)=>732,chr(162)=>544,chr(163)=>544,chr(164)=>910,chr(165)=>667,chr(166)=>760,chr(167)=>760,chr(168)=>776,chr(169)=>595,chr(170)=>694,chr(171)=>626,chr(172)=>788,chr(173)=>788,chr(174)=>788,chr(175)=>788, - chr(176)=>788,chr(177)=>788,chr(178)=>788,chr(179)=>788,chr(180)=>788,chr(181)=>788,chr(182)=>788,chr(183)=>788,chr(184)=>788,chr(185)=>788,chr(186)=>788,chr(187)=>788,chr(188)=>788,chr(189)=>788,chr(190)=>788,chr(191)=>788,chr(192)=>788,chr(193)=>788,chr(194)=>788,chr(195)=>788,chr(196)=>788,chr(197)=>788, - chr(198)=>788,chr(199)=>788,chr(200)=>788,chr(201)=>788,chr(202)=>788,chr(203)=>788,chr(204)=>788,chr(205)=>788,chr(206)=>788,chr(207)=>788,chr(208)=>788,chr(209)=>788,chr(210)=>788,chr(211)=>788,chr(212)=>894,chr(213)=>838,chr(214)=>1016,chr(215)=>458,chr(216)=>748,chr(217)=>924,chr(218)=>748,chr(219)=>918, - chr(220)=>927,chr(221)=>928,chr(222)=>928,chr(223)=>834,chr(224)=>873,chr(225)=>828,chr(226)=>924,chr(227)=>924,chr(228)=>917,chr(229)=>930,chr(230)=>931,chr(231)=>463,chr(232)=>883,chr(233)=>836,chr(234)=>836,chr(235)=>867,chr(236)=>867,chr(237)=>696,chr(238)=>696,chr(239)=>874,chr(240)=>0,chr(241)=>874, - chr(242)=>760,chr(243)=>946,chr(244)=>771,chr(245)=>865,chr(246)=>771,chr(247)=>888,chr(248)=>967,chr(249)=>888,chr(250)=>831,chr(251)=>873,chr(252)=>927,chr(253)=>970,chr(254)=>918,chr(255)=>0); -$uv = array(32=>32,33=>array(9985,4),37=>9742,38=>array(9990,4),42=>9755,43=>9758,44=>array(9996,28),72=>9733,73=>array(10025,35),108=>9679,109=>10061,110=>9632,111=>array(10063,4),115=>9650,116=>9660,117=>9670,118=>10070,119=>9687,120=>array(10072,7),128=>array(10088,14),161=>array(10081,7),168=>9827,169=>9830,170=>9829,171=>9824,172=>array(9312,10),182=>array(10102,31),213=>8594,214=>array(8596,2),216=>array(10136,24),241=>array(10161,14)); -?> +$cw = [ + chr(0) => 0, chr(1) => 0, chr(2) => 0, chr(3) => 0, chr(4) => 0, chr(5) => 0, chr(6) => 0, chr(7) => 0, chr(8) => 0, chr(9) => 0, chr(10) => 0, chr(11) => 0, chr(12) => 0, chr(13) => 0, chr(14) => 0, chr(15) => 0, chr(16) => 0, chr(17) => 0, chr(18) => 0, chr(19) => 0, chr(20) => 0, chr(21) => 0, + chr(22) => 0, chr(23) => 0, chr(24) => 0, chr(25) => 0, chr(26) => 0, chr(27) => 0, chr(28) => 0, chr(29) => 0, chr(30) => 0, chr(31) => 0, ' ' => 278, '!' => 974, '"' => 961, '#' => 974, '$' => 980, '%' => 719, '&' => 789, '\'' => 790, '(' => 791, ')' => 690, '*' => 960, '+' => 939, + ',' => 549, '-' => 855, '.' => 911, '/' => 933, '0' => 911, '1' => 945, '2' => 974, '3' => 755, '4' => 846, '5' => 762, '6' => 761, '7' => 571, '8' => 677, '9' => 763, ':' => 760, ';' => 759, '<' => 754, '=' => 494, '>' => 552, '?' => 537, '@' => 577, 'A' => 692, + 'B' => 786, 'C' => 788, 'D' => 788, 'E' => 790, 'F' => 793, 'G' => 794, 'H' => 816, 'I' => 823, 'J' => 789, 'K' => 841, 'L' => 823, 'M' => 833, 'N' => 816, 'O' => 831, 'P' => 923, 'Q' => 744, 'R' => 723, 'S' => 749, 'T' => 790, 'U' => 792, 'V' => 695, 'W' => 776, + 'X' => 768, 'Y' => 792, 'Z' => 759, '[' => 707, '\\' => 708, ']' => 682, '^' => 701, '_' => 826, '`' => 815, 'a' => 789, 'b' => 789, 'c' => 707, 'd' => 687, 'e' => 696, 'f' => 689, 'g' => 786, 'h' => 787, 'i' => 713, 'j' => 791, 'k' => 785, 'l' => 791, 'm' => 873, + 'n' => 761, 'o' => 762, 'p' => 762, 'q' => 759, 'r' => 759, 's' => 892, 't' => 892, 'u' => 788, 'v' => 784, 'w' => 438, 'x' => 138, 'y' => 277, 'z' => 415, '{' => 392, '|' => 392, '}' => 668, '~' => 668, chr(127) => 0, chr(128) => 390, chr(129) => 390, chr(130) => 317, chr(131) => 317, + chr(132) => 276, chr(133) => 276, chr(134) => 509, chr(135) => 509, chr(136) => 410, chr(137) => 410, chr(138) => 234, chr(139) => 234, chr(140) => 334, chr(141) => 334, chr(142) => 0, chr(143) => 0, chr(144) => 0, chr(145) => 0, chr(146) => 0, chr(147) => 0, chr(148) => 0, chr(149) => 0, chr(150) => 0, chr(151) => 0, chr(152) => 0, chr(153) => 0, + chr(154) => 0, chr(155) => 0, chr(156) => 0, chr(157) => 0, chr(158) => 0, chr(159) => 0, chr(160) => 0, chr(161) => 732, chr(162) => 544, chr(163) => 544, chr(164) => 910, chr(165) => 667, chr(166) => 760, chr(167) => 760, chr(168) => 776, chr(169) => 595, chr(170) => 694, chr(171) => 626, chr(172) => 788, chr(173) => 788, chr(174) => 788, chr(175) => 788, + chr(176) => 788, chr(177) => 788, chr(178) => 788, chr(179) => 788, chr(180) => 788, chr(181) => 788, chr(182) => 788, chr(183) => 788, chr(184) => 788, chr(185) => 788, chr(186) => 788, chr(187) => 788, chr(188) => 788, chr(189) => 788, chr(190) => 788, chr(191) => 788, chr(192) => 788, chr(193) => 788, chr(194) => 788, chr(195) => 788, chr(196) => 788, chr(197) => 788, + chr(198) => 788, chr(199) => 788, chr(200) => 788, chr(201) => 788, chr(202) => 788, chr(203) => 788, chr(204) => 788, chr(205) => 788, chr(206) => 788, chr(207) => 788, chr(208) => 788, chr(209) => 788, chr(210) => 788, chr(211) => 788, chr(212) => 894, chr(213) => 838, chr(214) => 1016, chr(215) => 458, chr(216) => 748, chr(217) => 924, chr(218) => 748, chr(219) => 918, + chr(220) => 927, chr(221) => 928, chr(222) => 928, chr(223) => 834, chr(224) => 873, chr(225) => 828, chr(226) => 924, chr(227) => 924, chr(228) => 917, chr(229) => 930, chr(230) => 931, chr(231) => 463, chr(232) => 883, chr(233) => 836, chr(234) => 836, chr(235) => 867, chr(236) => 867, chr(237) => 696, chr(238) => 696, chr(239) => 874, chr(240) => 0, chr(241) => 874, + chr(242) => 760, chr(243) => 946, chr(244) => 771, chr(245) => 865, chr(246) => 771, chr(247) => 888, chr(248) => 967, chr(249) => 888, chr(250) => 831, chr(251) => 873, chr(252) => 927, chr(253) => 970, chr(254) => 918, chr(255) => 0]; +$uv = [32 => 32, 33 => [9985, 4], 37 => 9742, 38 => [9990, 4], 42 => 9755, 43 => 9758, 44 => [9996, 28], 72 => 9733, 73 => [10025, 35], 108 => 9679, 109 => 10061, 110 => 9632, 111 => [10063, 4], 115 => 9650, 116 => 9660, 117 => 9670, 118 => 10070, 119 => 9687, 120 => [10072, 7], 128 => [10088, 14], 161 => [10081, 7], 168 => 9827, 169 => 9830, 170 => 9829, 171 => 9824, 172 => [9312, 10], 182 => [10102, 31], 213 => 8594, 214 => [8596, 2], 216 => [10136, 24], 241 => [10161, 14]]; diff --git a/app/ThirdParty/fpdf/fpdf.php b/app/ThirdParty/fpdf/fpdf.php index 3c76bd5c..cefd575c 100644 --- a/app/ThirdParty/fpdf/fpdf.php +++ b/app/ThirdParty/fpdf/fpdf.php @@ -10,1810 +10,2065 @@ class FPDF { - const VERSION = '1.86'; - protected $page; // current page number - protected $n; // current object number - protected $offsets; // array of object offsets - protected $buffer; // buffer holding in-memory PDF - protected $pages; // array containing pages - protected $state; // current document state - protected $compress; // compression flag - protected $iconv; // whether iconv is available - protected $k; // scale factor (number of points in user unit) - protected $DefOrientation; // default orientation - protected $CurOrientation; // current orientation - protected $StdPageSizes; // standard page sizes - protected $DefPageSize; // default page size - protected $CurPageSize; // current page size - protected $CurRotation; // current page rotation - protected $PageInfo; // page-related data - protected $wPt, $hPt; // dimensions of current page in points - protected $w, $h; // dimensions of current page in user unit - protected $lMargin; // left margin - protected $tMargin; // top margin - protected $rMargin; // right margin - protected $bMargin; // page break margin - protected $cMargin; // cell margin - protected $x, $y; // current position in user unit - protected $lasth; // height of last printed cell - protected $LineWidth; // line width in user unit - protected $fontpath; // directory containing fonts - protected $CoreFonts; // array of core font names - protected $fonts; // array of used fonts - protected $FontFiles; // array of font files - protected $encodings; // array of encodings - protected $cmaps; // array of ToUnicode CMaps - protected $FontFamily; // current font family - protected $FontStyle; // current font style - protected $underline; // underlining flag - protected $CurrentFont; // current font info - protected $FontSizePt; // current font size in points - protected $FontSize; // current font size in user unit - protected $DrawColor; // commands for drawing color - protected $FillColor; // commands for filling color - protected $TextColor; // commands for text color - protected $ColorFlag; // indicates whether fill and text colors are different - protected $WithAlpha; // indicates whether alpha channel is used - protected $ws; // word spacing - protected $images; // array of used images - protected $PageLinks; // array of links in pages - protected $links; // array of internal links - protected $AutoPageBreak; // automatic page breaking - protected $PageBreakTrigger; // threshold used to trigger page breaks - protected $InHeader; // flag set when processing header - protected $InFooter; // flag set when processing footer - protected $AliasNbPages; // alias for total number of pages - protected $ZoomMode; // zoom display mode - protected $LayoutMode; // layout display mode - protected $metadata; // document properties - protected $CreationDate; // document creation date - protected $PDFVersion; // PDF version number - - /******************************************************************************* - * Public methods * - *******************************************************************************/ - - function __construct($orientation = 'P', $unit = 'mm', $size = 'A4') - { - // FIXED: Set font path directly - $this->fontpath = __DIR__ . '/font/'; - - // Initialization of properties - $this->state = 0; - $this->page = 0; - $this->n = 2; - $this->buffer = ''; - $this->pages = array(); - $this->PageInfo = array(); - $this->fonts = array(); - $this->FontFiles = array(); - $this->encodings = array(); - $this->cmaps = array(); - $this->images = array(); - $this->links = array(); - $this->InHeader = false; - $this->InFooter = false; - $this->lasth = 0; - $this->FontFamily = ''; - $this->FontStyle = ''; - $this->FontSizePt = 12; - $this->underline = false; - $this->DrawColor = '0 G'; - $this->FillColor = '0 g'; - $this->TextColor = '0 g'; - $this->ColorFlag = false; - $this->WithAlpha = false; - $this->ws = 0; - $this->iconv = function_exists('iconv'); - - // Core fonts - $this->CoreFonts = array('courier', 'helvetica', 'times', 'symbol', 'zapfdingbats'); - - // Scale factor - if ($unit == 'pt') - $this->k = 1; - elseif ($unit == 'mm') - $this->k = 72 / 25.4; - elseif ($unit == 'cm') - $this->k = 72 / 2.54; - elseif ($unit == 'in') - $this->k = 72; - else - $this->Error('Incorrect unit: ' . $unit); - - // Page sizes - $this->StdPageSizes = array( - 'a3' => array(841.89, 1190.55), - 'a4' => array(595.28, 841.89), - 'a5' => array(420.94, 595.28), - 'letter' => array(612, 792), - 'legal' => array(612, 1008) - ); - $size = $this->_getpagesize($size); - $this->DefPageSize = $size; - $this->CurPageSize = $size; - - // Page orientation - $orientation = strtolower($orientation); - if ($orientation == 'p' || $orientation == 'portrait') { - $this->DefOrientation = 'P'; - $this->w = $size[0]; - $this->h = $size[1]; - } elseif ($orientation == 'l' || $orientation == 'landscape') { - $this->DefOrientation = 'L'; - $this->w = $size[1]; - $this->h = $size[0]; - } else { - $this->Error('Incorrect orientation: ' . $orientation); - } - $this->CurOrientation = $this->DefOrientation; - $this->wPt = $this->w * $this->k; - $this->hPt = $this->h * $this->k; - - // Page rotation - $this->CurRotation = 0; - - // Margins - $margin = 28.35 / $this->k; - $this->SetMargins($margin, $margin); - $this->cMargin = $margin / 10; - $this->LineWidth = .567 / $this->k; - - // Page break - $this->SetAutoPageBreak(true, 2 * $margin); - - // Display and compression - $this->SetDisplayMode('default'); - $this->SetCompression(true); - - // Metadata - $this->metadata = array('Producer' => 'FPDF ' . self::VERSION); - $this->PDFVersion = '1.3'; - } - - - function SetMargins($left, $top, $right = null) - { - // Set left, top and right margins - $this->lMargin = $left; - $this->tMargin = $top; - if ($right === null) - $right = $left; - $this->rMargin = $right; - } - - function SetLeftMargin($margin) - { - // Set left margin - $this->lMargin = $margin; - if ($this->page > 0 && $this->x < $margin) - $this->x = $margin; - } - - function SetTopMargin($margin) - { - // Set top margin - $this->tMargin = $margin; - } - - function SetRightMargin($margin) - { - // Set right margin - $this->rMargin = $margin; - } - - function SetAutoPageBreak($auto, $margin = 0) - { - // Set auto page break mode and triggering margin - $this->AutoPageBreak = $auto; - $this->bMargin = $margin; - $this->PageBreakTrigger = $this->h - $margin; - } - - function SetDisplayMode($zoom, $layout = 'default') - { - // Set display mode in viewer - if ($zoom == 'fullpage' || $zoom == 'fullwidth' || $zoom == 'real' || $zoom == 'default' || !is_string($zoom)) - $this->ZoomMode = $zoom; - else - $this->Error('Incorrect zoom display mode: ' . $zoom); - if ($layout == 'single' || $layout == 'continuous' || $layout == 'two' || $layout == 'default') - $this->LayoutMode = $layout; - else - $this->Error('Incorrect layout display mode: ' . $layout); - } - - function SetCompression($compress) - { - // Set page compression - if (function_exists('gzcompress')) - $this->compress = $compress; - else - $this->compress = false; - } - - function SetTitle($title, $isUTF8 = false) - { - // Title of document - $this->metadata['Title'] = $isUTF8 ? $title : $this->_UTF8encode($title); - } - - function SetAuthor($author, $isUTF8 = false) - { - // Author of document - $this->metadata['Author'] = $isUTF8 ? $author : $this->_UTF8encode($author); - } - - function SetSubject($subject, $isUTF8 = false) - { - // Subject of document - $this->metadata['Subject'] = $isUTF8 ? $subject : $this->_UTF8encode($subject); - } - - function SetKeywords($keywords, $isUTF8 = false) - { - // Keywords of document - $this->metadata['Keywords'] = $isUTF8 ? $keywords : $this->_UTF8encode($keywords); - } - - function SetCreator($creator, $isUTF8 = false) - { - // Creator of document - $this->metadata['Creator'] = $isUTF8 ? $creator : $this->_UTF8encode($creator); - } - - function AliasNbPages($alias = '{nb}') - { - // Define an alias for total number of pages - $this->AliasNbPages = $alias; - } - - function Error($msg) - { - // Fatal error - throw new Exception('FPDF error: ' . $msg); - } - - function Close() - { - // Terminate document - if ($this->state == 3) - return; - if ($this->page == 0) - $this->AddPage(); - // Page footer - $this->InFooter = true; - $this->Footer(); - $this->InFooter = false; - // Close page - $this->_endpage(); - // Close document - $this->_enddoc(); - } - - function AddPage($orientation = '', $size = '', $rotation = 0) - { - // Start a new page - if ($this->state == 3) - $this->Error('The document is closed'); - $family = $this->FontFamily; - $style = $this->FontStyle . ($this->underline ? 'U' : ''); - $fontsize = $this->FontSizePt; - $lw = $this->LineWidth; - $dc = $this->DrawColor; - $fc = $this->FillColor; - $tc = $this->TextColor; - $cf = $this->ColorFlag; - if ($this->page > 0) { - // Page footer - $this->InFooter = true; - $this->Footer(); - $this->InFooter = false; - // Close page - $this->_endpage(); - } - // Start new page - $this->_beginpage($orientation, $size, $rotation); - // Set line cap style to square - $this->_out('2 J'); - // Set line width - $this->LineWidth = $lw; - $this->_out(sprintf('%.2F w', $lw * $this->k)); - // Set font - if ($family) - $this->SetFont($family, $style, $fontsize); - // Set colors - $this->DrawColor = $dc; - if ($dc != '0 G') - $this->_out($dc); - $this->FillColor = $fc; - if ($fc != '0 g') - $this->_out($fc); - $this->TextColor = $tc; - $this->ColorFlag = $cf; - // Page header - $this->InHeader = true; - $this->Header(); - $this->InHeader = false; - // Restore line width - if ($this->LineWidth != $lw) { - $this->LineWidth = $lw; - $this->_out(sprintf('%.2F w', $lw * $this->k)); - } - // Restore font - if ($family) - $this->SetFont($family, $style, $fontsize); - // Restore colors - if ($this->DrawColor != $dc) { - $this->DrawColor = $dc; - $this->_out($dc); - } - if ($this->FillColor != $fc) { - $this->FillColor = $fc; - $this->_out($fc); - } - $this->TextColor = $tc; - $this->ColorFlag = $cf; - } - - function Header() - { - // To be implemented in your own inherited class - } - - function Footer() - { - // To be implemented in your own inherited class - } - - function PageNo() - { - // Get current page number - return $this->page; - } - - function SetDrawColor($r, $g = null, $b = null) - { - // Set color for all stroking operations - if (($r == 0 && $g == 0 && $b == 0) || $g === null) - $this->DrawColor = sprintf('%.3F G', $r / 255); - else - $this->DrawColor = sprintf('%.3F %.3F %.3F RG', $r / 255, $g / 255, $b / 255); - if ($this->page > 0) - $this->_out($this->DrawColor); - } - - function SetFillColor($r, $g = null, $b = null) - { - // Set color for all filling operations - if (($r == 0 && $g == 0 && $b == 0) || $g === null) - $this->FillColor = sprintf('%.3F g', $r / 255); - else - $this->FillColor = sprintf('%.3F %.3F %.3F rg', $r / 255, $g / 255, $b / 255); - $this->ColorFlag = ($this->FillColor != $this->TextColor); - if ($this->page > 0) - $this->_out($this->FillColor); - } - - function SetTextColor($r, $g = null, $b = null) - { - // Set color for text - if (($r == 0 && $g == 0 && $b == 0) || $g === null) - $this->TextColor = sprintf('%.3F g', $r / 255); - else - $this->TextColor = sprintf('%.3F %.3F %.3F rg', $r / 255, $g / 255, $b / 255); - $this->ColorFlag = ($this->FillColor != $this->TextColor); - } - - function GetStringWidth($s) - { - // Get width of a string in the current font - $cw = $this->CurrentFont['cw']; - $w = 0; - $s = (string)$s; - $l = strlen($s); - for ($i = 0; $i < $l; $i++) - $w += $cw[$s[$i]]; - return $w * $this->FontSize / 1000; - } - - function SetLineWidth($width) - { - // Set line width - $this->LineWidth = $width; - if ($this->page > 0) - $this->_out(sprintf('%.2F w', $width * $this->k)); - } - - function Line($x1, $y1, $x2, $y2) - { - // Draw a line - $this->_out(sprintf('%.2F %.2F m %.2F %.2F l S', $x1 * $this->k, ($this->h - $y1) * $this->k, $x2 * $this->k, ($this->h - $y2) * $this->k)); - } - - function Rect($x, $y, $w, $h, $style = '') - { - // Draw a rectangle - if ($style == 'F') - $op = 'f'; - elseif ($style == 'FD' || $style == 'DF') - $op = 'B'; - else - $op = 'S'; - $this->_out(sprintf('%.2F %.2F %.2F %.2F re %s', $x * $this->k, ($this->h - $y) * $this->k, $w * $this->k, -$h * $this->k, $op)); - } - - function AddFont($family, $style = '', $file = '', $dir = '') - { - // Add a TrueType, OpenType or Type1 font - $family = strtolower($family); - if ($file == '') - $file = str_replace(' ', '', $family) . strtolower($style) . '.php'; - $style = strtoupper($style); - if ($style == 'IB') - $style = 'BI'; - $fontkey = $family . $style; - if (isset($this->fonts[$fontkey])) - return; - if (strpos($file, '/') !== false || strpos($file, "\\") !== false) - $this->Error('Incorrect font definition file name: ' . $file); - if ($dir == '') - $dir = $this->fontpath; - if (substr($dir, -1) != '/' && substr($dir, -1) != '\\') - $dir .= '/'; - $info = $this->_loadfont($dir . $file); - $info['i'] = count($this->fonts) + 1; - if (!empty($info['file'])) { - // Embedded font - $info['file'] = $dir . $info['file']; - if ($info['type'] == 'TrueType') - $this->FontFiles[$info['file']] = array('length1' => $info['originalsize']); - else - $this->FontFiles[$info['file']] = array('length1' => $info['size1'], 'length2' => $info['size2']); - } - $this->fonts[$fontkey] = $info; - } - - function SetFont($family, $style = '', $size = 0) - { - // Select a font; size given in points - if ($family == '') - $family = $this->FontFamily; - else - $family = strtolower($family); - $style = strtoupper($style); - if (strpos($style, 'U') !== false) { - $this->underline = true; - $style = str_replace('U', '', $style); - } else - $this->underline = false; - if ($style == 'IB') - $style = 'BI'; - if ($size == 0) - $size = $this->FontSizePt; - // Test if font is already selected - if ($this->FontFamily == $family && $this->FontStyle == $style && $this->FontSizePt == $size) - return; - // Test if font is already loaded - $fontkey = $family . $style; - if (!isset($this->fonts[$fontkey])) { - // Test if one of the core fonts - if ($family == 'arial') - $family = 'helvetica'; - if (in_array($family, $this->CoreFonts)) { - if ($family == 'symbol' || $family == 'zapfdingbats') - $style = ''; - $fontkey = $family . $style; - if (!isset($this->fonts[$fontkey])) - $this->AddFont($family, $style); - } else - $this->Error('Undefined font: ' . $family . ' ' . $style); - } - // Select it - $this->FontFamily = $family; - $this->FontStyle = $style; - $this->FontSizePt = $size; - $this->FontSize = $size / $this->k; - $this->CurrentFont = $this->fonts[$fontkey]; - if ($this->page > 0) - $this->_out(sprintf('BT /F%d %.2F Tf ET', $this->CurrentFont['i'], $this->FontSizePt)); - } - - function SetFontSize($size) - { - // Set font size in points - if ($this->FontSizePt == $size) - return; - $this->FontSizePt = $size; - $this->FontSize = $size / $this->k; - if ($this->page > 0 && isset($this->CurrentFont)) - $this->_out(sprintf('BT /F%d %.2F Tf ET', $this->CurrentFont['i'], $this->FontSizePt)); - } - - function AddLink() - { - // Create a new internal link - $n = count($this->links) + 1; - $this->links[$n] = array(0, 0); - return $n; - } - - function SetLink($link, $y = 0, $page = -1) - { - // Set destination of internal link - if ($y == -1) - $y = $this->y; - if ($page == -1) - $page = $this->page; - $this->links[$link] = array($page, $y); - } - - function Link($x, $y, $w, $h, $link) - { - // Put a link on the page - $this->PageLinks[$this->page][] = array($x * $this->k, $this->hPt - $y * $this->k, $w * $this->k, $h * $this->k, $link); - } - - function Text($x, $y, $txt) - { - // Output a string - if (!isset($this->CurrentFont)) - $this->Error('No font has been set'); - $txt = (string)$txt; - $s = sprintf('BT %.2F %.2F Td (%s) Tj ET', $x * $this->k, ($this->h - $y) * $this->k, $this->_escape($txt)); - if ($this->underline && $txt !== '') - $s .= ' ' . $this->_dounderline($x, $y, $txt); - if ($this->ColorFlag) - $s = 'q ' . $this->TextColor . ' ' . $s . ' Q'; - $this->_out($s); - } - - function AcceptPageBreak() - { - // Accept automatic page break or not - return $this->AutoPageBreak; - } - - function Cell($w, $h = 0, $txt = '', $border = 0, $ln = 0, $align = '', $fill = false, $link = '') - { - // Output a cell - $k = $this->k; - if ($this->y + $h > $this->PageBreakTrigger && !$this->InHeader && !$this->InFooter && $this->AcceptPageBreak()) { - // Automatic page break - $x = $this->x; - $ws = $this->ws; - if ($ws > 0) { - $this->ws = 0; - $this->_out('0 Tw'); - } - $this->AddPage($this->CurOrientation, $this->CurPageSize, $this->CurRotation); - $this->x = $x; - if ($ws > 0) { - $this->ws = $ws; - $this->_out(sprintf('%.3F Tw', $ws * $k)); - } - } - if ($w == 0) - $w = $this->w - $this->rMargin - $this->x; - $s = ''; - if ($fill || $border == 1) { - if ($fill) - $op = ($border == 1) ? 'B' : 'f'; - else - $op = 'S'; - $s = sprintf('%.2F %.2F %.2F %.2F re %s ', $this->x * $k, ($this->h - $this->y) * $k, $w * $k, -$h * $k, $op); - } - if (is_string($border)) { - $x = $this->x; - $y = $this->y; - if (strpos($border, 'L') !== false) - $s .= sprintf('%.2F %.2F m %.2F %.2F l S ', $x * $k, ($this->h - $y) * $k, $x * $k, ($this->h - ($y + $h)) * $k); - if (strpos($border, 'T') !== false) - $s .= sprintf('%.2F %.2F m %.2F %.2F l S ', $x * $k, ($this->h - $y) * $k, ($x + $w) * $k, ($this->h - $y) * $k); - if (strpos($border, 'R') !== false) - $s .= sprintf('%.2F %.2F m %.2F %.2F l S ', ($x + $w) * $k, ($this->h - $y) * $k, ($x + $w) * $k, ($this->h - ($y + $h)) * $k); - if (strpos($border, 'B') !== false) - $s .= sprintf('%.2F %.2F m %.2F %.2F l S ', $x * $k, ($this->h - ($y + $h)) * $k, ($x + $w) * $k, ($this->h - ($y + $h)) * $k); - } - $txt = (string)$txt; - if ($txt !== '') { - if (!isset($this->CurrentFont)) - $this->Error('No font has been set'); - if ($align == 'R') - $dx = $w - $this->cMargin - $this->GetStringWidth($txt); - elseif ($align == 'C') - $dx = ($w - $this->GetStringWidth($txt)) / 2; - else - $dx = $this->cMargin; - if ($this->ColorFlag) - $s .= 'q ' . $this->TextColor . ' '; - $s .= sprintf('BT %.2F %.2F Td (%s) Tj ET', ($this->x + $dx) * $k, ($this->h - ($this->y + .5 * $h + .3 * $this->FontSize)) * $k, $this->_escape($txt)); - if ($this->underline) - $s .= ' ' . $this->_dounderline($this->x + $dx, $this->y + .5 * $h + .3 * $this->FontSize, $txt); - if ($this->ColorFlag) - $s .= ' Q'; - if ($link) - $this->Link($this->x + $dx, $this->y + .5 * $h - .5 * $this->FontSize, $this->GetStringWidth($txt), $this->FontSize, $link); - } - if ($s) - $this->_out($s); - $this->lasth = $h; - if ($ln > 0) { - // Go to next line - $this->y += $h; - if ($ln == 1) - $this->x = $this->lMargin; - } else - $this->x += $w; - } - - function MultiCell($w, $h, $txt, $border = 0, $align = 'J', $fill = false) - { - // Output text with automatic or explicit line breaks - if (!isset($this->CurrentFont)) - $this->Error('No font has been set'); - $cw = $this->CurrentFont['cw']; - if ($w == 0) - $w = $this->w - $this->rMargin - $this->x; - $wmax = ($w - 2 * $this->cMargin) * 1000 / $this->FontSize; - $s = str_replace("\r", '', (string)$txt); - $nb = strlen($s); - if ($nb > 0 && $s[$nb - 1] == "\n") - $nb--; - $b = 0; - if ($border) { - if ($border == 1) { - $border = 'LTRB'; - $b = 'LRT'; - $b2 = 'LR'; - } else { - $b2 = ''; - if (strpos($border, 'L') !== false) - $b2 .= 'L'; - if (strpos($border, 'R') !== false) - $b2 .= 'R'; - $b = (strpos($border, 'T') !== false) ? $b2 . 'T' : $b2; - } - } - $sep = -1; - $i = 0; - $j = 0; - $l = 0; - $ns = 0; - $nl = 1; - while ($i < $nb) { - // Get next character - $c = $s[$i]; - if ($c == "\n") { - // Explicit line break - if ($this->ws > 0) { - $this->ws = 0; - $this->_out('0 Tw'); - } - $this->Cell($w, $h, substr($s, $j, $i - $j), $b, 2, $align, $fill); - $i++; - $sep = -1; - $j = $i; - $l = 0; - $ns = 0; - $nl++; - if ($border && $nl == 2) - $b = $b2; - continue; - } - if ($c == ' ') { - $sep = $i; - $ls = $l; - $ns++; - } - $l += $cw[$c]; - if ($l > $wmax) { - // Automatic line break - if ($sep == -1) { - if ($i == $j) - $i++; - if ($this->ws > 0) { - $this->ws = 0; - $this->_out('0 Tw'); - } - $this->Cell($w, $h, substr($s, $j, $i - $j), $b, 2, $align, $fill); - } else { - if ($align == 'J') { - $this->ws = ($ns > 1) ? ($wmax - $ls) / 1000 * $this->FontSize / ($ns - 1) : 0; - $this->_out(sprintf('%.3F Tw', $this->ws * $this->k)); - } - $this->Cell($w, $h, substr($s, $j, $sep - $j), $b, 2, $align, $fill); - $i = $sep + 1; - } - $sep = -1; - $j = $i; - $l = 0; - $ns = 0; - $nl++; - if ($border && $nl == 2) - $b = $b2; - } else - $i++; - } - // Last chunk - if ($this->ws > 0) { - $this->ws = 0; - $this->_out('0 Tw'); - } - if ($border && strpos($border, 'B') !== false) - $b .= 'B'; - $this->Cell($w, $h, substr($s, $j, $i - $j), $b, 2, $align, $fill); - $this->x = $this->lMargin; - } - - function Write($h, $txt, $link = '') - { - // Output text in flowing mode - if (!isset($this->CurrentFont)) - $this->Error('No font has been set'); - $cw = $this->CurrentFont['cw']; - $w = $this->w - $this->rMargin - $this->x; - $wmax = ($w - 2 * $this->cMargin) * 1000 / $this->FontSize; - $s = str_replace("\r", '', (string)$txt); - $nb = strlen($s); - $sep = -1; - $i = 0; - $j = 0; - $l = 0; - $nl = 1; - while ($i < $nb) { - // Get next character - $c = $s[$i]; - if ($c == "\n") { - // Explicit line break - $this->Cell($w, $h, substr($s, $j, $i - $j), 0, 2, '', false, $link); - $i++; - $sep = -1; - $j = $i; - $l = 0; - if ($nl == 1) { - $this->x = $this->lMargin; - $w = $this->w - $this->rMargin - $this->x; - $wmax = ($w - 2 * $this->cMargin) * 1000 / $this->FontSize; - } - $nl++; - continue; - } - if ($c == ' ') - $sep = $i; - $l += $cw[$c]; - if ($l > $wmax) { - // Automatic line break - if ($sep == -1) { - if ($this->x > $this->lMargin) { - // Move to next line - $this->x = $this->lMargin; - $this->y += $h; - $w = $this->w - $this->rMargin - $this->x; - $wmax = ($w - 2 * $this->cMargin) * 1000 / $this->FontSize; - $i++; - $nl++; - continue; - } - if ($i == $j) - $i++; - $this->Cell($w, $h, substr($s, $j, $i - $j), 0, 2, '', false, $link); - } else { - $this->Cell($w, $h, substr($s, $j, $sep - $j), 0, 2, '', false, $link); - $i = $sep + 1; - } - $sep = -1; - $j = $i; - $l = 0; - if ($nl == 1) { - $this->x = $this->lMargin; - $w = $this->w - $this->rMargin - $this->x; - $wmax = ($w - 2 * $this->cMargin) * 1000 / $this->FontSize; - } - $nl++; - } else - $i++; - } - // Last chunk - if ($i != $j) - $this->Cell($l / 1000 * $this->FontSize, $h, substr($s, $j), 0, 0, '', false, $link); - } - - function Ln($h = null) - { - // Line feed; default value is the last cell height - $this->x = $this->lMargin; - if ($h === null) - $this->y += $this->lasth; - else - $this->y += $h; - } - - function Image($file, $x = null, $y = null, $w = 0, $h = 0, $type = '', $link = '') - { - // Put an image on the page - if ($file == '') - $this->Error('Image file name is empty'); - if (!isset($this->images[$file])) { - // First use of this image, get info - if ($type == '') { - $pos = strrpos($file, '.'); - if (!$pos) - $this->Error('Image file has no extension and no type was specified: ' . $file); - $type = substr($file, $pos + 1); - } - $type = strtolower($type); - if ($type == 'jpeg') - $type = 'jpg'; - $mtd = '_parse' . $type; - if (!method_exists($this, $mtd)) - $this->Error('Unsupported image type: ' . $type); - $info = $this->$mtd($file); - $info['i'] = count($this->images) + 1; - $this->images[$file] = $info; - } else - $info = $this->images[$file]; - - // Automatic width and height calculation if needed - if ($w == 0 && $h == 0) { - // Put image at 96 dpi - $w = -96; - $h = -96; - } - if ($w < 0) - $w = -$info['w'] * 72 / $w / $this->k; - if ($h < 0) - $h = -$info['h'] * 72 / $h / $this->k; - if ($w == 0) - $w = $h * $info['w'] / $info['h']; - if ($h == 0) - $h = $w * $info['h'] / $info['w']; - - // Flowing mode - if ($y === null) { - if ($this->y + $h > $this->PageBreakTrigger && !$this->InHeader && !$this->InFooter && $this->AcceptPageBreak()) { - // Automatic page break - $x2 = $this->x; - $this->AddPage($this->CurOrientation, $this->CurPageSize, $this->CurRotation); - $this->x = $x2; - } - $y = $this->y; - $this->y += $h; - } - - if ($x === null) - $x = $this->x; - $this->_out(sprintf('q %.2F 0 0 %.2F %.2F %.2F cm /I%d Do Q', $w * $this->k, $h * $this->k, $x * $this->k, ($this->h - ($y + $h)) * $this->k, $info['i'])); - if ($link) - $this->Link($x, $y, $w, $h, $link); - } - - function GetPageWidth() - { - // Get current page width - return $this->w; - } - - function GetPageHeight() - { - // Get current page height - return $this->h; - } - - function GetX() - { - // Get x position - return $this->x; - } - - function SetX($x) - { - // Set x position - if ($x >= 0) - $this->x = $x; - else - $this->x = $this->w + $x; - } - - function GetY() - { - // Get y position - return $this->y; - } - - function SetY($y, $resetX = true) - { - // Set y position and optionally reset x - if ($y >= 0) - $this->y = $y; - else - $this->y = $this->h + $y; - if ($resetX) - $this->x = $this->lMargin; - } - - function SetXY($x, $y) - { - // Set x and y positions - $this->SetX($x); - $this->SetY($y, false); - } - - function Output($dest = '', $name = '', $isUTF8 = false) - { - // Output PDF to some destination - $this->Close(); - if (strlen($name) == 1 && strlen($dest) != 1) { - // Fix parameter order - $tmp = $dest; - $dest = $name; - $name = $tmp; - } - if ($dest == '') - $dest = 'I'; - if ($name == '') - $name = 'doc.pdf'; - switch (strtoupper($dest)) { - case 'I': - // Send to standard output - $this->_checkoutput(); - if (PHP_SAPI != 'cli') { - // We send to a browser - header('Content-Type: application/pdf'); - header('Content-Disposition: inline; ' . $this->_httpencode('filename', $name, $isUTF8)); - header('Cache-Control: private, max-age=0, must-revalidate'); - header('Pragma: public'); - } - echo $this->buffer; - break; - case 'D': - // Download file - $this->_checkoutput(); - header('Content-Type: application/pdf'); - header('Content-Disposition: attachment; ' . $this->_httpencode('filename', $name, $isUTF8)); - header('Cache-Control: private, max-age=0, must-revalidate'); - header('Pragma: public'); - echo $this->buffer; - break; - case 'F': - // Save to local file - if (!file_put_contents($name, $this->buffer)) - $this->Error('Unable to create output file: ' . $name); - break; - case 'S': - // Return as a string - return $this->buffer; - default: - $this->Error('Incorrect output destination: ' . $dest); - } - return ''; - } - - /******************************************************************************* - * Protected methods * - *******************************************************************************/ - - protected function _checkoutput() - { - if (PHP_SAPI != 'cli') { - if (headers_sent($file, $line)) - $this->Error("Some data has already been output, can't send PDF file (output started at $file:$line)"); - } - if (ob_get_length()) { - // The output buffer is not empty - if (preg_match('/^(\xEF\xBB\xBF)?\s*$/', ob_get_contents())) { - // It contains only a UTF-8 BOM and/or whitespace, let's clean it - ob_clean(); - } else - $this->Error("Some data has already been output, can't send PDF file"); - } - } - - protected function _getpagesize($size) - { - if (is_string($size)) { - $size = strtolower($size); - if (!isset($this->StdPageSizes[$size])) - $this->Error('Unknown page size: ' . $size); - $a = $this->StdPageSizes[$size]; - return array($a[0] / $this->k, $a[1] / $this->k); - } else { - if ($size[0] > $size[1]) - return array($size[1], $size[0]); - else - return $size; - } - } - - protected function _beginpage($orientation, $size, $rotation) - { - $this->page++; - $this->pages[$this->page] = ''; - $this->PageLinks[$this->page] = array(); - $this->state = 2; - $this->x = $this->lMargin; - $this->y = $this->tMargin; - $this->FontFamily = ''; - // Check page size and orientation - if ($orientation == '') - $orientation = $this->DefOrientation; - else - $orientation = strtoupper($orientation[0]); - if ($size == '') - $size = $this->DefPageSize; - else - $size = $this->_getpagesize($size); - if ($orientation != $this->CurOrientation || $size[0] != $this->CurPageSize[0] || $size[1] != $this->CurPageSize[1]) { - // New size or orientation - if ($orientation == 'P') { - $this->w = $size[0]; - $this->h = $size[1]; - } else { - $this->w = $size[1]; - $this->h = $size[0]; - } - $this->wPt = $this->w * $this->k; - $this->hPt = $this->h * $this->k; - $this->PageBreakTrigger = $this->h - $this->bMargin; - $this->CurOrientation = $orientation; - $this->CurPageSize = $size; - } - if ($orientation != $this->DefOrientation || $size[0] != $this->DefPageSize[0] || $size[1] != $this->DefPageSize[1]) - $this->PageInfo[$this->page]['size'] = array($this->wPt, $this->hPt); - if ($rotation != 0) { - if ($rotation % 90 != 0) - $this->Error('Incorrect rotation value: ' . $rotation); - $this->PageInfo[$this->page]['rotation'] = $rotation; - } - $this->CurRotation = $rotation; - } - - protected function _endpage() - { - $this->state = 1; - } - - protected function _loadfont($path) - { - // Load a font definition file - include($path); - if (!isset($name)) - $this->Error('Could not include font definition file: ' . $path); - if (isset($enc)) - $enc = strtolower($enc); - if (!isset($subsetted)) - $subsetted = false; - return get_defined_vars(); - } - - protected function _isascii($s) - { - // Test if string is ASCII - $nb = strlen($s); - for ($i = 0; $i < $nb; $i++) { - if (ord($s[$i]) > 127) - return false; - } - return true; - } - - protected function _httpencode($param, $value, $isUTF8) - { - // Encode HTTP header field parameter - if ($this->_isascii($value)) - return $param . '="' . $value . '"'; - if (!$isUTF8) - $value = $this->_UTF8encode($value); - return $param . "*=UTF-8''" . rawurlencode($value); - } - - protected function _UTF8encode($s) - { - // Convert ISO-8859-1 to UTF-8 - if ($this->iconv) - return iconv('ISO-8859-1', 'UTF-8', $s); - $res = ''; - $nb = strlen($s); - for ($i = 0; $i < $nb; $i++) { - $c = $s[$i]; - $v = ord($c); - if ($v >= 128) { - $res .= chr(0xC0 | ($v >> 6)); - $res .= chr(0x80 | ($v & 0x3F)); - } else - $res .= $c; - } - return $res; - } - - protected function _UTF8toUTF16($s) - { - // Convert UTF-8 to UTF-16BE with BOM - $res = "\xFE\xFF"; - if ($this->iconv) - return $res . iconv('UTF-8', 'UTF-16BE', $s); - $nb = strlen($s); - $i = 0; - while ($i < $nb) { - $c1 = ord($s[$i++]); - if ($c1 >= 224) { - // 3-byte character - $c2 = ord($s[$i++]); - $c3 = ord($s[$i++]); - $res .= chr((($c1 & 0x0F) << 4) + (($c2 & 0x3C) >> 2)); - $res .= chr((($c2 & 0x03) << 6) + ($c3 & 0x3F)); - } elseif ($c1 >= 192) { - // 2-byte character - $c2 = ord($s[$i++]); - $res .= chr(($c1 & 0x1C) >> 2); - $res .= chr((($c1 & 0x03) << 6) + ($c2 & 0x3F)); - } else { - // Single-byte character - $res .= "\0" . chr($c1); - } - } - return $res; - } - - protected function _escape($s) - { - // Escape special characters - if (strpos($s, '(') !== false || strpos($s, ')') !== false || strpos($s, '\\') !== false || strpos($s, "\r") !== false) - return str_replace(array('\\', '(', ')', "\r"), array('\\\\', '\\(', '\\)', '\\r'), $s); - else - return $s; - } - - protected function _textstring($s) - { - // Format a text string - if (!$this->_isascii($s)) - $s = $this->_UTF8toUTF16($s); - return '(' . $this->_escape($s) . ')'; - } - - protected function _dounderline($x, $y, $txt) - { - // Underline text - $up = $this->CurrentFont['up']; - $ut = $this->CurrentFont['ut']; - $w = $this->GetStringWidth($txt) + $this->ws * substr_count($txt, ' '); - return sprintf('%.2F %.2F %.2F %.2F re f', $x * $this->k, ($this->h - ($y - $up / 1000 * $this->FontSize)) * $this->k, $w * $this->k, -$ut / 1000 * $this->FontSizePt); - } - - protected function _parsejpg($file) - { - // Extract info from a JPEG file - $a = getimagesize($file); - if (!$a) - $this->Error('Missing or incorrect image file: ' . $file); - if ($a[2] != 2) - $this->Error('Not a JPEG file: ' . $file); - if (!isset($a['channels']) || $a['channels'] == 3) - $colspace = 'DeviceRGB'; - elseif ($a['channels'] == 4) - $colspace = 'DeviceCMYK'; - else - $colspace = 'DeviceGray'; - $bpc = isset($a['bits']) ? $a['bits'] : 8; - $data = file_get_contents($file); - return array('w' => $a[0], 'h' => $a[1], 'cs' => $colspace, 'bpc' => $bpc, 'f' => 'DCTDecode', 'data' => $data); - } - - protected function _parsepng($file) - { - // Extract info from a PNG file - $f = fopen($file, 'rb'); - if (!$f) - $this->Error('Can\'t open image file: ' . $file); - $info = $this->_parsepngstream($f, $file); - fclose($f); - return $info; - } - - protected function _parsepngstream($f, $file) - { - // Check signature - if ($this->_readstream($f, 8) != chr(137) . 'PNG' . chr(13) . chr(10) . chr(26) . chr(10)) - $this->Error('Not a PNG file: ' . $file); - - // Read header chunk - $this->_readstream($f, 4); - if ($this->_readstream($f, 4) != 'IHDR') - $this->Error('Incorrect PNG file: ' . $file); - $w = $this->_readint($f); - $h = $this->_readint($f); - $bpc = ord($this->_readstream($f, 1)); - if ($bpc > 8) - $this->Error('16-bit depth not supported: ' . $file); - $ct = ord($this->_readstream($f, 1)); - if ($ct == 0 || $ct == 4) - $colspace = 'DeviceGray'; - elseif ($ct == 2 || $ct == 6) - $colspace = 'DeviceRGB'; - elseif ($ct == 3) - $colspace = 'Indexed'; - else - $this->Error('Unknown color type: ' . $file); - if (ord($this->_readstream($f, 1)) != 0) - $this->Error('Unknown compression method: ' . $file); - if (ord($this->_readstream($f, 1)) != 0) - $this->Error('Unknown filter method: ' . $file); - if (ord($this->_readstream($f, 1)) != 0) - $this->Error('Interlacing not supported: ' . $file); - $this->_readstream($f, 4); - $dp = '/Predictor 15 /Colors ' . ($colspace == 'DeviceRGB' ? 3 : 1) . ' /BitsPerComponent ' . $bpc . ' /Columns ' . $w; - - // Scan chunks looking for palette, transparency and image data - $pal = ''; - $trns = ''; - $data = ''; - do { - $n = $this->_readint($f); - $type = $this->_readstream($f, 4); - if ($type == 'PLTE') { - // Read palette - $pal = $this->_readstream($f, $n); - $this->_readstream($f, 4); - } elseif ($type == 'tRNS') { - // Read transparency info - $t = $this->_readstream($f, $n); - if ($ct == 0) - $trns = array(ord(substr($t, 1, 1))); - elseif ($ct == 2) - $trns = array(ord(substr($t, 1, 1)), ord(substr($t, 3, 1)), ord(substr($t, 5, 1))); - else { - $pos = strpos($t, chr(0)); - if ($pos !== false) - $trns = array($pos); - } - $this->_readstream($f, 4); - } elseif ($type == 'IDAT') { - // Read image data block - $data .= $this->_readstream($f, $n); - $this->_readstream($f, 4); - } elseif ($type == 'IEND') - break; - else - $this->_readstream($f, $n + 4); - } while ($n); - - if ($colspace == 'Indexed' && empty($pal)) - $this->Error('Missing palette in ' . $file); - $info = array('w' => $w, 'h' => $h, 'cs' => $colspace, 'bpc' => $bpc, 'f' => 'FlateDecode', 'dp' => $dp, 'pal' => $pal, 'trns' => $trns); - if ($ct >= 4) { - // Extract alpha channel - if (!function_exists('gzuncompress')) - $this->Error('Zlib not available, can\'t handle alpha channel: ' . $file); - $data = gzuncompress($data); - $color = ''; - $alpha = ''; - if ($ct == 4) { - // Gray image - $len = 2 * $w; - for ($i = 0; $i < $h; $i++) { - $pos = (1 + $len) * $i; - $color .= $data[$pos]; - $alpha .= $data[$pos]; - $line = substr($data, $pos + 1, $len); - $color .= preg_replace('/(.)./s', '$1', $line); - $alpha .= preg_replace('/.(.)/s', '$1', $line); - } - } else { - // RGB image - $len = 4 * $w; - for ($i = 0; $i < $h; $i++) { - $pos = (1 + $len) * $i; - $color .= $data[$pos]; - $alpha .= $data[$pos]; - $line = substr($data, $pos + 1, $len); - $color .= preg_replace('/(.{3})./s', '$1', $line); - $alpha .= preg_replace('/.{3}(.)/s', '$1', $line); - } - } - unset($data); - $data = gzcompress($color); - $info['smask'] = gzcompress($alpha); - $this->WithAlpha = true; - if ($this->PDFVersion < '1.4') - $this->PDFVersion = '1.4'; - } - $info['data'] = $data; - return $info; - } - - protected function _readstream($f, $n) - { - // Read n bytes from stream - $res = ''; - while ($n > 0 && !feof($f)) { - $s = fread($f, $n); - if ($s === false) - $this->Error('Error while reading stream'); - $n -= strlen($s); - $res .= $s; - } - if ($n > 0) - $this->Error('Unexpected end of stream'); - return $res; - } - - protected function _readint($f) - { - // Read a 4-byte integer from stream - $a = unpack('Ni', $this->_readstream($f, 4)); - return $a['i']; - } - - protected function _parsegif($file) - { - // Extract info from a GIF file (via PNG conversion) - if (!function_exists('imagepng')) - $this->Error('GD extension is required for GIF support'); - if (!function_exists('imagecreatefromgif')) - $this->Error('GD has no GIF read support'); - $im = imagecreatefromgif($file); - if (!$im) - $this->Error('Missing or incorrect image file: ' . $file); - imageinterlace($im, 0); - ob_start(); - imagepng($im); - $data = ob_get_clean(); - imagedestroy($im); - $f = fopen('php://temp', 'rb+'); - if (!$f) - $this->Error('Unable to create memory stream'); - fwrite($f, $data); - rewind($f); - $info = $this->_parsepngstream($f, $file); - fclose($f); - return $info; - } - - protected function _out($s) - { - // Add a line to the current page - if ($this->state == 2) - $this->pages[$this->page] .= $s . "\n"; - elseif ($this->state == 0) - $this->Error('No page has been added yet'); - elseif ($this->state == 1) - $this->Error('Invalid call'); - elseif ($this->state == 3) - $this->Error('The document is closed'); - } - - protected function _put($s) - { - // Add a line to the document - $this->buffer .= $s . "\n"; - } - - protected function _getoffset() - { - return strlen($this->buffer); - } - - protected function _newobj($n = null) - { - // Begin a new object - if ($n === null) - $n = ++$this->n; - $this->offsets[$n] = $this->_getoffset(); - $this->_put($n . ' 0 obj'); - } - - protected function _putstream($data) - { - $this->_put('stream'); - $this->_put($data); - $this->_put('endstream'); - } - - protected function _putstreamobject($data) - { - if ($this->compress) { - $entries = '/Filter /FlateDecode '; - $data = gzcompress($data); - } else - $entries = ''; - $entries .= '/Length ' . strlen($data); - $this->_newobj(); - $this->_put('<<' . $entries . '>>'); - $this->_putstream($data); - $this->_put('endobj'); - } - - protected function _putlinks($n) - { - foreach ($this->PageLinks[$n] as $pl) { - $this->_newobj(); - $rect = sprintf('%.2F %.2F %.2F %.2F', $pl[0], $pl[1], $pl[0] + $pl[2], $pl[1] - $pl[3]); - $s = '<_textstring($pl[4]) . '>>>>'; - else { - $l = $this->links[$pl[4]]; - if (isset($this->PageInfo[$l[0]]['size'])) - $h = $this->PageInfo[$l[0]]['size'][1]; - else - $h = ($this->DefOrientation == 'P') ? $this->DefPageSize[1] * $this->k : $this->DefPageSize[0] * $this->k; - $s .= sprintf('/Dest [%d 0 R /XYZ 0 %.2F null]>>', $this->PageInfo[$l[0]]['n'], $h - $l[1] * $this->k); - } - $this->_put($s); - $this->_put('endobj'); - } - } - - protected function _putpage($n) - { - $this->_newobj(); - $this->_put('<_put('/Parent 1 0 R'); - if (isset($this->PageInfo[$n]['size'])) - $this->_put(sprintf('/MediaBox [0 0 %.2F %.2F]', $this->PageInfo[$n]['size'][0], $this->PageInfo[$n]['size'][1])); - if (isset($this->PageInfo[$n]['rotation'])) - $this->_put('/Rotate ' . $this->PageInfo[$n]['rotation']); - $this->_put('/Resources 2 0 R'); - if (!empty($this->PageLinks[$n])) { - $s = '/Annots ['; - foreach ($this->PageLinks[$n] as $pl) - $s .= $pl[5] . ' 0 R '; - $s .= ']'; - $this->_put($s); - } - if ($this->WithAlpha) - $this->_put('/Group <>'); - $this->_put('/Contents ' . ($this->n + 1) . ' 0 R>>'); - $this->_put('endobj'); - // Page content - if (!empty($this->AliasNbPages)) - $this->pages[$n] = str_replace($this->AliasNbPages, $this->page, $this->pages[$n]); - $this->_putstreamobject($this->pages[$n]); - // Link annotations - $this->_putlinks($n); - } - - protected function _putpages() - { - $nb = $this->page; - $n = $this->n; - for ($i = 1; $i <= $nb; $i++) { - $this->PageInfo[$i]['n'] = ++$n; - $n++; - foreach ($this->PageLinks[$i] as &$pl) - $pl[5] = ++$n; - unset($pl); - } - for ($i = 1; $i <= $nb; $i++) - $this->_putpage($i); - // Pages root - $this->_newobj(1); - $this->_put('<PageInfo[$i]['n'] . ' 0 R '; - $kids .= ']'; - $this->_put($kids); - $this->_put('/Count ' . $nb); - if ($this->DefOrientation == 'P') { - $w = $this->DefPageSize[0]; - $h = $this->DefPageSize[1]; - } else { - $w = $this->DefPageSize[1]; - $h = $this->DefPageSize[0]; - } - $this->_put(sprintf('/MediaBox [0 0 %.2F %.2F]', $w * $this->k, $h * $this->k)); - $this->_put('>>'); - $this->_put('endobj'); - } - - protected function _putfonts() - { - foreach ($this->FontFiles as $file => $info) { - // Font file embedding - $this->_newobj(); - $this->FontFiles[$file]['n'] = $this->n; - $font = file_get_contents($file); - if (!$font) - $this->Error('Font file not found: ' . $file); - $compressed = (substr($file, -2) == '.z'); - if (!$compressed && isset($info['length2'])) - $font = substr($font, 6, $info['length1']) . substr($font, 6 + $info['length1'] + 6, $info['length2']); - $this->_put('<_put('/Filter /FlateDecode'); - $this->_put('/Length1 ' . $info['length1']); - if (isset($info['length2'])) - $this->_put('/Length2 ' . $info['length2'] . ' /Length3 0'); - $this->_put('>>'); - $this->_putstream($font); - $this->_put('endobj'); - } - foreach ($this->fonts as $k => $font) { - // Encoding - if (isset($font['diff'])) { - if (!isset($this->encodings[$font['enc']])) { - $this->_newobj(); - $this->_put('<>'); - $this->_put('endobj'); - $this->encodings[$font['enc']] = $this->n; - } - } - // ToUnicode CMap - if (isset($font['uv'])) { - if (isset($font['enc'])) - $cmapkey = $font['enc']; - else - $cmapkey = $font['name']; - if (!isset($this->cmaps[$cmapkey])) { - $cmap = $this->_tounicodecmap($font['uv']); - $this->_putstreamobject($cmap); - $this->cmaps[$cmapkey] = $this->n; - } - } - // Font object - $this->fonts[$k]['n'] = $this->n + 1; - $type = $font['type']; - $name = $font['name']; - if ($font['subsetted']) - $name = 'AAAAAA+' . $name; - if ($type == 'Core') { - // Core font - $this->_newobj(); - $this->_put('<_put('/BaseFont /' . $name); - $this->_put('/Subtype /Type1'); - if ($name != 'Symbol' && $name != 'ZapfDingbats') - $this->_put('/Encoding /WinAnsiEncoding'); - if (isset($font['uv'])) - $this->_put('/ToUnicode ' . $this->cmaps[$cmapkey] . ' 0 R'); - $this->_put('>>'); - $this->_put('endobj'); - } elseif ($type == 'Type1' || $type == 'TrueType') { - // Additional Type1 or TrueType/OpenType font - $this->_newobj(); - $this->_put('<_put('/BaseFont /' . $name); - $this->_put('/Subtype /' . $type); - $this->_put('/FirstChar 32 /LastChar 255'); - $this->_put('/Widths ' . ($this->n + 1) . ' 0 R'); - $this->_put('/FontDescriptor ' . ($this->n + 2) . ' 0 R'); - if (isset($font['diff'])) - $this->_put('/Encoding ' . $this->encodings[$font['enc']] . ' 0 R'); - else - $this->_put('/Encoding /WinAnsiEncoding'); - if (isset($font['uv'])) - $this->_put('/ToUnicode ' . $this->cmaps[$cmapkey] . ' 0 R'); - $this->_put('>>'); - $this->_put('endobj'); - // Widths - $this->_newobj(); - $cw = $font['cw']; - $s = '['; - for ($i = 32; $i <= 255; $i++) - $s .= $cw[chr($i)] . ' '; - $this->_put($s . ']'); - $this->_put('endobj'); - // Descriptor - $this->_newobj(); - $s = '< $v) - $s .= ' /' . $k . ' ' . $v; - if (!empty($font['file'])) - $s .= ' /FontFile' . ($type == 'Type1' ? '' : '2') . ' ' . $this->FontFiles[$font['file']]['n'] . ' 0 R'; - $this->_put($s . '>>'); - $this->_put('endobj'); - } else { - // Allow for additional types - $mtd = '_put' . strtolower($type); - if (!method_exists($this, $mtd)) - $this->Error('Unsupported font type: ' . $type); - $this->$mtd($font); - } - } - } - - protected function _tounicodecmap($uv) - { - $ranges = ''; - $nbr = 0; - $chars = ''; - $nbc = 0; - foreach ($uv as $c => $v) { - if (is_array($v)) { - $ranges .= sprintf("<%02X> <%02X> <%04X>\n", $c, $c + $v[1] - 1, $v[0]); - $nbr++; - } else { - $chars .= sprintf("<%02X> <%04X>\n", $c, $v); - $nbc++; - } - } - $s = "/CIDInit /ProcSet findresource begin\n"; - $s .= "12 dict begin\n"; - $s .= "begincmap\n"; - $s .= "/CIDSystemInfo\n"; - $s .= "< 0) { - $s .= "$nbr beginbfrange\n"; - $s .= $ranges; - $s .= "endbfrange\n"; - } - if ($nbc > 0) { - $s .= "$nbc beginbfchar\n"; - $s .= $chars; - $s .= "endbfchar\n"; - } - $s .= "endcmap\n"; - $s .= "CMapName currentdict /CMap defineresource pop\n"; - $s .= "end\n"; - $s .= "end"; - return $s; - } - - protected function _putimages() - { - foreach (array_keys($this->images) as $file) { - $this->_putimage($this->images[$file]); - unset($this->images[$file]['data']); - unset($this->images[$file]['smask']); - } - } - - protected function _putimage(&$info) - { - $this->_newobj(); - $info['n'] = $this->n; - $this->_put('<_put('/Subtype /Image'); - $this->_put('/Width ' . $info['w']); - $this->_put('/Height ' . $info['h']); - if ($info['cs'] == 'Indexed') - $this->_put('/ColorSpace [/Indexed /DeviceRGB ' . (strlen($info['pal']) / 3 - 1) . ' ' . ($this->n + 1) . ' 0 R]'); - else { - $this->_put('/ColorSpace /' . $info['cs']); - if ($info['cs'] == 'DeviceCMYK') - $this->_put('/Decode [1 0 1 0 1 0 1 0]'); - } - $this->_put('/BitsPerComponent ' . $info['bpc']); - if (isset($info['f'])) - $this->_put('/Filter /' . $info['f']); - if (isset($info['dp'])) - $this->_put('/DecodeParms <<' . $info['dp'] . '>>'); - if (isset($info['trns']) && is_array($info['trns'])) { - $trns = ''; - for ($i = 0; $i < count($info['trns']); $i++) - $trns .= $info['trns'][$i] . ' ' . $info['trns'][$i] . ' '; - $this->_put('/Mask [' . $trns . ']'); - } - if (isset($info['smask'])) - $this->_put('/SMask ' . ($this->n + 1) . ' 0 R'); - $this->_put('/Length ' . strlen($info['data']) . '>>'); - $this->_putstream($info['data']); - $this->_put('endobj'); - // Soft mask - if (isset($info['smask'])) { - $dp = '/Predictor 15 /Colors 1 /BitsPerComponent 8 /Columns ' . $info['w']; - $smask = array('w' => $info['w'], 'h' => $info['h'], 'cs' => 'DeviceGray', 'bpc' => 8, 'f' => $info['f'], 'dp' => $dp, 'data' => $info['smask']); - $this->_putimage($smask); - } - // Palette - if ($info['cs'] == 'Indexed') - $this->_putstreamobject($info['pal']); - } - - protected function _putxobjectdict() - { - foreach ($this->images as $image) - $this->_put('/I' . $image['i'] . ' ' . $image['n'] . ' 0 R'); - } - - protected function _putresourcedict() - { - $this->_put('/ProcSet [/PDF /Text /ImageB /ImageC /ImageI]'); - $this->_put('/Font <<'); - foreach ($this->fonts as $font) - $this->_put('/F' . $font['i'] . ' ' . $font['n'] . ' 0 R'); - $this->_put('>>'); - $this->_put('/XObject <<'); - $this->_putxobjectdict(); - $this->_put('>>'); - } - - protected function _putresources() - { - $this->_putfonts(); - $this->_putimages(); - // Resource dictionary - $this->_newobj(2); - $this->_put('<<'); - $this->_putresourcedict(); - $this->_put('>>'); - $this->_put('endobj'); - } - - protected function _putinfo() - { - $date = @date('YmdHisO', $this->CreationDate); - $this->metadata['CreationDate'] = 'D:' . substr($date, 0, -2) . "'" . substr($date, -2) . "'"; - foreach ($this->metadata as $key => $value) - $this->_put('/' . $key . ' ' . $this->_textstring($value)); - } - - protected function _putcatalog() - { - $n = $this->PageInfo[1]['n']; - $this->_put('/Type /Catalog'); - $this->_put('/Pages 1 0 R'); - if ($this->ZoomMode == 'fullpage') - $this->_put('/OpenAction [' . $n . ' 0 R /Fit]'); - elseif ($this->ZoomMode == 'fullwidth') - $this->_put('/OpenAction [' . $n . ' 0 R /FitH null]'); - elseif ($this->ZoomMode == 'real') - $this->_put('/OpenAction [' . $n . ' 0 R /XYZ null null 1]'); - elseif (!is_string($this->ZoomMode)) - $this->_put('/OpenAction [' . $n . ' 0 R /XYZ null null ' . sprintf('%.2F', $this->ZoomMode / 100) . ']'); - if ($this->LayoutMode == 'single') - $this->_put('/PageLayout /SinglePage'); - elseif ($this->LayoutMode == 'continuous') - $this->_put('/PageLayout /OneColumn'); - elseif ($this->LayoutMode == 'two') - $this->_put('/PageLayout /TwoColumnLeft'); - } - - protected function _putheader() - { - $this->_put('%PDF-' . $this->PDFVersion); - } - - protected function _puttrailer() - { - $this->_put('/Size ' . ($this->n + 1)); - $this->_put('/Root ' . $this->n . ' 0 R'); - $this->_put('/Info ' . ($this->n - 1) . ' 0 R'); - } - - protected function _enddoc() - { - $this->CreationDate = time(); - $this->_putheader(); - $this->_putpages(); - $this->_putresources(); - // Info - $this->_newobj(); - $this->_put('<<'); - $this->_putinfo(); - $this->_put('>>'); - $this->_put('endobj'); - // Catalog - $this->_newobj(); - $this->_put('<<'); - $this->_putcatalog(); - $this->_put('>>'); - $this->_put('endobj'); - // Cross-ref - $offset = $this->_getoffset(); - $this->_put('xref'); - $this->_put('0 ' . ($this->n + 1)); - $this->_put('0000000000 65535 f '); - for ($i = 1; $i <= $this->n; $i++) - $this->_put(sprintf('%010d 00000 n ', $this->offsets[$i])); - // Trailer - $this->_put('trailer'); - $this->_put('<<'); - $this->_puttrailer(); - $this->_put('>>'); - $this->_put('startxref'); - $this->_put($offset); - $this->_put('%%EOF'); - $this->state = 3; - } -} \ No newline at end of file + const VERSION = '1.86'; + + protected $page; // current page number + + protected $n; // current object number + + protected $offsets; // array of object offsets + + protected $buffer; // buffer holding in-memory PDF + + protected $pages; // array containing pages + + protected $state; // current document state + + protected $compress; // compression flag + + protected $iconv; // whether iconv is available + + protected $k; // scale factor (number of points in user unit) + + protected $DefOrientation; // default orientation + + protected $CurOrientation; // current orientation + + protected $StdPageSizes; // standard page sizes + + protected $DefPageSize; // default page size + + protected $CurPageSize; // current page size + + protected $CurRotation; // current page rotation + + protected $PageInfo; // page-related data + + protected $wPt; + + protected $hPt; // dimensions of current page in points + + protected $w; + + protected $h; // dimensions of current page in user unit + + protected $lMargin; // left margin + + protected $tMargin; // top margin + + protected $rMargin; // right margin + + protected $bMargin; // page break margin + + protected $cMargin; // cell margin + + protected $x; + + protected $y; // current position in user unit + + protected $lasth; // height of last printed cell + + protected $LineWidth; // line width in user unit + + protected $fontpath; // directory containing fonts + + protected $CoreFonts; // array of core font names + + protected $fonts; // array of used fonts + + protected $FontFiles; // array of font files + + protected $encodings; // array of encodings + + protected $cmaps; // array of ToUnicode CMaps + + protected $FontFamily; // current font family + + protected $FontStyle; // current font style + + protected $underline; // underlining flag + + protected $CurrentFont; // current font info + + protected $FontSizePt; // current font size in points + + protected $FontSize; // current font size in user unit + + protected $DrawColor; // commands for drawing color + + protected $FillColor; // commands for filling color + + protected $TextColor; // commands for text color + + protected $ColorFlag; // indicates whether fill and text colors are different + + protected $WithAlpha; // indicates whether alpha channel is used + + protected $ws; // word spacing + + protected $images; // array of used images + + protected $PageLinks; // array of links in pages + + protected $links; // array of internal links + + protected $AutoPageBreak; // automatic page breaking + + protected $PageBreakTrigger; // threshold used to trigger page breaks + + protected $InHeader; // flag set when processing header + + protected $InFooter; // flag set when processing footer + + protected $AliasNbPages; // alias for total number of pages + + protected $ZoomMode; // zoom display mode + + protected $LayoutMode; // layout display mode + + protected $metadata; // document properties + + protected $CreationDate; // document creation date + + protected $PDFVersion; // PDF version number + + /******************************************************************************* + * Public methods * + *******************************************************************************/ + + public function __construct($orientation = 'P', $unit = 'mm', $size = 'A4') + { + // FIXED: Set font path directly + $this->fontpath = __DIR__.'/font/'; + + // Initialization of properties + $this->state = 0; + $this->page = 0; + $this->n = 2; + $this->buffer = ''; + $this->pages = []; + $this->PageInfo = []; + $this->fonts = []; + $this->FontFiles = []; + $this->encodings = []; + $this->cmaps = []; + $this->images = []; + $this->links = []; + $this->InHeader = false; + $this->InFooter = false; + $this->lasth = 0; + $this->FontFamily = ''; + $this->FontStyle = ''; + $this->FontSizePt = 12; + $this->underline = false; + $this->DrawColor = '0 G'; + $this->FillColor = '0 g'; + $this->TextColor = '0 g'; + $this->ColorFlag = false; + $this->WithAlpha = false; + $this->ws = 0; + $this->iconv = function_exists('iconv'); + + // Core fonts + $this->CoreFonts = ['courier', 'helvetica', 'times', 'symbol', 'zapfdingbats']; + + // Scale factor + if ($unit == 'pt') { + $this->k = 1; + } elseif ($unit == 'mm') { + $this->k = 72 / 25.4; + } elseif ($unit == 'cm') { + $this->k = 72 / 2.54; + } elseif ($unit == 'in') { + $this->k = 72; + } else { + $this->Error('Incorrect unit: '.$unit); + } + + // Page sizes + $this->StdPageSizes = [ + 'a3' => [841.89, 1190.55], + 'a4' => [595.28, 841.89], + 'a5' => [420.94, 595.28], + 'letter' => [612, 792], + 'legal' => [612, 1008], + ]; + $size = $this->_getpagesize($size); + $this->DefPageSize = $size; + $this->CurPageSize = $size; + + // Page orientation + $orientation = strtolower($orientation); + if ($orientation == 'p' || $orientation == 'portrait') { + $this->DefOrientation = 'P'; + $this->w = $size[0]; + $this->h = $size[1]; + } elseif ($orientation == 'l' || $orientation == 'landscape') { + $this->DefOrientation = 'L'; + $this->w = $size[1]; + $this->h = $size[0]; + } else { + $this->Error('Incorrect orientation: '.$orientation); + } + $this->CurOrientation = $this->DefOrientation; + $this->wPt = $this->w * $this->k; + $this->hPt = $this->h * $this->k; + + // Page rotation + $this->CurRotation = 0; + + // Margins + $margin = 28.35 / $this->k; + $this->SetMargins($margin, $margin); + $this->cMargin = $margin / 10; + $this->LineWidth = .567 / $this->k; + + // Page break + $this->SetAutoPageBreak(true, 2 * $margin); + + // Display and compression + $this->SetDisplayMode('default'); + $this->SetCompression(true); + + // Metadata + $this->metadata = ['Producer' => 'FPDF '.self::VERSION]; + $this->PDFVersion = '1.3'; + } + + public function SetMargins($left, $top, $right = null) + { + // Set left, top and right margins + $this->lMargin = $left; + $this->tMargin = $top; + if ($right === null) { + $right = $left; + } + $this->rMargin = $right; + } + + public function SetLeftMargin($margin) + { + // Set left margin + $this->lMargin = $margin; + if ($this->page > 0 && $this->x < $margin) { + $this->x = $margin; + } + } + + public function SetTopMargin($margin) + { + // Set top margin + $this->tMargin = $margin; + } + + public function SetRightMargin($margin) + { + // Set right margin + $this->rMargin = $margin; + } + + public function SetAutoPageBreak($auto, $margin = 0) + { + // Set auto page break mode and triggering margin + $this->AutoPageBreak = $auto; + $this->bMargin = $margin; + $this->PageBreakTrigger = $this->h - $margin; + } + + public function SetDisplayMode($zoom, $layout = 'default') + { + // Set display mode in viewer + if ($zoom == 'fullpage' || $zoom == 'fullwidth' || $zoom == 'real' || $zoom == 'default' || ! is_string($zoom)) { + $this->ZoomMode = $zoom; + } else { + $this->Error('Incorrect zoom display mode: '.$zoom); + } + if ($layout == 'single' || $layout == 'continuous' || $layout == 'two' || $layout == 'default') { + $this->LayoutMode = $layout; + } else { + $this->Error('Incorrect layout display mode: '.$layout); + } + } + + public function SetCompression($compress) + { + // Set page compression + if (function_exists('gzcompress')) { + $this->compress = $compress; + } else { + $this->compress = false; + } + } + + public function SetTitle($title, $isUTF8 = false) + { + // Title of document + $this->metadata['Title'] = $isUTF8 ? $title : $this->_UTF8encode($title); + } + + public function SetAuthor($author, $isUTF8 = false) + { + // Author of document + $this->metadata['Author'] = $isUTF8 ? $author : $this->_UTF8encode($author); + } + + public function SetSubject($subject, $isUTF8 = false) + { + // Subject of document + $this->metadata['Subject'] = $isUTF8 ? $subject : $this->_UTF8encode($subject); + } + + public function SetKeywords($keywords, $isUTF8 = false) + { + // Keywords of document + $this->metadata['Keywords'] = $isUTF8 ? $keywords : $this->_UTF8encode($keywords); + } + + public function SetCreator($creator, $isUTF8 = false) + { + // Creator of document + $this->metadata['Creator'] = $isUTF8 ? $creator : $this->_UTF8encode($creator); + } + + public function AliasNbPages($alias = '{nb}') + { + // Define an alias for total number of pages + $this->AliasNbPages = $alias; + } + + public function Error($msg) + { + // Fatal error + throw new Exception('FPDF error: '.$msg); + } + + public function Close() + { + // Terminate document + if ($this->state == 3) { + return; + } + if ($this->page == 0) { + $this->AddPage(); + } + // Page footer + $this->InFooter = true; + $this->Footer(); + $this->InFooter = false; + // Close page + $this->_endpage(); + // Close document + $this->_enddoc(); + } + + public function AddPage($orientation = '', $size = '', $rotation = 0) + { + // Start a new page + if ($this->state == 3) { + $this->Error('The document is closed'); + } + $family = $this->FontFamily; + $style = $this->FontStyle.($this->underline ? 'U' : ''); + $fontsize = $this->FontSizePt; + $lw = $this->LineWidth; + $dc = $this->DrawColor; + $fc = $this->FillColor; + $tc = $this->TextColor; + $cf = $this->ColorFlag; + if ($this->page > 0) { + // Page footer + $this->InFooter = true; + $this->Footer(); + $this->InFooter = false; + // Close page + $this->_endpage(); + } + // Start new page + $this->_beginpage($orientation, $size, $rotation); + // Set line cap style to square + $this->_out('2 J'); + // Set line width + $this->LineWidth = $lw; + $this->_out(sprintf('%.2F w', $lw * $this->k)); + // Set font + if ($family) { + $this->SetFont($family, $style, $fontsize); + } + // Set colors + $this->DrawColor = $dc; + if ($dc != '0 G') { + $this->_out($dc); + } + $this->FillColor = $fc; + if ($fc != '0 g') { + $this->_out($fc); + } + $this->TextColor = $tc; + $this->ColorFlag = $cf; + // Page header + $this->InHeader = true; + $this->Header(); + $this->InHeader = false; + // Restore line width + if ($this->LineWidth != $lw) { + $this->LineWidth = $lw; + $this->_out(sprintf('%.2F w', $lw * $this->k)); + } + // Restore font + if ($family) { + $this->SetFont($family, $style, $fontsize); + } + // Restore colors + if ($this->DrawColor != $dc) { + $this->DrawColor = $dc; + $this->_out($dc); + } + if ($this->FillColor != $fc) { + $this->FillColor = $fc; + $this->_out($fc); + } + $this->TextColor = $tc; + $this->ColorFlag = $cf; + } + + public function Header() + { + // To be implemented in your own inherited class + } + + public function Footer() + { + // To be implemented in your own inherited class + } + + public function PageNo() + { + // Get current page number + return $this->page; + } + + public function SetDrawColor($r, $g = null, $b = null) + { + // Set color for all stroking operations + if (($r == 0 && $g == 0 && $b == 0) || $g === null) { + $this->DrawColor = sprintf('%.3F G', $r / 255); + } else { + $this->DrawColor = sprintf('%.3F %.3F %.3F RG', $r / 255, $g / 255, $b / 255); + } + if ($this->page > 0) { + $this->_out($this->DrawColor); + } + } + + public function SetFillColor($r, $g = null, $b = null) + { + // Set color for all filling operations + if (($r == 0 && $g == 0 && $b == 0) || $g === null) { + $this->FillColor = sprintf('%.3F g', $r / 255); + } else { + $this->FillColor = sprintf('%.3F %.3F %.3F rg', $r / 255, $g / 255, $b / 255); + } + $this->ColorFlag = ($this->FillColor != $this->TextColor); + if ($this->page > 0) { + $this->_out($this->FillColor); + } + } + + public function SetTextColor($r, $g = null, $b = null) + { + // Set color for text + if (($r == 0 && $g == 0 && $b == 0) || $g === null) { + $this->TextColor = sprintf('%.3F g', $r / 255); + } else { + $this->TextColor = sprintf('%.3F %.3F %.3F rg', $r / 255, $g / 255, $b / 255); + } + $this->ColorFlag = ($this->FillColor != $this->TextColor); + } + + public function GetStringWidth($s) + { + // Get width of a string in the current font + $cw = $this->CurrentFont['cw']; + $w = 0; + $s = (string) $s; + $l = strlen($s); + for ($i = 0; $i < $l; $i++) { + $w += $cw[$s[$i]]; + } + + return $w * $this->FontSize / 1000; + } + + public function SetLineWidth($width) + { + // Set line width + $this->LineWidth = $width; + if ($this->page > 0) { + $this->_out(sprintf('%.2F w', $width * $this->k)); + } + } + + public function Line($x1, $y1, $x2, $y2) + { + // Draw a line + $this->_out(sprintf('%.2F %.2F m %.2F %.2F l S', $x1 * $this->k, ($this->h - $y1) * $this->k, $x2 * $this->k, ($this->h - $y2) * $this->k)); + } + + public function Rect($x, $y, $w, $h, $style = '') + { + // Draw a rectangle + if ($style == 'F') { + $op = 'f'; + } elseif ($style == 'FD' || $style == 'DF') { + $op = 'B'; + } else { + $op = 'S'; + } + $this->_out(sprintf('%.2F %.2F %.2F %.2F re %s', $x * $this->k, ($this->h - $y) * $this->k, $w * $this->k, -$h * $this->k, $op)); + } + + public function AddFont($family, $style = '', $file = '', $dir = '') + { + // Add a TrueType, OpenType or Type1 font + $family = strtolower($family); + if ($file == '') { + $file = str_replace(' ', '', $family).strtolower($style).'.php'; + } + $style = strtoupper($style); + if ($style == 'IB') { + $style = 'BI'; + } + $fontkey = $family.$style; + if (isset($this->fonts[$fontkey])) { + return; + } + if (strpos($file, '/') !== false || strpos($file, '\\') !== false) { + $this->Error('Incorrect font definition file name: '.$file); + } + if ($dir == '') { + $dir = $this->fontpath; + } + if (substr($dir, -1) != '/' && substr($dir, -1) != '\\') { + $dir .= '/'; + } + $info = $this->_loadfont($dir.$file); + $info['i'] = count($this->fonts) + 1; + if (! empty($info['file'])) { + // Embedded font + $info['file'] = $dir.$info['file']; + if ($info['type'] == 'TrueType') { + $this->FontFiles[$info['file']] = ['length1' => $info['originalsize']]; + } else { + $this->FontFiles[$info['file']] = ['length1' => $info['size1'], 'length2' => $info['size2']]; + } + } + $this->fonts[$fontkey] = $info; + } + + public function SetFont($family, $style = '', $size = 0) + { + // Select a font; size given in points + if ($family == '') { + $family = $this->FontFamily; + } else { + $family = strtolower($family); + } + $style = strtoupper($style); + if (strpos($style, 'U') !== false) { + $this->underline = true; + $style = str_replace('U', '', $style); + } else { + $this->underline = false; + } + if ($style == 'IB') { + $style = 'BI'; + } + if ($size == 0) { + $size = $this->FontSizePt; + } + // Test if font is already selected + if ($this->FontFamily == $family && $this->FontStyle == $style && $this->FontSizePt == $size) { + return; + } + // Test if font is already loaded + $fontkey = $family.$style; + if (! isset($this->fonts[$fontkey])) { + // Test if one of the core fonts + if ($family == 'arial') { + $family = 'helvetica'; + } + if (in_array($family, $this->CoreFonts)) { + if ($family == 'symbol' || $family == 'zapfdingbats') { + $style = ''; + } + $fontkey = $family.$style; + if (! isset($this->fonts[$fontkey])) { + $this->AddFont($family, $style); + } + } else { + $this->Error('Undefined font: '.$family.' '.$style); + } + } + // Select it + $this->FontFamily = $family; + $this->FontStyle = $style; + $this->FontSizePt = $size; + $this->FontSize = $size / $this->k; + $this->CurrentFont = $this->fonts[$fontkey]; + if ($this->page > 0) { + $this->_out(sprintf('BT /F%d %.2F Tf ET', $this->CurrentFont['i'], $this->FontSizePt)); + } + } + + public function SetFontSize($size) + { + // Set font size in points + if ($this->FontSizePt == $size) { + return; + } + $this->FontSizePt = $size; + $this->FontSize = $size / $this->k; + if ($this->page > 0 && isset($this->CurrentFont)) { + $this->_out(sprintf('BT /F%d %.2F Tf ET', $this->CurrentFont['i'], $this->FontSizePt)); + } + } + + public function AddLink() + { + // Create a new internal link + $n = count($this->links) + 1; + $this->links[$n] = [0, 0]; + + return $n; + } + + public function SetLink($link, $y = 0, $page = -1) + { + // Set destination of internal link + if ($y == -1) { + $y = $this->y; + } + if ($page == -1) { + $page = $this->page; + } + $this->links[$link] = [$page, $y]; + } + + public function Link($x, $y, $w, $h, $link) + { + // Put a link on the page + $this->PageLinks[$this->page][] = [$x * $this->k, $this->hPt - $y * $this->k, $w * $this->k, $h * $this->k, $link]; + } + + public function Text($x, $y, $txt) + { + // Output a string + if (! isset($this->CurrentFont)) { + $this->Error('No font has been set'); + } + $txt = (string) $txt; + $s = sprintf('BT %.2F %.2F Td (%s) Tj ET', $x * $this->k, ($this->h - $y) * $this->k, $this->_escape($txt)); + if ($this->underline && $txt !== '') { + $s .= ' '.$this->_dounderline($x, $y, $txt); + } + if ($this->ColorFlag) { + $s = 'q '.$this->TextColor.' '.$s.' Q'; + } + $this->_out($s); + } + + public function AcceptPageBreak() + { + // Accept automatic page break or not + return $this->AutoPageBreak; + } + + public function Cell($w, $h = 0, $txt = '', $border = 0, $ln = 0, $align = '', $fill = false, $link = '') + { + // Output a cell + $k = $this->k; + if ($this->y + $h > $this->PageBreakTrigger && ! $this->InHeader && ! $this->InFooter && $this->AcceptPageBreak()) { + // Automatic page break + $x = $this->x; + $ws = $this->ws; + if ($ws > 0) { + $this->ws = 0; + $this->_out('0 Tw'); + } + $this->AddPage($this->CurOrientation, $this->CurPageSize, $this->CurRotation); + $this->x = $x; + if ($ws > 0) { + $this->ws = $ws; + $this->_out(sprintf('%.3F Tw', $ws * $k)); + } + } + if ($w == 0) { + $w = $this->w - $this->rMargin - $this->x; + } + $s = ''; + if ($fill || $border == 1) { + if ($fill) { + $op = ($border == 1) ? 'B' : 'f'; + } else { + $op = 'S'; + } + $s = sprintf('%.2F %.2F %.2F %.2F re %s ', $this->x * $k, ($this->h - $this->y) * $k, $w * $k, -$h * $k, $op); + } + if (is_string($border)) { + $x = $this->x; + $y = $this->y; + if (strpos($border, 'L') !== false) { + $s .= sprintf('%.2F %.2F m %.2F %.2F l S ', $x * $k, ($this->h - $y) * $k, $x * $k, ($this->h - ($y + $h)) * $k); + } + if (strpos($border, 'T') !== false) { + $s .= sprintf('%.2F %.2F m %.2F %.2F l S ', $x * $k, ($this->h - $y) * $k, ($x + $w) * $k, ($this->h - $y) * $k); + } + if (strpos($border, 'R') !== false) { + $s .= sprintf('%.2F %.2F m %.2F %.2F l S ', ($x + $w) * $k, ($this->h - $y) * $k, ($x + $w) * $k, ($this->h - ($y + $h)) * $k); + } + if (strpos($border, 'B') !== false) { + $s .= sprintf('%.2F %.2F m %.2F %.2F l S ', $x * $k, ($this->h - ($y + $h)) * $k, ($x + $w) * $k, ($this->h - ($y + $h)) * $k); + } + } + $txt = (string) $txt; + if ($txt !== '') { + if (! isset($this->CurrentFont)) { + $this->Error('No font has been set'); + } + if ($align == 'R') { + $dx = $w - $this->cMargin - $this->GetStringWidth($txt); + } elseif ($align == 'C') { + $dx = ($w - $this->GetStringWidth($txt)) / 2; + } else { + $dx = $this->cMargin; + } + if ($this->ColorFlag) { + $s .= 'q '.$this->TextColor.' '; + } + $s .= sprintf('BT %.2F %.2F Td (%s) Tj ET', ($this->x + $dx) * $k, ($this->h - ($this->y + .5 * $h + .3 * $this->FontSize)) * $k, $this->_escape($txt)); + if ($this->underline) { + $s .= ' '.$this->_dounderline($this->x + $dx, $this->y + .5 * $h + .3 * $this->FontSize, $txt); + } + if ($this->ColorFlag) { + $s .= ' Q'; + } + if ($link) { + $this->Link($this->x + $dx, $this->y + .5 * $h - .5 * $this->FontSize, $this->GetStringWidth($txt), $this->FontSize, $link); + } + } + if ($s) { + $this->_out($s); + } + $this->lasth = $h; + if ($ln > 0) { + // Go to next line + $this->y += $h; + if ($ln == 1) { + $this->x = $this->lMargin; + } + } else { + $this->x += $w; + } + } + + public function MultiCell($w, $h, $txt, $border = 0, $align = 'J', $fill = false) + { + // Output text with automatic or explicit line breaks + if (! isset($this->CurrentFont)) { + $this->Error('No font has been set'); + } + $cw = $this->CurrentFont['cw']; + if ($w == 0) { + $w = $this->w - $this->rMargin - $this->x; + } + $wmax = ($w - 2 * $this->cMargin) * 1000 / $this->FontSize; + $s = str_replace("\r", '', (string) $txt); + $nb = strlen($s); + if ($nb > 0 && $s[$nb - 1] == "\n") { + $nb--; + } + $b = 0; + if ($border) { + if ($border == 1) { + $border = 'LTRB'; + $b = 'LRT'; + $b2 = 'LR'; + } else { + $b2 = ''; + if (strpos($border, 'L') !== false) { + $b2 .= 'L'; + } + if (strpos($border, 'R') !== false) { + $b2 .= 'R'; + } + $b = (strpos($border, 'T') !== false) ? $b2.'T' : $b2; + } + } + $sep = -1; + $i = 0; + $j = 0; + $l = 0; + $ns = 0; + $nl = 1; + while ($i < $nb) { + // Get next character + $c = $s[$i]; + if ($c == "\n") { + // Explicit line break + if ($this->ws > 0) { + $this->ws = 0; + $this->_out('0 Tw'); + } + $this->Cell($w, $h, substr($s, $j, $i - $j), $b, 2, $align, $fill); + $i++; + $sep = -1; + $j = $i; + $l = 0; + $ns = 0; + $nl++; + if ($border && $nl == 2) { + $b = $b2; + } + + continue; + } + if ($c == ' ') { + $sep = $i; + $ls = $l; + $ns++; + } + $l += $cw[$c]; + if ($l > $wmax) { + // Automatic line break + if ($sep == -1) { + if ($i == $j) { + $i++; + } + if ($this->ws > 0) { + $this->ws = 0; + $this->_out('0 Tw'); + } + $this->Cell($w, $h, substr($s, $j, $i - $j), $b, 2, $align, $fill); + } else { + if ($align == 'J') { + $this->ws = ($ns > 1) ? ($wmax - $ls) / 1000 * $this->FontSize / ($ns - 1) : 0; + $this->_out(sprintf('%.3F Tw', $this->ws * $this->k)); + } + $this->Cell($w, $h, substr($s, $j, $sep - $j), $b, 2, $align, $fill); + $i = $sep + 1; + } + $sep = -1; + $j = $i; + $l = 0; + $ns = 0; + $nl++; + if ($border && $nl == 2) { + $b = $b2; + } + } else { + $i++; + } + } + // Last chunk + if ($this->ws > 0) { + $this->ws = 0; + $this->_out('0 Tw'); + } + if ($border && strpos($border, 'B') !== false) { + $b .= 'B'; + } + $this->Cell($w, $h, substr($s, $j, $i - $j), $b, 2, $align, $fill); + $this->x = $this->lMargin; + } + + public function Write($h, $txt, $link = '') + { + // Output text in flowing mode + if (! isset($this->CurrentFont)) { + $this->Error('No font has been set'); + } + $cw = $this->CurrentFont['cw']; + $w = $this->w - $this->rMargin - $this->x; + $wmax = ($w - 2 * $this->cMargin) * 1000 / $this->FontSize; + $s = str_replace("\r", '', (string) $txt); + $nb = strlen($s); + $sep = -1; + $i = 0; + $j = 0; + $l = 0; + $nl = 1; + while ($i < $nb) { + // Get next character + $c = $s[$i]; + if ($c == "\n") { + // Explicit line break + $this->Cell($w, $h, substr($s, $j, $i - $j), 0, 2, '', false, $link); + $i++; + $sep = -1; + $j = $i; + $l = 0; + if ($nl == 1) { + $this->x = $this->lMargin; + $w = $this->w - $this->rMargin - $this->x; + $wmax = ($w - 2 * $this->cMargin) * 1000 / $this->FontSize; + } + $nl++; + + continue; + } + if ($c == ' ') { + $sep = $i; + } + $l += $cw[$c]; + if ($l > $wmax) { + // Automatic line break + if ($sep == -1) { + if ($this->x > $this->lMargin) { + // Move to next line + $this->x = $this->lMargin; + $this->y += $h; + $w = $this->w - $this->rMargin - $this->x; + $wmax = ($w - 2 * $this->cMargin) * 1000 / $this->FontSize; + $i++; + $nl++; + + continue; + } + if ($i == $j) { + $i++; + } + $this->Cell($w, $h, substr($s, $j, $i - $j), 0, 2, '', false, $link); + } else { + $this->Cell($w, $h, substr($s, $j, $sep - $j), 0, 2, '', false, $link); + $i = $sep + 1; + } + $sep = -1; + $j = $i; + $l = 0; + if ($nl == 1) { + $this->x = $this->lMargin; + $w = $this->w - $this->rMargin - $this->x; + $wmax = ($w - 2 * $this->cMargin) * 1000 / $this->FontSize; + } + $nl++; + } else { + $i++; + } + } + // Last chunk + if ($i != $j) { + $this->Cell($l / 1000 * $this->FontSize, $h, substr($s, $j), 0, 0, '', false, $link); + } + } + + public function Ln($h = null) + { + // Line feed; default value is the last cell height + $this->x = $this->lMargin; + if ($h === null) { + $this->y += $this->lasth; + } else { + $this->y += $h; + } + } + + public function Image($file, $x = null, $y = null, $w = 0, $h = 0, $type = '', $link = '') + { + // Put an image on the page + if ($file == '') { + $this->Error('Image file name is empty'); + } + if (! isset($this->images[$file])) { + // First use of this image, get info + if ($type == '') { + $pos = strrpos($file, '.'); + if (! $pos) { + $this->Error('Image file has no extension and no type was specified: '.$file); + } + $type = substr($file, $pos + 1); + } + $type = strtolower($type); + if ($type == 'jpeg') { + $type = 'jpg'; + } + $mtd = '_parse'.$type; + if (! method_exists($this, $mtd)) { + $this->Error('Unsupported image type: '.$type); + } + $info = $this->$mtd($file); + $info['i'] = count($this->images) + 1; + $this->images[$file] = $info; + } else { + $info = $this->images[$file]; + } + + // Automatic width and height calculation if needed + if ($w == 0 && $h == 0) { + // Put image at 96 dpi + $w = -96; + $h = -96; + } + if ($w < 0) { + $w = -$info['w'] * 72 / $w / $this->k; + } + if ($h < 0) { + $h = -$info['h'] * 72 / $h / $this->k; + } + if ($w == 0) { + $w = $h * $info['w'] / $info['h']; + } + if ($h == 0) { + $h = $w * $info['h'] / $info['w']; + } + + // Flowing mode + if ($y === null) { + if ($this->y + $h > $this->PageBreakTrigger && ! $this->InHeader && ! $this->InFooter && $this->AcceptPageBreak()) { + // Automatic page break + $x2 = $this->x; + $this->AddPage($this->CurOrientation, $this->CurPageSize, $this->CurRotation); + $this->x = $x2; + } + $y = $this->y; + $this->y += $h; + } + + if ($x === null) { + $x = $this->x; + } + $this->_out(sprintf('q %.2F 0 0 %.2F %.2F %.2F cm /I%d Do Q', $w * $this->k, $h * $this->k, $x * $this->k, ($this->h - ($y + $h)) * $this->k, $info['i'])); + if ($link) { + $this->Link($x, $y, $w, $h, $link); + } + } + + public function GetPageWidth() + { + // Get current page width + return $this->w; + } + + public function GetPageHeight() + { + // Get current page height + return $this->h; + } + + public function GetX() + { + // Get x position + return $this->x; + } + + public function SetX($x) + { + // Set x position + if ($x >= 0) { + $this->x = $x; + } else { + $this->x = $this->w + $x; + } + } + + public function GetY() + { + // Get y position + return $this->y; + } + + public function SetY($y, $resetX = true) + { + // Set y position and optionally reset x + if ($y >= 0) { + $this->y = $y; + } else { + $this->y = $this->h + $y; + } + if ($resetX) { + $this->x = $this->lMargin; + } + } + + public function SetXY($x, $y) + { + // Set x and y positions + $this->SetX($x); + $this->SetY($y, false); + } + + public function Output($dest = '', $name = '', $isUTF8 = false) + { + // Output PDF to some destination + $this->Close(); + if (strlen($name) == 1 && strlen($dest) != 1) { + // Fix parameter order + $tmp = $dest; + $dest = $name; + $name = $tmp; + } + if ($dest == '') { + $dest = 'I'; + } + if ($name == '') { + $name = 'doc.pdf'; + } + switch (strtoupper($dest)) { + case 'I': + // Send to standard output + $this->_checkoutput(); + if (PHP_SAPI != 'cli') { + // We send to a browser + header('Content-Type: application/pdf'); + header('Content-Disposition: inline; '.$this->_httpencode('filename', $name, $isUTF8)); + header('Cache-Control: private, max-age=0, must-revalidate'); + header('Pragma: public'); + } + echo $this->buffer; + break; + case 'D': + // Download file + $this->_checkoutput(); + header('Content-Type: application/pdf'); + header('Content-Disposition: attachment; '.$this->_httpencode('filename', $name, $isUTF8)); + header('Cache-Control: private, max-age=0, must-revalidate'); + header('Pragma: public'); + echo $this->buffer; + break; + case 'F': + // Save to local file + if (! file_put_contents($name, $this->buffer)) { + $this->Error('Unable to create output file: '.$name); + } + break; + case 'S': + // Return as a string + return $this->buffer; + default: + $this->Error('Incorrect output destination: '.$dest); + } + + return ''; + } + + /******************************************************************************* + * Protected methods * + *******************************************************************************/ + + protected function _checkoutput() + { + if (PHP_SAPI != 'cli') { + if (headers_sent($file, $line)) { + $this->Error("Some data has already been output, can't send PDF file (output started at $file:$line)"); + } + } + if (ob_get_length()) { + // The output buffer is not empty + if (preg_match('/^(\xEF\xBB\xBF)?\s*$/', ob_get_contents())) { + // It contains only a UTF-8 BOM and/or whitespace, let's clean it + ob_clean(); + } else { + $this->Error("Some data has already been output, can't send PDF file"); + } + } + } + + protected function _getpagesize($size) + { + if (is_string($size)) { + $size = strtolower($size); + if (! isset($this->StdPageSizes[$size])) { + $this->Error('Unknown page size: '.$size); + } + $a = $this->StdPageSizes[$size]; + + return [$a[0] / $this->k, $a[1] / $this->k]; + } else { + if ($size[0] > $size[1]) { + return [$size[1], $size[0]]; + } else { + return $size; + } + } + } + + protected function _beginpage($orientation, $size, $rotation) + { + $this->page++; + $this->pages[$this->page] = ''; + $this->PageLinks[$this->page] = []; + $this->state = 2; + $this->x = $this->lMargin; + $this->y = $this->tMargin; + $this->FontFamily = ''; + // Check page size and orientation + if ($orientation == '') { + $orientation = $this->DefOrientation; + } else { + $orientation = strtoupper($orientation[0]); + } + if ($size == '') { + $size = $this->DefPageSize; + } else { + $size = $this->_getpagesize($size); + } + if ($orientation != $this->CurOrientation || $size[0] != $this->CurPageSize[0] || $size[1] != $this->CurPageSize[1]) { + // New size or orientation + if ($orientation == 'P') { + $this->w = $size[0]; + $this->h = $size[1]; + } else { + $this->w = $size[1]; + $this->h = $size[0]; + } + $this->wPt = $this->w * $this->k; + $this->hPt = $this->h * $this->k; + $this->PageBreakTrigger = $this->h - $this->bMargin; + $this->CurOrientation = $orientation; + $this->CurPageSize = $size; + } + if ($orientation != $this->DefOrientation || $size[0] != $this->DefPageSize[0] || $size[1] != $this->DefPageSize[1]) { + $this->PageInfo[$this->page]['size'] = [$this->wPt, $this->hPt]; + } + if ($rotation != 0) { + if ($rotation % 90 != 0) { + $this->Error('Incorrect rotation value: '.$rotation); + } + $this->PageInfo[$this->page]['rotation'] = $rotation; + } + $this->CurRotation = $rotation; + } + + protected function _endpage() + { + $this->state = 1; + } + + protected function _loadfont($path) + { + // Load a font definition file + include $path; + if (! isset($name)) { + $this->Error('Could not include font definition file: '.$path); + } + if (isset($enc)) { + $enc = strtolower($enc); + } + if (! isset($subsetted)) { + $subsetted = false; + } + + return get_defined_vars(); + } + + protected function _isascii($s) + { + // Test if string is ASCII + $nb = strlen($s); + for ($i = 0; $i < $nb; $i++) { + if (ord($s[$i]) > 127) { + return false; + } + } + + return true; + } + + protected function _httpencode($param, $value, $isUTF8) + { + // Encode HTTP header field parameter + if ($this->_isascii($value)) { + return $param.'="'.$value.'"'; + } + if (! $isUTF8) { + $value = $this->_UTF8encode($value); + } + + return $param."*=UTF-8''".rawurlencode($value); + } + + protected function _UTF8encode($s) + { + // Convert ISO-8859-1 to UTF-8 + if ($this->iconv) { + return iconv('ISO-8859-1', 'UTF-8', $s); + } + $res = ''; + $nb = strlen($s); + for ($i = 0; $i < $nb; $i++) { + $c = $s[$i]; + $v = ord($c); + if ($v >= 128) { + $res .= chr(0xC0 | ($v >> 6)); + $res .= chr(0x80 | ($v & 0x3F)); + } else { + $res .= $c; + } + } + + return $res; + } + + protected function _UTF8toUTF16($s) + { + // Convert UTF-8 to UTF-16BE with BOM + $res = "\xFE\xFF"; + if ($this->iconv) { + return $res.iconv('UTF-8', 'UTF-16BE', $s); + } + $nb = strlen($s); + $i = 0; + while ($i < $nb) { + $c1 = ord($s[$i++]); + if ($c1 >= 224) { + // 3-byte character + $c2 = ord($s[$i++]); + $c3 = ord($s[$i++]); + $res .= chr((($c1 & 0x0F) << 4) + (($c2 & 0x3C) >> 2)); + $res .= chr((($c2 & 0x03) << 6) + ($c3 & 0x3F)); + } elseif ($c1 >= 192) { + // 2-byte character + $c2 = ord($s[$i++]); + $res .= chr(($c1 & 0x1C) >> 2); + $res .= chr((($c1 & 0x03) << 6) + ($c2 & 0x3F)); + } else { + // Single-byte character + $res .= "\0".chr($c1); + } + } + + return $res; + } + + protected function _escape($s) + { + // Escape special characters + if (strpos($s, '(') !== false || strpos($s, ')') !== false || strpos($s, '\\') !== false || strpos($s, "\r") !== false) { + return str_replace(['\\', '(', ')', "\r"], ['\\\\', '\\(', '\\)', '\\r'], $s); + } else { + return $s; + } + } + + protected function _textstring($s) + { + // Format a text string + if (! $this->_isascii($s)) { + $s = $this->_UTF8toUTF16($s); + } + + return '('.$this->_escape($s).')'; + } + + protected function _dounderline($x, $y, $txt) + { + // Underline text + $up = $this->CurrentFont['up']; + $ut = $this->CurrentFont['ut']; + $w = $this->GetStringWidth($txt) + $this->ws * substr_count($txt, ' '); + + return sprintf('%.2F %.2F %.2F %.2F re f', $x * $this->k, ($this->h - ($y - $up / 1000 * $this->FontSize)) * $this->k, $w * $this->k, -$ut / 1000 * $this->FontSizePt); + } + + protected function _parsejpg($file) + { + // Extract info from a JPEG file + $a = getimagesize($file); + if (! $a) { + $this->Error('Missing or incorrect image file: '.$file); + } + if ($a[2] != 2) { + $this->Error('Not a JPEG file: '.$file); + } + if (! isset($a['channels']) || $a['channels'] == 3) { + $colspace = 'DeviceRGB'; + } elseif ($a['channels'] == 4) { + $colspace = 'DeviceCMYK'; + } else { + $colspace = 'DeviceGray'; + } + $bpc = isset($a['bits']) ? $a['bits'] : 8; + $data = file_get_contents($file); + + return ['w' => $a[0], 'h' => $a[1], 'cs' => $colspace, 'bpc' => $bpc, 'f' => 'DCTDecode', 'data' => $data]; + } + + protected function _parsepng($file) + { + // Extract info from a PNG file + $f = fopen($file, 'rb'); + if (! $f) { + $this->Error('Can\'t open image file: '.$file); + } + $info = $this->_parsepngstream($f, $file); + fclose($f); + + return $info; + } + + protected function _parsepngstream($f, $file) + { + // Check signature + if ($this->_readstream($f, 8) != chr(137).'PNG'.chr(13).chr(10).chr(26).chr(10)) { + $this->Error('Not a PNG file: '.$file); + } + + // Read header chunk + $this->_readstream($f, 4); + if ($this->_readstream($f, 4) != 'IHDR') { + $this->Error('Incorrect PNG file: '.$file); + } + $w = $this->_readint($f); + $h = $this->_readint($f); + $bpc = ord($this->_readstream($f, 1)); + if ($bpc > 8) { + $this->Error('16-bit depth not supported: '.$file); + } + $ct = ord($this->_readstream($f, 1)); + if ($ct == 0 || $ct == 4) { + $colspace = 'DeviceGray'; + } elseif ($ct == 2 || $ct == 6) { + $colspace = 'DeviceRGB'; + } elseif ($ct == 3) { + $colspace = 'Indexed'; + } else { + $this->Error('Unknown color type: '.$file); + } + if (ord($this->_readstream($f, 1)) != 0) { + $this->Error('Unknown compression method: '.$file); + } + if (ord($this->_readstream($f, 1)) != 0) { + $this->Error('Unknown filter method: '.$file); + } + if (ord($this->_readstream($f, 1)) != 0) { + $this->Error('Interlacing not supported: '.$file); + } + $this->_readstream($f, 4); + $dp = '/Predictor 15 /Colors '.($colspace == 'DeviceRGB' ? 3 : 1).' /BitsPerComponent '.$bpc.' /Columns '.$w; + + // Scan chunks looking for palette, transparency and image data + $pal = ''; + $trns = ''; + $data = ''; + do { + $n = $this->_readint($f); + $type = $this->_readstream($f, 4); + if ($type == 'PLTE') { + // Read palette + $pal = $this->_readstream($f, $n); + $this->_readstream($f, 4); + } elseif ($type == 'tRNS') { + // Read transparency info + $t = $this->_readstream($f, $n); + if ($ct == 0) { + $trns = [ord(substr($t, 1, 1))]; + } elseif ($ct == 2) { + $trns = [ord(substr($t, 1, 1)), ord(substr($t, 3, 1)), ord(substr($t, 5, 1))]; + } else { + $pos = strpos($t, chr(0)); + if ($pos !== false) { + $trns = [$pos]; + } + } + $this->_readstream($f, 4); + } elseif ($type == 'IDAT') { + // Read image data block + $data .= $this->_readstream($f, $n); + $this->_readstream($f, 4); + } elseif ($type == 'IEND') { + break; + } else { + $this->_readstream($f, $n + 4); + } + } while ($n); + + if ($colspace == 'Indexed' && empty($pal)) { + $this->Error('Missing palette in '.$file); + } + $info = ['w' => $w, 'h' => $h, 'cs' => $colspace, 'bpc' => $bpc, 'f' => 'FlateDecode', 'dp' => $dp, 'pal' => $pal, 'trns' => $trns]; + if ($ct >= 4) { + // Extract alpha channel + if (! function_exists('gzuncompress')) { + $this->Error('Zlib not available, can\'t handle alpha channel: '.$file); + } + $data = gzuncompress($data); + $color = ''; + $alpha = ''; + if ($ct == 4) { + // Gray image + $len = 2 * $w; + for ($i = 0; $i < $h; $i++) { + $pos = (1 + $len) * $i; + $color .= $data[$pos]; + $alpha .= $data[$pos]; + $line = substr($data, $pos + 1, $len); + $color .= preg_replace('/(.)./s', '$1', $line); + $alpha .= preg_replace('/.(.)/s', '$1', $line); + } + } else { + // RGB image + $len = 4 * $w; + for ($i = 0; $i < $h; $i++) { + $pos = (1 + $len) * $i; + $color .= $data[$pos]; + $alpha .= $data[$pos]; + $line = substr($data, $pos + 1, $len); + $color .= preg_replace('/(.{3})./s', '$1', $line); + $alpha .= preg_replace('/.{3}(.)/s', '$1', $line); + } + } + unset($data); + $data = gzcompress($color); + $info['smask'] = gzcompress($alpha); + $this->WithAlpha = true; + if ($this->PDFVersion < '1.4') { + $this->PDFVersion = '1.4'; + } + } + $info['data'] = $data; + + return $info; + } + + protected function _readstream($f, $n) + { + // Read n bytes from stream + $res = ''; + while ($n > 0 && ! feof($f)) { + $s = fread($f, $n); + if ($s === false) { + $this->Error('Error while reading stream'); + } + $n -= strlen($s); + $res .= $s; + } + if ($n > 0) { + $this->Error('Unexpected end of stream'); + } + + return $res; + } + + protected function _readint($f) + { + // Read a 4-byte integer from stream + $a = unpack('Ni', $this->_readstream($f, 4)); + + return $a['i']; + } + + protected function _parsegif($file) + { + // Extract info from a GIF file (via PNG conversion) + if (! function_exists('imagepng')) { + $this->Error('GD extension is required for GIF support'); + } + if (! function_exists('imagecreatefromgif')) { + $this->Error('GD has no GIF read support'); + } + $im = imagecreatefromgif($file); + if (! $im) { + $this->Error('Missing or incorrect image file: '.$file); + } + imageinterlace($im, 0); + ob_start(); + imagepng($im); + $data = ob_get_clean(); + imagedestroy($im); + $f = fopen('php://temp', 'rb+'); + if (! $f) { + $this->Error('Unable to create memory stream'); + } + fwrite($f, $data); + rewind($f); + $info = $this->_parsepngstream($f, $file); + fclose($f); + + return $info; + } + + protected function _out($s) + { + // Add a line to the current page + if ($this->state == 2) { + $this->pages[$this->page] .= $s."\n"; + } elseif ($this->state == 0) { + $this->Error('No page has been added yet'); + } elseif ($this->state == 1) { + $this->Error('Invalid call'); + } elseif ($this->state == 3) { + $this->Error('The document is closed'); + } + } + + protected function _put($s) + { + // Add a line to the document + $this->buffer .= $s."\n"; + } + + protected function _getoffset() + { + return strlen($this->buffer); + } + + protected function _newobj($n = null) + { + // Begin a new object + if ($n === null) { + $n = ++$this->n; + } + $this->offsets[$n] = $this->_getoffset(); + $this->_put($n.' 0 obj'); + } + + protected function _putstream($data) + { + $this->_put('stream'); + $this->_put($data); + $this->_put('endstream'); + } + + protected function _putstreamobject($data) + { + if ($this->compress) { + $entries = '/Filter /FlateDecode '; + $data = gzcompress($data); + } else { + $entries = ''; + } + $entries .= '/Length '.strlen($data); + $this->_newobj(); + $this->_put('<<'.$entries.'>>'); + $this->_putstream($data); + $this->_put('endobj'); + } + + protected function _putlinks($n) + { + foreach ($this->PageLinks[$n] as $pl) { + $this->_newobj(); + $rect = sprintf('%.2F %.2F %.2F %.2F', $pl[0], $pl[1], $pl[0] + $pl[2], $pl[1] - $pl[3]); + $s = '<_textstring($pl[4]).'>>>>'; + } else { + $l = $this->links[$pl[4]]; + if (isset($this->PageInfo[$l[0]]['size'])) { + $h = $this->PageInfo[$l[0]]['size'][1]; + } else { + $h = ($this->DefOrientation == 'P') ? $this->DefPageSize[1] * $this->k : $this->DefPageSize[0] * $this->k; + } + $s .= sprintf('/Dest [%d 0 R /XYZ 0 %.2F null]>>', $this->PageInfo[$l[0]]['n'], $h - $l[1] * $this->k); + } + $this->_put($s); + $this->_put('endobj'); + } + } + + protected function _putpage($n) + { + $this->_newobj(); + $this->_put('<_put('/Parent 1 0 R'); + if (isset($this->PageInfo[$n]['size'])) { + $this->_put(sprintf('/MediaBox [0 0 %.2F %.2F]', $this->PageInfo[$n]['size'][0], $this->PageInfo[$n]['size'][1])); + } + if (isset($this->PageInfo[$n]['rotation'])) { + $this->_put('/Rotate '.$this->PageInfo[$n]['rotation']); + } + $this->_put('/Resources 2 0 R'); + if (! empty($this->PageLinks[$n])) { + $s = '/Annots ['; + foreach ($this->PageLinks[$n] as $pl) { + $s .= $pl[5].' 0 R '; + } + $s .= ']'; + $this->_put($s); + } + if ($this->WithAlpha) { + $this->_put('/Group <>'); + } + $this->_put('/Contents '.($this->n + 1).' 0 R>>'); + $this->_put('endobj'); + // Page content + if (! empty($this->AliasNbPages)) { + $this->pages[$n] = str_replace($this->AliasNbPages, $this->page, $this->pages[$n]); + } + $this->_putstreamobject($this->pages[$n]); + // Link annotations + $this->_putlinks($n); + } + + protected function _putpages() + { + $nb = $this->page; + $n = $this->n; + for ($i = 1; $i <= $nb; $i++) { + $this->PageInfo[$i]['n'] = ++$n; + $n++; + foreach ($this->PageLinks[$i] as &$pl) { + $pl[5] = ++$n; + } + unset($pl); + } + for ($i = 1; $i <= $nb; $i++) { + $this->_putpage($i); + } + // Pages root + $this->_newobj(1); + $this->_put('<PageInfo[$i]['n'].' 0 R '; + } + $kids .= ']'; + $this->_put($kids); + $this->_put('/Count '.$nb); + if ($this->DefOrientation == 'P') { + $w = $this->DefPageSize[0]; + $h = $this->DefPageSize[1]; + } else { + $w = $this->DefPageSize[1]; + $h = $this->DefPageSize[0]; + } + $this->_put(sprintf('/MediaBox [0 0 %.2F %.2F]', $w * $this->k, $h * $this->k)); + $this->_put('>>'); + $this->_put('endobj'); + } + + protected function _putfonts() + { + foreach ($this->FontFiles as $file => $info) { + // Font file embedding + $this->_newobj(); + $this->FontFiles[$file]['n'] = $this->n; + $font = file_get_contents($file); + if (! $font) { + $this->Error('Font file not found: '.$file); + } + $compressed = (substr($file, -2) == '.z'); + if (! $compressed && isset($info['length2'])) { + $font = substr($font, 6, $info['length1']).substr($font, 6 + $info['length1'] + 6, $info['length2']); + } + $this->_put('<_put('/Filter /FlateDecode'); + } + $this->_put('/Length1 '.$info['length1']); + if (isset($info['length2'])) { + $this->_put('/Length2 '.$info['length2'].' /Length3 0'); + } + $this->_put('>>'); + $this->_putstream($font); + $this->_put('endobj'); + } + foreach ($this->fonts as $k => $font) { + // Encoding + if (isset($font['diff'])) { + if (! isset($this->encodings[$font['enc']])) { + $this->_newobj(); + $this->_put('<>'); + $this->_put('endobj'); + $this->encodings[$font['enc']] = $this->n; + } + } + // ToUnicode CMap + if (isset($font['uv'])) { + if (isset($font['enc'])) { + $cmapkey = $font['enc']; + } else { + $cmapkey = $font['name']; + } + if (! isset($this->cmaps[$cmapkey])) { + $cmap = $this->_tounicodecmap($font['uv']); + $this->_putstreamobject($cmap); + $this->cmaps[$cmapkey] = $this->n; + } + } + // Font object + $this->fonts[$k]['n'] = $this->n + 1; + $type = $font['type']; + $name = $font['name']; + if ($font['subsetted']) { + $name = 'AAAAAA+'.$name; + } + if ($type == 'Core') { + // Core font + $this->_newobj(); + $this->_put('<_put('/BaseFont /'.$name); + $this->_put('/Subtype /Type1'); + if ($name != 'Symbol' && $name != 'ZapfDingbats') { + $this->_put('/Encoding /WinAnsiEncoding'); + } + if (isset($font['uv'])) { + $this->_put('/ToUnicode '.$this->cmaps[$cmapkey].' 0 R'); + } + $this->_put('>>'); + $this->_put('endobj'); + } elseif ($type == 'Type1' || $type == 'TrueType') { + // Additional Type1 or TrueType/OpenType font + $this->_newobj(); + $this->_put('<_put('/BaseFont /'.$name); + $this->_put('/Subtype /'.$type); + $this->_put('/FirstChar 32 /LastChar 255'); + $this->_put('/Widths '.($this->n + 1).' 0 R'); + $this->_put('/FontDescriptor '.($this->n + 2).' 0 R'); + if (isset($font['diff'])) { + $this->_put('/Encoding '.$this->encodings[$font['enc']].' 0 R'); + } else { + $this->_put('/Encoding /WinAnsiEncoding'); + } + if (isset($font['uv'])) { + $this->_put('/ToUnicode '.$this->cmaps[$cmapkey].' 0 R'); + } + $this->_put('>>'); + $this->_put('endobj'); + // Widths + $this->_newobj(); + $cw = $font['cw']; + $s = '['; + for ($i = 32; $i <= 255; $i++) { + $s .= $cw[chr($i)].' '; + } + $this->_put($s.']'); + $this->_put('endobj'); + // Descriptor + $this->_newobj(); + $s = '< $v) { + $s .= ' /'.$k.' '.$v; + } + if (! empty($font['file'])) { + $s .= ' /FontFile'.($type == 'Type1' ? '' : '2').' '.$this->FontFiles[$font['file']]['n'].' 0 R'; + } + $this->_put($s.'>>'); + $this->_put('endobj'); + } else { + // Allow for additional types + $mtd = '_put'.strtolower($type); + if (! method_exists($this, $mtd)) { + $this->Error('Unsupported font type: '.$type); + } + $this->$mtd($font); + } + } + } + + protected function _tounicodecmap($uv) + { + $ranges = ''; + $nbr = 0; + $chars = ''; + $nbc = 0; + foreach ($uv as $c => $v) { + if (is_array($v)) { + $ranges .= sprintf("<%02X> <%02X> <%04X>\n", $c, $c + $v[1] - 1, $v[0]); + $nbr++; + } else { + $chars .= sprintf("<%02X> <%04X>\n", $c, $v); + $nbc++; + } + } + $s = "/CIDInit /ProcSet findresource begin\n"; + $s .= "12 dict begin\n"; + $s .= "begincmap\n"; + $s .= "/CIDSystemInfo\n"; + $s .= "< 0) { + $s .= "$nbr beginbfrange\n"; + $s .= $ranges; + $s .= "endbfrange\n"; + } + if ($nbc > 0) { + $s .= "$nbc beginbfchar\n"; + $s .= $chars; + $s .= "endbfchar\n"; + } + $s .= "endcmap\n"; + $s .= "CMapName currentdict /CMap defineresource pop\n"; + $s .= "end\n"; + $s .= 'end'; + + return $s; + } + + protected function _putimages() + { + foreach (array_keys($this->images) as $file) { + $this->_putimage($this->images[$file]); + unset($this->images[$file]['data']); + unset($this->images[$file]['smask']); + } + } + + protected function _putimage(&$info) + { + $this->_newobj(); + $info['n'] = $this->n; + $this->_put('<_put('/Subtype /Image'); + $this->_put('/Width '.$info['w']); + $this->_put('/Height '.$info['h']); + if ($info['cs'] == 'Indexed') { + $this->_put('/ColorSpace [/Indexed /DeviceRGB '.(strlen($info['pal']) / 3 - 1).' '.($this->n + 1).' 0 R]'); + } else { + $this->_put('/ColorSpace /'.$info['cs']); + if ($info['cs'] == 'DeviceCMYK') { + $this->_put('/Decode [1 0 1 0 1 0 1 0]'); + } + } + $this->_put('/BitsPerComponent '.$info['bpc']); + if (isset($info['f'])) { + $this->_put('/Filter /'.$info['f']); + } + if (isset($info['dp'])) { + $this->_put('/DecodeParms <<'.$info['dp'].'>>'); + } + if (isset($info['trns']) && is_array($info['trns'])) { + $trns = ''; + for ($i = 0; $i < count($info['trns']); $i++) { + $trns .= $info['trns'][$i].' '.$info['trns'][$i].' '; + } + $this->_put('/Mask ['.$trns.']'); + } + if (isset($info['smask'])) { + $this->_put('/SMask '.($this->n + 1).' 0 R'); + } + $this->_put('/Length '.strlen($info['data']).'>>'); + $this->_putstream($info['data']); + $this->_put('endobj'); + // Soft mask + if (isset($info['smask'])) { + $dp = '/Predictor 15 /Colors 1 /BitsPerComponent 8 /Columns '.$info['w']; + $smask = ['w' => $info['w'], 'h' => $info['h'], 'cs' => 'DeviceGray', 'bpc' => 8, 'f' => $info['f'], 'dp' => $dp, 'data' => $info['smask']]; + $this->_putimage($smask); + } + // Palette + if ($info['cs'] == 'Indexed') { + $this->_putstreamobject($info['pal']); + } + } + + protected function _putxobjectdict() + { + foreach ($this->images as $image) { + $this->_put('/I'.$image['i'].' '.$image['n'].' 0 R'); + } + } + + protected function _putresourcedict() + { + $this->_put('/ProcSet [/PDF /Text /ImageB /ImageC /ImageI]'); + $this->_put('/Font <<'); + foreach ($this->fonts as $font) { + $this->_put('/F'.$font['i'].' '.$font['n'].' 0 R'); + } + $this->_put('>>'); + $this->_put('/XObject <<'); + $this->_putxobjectdict(); + $this->_put('>>'); + } + + protected function _putresources() + { + $this->_putfonts(); + $this->_putimages(); + // Resource dictionary + $this->_newobj(2); + $this->_put('<<'); + $this->_putresourcedict(); + $this->_put('>>'); + $this->_put('endobj'); + } + + protected function _putinfo() + { + $date = @date('YmdHisO', $this->CreationDate); + $this->metadata['CreationDate'] = 'D:'.substr($date, 0, -2)."'".substr($date, -2)."'"; + foreach ($this->metadata as $key => $value) { + $this->_put('/'.$key.' '.$this->_textstring($value)); + } + } + + protected function _putcatalog() + { + $n = $this->PageInfo[1]['n']; + $this->_put('/Type /Catalog'); + $this->_put('/Pages 1 0 R'); + if ($this->ZoomMode == 'fullpage') { + $this->_put('/OpenAction ['.$n.' 0 R /Fit]'); + } elseif ($this->ZoomMode == 'fullwidth') { + $this->_put('/OpenAction ['.$n.' 0 R /FitH null]'); + } elseif ($this->ZoomMode == 'real') { + $this->_put('/OpenAction ['.$n.' 0 R /XYZ null null 1]'); + } elseif (! is_string($this->ZoomMode)) { + $this->_put('/OpenAction ['.$n.' 0 R /XYZ null null '.sprintf('%.2F', $this->ZoomMode / 100).']'); + } + if ($this->LayoutMode == 'single') { + $this->_put('/PageLayout /SinglePage'); + } elseif ($this->LayoutMode == 'continuous') { + $this->_put('/PageLayout /OneColumn'); + } elseif ($this->LayoutMode == 'two') { + $this->_put('/PageLayout /TwoColumnLeft'); + } + } + + protected function _putheader() + { + $this->_put('%PDF-'.$this->PDFVersion); + } + + protected function _puttrailer() + { + $this->_put('/Size '.($this->n + 1)); + $this->_put('/Root '.$this->n.' 0 R'); + $this->_put('/Info '.($this->n - 1).' 0 R'); + } + + protected function _enddoc() + { + $this->CreationDate = time(); + $this->_putheader(); + $this->_putpages(); + $this->_putresources(); + // Info + $this->_newobj(); + $this->_put('<<'); + $this->_putinfo(); + $this->_put('>>'); + $this->_put('endobj'); + // Catalog + $this->_newobj(); + $this->_put('<<'); + $this->_putcatalog(); + $this->_put('>>'); + $this->_put('endobj'); + // Cross-ref + $offset = $this->_getoffset(); + $this->_put('xref'); + $this->_put('0 '.($this->n + 1)); + $this->_put('0000000000 65535 f '); + for ($i = 1; $i <= $this->n; $i++) { + $this->_put(sprintf('%010d 00000 n ', $this->offsets[$i])); + } + // Trailer + $this->_put('trailer'); + $this->_put('<<'); + $this->_puttrailer(); + $this->_put('>>'); + $this->_put('startxref'); + $this->_put($offset); + $this->_put('%%EOF'); + $this->state = 3; + } +} diff --git a/bootstrap/app.php b/bootstrap/app.php index 4f387ec5..0b4f4ca5 100644 --- a/bootstrap/app.php +++ b/bootstrap/app.php @@ -1,11 +1,27 @@ withRouting( @@ -15,39 +31,39 @@ return Application::configure(basePath: dirname(__DIR__)) health: '/up', ) ->withMiddleware(function (Middleware $middleware): void { - $middleware->append(\App\Http\Middleware\SecurityHeaders::class); + $middleware->append(SecurityHeaders::class); $middleware->alias([ // JWT auth - 'jwt.auth' => \PHPOpenSourceSaver\JWTAuth\Http\Middleware\Authenticate::class, - 'jwt.refresh' => \PHPOpenSourceSaver\JWTAuth\Http\Middleware\RefreshToken::class, - 'api.jwt' => \App\Http\Middleware\ApiJwtAuth::class, + 'jwt.auth' => Authenticate::class, + 'jwt.refresh' => RefreshToken::class, + 'api.jwt' => ApiJwtAuth::class, // Multi-guard auth (both the new and legacy alias point at the same class) - 'auth.multi' => \App\Http\Middleware\MultiAuth::class, - 'multi.auth' => \App\Http\Middleware\MultiAuth::class, + 'auth.multi' => MultiAuth::class, + 'multi.auth' => MultiAuth::class, // Permission / role gates - 'perm' => \App\Http\Middleware\RequirePermission::class, - 'require.permission' => \App\Http\Middleware\RequirePermission::class, - 'admin.access' => \App\Http\Middleware\EnsurePrintRequestsAdminAccess::class, - 'parent.access' => \App\Http\Middleware\EnsureParentProgressAccess::class, - 'parent.progress' => \App\Http\Middleware\EnsureParentProgressAccess::class, - 'teacher.progress' => \App\Http\Middleware\EnsureClassProgressTeacherPortalAccess::class, - 'class_progress.teacher' => \App\Http\Middleware\EnsureClassProgressTeacherPortalAccess::class, - 'school_year.editable' => \App\Http\Middleware\EnsureSchoolYearEditable::class, - 'print_requests.teacher' => \App\Http\Middleware\EnsurePrintRequestsTeacherAccess::class, - 'print_requests.admin' => \App\Http\Middleware\EnsurePrintRequestsAdminAccess::class, - 'badge_scan.logs' => \App\Http\Middleware\EnsureBadgeScanLogsAccess::class, + 'perm' => RequirePermission::class, + 'require.permission' => RequirePermission::class, + 'admin.access' => EnsurePrintRequestsAdminAccess::class, + 'parent.access' => EnsureParentProgressAccess::class, + 'parent.progress' => EnsureParentProgressAccess::class, + 'teacher.progress' => EnsureClassProgressTeacherPortalAccess::class, + 'class_progress.teacher' => EnsureClassProgressTeacherPortalAccess::class, + 'school_year.editable' => EnsureSchoolYearEditable::class, + 'print_requests.teacher' => EnsurePrintRequestsTeacherAccess::class, + 'print_requests.admin' => EnsurePrintRequestsAdminAccess::class, + 'badge_scan.logs' => EnsureBadgeScanLogsAccess::class, // Docs + teacher portal - 'auth.docs' => \App\Http\Middleware\ApiDocsAuth::class, - 'teacher.portal.auth' => \App\Http\Middleware\TeacherPortalAuthenticate::class, + 'auth.docs' => ApiDocsAuth::class, + 'teacher.portal.auth' => TeacherPortalAuthenticate::class, // Utility - 'timezone' => \App\Http\Middleware\PrimeTimezone::class, - 'prime.timezone' => \App\Http\Middleware\PrimeTimezone::class, - 'cleanup.scheduler' => \App\Http\Middleware\CleanupScheduler::class, + 'timezone' => PrimeTimezone::class, + 'prime.timezone' => PrimeTimezone::class, + 'cleanup.scheduler' => CleanupScheduler::class, ]); $middleware->redirectGuestsTo(function (Request $request) { diff --git a/bootstrap/providers.php b/bootstrap/providers.php index 86f5297a..78c06f72 100644 --- a/bootstrap/providers.php +++ b/bootstrap/providers.php @@ -1,6 +1,9 @@ [ 'users' => [ 'driver' => 'eloquent', - 'model' => env('AUTH_MODEL', App\Models\User::class), + 'model' => env('AUTH_MODEL', User::class), ], // 'users' => [ diff --git a/config/jwt.php b/config/jwt.php index d131f6e4..50871625 100644 --- a/config/jwt.php +++ b/config/jwt.php @@ -1,5 +1,8 @@ PHPOpenSourceSaver\JWTAuth\Providers\JWT\Lcobucci::class, + 'jwt' => Lcobucci::class, /* |-------------------------------------------------------------------------- @@ -306,7 +309,7 @@ return [ | */ - 'auth' => PHPOpenSourceSaver\JWTAuth\Providers\Auth\Illuminate::class, + 'auth' => Illuminate::class, /* |-------------------------------------------------------------------------- diff --git a/config/sanctum.php b/config/sanctum.php index 44527d68..cde73cff 100644 --- a/config/sanctum.php +++ b/config/sanctum.php @@ -1,5 +1,8 @@ [ - 'authenticate_session' => Laravel\Sanctum\Http\Middleware\AuthenticateSession::class, - 'encrypt_cookies' => Illuminate\Cookie\Middleware\EncryptCookies::class, - 'validate_csrf_token' => Illuminate\Foundation\Http\Middleware\ValidateCsrfToken::class, + 'authenticate_session' => AuthenticateSession::class, + 'encrypt_cookies' => EncryptCookies::class, + 'validate_csrf_token' => ValidateCsrfToken::class, ], ]; diff --git a/database/factories/ClassSectionFactory.php b/database/factories/ClassSectionFactory.php index 69a733bd..da024b77 100644 --- a/database/factories/ClassSectionFactory.php +++ b/database/factories/ClassSectionFactory.php @@ -6,7 +6,7 @@ use App\Models\ClassSection; use Illuminate\Database\Eloquent\Factories\Factory; /** - * @extends \Illuminate\Database\Eloquent\Factories\Factory<\App\Models\ClassSection> + * @extends Factory */ class ClassSectionFactory extends Factory { @@ -19,7 +19,7 @@ class ClassSectionFactory extends Factory return [ 'class_id' => fake()->numberBetween(1, 20), 'class_section_id' => $sectionId, - 'class_section_name' => (string) $sectionId . fake()->randomElement(['A', 'B', 'C']), + 'class_section_name' => (string) $sectionId.fake()->randomElement(['A', 'B', 'C']), 'semester' => 'Fall', 'school_year' => '2025-2026', ]; diff --git a/database/factories/StudentFactory.php b/database/factories/StudentFactory.php index e50def9f..4bd700bd 100644 --- a/database/factories/StudentFactory.php +++ b/database/factories/StudentFactory.php @@ -6,7 +6,7 @@ use App\Models\Student; use Illuminate\Database\Eloquent\Factories\Factory; /** - * @extends \Illuminate\Database\Eloquent\Factories\Factory<\App\Models\Student> + * @extends Factory */ class StudentFactory extends Factory { @@ -15,13 +15,13 @@ class StudentFactory extends Factory public function definition(): array { return [ - 'school_id' => 'S-' . fake()->numberBetween(100, 999), + 'school_id' => 'S-'.fake()->numberBetween(100, 999), 'firstname' => fake()->firstName(), 'lastname' => fake()->lastName(), 'age' => fake()->numberBetween(6, 18), 'gender' => fake()->randomElement(['Male', 'Female']), 'is_active' => 1, - 'registration_grade' => 'Grade ' . fake()->numberBetween(1, 8), + 'registration_grade' => 'Grade '.fake()->numberBetween(1, 8), 'is_new' => 1, 'photo_consent' => 1, 'parent_id' => fake()->numberBetween(1, 50), diff --git a/database/factories/UserFactory.php b/database/factories/UserFactory.php index 5212a445..6da14826 100644 --- a/database/factories/UserFactory.php +++ b/database/factories/UserFactory.php @@ -2,12 +2,12 @@ namespace Database\Factories; +use App\Models\User; use Illuminate\Database\Eloquent\Factories\Factory; use Illuminate\Support\Facades\Hash; -use Illuminate\Support\Str; /** - * @extends \Illuminate\Database\Eloquent\Factories\Factory<\App\Models\User> + * @extends Factory */ class UserFactory extends Factory { diff --git a/database/migrations/0001_01_01_000000_create_users_table.php b/database/migrations/0001_01_01_000000_create_users_table.php index eaafe5d6..e3adbe14 100644 --- a/database/migrations/0001_01_01_000000_create_users_table.php +++ b/database/migrations/0001_01_01_000000_create_users_table.php @@ -1,7 +1,6 @@ string('email')->nullable()->after('name'); } - if (!Schema::hasColumn('suppliers', 'phone')) { + if (! Schema::hasColumn('suppliers', 'phone')) { $table->string('phone')->nullable()->after('email'); } - if (!Schema::hasColumn('suppliers', 'address')) { + if (! Schema::hasColumn('suppliers', 'address')) { $table->string('address')->nullable()->after('phone'); } - if (!Schema::hasColumn('suppliers', 'notes')) { + if (! Schema::hasColumn('suppliers', 'notes')) { $table->text('notes')->nullable()->after('address'); } }); diff --git a/database/migrations/2026_06_04_120000_create_certificate_records_table.php b/database/migrations/2026_06_04_120000_create_certificate_records_table.php index a5593ded..4da26ef1 100644 --- a/database/migrations/2026_06_04_120000_create_certificate_records_table.php +++ b/database/migrations/2026_06_04_120000_create_certificate_records_table.php @@ -1,5 +1,6 @@ string('lifecycle_mode', 20)->default('legacy'); } - if (!Schema::hasColumn($table, 'lifecycle_policy_version')) { + if (! Schema::hasColumn($table, 'lifecycle_policy_version')) { $t->string('lifecycle_policy_version', 30)->default('legacy_v1'); } }); } - if (!Schema::hasTable('section_placement_batches')) { + if (! Schema::hasTable('section_placement_batches')) { Schema::create('section_placement_batches', function (Blueprint $table) { $table->id(); $table->string('from_school_year', 25); @@ -45,7 +45,7 @@ return new class extends Migration }); } - if (!Schema::hasTable('section_placement_batch_students')) { + if (! Schema::hasTable('section_placement_batch_students')) { Schema::create('section_placement_batch_students', function (Blueprint $table) { $table->id(); $table->unsignedBigInteger('batch_id'); @@ -70,7 +70,7 @@ return new class extends Migration if (Schema::hasTable('configuration')) { $exists = DB::table('configuration')->where('config_key', 'promotion.section_capacity')->exists(); - if (!$exists) { + if (! $exists) { DB::table('configuration')->insert([ 'config_key' => 'promotion.section_capacity', 'config_value' => '20', diff --git a/database/migrations/2026_06_04_200000_add_badge_scanning_list_nav_item.php b/database/migrations/2026_06_04_200000_add_badge_scanning_list_nav_item.php index 458d827b..3cdcbf0c 100644 --- a/database/migrations/2026_06_04_200000_add_badge_scanning_list_nav_item.php +++ b/database/migrations/2026_06_04_200000_add_badge_scanning_list_nav_item.php @@ -8,7 +8,7 @@ return new class extends Migration { public function up(): void { - if (!Schema::hasTable('nav_items') || !Schema::hasTable('role_nav_items') || !Schema::hasTable('roles')) { + if (! Schema::hasTable('nav_items') || ! Schema::hasTable('role_nav_items') || ! Schema::hasTable('roles')) { return; } @@ -26,7 +26,7 @@ return new class extends Migration ->where('url', $url) ->value('id'); - if (!$navId) { + if (! $navId) { $navId = DB::table('nav_items')->insertGetId([ 'menu_parent_id' => $parentId ?: null, 'label' => $label, @@ -70,7 +70,7 @@ return new class extends Migration ->where('nav_item_id', $navId) ->exists(); - if (!$exists) { + if (! $exists) { DB::table('role_nav_items')->insert([ 'role_id' => $roleId, 'nav_item_id' => $navId, @@ -83,7 +83,7 @@ return new class extends Migration public function down(): void { - if (!Schema::hasTable('nav_items')) { + if (! Schema::hasTable('nav_items')) { return; } diff --git a/database/migrations/2026_06_04_210000_add_strong_grading_backward_compatibility.php b/database/migrations/2026_06_04_210000_add_strong_grading_backward_compatibility.php index f54c8795..639e67f3 100644 --- a/database/migrations/2026_06_04_210000_add_strong_grading_backward_compatibility.php +++ b/database/migrations/2026_06_04_210000_add_strong_grading_backward_compatibility.php @@ -10,24 +10,24 @@ return new class extends Migration public function up(): void { foreach (['homework', 'quiz', 'project', 'participation', 'midterm_exam', 'final_exam'] as $table) { - if (!Schema::hasTable($table)) { + if (! Schema::hasTable($table)) { continue; } Schema::table($table, function (Blueprint $tableBlueprint) use ($table) { - if (!Schema::hasColumn($table, 'max_points')) { + if (! Schema::hasColumn($table, 'max_points')) { $tableBlueprint->decimal('max_points', 8, 2)->default(100)->after('score'); } - if (!Schema::hasColumn($table, 'status')) { + if (! Schema::hasColumn($table, 'status')) { $tableBlueprint->string('status', 30)->nullable()->after('max_points'); } - if (!Schema::hasColumn($table, 'excused_reason')) { + if (! Schema::hasColumn($table, 'excused_reason')) { $tableBlueprint->text('excused_reason')->nullable()->after('status'); } - if (!Schema::hasColumn($table, 'locked_at')) { + if (! Schema::hasColumn($table, 'locked_at')) { $tableBlueprint->timestamp('locked_at')->nullable()->after('excused_reason'); } - if (!Schema::hasColumn($table, 'locked_by')) { + if (! Schema::hasColumn($table, 'locked_by')) { $tableBlueprint->unsignedBigInteger('locked_by')->nullable()->after('locked_at'); } }); @@ -41,13 +41,13 @@ return new class extends Migration if (Schema::hasTable('semester_scores')) { Schema::table('semester_scores', function (Blueprint $table) { - if (!Schema::hasColumn('semester_scores', 'calculation_mode')) { + if (! Schema::hasColumn('semester_scores', 'calculation_mode')) { $table->string('calculation_mode', 30)->default('legacy')->after('semester_score'); } - if (!Schema::hasColumn('semester_scores', 'calculation_policy_version')) { + if (! Schema::hasColumn('semester_scores', 'calculation_policy_version')) { $table->string('calculation_policy_version', 50)->default('legacy_v1')->after('calculation_mode'); } - if (!Schema::hasColumn('semester_scores', 'snapshot_id')) { + if (! Schema::hasColumn('semester_scores', 'snapshot_id')) { $table->unsignedBigInteger('snapshot_id')->nullable()->after('calculation_policy_version'); } }); @@ -56,7 +56,7 @@ return new class extends Migration DB::table('semester_scores')->whereNull('calculation_policy_version')->update(['calculation_policy_version' => 'legacy_v1']); } - if (!Schema::hasTable('semester_score_snapshots')) { + if (! Schema::hasTable('semester_score_snapshots')) { Schema::create('semester_score_snapshots', function (Blueprint $table) { $table->id(); $table->unsignedBigInteger('student_id'); @@ -96,7 +96,7 @@ return new class extends Migration } foreach (['homework', 'quiz', 'project', 'participation', 'midterm_exam', 'final_exam'] as $tableName) { - if (!Schema::hasTable($tableName)) { + if (! Schema::hasTable($tableName)) { continue; } Schema::table($tableName, function (Blueprint $table) use ($tableName) { diff --git a/database/migrations/2026_06_04_230000_add_finance_lifecycle_tables.php b/database/migrations/2026_06_04_230000_add_finance_lifecycle_tables.php index 9bb7c9eb..140c4e7f 100644 --- a/database/migrations/2026_06_04_230000_add_finance_lifecycle_tables.php +++ b/database/migrations/2026_06_04_230000_add_finance_lifecycle_tables.php @@ -8,7 +8,7 @@ return new class extends Migration { public function up(): void { - if (!Schema::hasTable('finance_follow_up_notes')) { + if (! Schema::hasTable('finance_follow_up_notes')) { Schema::create('finance_follow_up_notes', function (Blueprint $table) { $table->id(); $table->unsignedBigInteger('parent_id')->index(); @@ -26,7 +26,7 @@ return new class extends Migration }); } - if (!Schema::hasTable('finance_balance_carryforwards')) { + if (! Schema::hasTable('finance_balance_carryforwards')) { Schema::create('finance_balance_carryforwards', function (Blueprint $table) { $table->id(); $table->unsignedBigInteger('parent_id')->index(); @@ -52,7 +52,7 @@ return new class extends Migration }); } - if (!Schema::hasTable('invoice_installment_plans')) { + if (! Schema::hasTable('invoice_installment_plans')) { Schema::create('invoice_installment_plans', function (Blueprint $table) { $table->id(); $table->unsignedBigInteger('invoice_id')->index(); @@ -68,7 +68,7 @@ return new class extends Migration }); } - if (!Schema::hasTable('invoice_installments')) { + if (! Schema::hasTable('invoice_installments')) { Schema::create('invoice_installments', function (Blueprint $table) { $table->id(); $table->unsignedBigInteger('installment_plan_id')->index(); @@ -85,7 +85,7 @@ return new class extends Migration }); } - if (!Schema::hasTable('payment_installment_allocations')) { + if (! Schema::hasTable('payment_installment_allocations')) { Schema::create('payment_installment_allocations', function (Blueprint $table) { $table->id(); $table->unsignedBigInteger('payment_id')->index(); @@ -96,7 +96,7 @@ return new class extends Migration }); } - if (!Schema::hasTable('finance_notification_logs')) { + if (! Schema::hasTable('finance_notification_logs')) { Schema::create('finance_notification_logs', function (Blueprint $table) { $table->id(); $table->unsignedBigInteger('parent_id')->nullable()->index(); @@ -119,7 +119,7 @@ return new class extends Migration }); } - if (!Schema::hasTable('finance_notification_preferences')) { + if (! Schema::hasTable('finance_notification_preferences')) { Schema::create('finance_notification_preferences', function (Blueprint $table) { $table->id(); $table->unsignedBigInteger('parent_id')->unique(); @@ -132,7 +132,7 @@ return new class extends Migration }); } - if (!Schema::hasTable('finance_receipt_sequences')) { + if (! Schema::hasTable('finance_receipt_sequences')) { Schema::create('finance_receipt_sequences', function (Blueprint $table) { $table->id(); $table->string('school_year')->unique(); @@ -142,7 +142,7 @@ return new class extends Migration }); } - if (!Schema::hasTable('payment_carryforward_allocations')) { + if (! Schema::hasTable('payment_carryforward_allocations')) { Schema::create('payment_carryforward_allocations', function (Blueprint $table) { $table->id(); $table->unsignedBigInteger('payment_id')->index(); diff --git a/database/migrations/2026_06_05_000000_create_school_year_closure_tables.php b/database/migrations/2026_06_05_000000_create_school_year_closure_tables.php index 5c0a638a..6206ba38 100644 --- a/database/migrations/2026_06_05_000000_create_school_year_closure_tables.php +++ b/database/migrations/2026_06_05_000000_create_school_year_closure_tables.php @@ -9,7 +9,7 @@ return new class extends Migration { public function up(): void { - if (!Schema::hasTable('school_years')) { + if (! Schema::hasTable('school_years')) { Schema::create('school_years', function (Blueprint $table) { $table->id(); $table->string('name', 50)->unique(); @@ -23,7 +23,7 @@ return new class extends Migration }); } - if (!Schema::hasTable('parent_accounts')) { + if (! Schema::hasTable('parent_accounts')) { Schema::create('parent_accounts', function (Blueprint $table) { $table->id(); $table->unsignedBigInteger('parent_id')->index(); @@ -35,7 +35,7 @@ return new class extends Migration }); } - if (!Schema::hasTable('parent_balance_transfers')) { + if (! Schema::hasTable('parent_balance_transfers')) { Schema::create('parent_balance_transfers', function (Blueprint $table) { $table->id(); $table->unsignedBigInteger('parent_id')->index(); @@ -51,7 +51,7 @@ return new class extends Migration }); } - if (!Schema::hasTable('audit_logs')) { + if (! Schema::hasTable('audit_logs')) { Schema::create('audit_logs', function (Blueprint $table) { $table->id(); $table->unsignedBigInteger('user_id')->nullable()->index(); @@ -65,7 +65,7 @@ return new class extends Migration }); } - if (Schema::hasTable('invoices') && !Schema::hasColumn('invoices', 'balance_transfer_id')) { + if (Schema::hasTable('invoices') && ! Schema::hasColumn('invoices', 'balance_transfer_id')) { Schema::table('invoices', function (Blueprint $table) { $table->unsignedBigInteger('balance_transfer_id')->nullable()->index()->after('semester'); }); @@ -90,7 +90,7 @@ return new class extends Migration private function seedCurrentSchoolYear(): void { - if (!Schema::hasTable('configuration') || !Schema::hasTable('school_years')) { + if (! Schema::hasTable('configuration') || ! Schema::hasTable('school_years')) { return; } @@ -111,6 +111,7 @@ return new class extends Migration 'is_current' => true, 'updated_at' => now(), ]); + return; } diff --git a/database/migrations/2026_06_05_000000_sort_nav_items_alphabetically.php b/database/migrations/2026_06_05_000000_sort_nav_items_alphabetically.php index 4672ca80..505dd65a 100644 --- a/database/migrations/2026_06_05_000000_sort_nav_items_alphabetically.php +++ b/database/migrations/2026_06_05_000000_sort_nav_items_alphabetically.php @@ -8,7 +8,7 @@ return new class extends Migration { public function up(): void { - if (!Schema::hasTable('nav_items')) { + if (! Schema::hasTable('nav_items')) { return; } diff --git a/database/migrations/2026_06_11_074642_add_inventory_item_id_to_purchase_order_items.php b/database/migrations/2026_06_11_074642_add_inventory_item_id_to_purchase_order_items.php index 46e4675e..9fe09787 100644 --- a/database/migrations/2026_06_11_074642_add_inventory_item_id_to_purchase_order_items.php +++ b/database/migrations/2026_06_11_074642_add_inventory_item_id_to_purchase_order_items.php @@ -13,7 +13,7 @@ return new class extends Migration public function up(): void { Schema::table('purchase_order_items', function (Blueprint $table) { - if (!Schema::hasColumn('purchase_order_items', 'inventory_item_id')) { + if (! Schema::hasColumn('purchase_order_items', 'inventory_item_id')) { $table->unsignedInteger('inventory_item_id')->nullable()->after('supply_id'); } }); diff --git a/routes/api.php b/routes/api.php index 693af476..bdfb68b8 100644 --- a/routes/api.php +++ b/routes/api.php @@ -1,138 +1,131 @@ middleware('throttle:10,1'); Route::post('register', [RegisterController::class, 'store'])->middleware('throttle:5,1'); @@ -613,8 +606,8 @@ Route::prefix('v1')->group(function () { Route::get('teacher/select-semester', function () { return response()->json([ 'ok' => true, - 'school_year' => \App\Models\Configuration::getConfig('school_year'), - 'semester' => \App\Models\Configuration::getConfig('semester'), + 'school_year' => Configuration::getConfig('school_year'), + 'semester' => Configuration::getConfig('semester'), 'semester_options' => ['Fall', 'Spring'], ]); }); @@ -1015,7 +1008,7 @@ Route::prefix('v1')->group(function () { Route::post('event-charges/{eventCharge}/void', [EventChargeController::class, 'void'])->whereNumber('eventCharge'); Route::post('event-charges/{eventCharge}/attach-to-invoice', [EventChargeController::class, 'attachToInvoice'])->whereNumber('eventCharge'); - Route::apiResource('installment-plans', InstallmentPlanController::class)->only(['index','show']); + Route::apiResource('installment-plans', InstallmentPlanController::class)->only(['index', 'show']); Route::post('invoices/{invoice}/installment-plans', [InstallmentPlanController::class, 'store'])->whereNumber('invoice'); Route::post('installment-plans/{plan}/activate', [InstallmentPlanController::class, 'activate'])->whereNumber('plan'); Route::post('installment-plans/{plan}/cancel', [InstallmentPlanController::class, 'cancel'])->whereNumber('plan'); diff --git a/tests/Concerns/AssertsE2EApiResponses.php b/tests/Concerns/AssertsE2EApiResponses.php index c9b754cc..b42eb071 100644 --- a/tests/Concerns/AssertsE2EApiResponses.php +++ b/tests/Concerns/AssertsE2EApiResponses.php @@ -11,19 +11,19 @@ trait AssertsE2EApiResponses $this->assertLessThan( 500, $response->getStatusCode(), - $context . ' should return a controlled response, not a 5xx crash. Body: ' . $response->getContent() + $context.' should return a controlled response, not a 5xx crash. Body: '.$response->getContent() ); } /** - * @param array $statuses + * @param array $statuses */ protected function assertStatusIn(TestResponse $response, array $statuses, string $context): void { $this->assertContains( $response->getStatusCode(), $statuses, - $context . ' returned unexpected status ' . $response->getStatusCode() . '. Body: ' . $response->getContent() + $context.' returned unexpected status '.$response->getStatusCode().'. Body: '.$response->getContent() ); } diff --git a/tests/Concerns/CreatesApiTestUsers.php b/tests/Concerns/CreatesApiTestUsers.php index 5f22084b..0d7a1ea2 100644 --- a/tests/Concerns/CreatesApiTestUsers.php +++ b/tests/Concerns/CreatesApiTestUsers.php @@ -68,7 +68,7 @@ trait CreatesApiTestUsers 'status' => 'Active', 'is_verified' => 1, 'is_suspended' => 0, - 'email' => $roleName . '-api-test-' . str_replace('.', '', uniqid('', true)) . '@example.test', + 'email' => $roleName.'-api-test-'.str_replace('.', '', uniqid('', true)).'@example.test', ], $overrides)); $role = $roles[$roleName] ?? Role::query()->where('name', $roleName)->firstOrFail(); diff --git a/tests/Concerns/SeedsAdministratorApiConfig.php b/tests/Concerns/SeedsAdministratorApiConfig.php index 9eac17ad..d1f8e010 100644 --- a/tests/Concerns/SeedsAdministratorApiConfig.php +++ b/tests/Concerns/SeedsAdministratorApiConfig.php @@ -22,4 +22,4 @@ trait SeedsAdministratorApiConfig ['config_value' => $semester] ); } -} \ No newline at end of file +} diff --git a/tests/Concerns/SeedsE2ETestFixtures.php b/tests/Concerns/SeedsE2ETestFixtures.php index d3376cb6..59bda107 100644 --- a/tests/Concerns/SeedsE2ETestFixtures.php +++ b/tests/Concerns/SeedsE2ETestFixtures.php @@ -18,7 +18,9 @@ trait SeedsE2ETestFixtures use CreatesApiTestUsers; protected const E2E_SCHOOL_YEAR = '2025-2026'; + protected const E2E_PREV_SCHOOL_YEAR = '2024-2025'; + protected const E2E_SEMESTER = 'Fall'; protected function seedE2EConfiguration(): void @@ -108,18 +110,18 @@ trait SeedsE2ETestFixtures $teacher = $this->createApiUserWithRole('teacher', [ 'firstname' => 'Tara', 'lastname' => 'Teacher', - 'email' => 'e2e.teacher.' . uniqid('', true) . '@example.test', + 'email' => 'e2e.teacher.'.uniqid('', true).'@example.test', ]); $this->seedTeacherClassAssignment($teacher->id, $classSectionId); $parent = $this->createApiUserWithRole('parent', [ 'firstname' => 'Parent', 'lastname' => 'Fixture', - 'email' => 'e2e.parent.' . uniqid('', true) . '@example.test', + 'email' => 'e2e.parent.'.uniqid('', true).'@example.test', ]); $studentId = DB::table('students')->insertGetId([ - 'school_id' => 'S-E2E-' . random_int(1000, 9999), + 'school_id' => 'S-E2E-'.random_int(1000, 9999), 'firstname' => 'Kai', 'lastname' => 'Student', 'dob' => '2014-09-01', @@ -221,7 +223,7 @@ trait SeedsE2ETestFixtures protected function postEnrollmentStatuses(array $studentStatusMap): TestResponse { return $this->post( - '/api/v1/administrator/enrollment-withdrawal/update-statuses?' . + '/api/v1/administrator/enrollment-withdrawal/update-statuses?'. http_build_query(['enrollment_status' => $studentStatusMap]), [] ); @@ -231,7 +233,7 @@ trait SeedsE2ETestFixtures { return DB::table('invoices')->insertGetId(array_merge([ 'parent_id' => $parentId, - 'invoice_number' => 'INV-E2E-' . random_int(1000, 9999), + 'invoice_number' => 'INV-E2E-'.random_int(1000, 9999), 'total_amount' => 500.00, 'balance' => 500.00, 'paid_amount' => 0, @@ -250,7 +252,7 @@ trait SeedsE2ETestFixtures { return DB::table('invoices')->insertGetId([ 'parent_id' => $parentId, - 'invoice_number' => 'INV-PRIOR-' . random_int(1000, 9999), + 'invoice_number' => 'INV-PRIOR-'.random_int(1000, 9999), 'total_amount' => 200.00, 'balance' => 150.00, 'paid_amount' => 50.00, @@ -268,7 +270,7 @@ trait SeedsE2ETestFixtures protected function seedDiscountVoucher(): int { return DB::table('discount_vouchers')->insertGetId([ - 'code' => 'E2E10-' . random_int(100, 999), + 'code' => 'E2E10-'.random_int(100, 999), 'discount_type' => 'fixed', 'discount_value' => 50, 'max_uses' => 100, diff --git a/tests/Feature/Api/ApiAuthenticationAndAuthorizationTest.php b/tests/Feature/Api/ApiAuthenticationAndAuthorizationTest.php index ebe6a27a..edff0530 100644 --- a/tests/Feature/Api/ApiAuthenticationAndAuthorizationTest.php +++ b/tests/Feature/Api/ApiAuthenticationAndAuthorizationTest.php @@ -13,7 +13,6 @@ class ApiAuthenticationAndAuthorizationTest extends TestCase use CreatesApiTestUsers; use RefreshDatabase; - public function test_api_login_returns_token_and_user_object(): void { $user = $this->createApiUserWithRole('teacher', [ @@ -103,7 +102,7 @@ class ApiAuthenticationAndAuthorizationTest extends TestCase foreach ($this->sampleProtectedRoutes() as $route) { $method = $this->primaryMethod($route); - $uri = '/' . $this->uriWithPlaceholders($route->uri()); + $uri = '/'.$this->uriWithPlaceholders($route->uri()); $response = $this->json($method, $uri); $status = $response->getStatusCode(); @@ -113,7 +112,7 @@ class ApiAuthenticationAndAuthorizationTest extends TestCase } } - $this->assertSame([], $failures, "Anonymous requests reached unexpected responses:\n" . implode("\n", $failures)); + $this->assertSame([], $failures, "Anonymous requests reached unexpected responses:\n".implode("\n", $failures)); } /** diff --git a/tests/Feature/Api/ApiPreferencesFeatureTest.php b/tests/Feature/Api/ApiPreferencesFeatureTest.php index b01d52e4..041058e2 100644 --- a/tests/Feature/Api/ApiPreferencesFeatureTest.php +++ b/tests/Feature/Api/ApiPreferencesFeatureTest.php @@ -47,7 +47,6 @@ class ApiPreferencesFeatureTest extends TestCase ->assertJsonPath('status', true); } - public function test_non_admin_cannot_list_or_manage_another_users_preferences(): void { $owner = $this->createApiUserWithRole('teacher'); diff --git a/tests/Feature/Api/ApiPublicEndpointsTest.php b/tests/Feature/Api/ApiPublicEndpointsTest.php index 990670a7..7b78015f 100644 --- a/tests/Feature/Api/ApiPublicEndpointsTest.php +++ b/tests/Feature/Api/ApiPublicEndpointsTest.php @@ -36,7 +36,7 @@ class ApiPublicEndpointsTest extends TestCase public function test_public_frontend_content_endpoints_do_not_server_error(): void { foreach (['/', 'facility', 'team', 'call-to-action', 'testimonial', 'not-found'] as $path) { - $uri = '/api/v1/frontend' . ($path === '/' ? '' : '/' . $path); + $uri = '/api/v1/frontend'.($path === '/' ? '' : '/'.$path); $response = $this->getJson($uri); $this->assertLessThan(500, $response->getStatusCode(), "$uri returned a server error"); diff --git a/tests/Feature/Api/ApiRolePermissionFeatureTest.php b/tests/Feature/Api/ApiRolePermissionFeatureTest.php index 99e6d6a8..de0c6dc1 100644 --- a/tests/Feature/Api/ApiRolePermissionFeatureTest.php +++ b/tests/Feature/Api/ApiRolePermissionFeatureTest.php @@ -2,8 +2,6 @@ namespace Tests\Feature\Api; -use App\Models\Permission; -use App\Models\Role; use App\Models\User; use Illuminate\Foundation\Testing\RefreshDatabase; use Tests\Concerns\CreatesApiTestUsers; diff --git a/tests/Feature/Api/ApiRouteContractTest.php b/tests/Feature/Api/ApiRouteContractTest.php index 35df3ec7..70a87260 100644 --- a/tests/Feature/Api/ApiRouteContractTest.php +++ b/tests/Feature/Api/ApiRouteContractTest.php @@ -24,11 +24,11 @@ class ApiRouteContractTest extends TestCase [$class, $method] = explode('@', $action, 2); if (! class_exists($class) || ! method_exists($class, $method)) { - $missing[] = implode('|', $route->methods()) . ' ' . $route->uri() . ' -> ' . $action; + $missing[] = implode('|', $route->methods()).' '.$route->uri().' -> '.$action; } } - $this->assertSame([], $missing, "These API routes reference missing controllers or methods:\n" . implode("\n", $missing)); + $this->assertSame([], $missing, "These API routes reference missing controllers or methods:\n".implode("\n", $missing)); } public function test_api_routes_do_not_duplicate_method_and_uri_pairs(): void @@ -42,7 +42,7 @@ class ApiRouteContractTest extends TestCase continue; } - $key = $method . ' ' . $route->uri(); + $key = $method.' '.$route->uri(); if (isset($seen[$key])) { $duplicates[] = $key; } @@ -90,11 +90,11 @@ class ApiRouteContractTest extends TestCase }); if (! $hasProtection) { - $unprotected[] = implode('|', $methods) . ' ' . $route->uri(); + $unprotected[] = implode('|', $methods).' '.$route->uri(); } } - $this->assertSame([], $unprotected, "Mutating API routes without an obvious auth/throttle middleware:\n" . implode("\n", $unprotected)); + $this->assertSame([], $unprotected, "Mutating API routes without an obvious auth/throttle middleware:\n".implode("\n", $unprotected)); } /** diff --git a/tests/Feature/Api/ApiUseCaseCoverageMatrixTest.php b/tests/Feature/Api/ApiUseCaseCoverageMatrixTest.php index 84812b97..23b97766 100644 --- a/tests/Feature/Api/ApiUseCaseCoverageMatrixTest.php +++ b/tests/Feature/Api/ApiUseCaseCoverageMatrixTest.php @@ -26,14 +26,14 @@ class ApiUseCaseCoverageMatrixTest extends TestCase $uri = $route->uri(); if ($this->domainFor($uri) === null) { - $unmapped[] = implode('|', array_diff($route->methods(), ['HEAD'])) . ' ' . $uri; + $unmapped[] = implode('|', array_diff($route->methods(), ['HEAD'])).' '.$uri; } } $this->assertSame( [], $unmapped, - "API routes without an explicit use-case domain:\n" . implode("\n", $unmapped) . + "API routes without an explicit use-case domain:\n".implode("\n", $unmapped). "\n\nAdd the route to the matrix and then add/extend an E2E workflow test for that domain." ); } @@ -53,7 +53,7 @@ class ApiUseCaseCoverageMatrixTest extends TestCase $this->assertSame( [], $missing, - "Required use-case domains with no matching API route coverage:\n" . implode("\n", $missing) + "Required use-case domains with no matching API route coverage:\n".implode("\n", $missing) ); } @@ -63,7 +63,7 @@ class ApiUseCaseCoverageMatrixTest extends TestCase foreach ($this->protectedRoutes() as $route) { $method = $this->primaryMethod($route); - $uri = '/' . $this->uriWithSafePlaceholders($route->uri()); + $uri = '/'.$this->uriWithSafePlaceholders($route->uri()); $response = $this->json($method, $uri); $status = $response->getStatusCode(); @@ -76,7 +76,7 @@ class ApiUseCaseCoverageMatrixTest extends TestCase $this->assertSame( [], $failures, - "Anonymous requests reached protected API routes unexpectedly:\n" . implode("\n", $failures) + "Anonymous requests reached protected API routes unexpectedly:\n".implode("\n", $failures) ); } diff --git a/tests/Feature/Api/ApiUserManagementFeatureTest.php b/tests/Feature/Api/ApiUserManagementFeatureTest.php index 20058411..5c352464 100644 --- a/tests/Feature/Api/ApiUserManagementFeatureTest.php +++ b/tests/Feature/Api/ApiUserManagementFeatureTest.php @@ -2,7 +2,6 @@ namespace Tests\Feature\Api; -use App\Models\Role; use App\Models\User; use Illuminate\Foundation\Testing\RefreshDatabase; use Illuminate\Support\Facades\Hash; diff --git a/tests/Feature/Api/V1/Administrator/AdministratorAbsenceControllerTest.php b/tests/Feature/Api/V1/Administrator/AdministratorAbsenceControllerTest.php index cc354eab..203c6b48 100644 --- a/tests/Feature/Api/V1/Administrator/AdministratorAbsenceControllerTest.php +++ b/tests/Feature/Api/V1/Administrator/AdministratorAbsenceControllerTest.php @@ -121,4 +121,4 @@ class AdministratorAbsenceControllerTest extends TestCase 'dates' => ['2026-03-08'], ]); } -} \ No newline at end of file +} diff --git a/tests/Feature/Api/V1/Administrator/AdministratorEnrollmentControllerTest.php b/tests/Feature/Api/V1/Administrator/AdministratorEnrollmentControllerTest.php index 85c81f08..06c5daee 100644 --- a/tests/Feature/Api/V1/Administrator/AdministratorEnrollmentControllerTest.php +++ b/tests/Feature/Api/V1/Administrator/AdministratorEnrollmentControllerTest.php @@ -128,4 +128,4 @@ class AdministratorEnrollmentControllerTest extends TestCase 'message' => 'Enrollment statuses updated and notifications sent.', ]); } -} \ No newline at end of file +} diff --git a/tests/Feature/Api/V1/Administrator/AdministratorEnrollmentUpdateStatusesApiTest.php b/tests/Feature/Api/V1/Administrator/AdministratorEnrollmentUpdateStatusesApiTest.php index 15744d66..41e287ce 100644 --- a/tests/Feature/Api/V1/Administrator/AdministratorEnrollmentUpdateStatusesApiTest.php +++ b/tests/Feature/Api/V1/Administrator/AdministratorEnrollmentUpdateStatusesApiTest.php @@ -263,6 +263,7 @@ class AdministratorEnrollmentUpdateStatusesApiTest extends TestCase private function postUpdateStatuses(array $enrollmentStatus) { $query = http_build_query(['enrollment_status' => $enrollmentStatus]); - return $this->post('/api/v1/administrator/enrollment-withdrawal/update-statuses?' . $query, []); + + return $this->post('/api/v1/administrator/enrollment-withdrawal/update-statuses?'.$query, []); } } diff --git a/tests/Feature/Api/V1/Administrator/AdministratorNotificationControllerTest.php b/tests/Feature/Api/V1/Administrator/AdministratorNotificationControllerTest.php index 136c57e6..3b7dae6b 100644 --- a/tests/Feature/Api/V1/Administrator/AdministratorNotificationControllerTest.php +++ b/tests/Feature/Api/V1/Administrator/AdministratorNotificationControllerTest.php @@ -157,4 +157,4 @@ class AdministratorNotificationControllerTest extends TestCase 'message' => 'Print notification recipients updated.', ]); } -} \ No newline at end of file +} diff --git a/tests/Feature/Api/V1/Administrator/AdministratorTeacherSubmissionControllerTest.php b/tests/Feature/Api/V1/Administrator/AdministratorTeacherSubmissionControllerTest.php index 26020d38..9ca1e12b 100644 --- a/tests/Feature/Api/V1/Administrator/AdministratorTeacherSubmissionControllerTest.php +++ b/tests/Feature/Api/V1/Administrator/AdministratorTeacherSubmissionControllerTest.php @@ -2,10 +2,10 @@ namespace Tests\Feature\Api\Administrator; +use App\Http\Middleware\EnsurePrintRequestsAdminAccess; use App\Models\User; use App\Services\Administrator\TeacherSubmissionNotificationService; use App\Services\Administrator\TeacherSubmissionReportService; -use App\Http\Middleware\EnsurePrintRequestsAdminAccess; use Illuminate\Foundation\Testing\RefreshDatabase; use Laravel\Sanctum\Sanctum; use Mockery; diff --git a/tests/Feature/Api/V1/Administrator/AdministratorTeacherSubmissionReportApiTest.php b/tests/Feature/Api/V1/Administrator/AdministratorTeacherSubmissionReportApiTest.php index 3aef566f..d6274900 100644 --- a/tests/Feature/Api/V1/Administrator/AdministratorTeacherSubmissionReportApiTest.php +++ b/tests/Feature/Api/V1/Administrator/AdministratorTeacherSubmissionReportApiTest.php @@ -329,7 +329,7 @@ class AdministratorTeacherSubmissionReportApiTest extends TestCase 'class_section_id' => 12, 'admin_id' => 901, 'notification_category' => 'teacher_submissions', - 'message' => 'Reminder ' . $i, + 'message' => 'Reminder '.$i, 'status' => 'sent', 'school_year' => '2025-2026', 'semester' => 'Fall', diff --git a/tests/Feature/Api/V1/Administrator/EmergencyContactControllerTest.php b/tests/Feature/Api/V1/Administrator/EmergencyContactControllerTest.php index 6da928a2..4ecd47f4 100644 --- a/tests/Feature/Api/V1/Administrator/EmergencyContactControllerTest.php +++ b/tests/Feature/Api/V1/Administrator/EmergencyContactControllerTest.php @@ -80,7 +80,7 @@ class EmergencyContactControllerTest extends TestCase ]); Sanctum::actingAs($admin, [], 'api'); - $response = $this->patch('/api/v1/administrator/emergency-contacts/' . $contact->id, [ + $response = $this->patch('/api/v1/administrator/emergency-contacts/'.$contact->id, [ 'parent_id' => $parent->id, 'name' => 'New Name', 'cellphone' => '5555555555', @@ -116,7 +116,7 @@ class EmergencyContactControllerTest extends TestCase ]); Sanctum::actingAs($admin, [], 'api'); - $response = $this->delete('/api/v1/administrator/emergency-contacts/' . $contact->id, [ + $response = $this->delete('/api/v1/administrator/emergency-contacts/'.$contact->id, [ 'parent_id' => $parent->id, ], [ 'Accept' => 'application/json', @@ -145,7 +145,7 @@ class EmergencyContactControllerTest extends TestCase ]); Sanctum::actingAs($admin, [], 'api'); - $response = $this->get('/api/v1/administrator/emergency-contacts/' . $contact->id . '?parent_id=' . $parent->id, [ + $response = $this->get('/api/v1/administrator/emergency-contacts/'.$contact->id.'?parent_id='.$parent->id, [ 'Accept' => 'application/json', ]); @@ -171,7 +171,7 @@ class EmergencyContactControllerTest extends TestCase ]); Sanctum::actingAs($admin, [], 'api'); - $response = $this->patch('/api/v1/administrator/emergency-contacts/' . $contact->id, [ + $response = $this->patch('/api/v1/administrator/emergency-contacts/'.$contact->id, [ 'parent_id' => $otherParent->id, 'name' => 'New Name', 'cellphone' => '9999999999', @@ -205,7 +205,7 @@ class EmergencyContactControllerTest extends TestCase ]); Sanctum::actingAs($admin, [], 'api'); - $response = $this->delete('/api/v1/administrator/emergency-contacts/' . $contact->id, [ + $response = $this->delete('/api/v1/administrator/emergency-contacts/'.$contact->id, [ 'parent_id' => $otherParent->id, ], [ 'Accept' => 'application/json', diff --git a/tests/Feature/Api/V1/Assignment/AssignmentApiControllerTest.php b/tests/Feature/Api/V1/Assignment/AssignmentApiControllerTest.php index 3a37da73..1ee1b586 100644 --- a/tests/Feature/Api/V1/Assignment/AssignmentApiControllerTest.php +++ b/tests/Feature/Api/V1/Assignment/AssignmentApiControllerTest.php @@ -16,11 +16,17 @@ class AssignmentApiControllerTest extends TestCase use RefreshDatabase; protected User $user; + protected User $teacherMain; + protected User $teacherAssistant; + protected ClassSection $sectionA; + protected ClassSection $sectionB; + protected Student $studentOne; + protected Student $studentTwo; protected function setUp(): void @@ -418,4 +424,4 @@ class AssignmentApiControllerTest extends TestCase $this->assertSame(['Main Teacher'], $mainTeachers); } -} \ No newline at end of file +} diff --git a/tests/Feature/Api/V1/AssignmentApiTest.php b/tests/Feature/Api/V1/AssignmentApiTest.php index c66523d4..64c3f6a1 100644 --- a/tests/Feature/Api/V1/AssignmentApiTest.php +++ b/tests/Feature/Api/V1/AssignmentApiTest.php @@ -2,12 +2,12 @@ namespace Tests\Feature\Api; -use App\Models\User; +use App\Models\ClassSection; +use App\Models\Configuration; use App\Models\Student; use App\Models\StudentClass; use App\Models\TeacherClass; -use App\Models\ClassSection; -use App\Models\Configuration; +use App\Models\User; use Illuminate\Foundation\Testing\RefreshDatabase; use Laravel\Sanctum\Sanctum; use PHPUnit\Framework\Attributes\Test; @@ -48,36 +48,36 @@ class AssignmentApiTest extends TestCase $teacher = User::factory()->create([ 'firstname' => 'Ahmad', - 'lastname' => 'Ali', + 'lastname' => 'Ali', ]); TeacherClass::query()->create([ - 'teacher_id' => $teacher->id, - 'class_section_id' => $section->class_section_id, - 'position' => 'main', - 'semester' => 'Fall', - 'school_year' => '2025-2026', - 'description' => 'Main teacher assignment', + 'teacher_id' => $teacher->id, + 'class_section_id' => $section->class_section_id, + 'position' => 'main', + 'semester' => 'Fall', + 'school_year' => '2025-2026', + 'description' => 'Main teacher assignment', ]); $student = Student::factory()->create([ - 'firstname' => 'Sara', - 'lastname' => 'Yousef', - 'is_active' => 1, - 'gender' => 'F', + 'firstname' => 'Sara', + 'lastname' => 'Yousef', + 'is_active' => 1, + 'gender' => 'F', 'registration_grade' => '5', - 'photo_consent' => 1, - 'tuition_paid' => 1, - 'school_id' => 'SCH-1001', + 'photo_consent' => 1, + 'tuition_paid' => 1, + 'school_id' => 'SCH-1001', ]); StudentClass::query()->create([ - 'student_id' => $student->id, - 'class_section_id' => $section->class_section_id, - 'semester' => 'Fall', - 'school_year' => '2025-2026', - 'description' => 'Student assigned', - 'is_active' => 1, // remove if your table doesn’t have this column + 'student_id' => $student->id, + 'class_section_id' => $section->class_section_id, + 'semester' => 'Fall', + 'school_year' => '2025-2026', + 'description' => 'Student assigned', + 'is_active' => 1, // remove if your table doesn’t have this column ]); $response = $this->getJson('/api/assignments'); @@ -96,7 +96,7 @@ class AssignmentApiTest extends TestCase 'semester', 'school_year', 'description', - ] + ], ], 'schoolYears', 'selectedYear', @@ -122,19 +122,19 @@ class AssignmentApiTest extends TestCase $teacher = User::factory()->create(['firstname' => 'Test', 'lastname' => 'Teacher']); TeacherClass::query()->create([ - 'teacher_id' => $teacher->id, + 'teacher_id' => $teacher->id, 'class_section_id' => $section1->class_section_id, - 'position' => 'main', - 'semester' => 'Fall', - 'school_year' => '2024-2025', + 'position' => 'main', + 'semester' => 'Fall', + 'school_year' => '2024-2025', ]); TeacherClass::query()->create([ - 'teacher_id' => $teacher->id, + 'teacher_id' => $teacher->id, 'class_section_id' => $section2->class_section_id, - 'position' => 'main', - 'semester' => 'Fall', - 'school_year' => '2025-2026', + 'position' => 'main', + 'semester' => 'Fall', + 'school_year' => '2025-2026', ]); $response = $this->getJson('/api/assignments?school_year=2025-2026'); @@ -158,11 +158,11 @@ class AssignmentApiTest extends TestCase $section = ClassSection::factory()->create(); $payload = [ - 'student_id' => $student->id, + 'student_id' => $student->id, 'class_section_id' => $section->class_section_id, - 'semester' => 'Spring', - 'school_year' => '2025-2026', - 'description' => 'Moved after placement review', + 'semester' => 'Spring', + 'school_year' => '2025-2026', + 'description' => 'Moved after placement review', ]; $response = $this->postJson('/api/assignments', $payload); @@ -171,10 +171,10 @@ class AssignmentApiTest extends TestCase ->assertJsonPath('message', 'Assignment saved successfully.'); $this->assertDatabaseHas('student_class', [ - 'student_id' => $student->id, + 'student_id' => $student->id, 'class_section_id' => $section->class_section_id, - 'semester' => 'Spring', - 'school_year' => '2025-2026', + 'semester' => 'Spring', + 'school_year' => '2025-2026', ]); } @@ -203,19 +203,19 @@ class AssignmentApiTest extends TestCase // First create $this->postJson('/api/assignments', [ - 'student_id' => $student->id, + 'student_id' => $student->id, 'class_section_id' => $sectionA->class_section_id, - 'semester' => 'Fall', - 'school_year' => '2025-2026', + 'semester' => 'Fall', + 'school_year' => '2025-2026', ])->assertCreated(); // Second call same unique key should update (per service updateOrCreate) $this->postJson('/api/assignments', [ - 'student_id' => $student->id, + 'student_id' => $student->id, 'class_section_id' => $sectionA->class_section_id, - 'semester' => 'Fall', - 'school_year' => '2025-2026', - 'description' => 'Reassigned', + 'semester' => 'Fall', + 'school_year' => '2025-2026', + 'description' => 'Reassigned', ])->assertCreated(); $this->assertEquals( @@ -228,10 +228,10 @@ class AssignmentApiTest extends TestCase ); $this->assertDatabaseHas('student_class', [ - 'student_id' => $student->id, + 'student_id' => $student->id, 'class_section_id' => $sectionA->class_section_id, - 'semester' => 'Fall', - 'school_year' => '2025-2026', + 'semester' => 'Fall', + 'school_year' => '2025-2026', ]); } diff --git a/tests/Feature/Api/V1/Attendance/LateSlipLogsControllerTest.php b/tests/Feature/Api/V1/Attendance/LateSlipLogsControllerTest.php index 42b16cd4..d88fef78 100644 --- a/tests/Feature/Api/V1/Attendance/LateSlipLogsControllerTest.php +++ b/tests/Feature/Api/V1/Attendance/LateSlipLogsControllerTest.php @@ -113,7 +113,7 @@ class LateSlipLogsControllerTest extends TestCase $user = User::query()->create([ 'firstname' => 'Test', 'lastname' => 'User', - 'email' => $roleName . '@example.com', + 'email' => $roleName.'@example.com', 'cellphone' => '5555555555', 'address_street' => '123 Main', 'city' => 'City', diff --git a/tests/Feature/Api/V1/AttendanceCommentTemplateApiTest.php b/tests/Feature/Api/V1/AttendanceCommentTemplateApiTest.php index f99f0e41..511a4de6 100644 --- a/tests/Feature/Api/V1/AttendanceCommentTemplateApiTest.php +++ b/tests/Feature/Api/V1/AttendanceCommentTemplateApiTest.php @@ -85,10 +85,10 @@ class AttendanceCommentTemplateApiTest extends TestCase $mock->shouldReceive('create') ->once() ->with(Mockery::on(function ($data) use ($payload) { - return (int)$data['min_score'] === 80 - && (int)$data['max_score'] === 100 + return (int) $data['min_score'] === 80 + && (int) $data['max_score'] === 100 && $data['template_text'] === $payload['template_text'] - && (bool)$data['is_active'] === true; + && (bool) $data['is_active'] === true; })) ->andReturn([ 'id' => 10, @@ -159,7 +159,7 @@ class AttendanceCommentTemplateApiTest extends TestCase ->once() ->with(7, Mockery::on(function ($data) { return $data['template_text'] === 'Updated comment text' - && (bool)$data['is_active'] === false; + && (bool) $data['is_active'] === false; })) ->andReturn([ 'id' => 7, @@ -221,7 +221,7 @@ class AttendanceCommentTemplateApiTest extends TestCase 'max_score' => 49, 'template_text' => 'Low attendance', 'is_active' => true, - ] + ], ]); $this->app->instance(AttendanceCommentTemplateService::class, $mock); @@ -232,4 +232,4 @@ class AttendanceCommentTemplateApiTest extends TestCase ->assertJsonCount(1, 'templates') ->assertJsonPath('templates.0.id', 1); } -} \ No newline at end of file +} diff --git a/tests/Feature/Api/V1/AttendanceTracking/AttendanceTrackingControllerTest.php b/tests/Feature/Api/V1/AttendanceTracking/AttendanceTrackingControllerTest.php index bfbc9683..2c5ee2e2 100644 --- a/tests/Feature/Api/V1/AttendanceTracking/AttendanceTrackingControllerTest.php +++ b/tests/Feature/Api/V1/AttendanceTracking/AttendanceTrackingControllerTest.php @@ -4,6 +4,7 @@ namespace Tests\Feature\Api\V1\AttendanceTracking; use App\Services\AttendanceTracking\AttendanceTrackingService; use Mockery; +use Mockery\MockInterface; use Tests\TestCase; class AttendanceTrackingControllerTest extends TestCase @@ -14,7 +15,7 @@ class AttendanceTrackingControllerTest extends TestCase parent::tearDown(); } - protected function mockService(): \Mockery\MockInterface + protected function mockService(): MockInterface { $service = Mockery::mock(AttendanceTrackingService::class); $this->app->instance(AttendanceTrackingService::class, $service); diff --git a/tests/Feature/Api/V1/Auth/IpBanControllerTest.php b/tests/Feature/Api/V1/Auth/IpBanControllerTest.php index d12badd0..834829c8 100644 --- a/tests/Feature/Api/V1/Auth/IpBanControllerTest.php +++ b/tests/Feature/Api/V1/Auth/IpBanControllerTest.php @@ -159,7 +159,7 @@ class IpBanControllerTest extends TestCase $user = User::query()->create([ 'firstname' => 'Test', 'lastname' => 'User', - 'email' => $roleName . '@example.com', + 'email' => $roleName.'@example.com', 'cellphone' => '5555555555', 'address_street' => '123 Main', 'city' => 'City', diff --git a/tests/Feature/Api/V1/BadgeControllerTest.php b/tests/Feature/Api/V1/BadgeControllerTest.php index 8b6853f4..d4361a44 100644 --- a/tests/Feature/Api/V1/BadgeControllerTest.php +++ b/tests/Feature/Api/V1/BadgeControllerTest.php @@ -234,4 +234,4 @@ class BadgeControllerTest extends TestCase $this->postJson('/api/badges/log-print', ['user_ids' => [1]])->assertStatus(401); $this->postJson('/api/badges/pdf', ['user_ids' => [1]])->assertStatus(401); } -} \ No newline at end of file +} diff --git a/tests/Feature/Api/V1/BroadcastEmail/BroadcastEmailControllerTest.php b/tests/Feature/Api/V1/BroadcastEmail/BroadcastEmailControllerTest.php index 33ddaf1a..e5018890 100644 --- a/tests/Feature/Api/V1/BroadcastEmail/BroadcastEmailControllerTest.php +++ b/tests/Feature/Api/V1/BroadcastEmail/BroadcastEmailControllerTest.php @@ -31,7 +31,8 @@ class BroadcastEmailControllerTest extends TestCase { $this->seedParentData(); - $fake = new class extends EmailService { + $fake = new class extends EmailService + { public array $sent = []; public function send( @@ -41,9 +42,9 @@ class BroadcastEmailControllerTest extends TestCase string $fromKey = 'general', array $cc = [], array $bcc = [] - ): bool - { + ): bool { $this->sent[] = compact('to', 'subject', 'html', 'fromKey', 'cc', 'bcc'); + return true; } }; diff --git a/tests/Feature/Api/V1/ClassProgress/ClassProgressControllerTest.php b/tests/Feature/Api/V1/ClassProgress/ClassProgressControllerTest.php index cea23d1d..bf26a65d 100644 --- a/tests/Feature/Api/V1/ClassProgress/ClassProgressControllerTest.php +++ b/tests/Feature/Api/V1/ClassProgress/ClassProgressControllerTest.php @@ -92,7 +92,7 @@ class ClassProgressControllerTest extends TestCase Sanctum::actingAs($user); - $response = $this->getJson('/api/v1/class-progress/' . $report->id); + $response = $this->getJson('/api/v1/class-progress/'.$report->id); $response->assertOk(); $response->assertJsonPath('status', true); @@ -110,7 +110,7 @@ class ClassProgressControllerTest extends TestCase Sanctum::actingAs($user); - $response = $this->patchJson('/api/v1/class-progress/' . $report->id, [ + $response = $this->patchJson('/api/v1/class-progress/'.$report->id, [ 'status' => 'behind', ]); @@ -132,7 +132,7 @@ class ClassProgressControllerTest extends TestCase Sanctum::actingAs($user); - $response = $this->deleteJson('/api/v1/class-progress/' . $report->id); + $response = $this->deleteJson('/api/v1/class-progress/'.$report->id); $response->assertOk(); $this->assertDatabaseMissing('class_progress_reports', [ @@ -152,7 +152,7 @@ class ClassProgressControllerTest extends TestCase Sanctum::actingAs($other); - $response = $this->getJson('/api/v1/class-progress/' . $report->id); + $response = $this->getJson('/api/v1/class-progress/'.$report->id); $response->assertStatus(403); } @@ -187,7 +187,7 @@ class ClassProgressControllerTest extends TestCase Sanctum::actingAs($user); - $response = $this->get('/api/v1/class-progress/attachments/' . $attachment->id); + $response = $this->get('/api/v1/class-progress/attachments/'.$attachment->id); $response->assertOk(); } diff --git a/tests/Feature/Api/V1/Classes/ClassSectionControllerTest.php b/tests/Feature/Api/V1/Classes/ClassSectionControllerTest.php index 3deb5d96..34700615 100644 --- a/tests/Feature/Api/V1/Classes/ClassSectionControllerTest.php +++ b/tests/Feature/Api/V1/Classes/ClassSectionControllerTest.php @@ -257,7 +257,7 @@ class ClassSectionControllerTest extends TestCase 'firstname' => 'Test', 'lastname' => 'User', 'cellphone' => '5555555555', - 'email' => $roleName . '@example.com', + 'email' => $roleName.'@example.com', 'address_street' => '123 Main', 'city' => 'City', 'state' => 'ST', diff --git a/tests/Feature/Api/V1/Communication/CommunicationControllerTest.php b/tests/Feature/Api/V1/Communication/CommunicationControllerTest.php index dddf92bb..340bab06 100644 --- a/tests/Feature/Api/V1/Communication/CommunicationControllerTest.php +++ b/tests/Feature/Api/V1/Communication/CommunicationControllerTest.php @@ -50,7 +50,8 @@ class CommunicationControllerTest extends TestCase { $this->seedCommunicationData(); - $fake = new class extends EmailService { + $fake = new class extends EmailService + { public array $sent = []; public function send( @@ -62,6 +63,7 @@ class CommunicationControllerTest extends TestCase array $bcc = [] ): bool { $this->sent[] = compact('to', 'subject', 'html', 'fromKey', 'cc', 'bcc'); + return true; } }; diff --git a/tests/Feature/Api/V1/Family/FamilyAdminControllerTest.php b/tests/Feature/Api/V1/Family/FamilyAdminControllerTest.php index 06903981..472794fc 100644 --- a/tests/Feature/Api/V1/Family/FamilyAdminControllerTest.php +++ b/tests/Feature/Api/V1/Family/FamilyAdminControllerTest.php @@ -23,7 +23,7 @@ class FamilyAdminControllerTest extends TestCase $this->seedFamilyGuardian($familyId, $user->id); Sanctum::actingAs($user); - $response = $this->getJson('/api/v1/family-admin?student_id=' . $studentId); + $response = $this->getJson('/api/v1/family-admin?student_id='.$studentId); $response->assertOk(); $response->assertJsonPath('status', true); @@ -58,7 +58,7 @@ class FamilyAdminControllerTest extends TestCase $this->seedPayment($user->id); Sanctum::actingAs($user); - $response = $this->getJson('/api/v1/family-admin/card?family_id=' . $familyId); + $response = $this->getJson('/api/v1/family-admin/card?family_id='.$familyId); $response->assertOk(); $response->assertJsonPath('status', true); @@ -123,7 +123,7 @@ class FamilyAdminControllerTest 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', @@ -163,7 +163,7 @@ class FamilyAdminControllerTest 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(), diff --git a/tests/Feature/Api/V1/Family/FamilyControllerTest.php b/tests/Feature/Api/V1/Family/FamilyControllerTest.php index f9b1a40c..735433dd 100644 --- a/tests/Feature/Api/V1/Family/FamilyControllerTest.php +++ b/tests/Feature/Api/V1/Family/FamilyControllerTest.php @@ -20,7 +20,7 @@ class FamilyControllerTest extends TestCase $this->seedFamilyStudent($familyId, $studentId); Sanctum::actingAs($user); - $response = $this->getJson('/api/v1/families/by-student/' . $studentId); + $response = $this->getJson('/api/v1/families/by-student/'.$studentId); $response->assertOk(); $response->assertJsonPath('status', true); @@ -34,7 +34,7 @@ class FamilyControllerTest extends TestCase $this->seedFamilyGuardian($familyId, $user->id); Sanctum::actingAs($user); - $response = $this->getJson('/api/v1/families/' . $familyId . '/guardians'); + $response = $this->getJson('/api/v1/families/'.$familyId.'/guardians'); $response->assertOk(); $response->assertJsonPath('status', true); @@ -292,7 +292,7 @@ class FamilyControllerTest 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(), diff --git a/tests/Feature/Api/V1/Finance/EventChargeControllerTest.php b/tests/Feature/Api/V1/Finance/EventChargeControllerTest.php index 7d8300a6..7947f84a 100644 --- a/tests/Feature/Api/V1/Finance/EventChargeControllerTest.php +++ b/tests/Feature/Api/V1/Finance/EventChargeControllerTest.php @@ -28,7 +28,7 @@ class EventChargeControllerTest extends TestCase { $charge = $this->makeCharge(); - $this->getJson('/api/v1/finance/event-charges/' . $charge->id) + $this->getJson('/api/v1/finance/event-charges/'.$charge->id) ->assertStatus(401); } @@ -37,7 +37,7 @@ class EventChargeControllerTest extends TestCase $this->actingAsApiAdministrator(); $charge = $this->makeCharge(); - $this->getJson('/api/v1/finance/event-charges/' . $charge->id) + $this->getJson('/api/v1/finance/event-charges/'.$charge->id) ->assertOk() ->assertJsonPath('ok', true) ->assertJsonPath('event_charge.id', $charge->id) @@ -57,7 +57,7 @@ class EventChargeControllerTest extends TestCase $this->actingAsApiAdministrator(); $charge = $this->makeCharge(); - $this->putJson('/api/v1/finance/event-charges/' . $charge->id, [ + $this->putJson('/api/v1/finance/event-charges/'.$charge->id, [ 'event_name' => 'Museum Visit', 'amount' => 40.50, ]) @@ -76,7 +76,7 @@ class EventChargeControllerTest extends TestCase $this->actingAsApiAdministrator(); $charge = $this->makeCharge(['charged' => 0]); - $this->postJson('/api/v1/finance/event-charges/' . $charge->id . '/approve') + $this->postJson('/api/v1/finance/event-charges/'.$charge->id.'/approve') ->assertOk() ->assertJsonPath('ok', true); @@ -91,7 +91,7 @@ class EventChargeControllerTest extends TestCase $this->actingAsApiAdministrator(); $charge = $this->makeCharge(['charged' => 1]); - $this->postJson('/api/v1/finance/event-charges/' . $charge->id . '/void') + $this->postJson('/api/v1/finance/event-charges/'.$charge->id.'/void') ->assertOk() ->assertJsonPath('ok', true); @@ -106,7 +106,7 @@ class EventChargeControllerTest extends TestCase $this->actingAsApiAdministrator(); $charge = $this->makeCharge(['charged' => 1]); - $this->deleteJson('/api/v1/finance/event-charges/' . $charge->id) + $this->deleteJson('/api/v1/finance/event-charges/'.$charge->id) ->assertOk() ->assertJsonPath('ok', true); diff --git a/tests/Feature/Api/V1/Frontend/FrontendControllerTest.php b/tests/Feature/Api/V1/Frontend/FrontendControllerTest.php index fb68a7b2..22a00ea8 100644 --- a/tests/Feature/Api/V1/Frontend/FrontendControllerTest.php +++ b/tests/Feature/Api/V1/Frontend/FrontendControllerTest.php @@ -49,7 +49,7 @@ class FrontendControllerTest extends TestCase $user = User::query()->create([ 'firstname' => 'Test', 'lastname' => 'User', - 'email' => $roleName . '@example.com', + 'email' => $roleName.'@example.com', 'cellphone' => '5555555555', 'address_street' => '123 Main', 'city' => 'City', diff --git a/tests/Feature/Api/V1/Frontend/InfoIconControllerTest.php b/tests/Feature/Api/V1/Frontend/InfoIconControllerTest.php index ebd0a5e2..e04e2247 100644 --- a/tests/Feature/Api/V1/Frontend/InfoIconControllerTest.php +++ b/tests/Feature/Api/V1/Frontend/InfoIconControllerTest.php @@ -4,7 +4,6 @@ namespace Tests\Feature\Api\V1\Frontend; use App\Models\User; use Illuminate\Foundation\Testing\RefreshDatabase; -use Illuminate\Support\Facades\DB; use Laravel\Sanctum\Sanctum; use Tests\TestCase; diff --git a/tests/Feature/Api/V1/Frontend/LandingPageControllerTest.php b/tests/Feature/Api/V1/Frontend/LandingPageControllerTest.php index d60af1b5..cbe73476 100644 --- a/tests/Feature/Api/V1/Frontend/LandingPageControllerTest.php +++ b/tests/Feature/Api/V1/Frontend/LandingPageControllerTest.php @@ -57,7 +57,7 @@ class LandingPageControllerTest extends TestCase $user = User::query()->create([ 'firstname' => 'Test', 'lastname' => 'User', - 'email' => $roleName . '@example.com', + 'email' => $roleName.'@example.com', 'cellphone' => '5555555555', 'address_street' => '123 Main', 'city' => 'City', diff --git a/tests/Feature/Api/V1/Frontend/PageControllerTest.php b/tests/Feature/Api/V1/Frontend/PageControllerTest.php index 538d3cea..2b7df224 100644 --- a/tests/Feature/Api/V1/Frontend/PageControllerTest.php +++ b/tests/Feature/Api/V1/Frontend/PageControllerTest.php @@ -15,7 +15,7 @@ class PageControllerTest extends TestCase { $dir = public_path('html'); File::ensureDirectoryExists($dir); - File::put($dir . '/privacy_policy.html', '

Privacy

'); + File::put($dir.'/privacy_policy.html', '

Privacy

'); $response = $this->getJson('/api/v1/pages/privacy'); @@ -28,7 +28,7 @@ class PageControllerTest extends TestCase { $dir = public_path('html'); File::ensureDirectoryExists($dir); - File::delete($dir . '/help_center.html'); + File::delete($dir.'/help_center.html'); $response = $this->getJson('/api/v1/pages/help'); diff --git a/tests/Feature/Api/V1/FullSurface/ApiAcademicAttendanceFullSurfaceContractTest.php b/tests/Feature/Api/V1/FullSurface/ApiAcademicAttendanceFullSurfaceContractTest.php index a7579826..9e12e651 100644 --- a/tests/Feature/Api/V1/FullSurface/ApiAcademicAttendanceFullSurfaceContractTest.php +++ b/tests/Feature/Api/V1/FullSurface/ApiAcademicAttendanceFullSurfaceContractTest.php @@ -12,9 +12,9 @@ use Tests\TestCase; */ class ApiAcademicAttendanceFullSurfaceContractTest extends TestCase { + use AssertsE2EApiResponses; use RefreshDatabase; use SeedsE2ETestFixtures; - use AssertsE2EApiResponses; public function test_academic_attendance_and_grading_surfaces_have_controlled_contracts(): void { @@ -24,21 +24,21 @@ class ApiAcademicAttendanceFullSurfaceContractTest extends TestCase foreach ($this->teacherGetEndpoints($world) as $uri) { $response = $this->getJson($uri); - $this->assertNoServerError($response, 'Teacher GET ' . $uri); - $this->assertStatusIn($response, [200, 204, 302, 400, 401, 403, 404, 405, 409, 419, 422], 'Teacher GET ' . $uri); + $this->assertNoServerError($response, 'Teacher GET '.$uri); + $this->assertStatusIn($response, [200, 204, 302, 400, 401, 403, 404, 405, 409, 419, 422], 'Teacher GET '.$uri); } foreach ($this->teacherMutationEndpoints($world) as [$method, $uri, $payload]) { $response = $this->json($method, $uri, $payload); - $this->assertNoServerError($response, 'Teacher ' . $method . ' ' . $uri); - $this->assertStatusIn($response, [200, 201, 202, 204, 302, 400, 401, 403, 404, 405, 409, 419, 422], 'Teacher ' . $method . ' ' . $uri); + $this->assertNoServerError($response, 'Teacher '.$method.' '.$uri); + $this->assertStatusIn($response, [200, 201, 202, 204, 302, 400, 401, 403, 404, 405, 409, 419, 422], 'Teacher '.$method.' '.$uri); } $this->actingAsApiAdministrator(); foreach ($this->adminAcademicEndpoints($world) as [$method, $uri, $payload]) { $response = $this->json($method, $uri, $payload); - $this->assertNoServerError($response, 'Admin academic ' . $method . ' ' . $uri); - $this->assertStatusIn($response, [200, 201, 202, 204, 302, 400, 401, 403, 404, 405, 409, 419, 422], 'Admin academic ' . $method . ' ' . $uri); + $this->assertNoServerError($response, 'Admin academic '.$method.' '.$uri); + $this->assertStatusIn($response, [200, 201, 202, 204, 302, 400, 401, 403, 404, 405, 409, 419, 422], 'Admin academic '.$method.' '.$uri); } } @@ -49,35 +49,35 @@ class ApiAcademicAttendanceFullSurfaceContractTest extends TestCase $studentId = (int) $world['student_id']; return [ - '/api/v1/teachers/classes?class_section_id=' . $classSectionId, - '/api/v1/teacher/class-view?class_section_id=' . $classSectionId, + '/api/v1/teachers/classes?class_section_id='.$classSectionId, + '/api/v1/teacher/class-view?class_section_id='.$classSectionId, '/api/v1/teacher/no-classes', '/api/v1/teacher/select-semester', - '/api/v1/teacher/homework-list?class_section_id=' . $classSectionId, - '/api/v1/teacher/add-homework?class_section_id=' . $classSectionId, - '/api/v1/teacher/add-quiz?class_section_id=' . $classSectionId, - '/api/v1/teacher/add-quiz-column-form?class_section_id=' . $classSectionId, - '/api/v1/teacher/add-midterm-exam?class_section_id=' . $classSectionId, - '/api/v1/teacher/add-final-exam?class_section_id=' . $classSectionId, - '/api/v1/teacher/add-participation?class_section_id=' . $classSectionId, - '/api/v1/teacher/add-project?class_section_id=' . $classSectionId, + '/api/v1/teacher/homework-list?class_section_id='.$classSectionId, + '/api/v1/teacher/add-homework?class_section_id='.$classSectionId, + '/api/v1/teacher/add-quiz?class_section_id='.$classSectionId, + '/api/v1/teacher/add-quiz-column-form?class_section_id='.$classSectionId, + '/api/v1/teacher/add-midterm-exam?class_section_id='.$classSectionId, + '/api/v1/teacher/add-final-exam?class_section_id='.$classSectionId, + '/api/v1/teacher/add-participation?class_section_id='.$classSectionId, + '/api/v1/teacher/add-project?class_section_id='.$classSectionId, '/api/v1/teacher/teacher-assignment', '/api/v1/teacher/exam-drafts', '/api/v1/teacher/drafts', '/api/v1/teacher/inventory/books/distribute', '/api/v1/teacher/print-requests/context', '/api/v1/teacher/absence-vacation', - '/api/v1/teacher/showupdate-attendance?class_section_id=' . $classSectionId, - '/api/v1/teacher/showupdate_attendance?class_section_id=' . $classSectionId, - '/api/v1/teacher/scores?class_section_id=' . $classSectionId, + '/api/v1/teacher/showupdate-attendance?class_section_id='.$classSectionId, + '/api/v1/teacher/showupdate_attendance?class_section_id='.$classSectionId, + '/api/v1/teacher/scores?class_section_id='.$classSectionId, '/api/v1/teacher/calendar', - '/api/v1/teacher/class-progress-submit?class_section_id=' . $classSectionId, - '/api/v1/teacher/class-progress-history?class_section_id=' . $classSectionId, - '/api/v1/teacher/class-progress-view?class_section_id=' . $classSectionId, + '/api/v1/teacher/class-progress-submit?class_section_id='.$classSectionId, + '/api/v1/teacher/class-progress-history?class_section_id='.$classSectionId, + '/api/v1/teacher/class-progress-view?class_section_id='.$classSectionId, '/api/v1/teacher/competition-scores', - '/api/v1/attendance/teacher/grid?class_section_id=' . $classSectionId, - '/api/v1/attendance/teacher/form?class_section_id=' . $classSectionId, - '/api/v1/attendance/admin/daily?date=2025-10-05&class_section_id=' . $classSectionId, + '/api/v1/attendance/teacher/grid?class_section_id='.$classSectionId, + '/api/v1/attendance/teacher/form?class_section_id='.$classSectionId, + '/api/v1/attendance/admin/daily?date=2025-10-05&class_section_id='.$classSectionId, '/api/v1/attendance/staff/month?month=2025-10', '/api/v1/attendance/staff/admins', '/api/v1/attendance/late-slip-logs', @@ -87,7 +87,7 @@ class ApiAcademicAttendanceFullSurfaceContractTest extends TestCase '/api/v1/attendance/management/dashboard', '/api/v1/attendance-tracking/pending-violations', '/api/v1/attendance-tracking/notified-violations', - '/api/v1/attendance-tracking/student-case/' . $studentId, + '/api/v1/attendance-tracking/student-case/'.$studentId, '/api/v1/attendance-tracking/compose', '/api/v1/attendance-tracking/parents-info', '/api/v1/attendance-comment-templates', @@ -96,15 +96,15 @@ class ApiAcademicAttendanceFullSurfaceContractTest extends TestCase '/api/v1/attendance-templates', '/api/v1/assignments', '/api/v1/assignments/class-assignment-data', - '/api/v1/scores/overview?class_section_id=' . $classSectionId, - '/api/v1/scores/homework?class_section_id=' . $classSectionId, - '/api/v1/scores/quiz?class_section_id=' . $classSectionId, - '/api/v1/scores/project?class_section_id=' . $classSectionId, - '/api/v1/scores/midterm?class_section_id=' . $classSectionId, - '/api/v1/scores/final?class_section_id=' . $classSectionId, - '/api/v1/scores/participation?class_section_id=' . $classSectionId, - '/api/v1/grading/homework-tracking?class_section_id=' . $classSectionId, - '/api/v1/reports/report-cards/meta?class_section_id=' . $classSectionId, + '/api/v1/scores/overview?class_section_id='.$classSectionId, + '/api/v1/scores/homework?class_section_id='.$classSectionId, + '/api/v1/scores/quiz?class_section_id='.$classSectionId, + '/api/v1/scores/project?class_section_id='.$classSectionId, + '/api/v1/scores/midterm?class_section_id='.$classSectionId, + '/api/v1/scores/final?class_section_id='.$classSectionId, + '/api/v1/scores/participation?class_section_id='.$classSectionId, + '/api/v1/grading/homework-tracking?class_section_id='.$classSectionId, + '/api/v1/reports/report-cards/meta?class_section_id='.$classSectionId, ]; } @@ -159,8 +159,8 @@ class ApiAcademicAttendanceFullSurfaceContractTest extends TestCase private function adminAcademicEndpoints(array $world): array { return [ - ['GET', '/api/v1/reports/report-cards?student_id=' . $world['student_id'], []], - ['GET', '/api/v1/reports/stickers?student_id=' . $world['student_id'], []], + ['GET', '/api/v1/reports/report-cards?student_id='.$world['student_id'], []], + ['GET', '/api/v1/reports/stickers?student_id='.$world['student_id'], []], ['GET', '/api/v1/badges/form-options', []], ['POST', '/api/v1/print-requests', ['title' => 'Contract Probe', 'copies' => 1, 'needed_by' => '2025-10-12']], ['GET', '/api/v1/print-requests/admin', []], diff --git a/tests/Feature/Api/V1/FullSurface/ApiAccountStatusLifecycleContractTest.php b/tests/Feature/Api/V1/FullSurface/ApiAccountStatusLifecycleContractTest.php index fae87403..e1b0373e 100644 --- a/tests/Feature/Api/V1/FullSurface/ApiAccountStatusLifecycleContractTest.php +++ b/tests/Feature/Api/V1/FullSurface/ApiAccountStatusLifecycleContractTest.php @@ -24,7 +24,7 @@ class ApiAccountStatusLifecycleContractTest extends FullSurfaceE2EContractCase $response = $this->requestAs($actor, 'GET', $uri); $this->assertControlled($response, 'GET', $uri); - $this->assertNoServerError($response, 'account status ' . $status . ' at GET ' . $uri); + $this->assertNoServerError($response, 'account status '.$status.' at GET '.$uri); } } } diff --git a/tests/Feature/Api/V1/FullSurface/ApiAdminOperationsFullSurfaceContractTest.php b/tests/Feature/Api/V1/FullSurface/ApiAdminOperationsFullSurfaceContractTest.php index 5e572473..cc645853 100644 --- a/tests/Feature/Api/V1/FullSurface/ApiAdminOperationsFullSurfaceContractTest.php +++ b/tests/Feature/Api/V1/FullSurface/ApiAdminOperationsFullSurfaceContractTest.php @@ -12,9 +12,9 @@ use Tests\TestCase; */ class ApiAdminOperationsFullSurfaceContractTest extends TestCase { + use AssertsE2EApiResponses; use RefreshDatabase; use SeedsE2ETestFixtures; - use AssertsE2EApiResponses; public function test_administrator_operational_surface_responds_with_controlled_contracts(): void { @@ -24,19 +24,19 @@ class ApiAdminOperationsFullSurfaceContractTest extends TestCase foreach ($this->adminGetEndpoints($world) as $uri) { $response = $this->getJson($uri); - $this->assertNoServerError($response, 'Admin GET ' . $uri); - $this->assertStatusIn($response, [200, 204, 302, 400, 401, 403, 404, 405, 409, 419, 422], 'Admin GET ' . $uri); + $this->assertNoServerError($response, 'Admin GET '.$uri); + $this->assertStatusIn($response, [200, 204, 302, 400, 401, 403, 404, 405, 409, 419, 422], 'Admin GET '.$uri); } foreach ($this->adminMutationEndpoints($world, $admin->id) as [$method, $uri, $payload]) { $response = $this->json($method, $uri, $payload); - $this->assertNoServerError($response, 'Admin ' . $method . ' ' . $uri); - $this->assertStatusIn($response, [200, 201, 202, 204, 302, 400, 401, 403, 404, 405, 409, 419, 422], 'Admin ' . $method . ' ' . $uri); + $this->assertNoServerError($response, 'Admin '.$method.' '.$uri); + $this->assertStatusIn($response, [200, 201, 202, 204, 302, 400, 401, 403, 404, 405, 409, 419, 422], 'Admin '.$method.' '.$uri); } } /** - * @param array $world + * @param array $world * @return list */ private function adminGetEndpoints(array $world): array @@ -76,7 +76,7 @@ class ApiAdminOperationsFullSurfaceContractTest extends TestCase '/api/v1/administrator/flags/students/1', '/api/v1/administrator/flags/processed', '/api/v1/administrator/flags/analysis', - '/api/v1/administrator/grading/homework-tracking?class_section_id=' . $classSectionId, + '/api/v1/administrator/grading/homework-tracking?class_section_id='.$classSectionId, '/api/v1/administrator/expenses', '/api/v1/administrator/expenses/options', '/api/v1/administrator/expenses/1', @@ -110,13 +110,13 @@ class ApiAdminOperationsFullSurfaceContractTest extends TestCase '/api/v1/class-sections', '/api/v1/classes', '/api/v1/users', - '/api/v1/users/' . $world['parent_id'], - '/api/v1/students/' . $studentId, + '/api/v1/users/'.$world['parent_id'], + '/api/v1/students/'.$studentId, ]; } /** - * @param array $world + * @param array $world * @return list}> */ private function adminMutationEndpoints(array $world, int $adminId): array diff --git a/tests/Feature/Api/V1/FullSurface/ApiAttachmentUploadContentSafetyContractTest.php b/tests/Feature/Api/V1/FullSurface/ApiAttachmentUploadContentSafetyContractTest.php index 573fba77..9fa1cebf 100644 --- a/tests/Feature/Api/V1/FullSurface/ApiAttachmentUploadContentSafetyContractTest.php +++ b/tests/Feature/Api/V1/FullSurface/ApiAttachmentUploadContentSafetyContractTest.php @@ -43,7 +43,7 @@ class ApiAttachmentUploadContentSafetyContractTest extends FullSurfaceE2EContrac continue; } - $path = $this->materializePath($route->uri()) . '?filename=../../.env&path=../../storage/logs/laravel.log'; + $path = $this->materializePath($route->uri()).'?filename=../../.env&path=../../storage/logs/laravel.log'; $response = $this->actingAs($this->actorFor($route->uri()), 'api')->getJson($path); $this->assertStatusIn($response, [200, 204, 400, 401, 403, 404, 409, 422], "GET {$route->uri()} path traversal probe"); diff --git a/tests/Feature/Api/V1/FullSurface/ApiAuditLogAndAdministrativeTraceabilityContractTest.php b/tests/Feature/Api/V1/FullSurface/ApiAuditLogAndAdministrativeTraceabilityContractTest.php index f4ad154c..543756d7 100644 --- a/tests/Feature/Api/V1/FullSurface/ApiAuditLogAndAdministrativeTraceabilityContractTest.php +++ b/tests/Feature/Api/V1/FullSurface/ApiAuditLogAndAdministrativeTraceabilityContractTest.php @@ -21,8 +21,8 @@ class ApiAuditLogAndAdministrativeTraceabilityContractTest extends FullSurfaceE2 $this->assertNoServerError($response, "$method $uri admin mutation traceability"); $this->assertControlled($response, $method, $uri); - $this->assertStringNotContainsStringIgnoringCase('audit_log_id', $response->getContent(), $uri . ' should not leak raw internal audit identifiers by accident.'); - $this->assertStringNotContainsStringIgnoringCase('stack_trace', $response->getContent(), $uri . ' should not leak stack traces in mutation responses.'); + $this->assertStringNotContainsStringIgnoringCase('audit_log_id', $response->getContent(), $uri.' should not leak raw internal audit identifiers by accident.'); + $this->assertStringNotContainsStringIgnoringCase('stack_trace', $response->getContent(), $uri.' should not leak stack traces in mutation responses.'); } } diff --git a/tests/Feature/Api/V1/FullSurface/ApiAuthenticationLifecycleDepthContractTest.php b/tests/Feature/Api/V1/FullSurface/ApiAuthenticationLifecycleDepthContractTest.php index a623a1d4..7c244590 100644 --- a/tests/Feature/Api/V1/FullSurface/ApiAuthenticationLifecycleDepthContractTest.php +++ b/tests/Feature/Api/V1/FullSurface/ApiAuthenticationLifecycleDepthContractTest.php @@ -20,12 +20,12 @@ class ApiAuthenticationLifecycleDepthContractTest extends FullSurfaceE2EContract 'password' => 'password', ]); - $this->assertNoServerError($response, 'valid login at ' . $path); + $this->assertNoServerError($response, 'valid login at '.$path); if ($response->getStatusCode() >= 200 && $response->getStatusCode() < 300) { $this->assertTrue( $response->json('token') !== null || $response->json('access_token') !== null || $response->json('user') !== null, - $path . ' must return a usable login contract, not a mystery envelope.' + $path.' must return a usable login contract, not a mystery envelope.' ); } } @@ -36,18 +36,18 @@ class ApiAuthenticationLifecycleDepthContractTest extends FullSurfaceE2EContract 'password' => 'definitely-wrong-password', ]); - $this->assertStatusIn($response, [400, 401, 403, 419, 422], 'invalid login at ' . $path); - $this->assertNoServerError($response, 'invalid login at ' . $path); + $this->assertStatusIn($response, [400, 401, 403, 419, 422], 'invalid login at '.$path); + $this->assertNoServerError($response, 'invalid login at '.$path); } foreach (['/api/v1/auth/me', '/api/v1/me', '/api/user'] as $path) { $response = $this->actingAs($this->admin, 'api')->getJson($path); - $this->assertNoServerError($response, 'authenticated identity lookup at ' . $path); + $this->assertNoServerError($response, 'authenticated identity lookup at '.$path); if ($response->getStatusCode() === 200) { $this->assertTrue( $response->json('user') !== null || $response->json('id') !== null || $response->json('data') !== null, - $path . ' must expose a stable identity payload.' + $path.' must expose a stable identity payload.' ); } } @@ -56,10 +56,10 @@ class ApiAuthenticationLifecycleDepthContractTest extends FullSurfaceE2EContract $first = $this->actingAs($this->admin, 'api')->postJson($path); $second = $this->actingAs($this->admin, 'api')->postJson($path); - $this->assertNoServerError($first, 'first logout at ' . $path); - $this->assertNoServerError($second, 'repeat logout at ' . $path); - $this->assertStatusIn($first, [200, 202, 204, 302, 401, 404, 419, 422], 'first logout at ' . $path); - $this->assertStatusIn($second, [200, 202, 204, 302, 401, 404, 419, 422], 'repeat logout at ' . $path); + $this->assertNoServerError($first, 'first logout at '.$path); + $this->assertNoServerError($second, 'repeat logout at '.$path); + $this->assertStatusIn($first, [200, 202, 204, 302, 401, 404, 419, 422], 'first logout at '.$path); + $this->assertStatusIn($second, [200, 202, 204, 302, 401, 404, 419, 422], 'repeat logout at '.$path); } } @@ -75,16 +75,16 @@ class ApiAuthenticationLifecycleDepthContractTest extends FullSurfaceE2EContract $uri = $route->uri(); $response = $this->json($method, $this->materializePath($uri), [ - 'email' => 'nobody.' . uniqid() . '@example.test', + 'email' => 'nobody.'.uniqid().'@example.test', 'password' => 'password', 'password_confirmation' => 'not-the-same', 'token' => 'not-a-real-token', ]); $this->assertControlled($response, $method, $uri); - $this->assertNoServerError($response, $method . ' ' . $uri); - $this->assertStringNotContainsStringIgnoringCase('select * from', $response->getContent(), $uri . ' must not leak account lookup SQL.'); - $this->assertStringNotContainsStringIgnoringCase('No query results for model', $response->getContent(), $uri . ' must not leak framework model lookups.'); + $this->assertNoServerError($response, $method.' '.$uri); + $this->assertStringNotContainsStringIgnoringCase('select * from', $response->getContent(), $uri.' must not leak account lookup SQL.'); + $this->assertStringNotContainsStringIgnoringCase('No query results for model', $response->getContent(), $uri.' must not leak framework model lookups.'); } } } diff --git a/tests/Feature/Api/V1/FullSurface/ApiAuthorizationCacheInvalidationContractTest.php b/tests/Feature/Api/V1/FullSurface/ApiAuthorizationCacheInvalidationContractTest.php index 5f567ec5..e31ec3ea 100644 --- a/tests/Feature/Api/V1/FullSurface/ApiAuthorizationCacheInvalidationContractTest.php +++ b/tests/Feature/Api/V1/FullSurface/ApiAuthorizationCacheInvalidationContractTest.php @@ -22,8 +22,8 @@ class ApiAuthorizationCacheInvalidationContractTest extends FullSurfaceE2EContra $response = $this->requestAs($this->parent, $method, $uri, $payload); - $this->assertStatusIn($response, [401, 403, 404, 405, 419, 422], 'parent privilege escalation should be blocked at ' . $method . ' ' . $uri); - $this->assertNoServerError($response, 'permission cache escalation probe at ' . $method . ' ' . $uri); + $this->assertStatusIn($response, [401, 403, 404, 405, 419, 422], 'parent privilege escalation should be blocked at '.$method.' '.$uri); + $this->assertNoServerError($response, 'permission cache escalation probe at '.$method.' '.$uri); } } } diff --git a/tests/Feature/Api/V1/FullSurface/ApiBatch11RouteRegressionNetTest.php b/tests/Feature/Api/V1/FullSurface/ApiBatch11RouteRegressionNetTest.php index fab61286..45c19d84 100644 --- a/tests/Feature/Api/V1/FullSurface/ApiBatch11RouteRegressionNetTest.php +++ b/tests/Feature/Api/V1/FullSurface/ApiBatch11RouteRegressionNetTest.php @@ -24,7 +24,7 @@ class ApiBatch11RouteRegressionNetTest extends FullSurfaceE2EContractCase foreach ($families as $family => $needles) { $this->assertNotEmpty( $this->apiRoutesContainingAny($needles), - 'Batch 11 expects at least one route for the ' . $family . ' family. If this fails, update the route catalog instead of pretending the family vanished.' + 'Batch 11 expects at least one route for the '.$family.' family. If this fails, update the route catalog instead of pretending the family vanished.' ); } } @@ -53,7 +53,7 @@ class ApiBatch11RouteRegressionNetTest extends FullSurfaceE2EContractCase ])->json($method, $this->materializePath($uri), $payload); $this->assertControlled($response, $method, $uri); - $this->assertNoServerError($response, 'batch 11 combined hostile probe at ' . $method . ' ' . $uri); + $this->assertNoServerError($response, 'batch 11 combined hostile probe at '.$method.' '.$uri); } } } diff --git a/tests/Feature/Api/V1/FullSurface/ApiBatch12AttachmentLifecycleContractTest.php b/tests/Feature/Api/V1/FullSurface/ApiBatch12AttachmentLifecycleContractTest.php index 6fb6941e..d8066f2e 100644 --- a/tests/Feature/Api/V1/FullSurface/ApiBatch12AttachmentLifecycleContractTest.php +++ b/tests/Feature/Api/V1/FullSurface/ApiBatch12AttachmentLifecycleContractTest.php @@ -2,9 +2,8 @@ namespace Tests\Feature\Api\V1\FullSurface; -use Tests\Feature\Api\V1\FullSurface\Support\FullSurfaceE2EContractCase; - use Illuminate\Http\UploadedFile; +use Tests\Feature\Api\V1\FullSurface\Support\FullSurfaceE2EContractCase; class ApiBatch12AttachmentLifecycleContractTest extends FullSurfaceE2EContractCase { diff --git a/tests/Feature/Api/V1/FullSurface/ApiBatch14AcademicPromotionRollbackIntegrityContractTest.php b/tests/Feature/Api/V1/FullSurface/ApiBatch14AcademicPromotionRollbackIntegrityContractTest.php index d73cad49..cee04a48 100644 --- a/tests/Feature/Api/V1/FullSurface/ApiBatch14AcademicPromotionRollbackIntegrityContractTest.php +++ b/tests/Feature/Api/V1/FullSurface/ApiBatch14AcademicPromotionRollbackIntegrityContractTest.php @@ -6,7 +6,7 @@ use Tests\Feature\Api\V1\FullSurface\Support\FullSurfaceE2EContractCase; class ApiBatch14AcademicPromotionRollbackIntegrityContractTest extends FullSurfaceE2EContractCase { -public function test_promotion_and_school_year_routes_reject_unsafe_rollback_payloads(): void + public function test_promotion_and_school_year_routes_reject_unsafe_rollback_payloads(): void { foreach (array_slice($this->apiRoutesContainingAny(['promotion', 'promote', 'school-year', 'semester', 'close', 'reopen', 'rollback']), 0, 45) as $route) { $method = $this->primaryMethod($route); @@ -22,10 +22,12 @@ public function test_promotion_and_school_year_routes_reject_unsafe_rollback_pay { foreach (array_slice($this->apiRoutesContainingAny(['promotion', 'school-year', 'close', 'reopen']), 0, 30) as $route) { $method = $this->primaryMethod($route); - if (! in_array($method, ['POST', 'PUT', 'PATCH', 'DELETE'], true)) { continue; } + if (! in_array($method, ['POST', 'PUT', 'PATCH', 'DELETE'], true)) { + continue; + } foreach ([$this->teacher, $this->parent, $this->studentUser] as $actor) { $response = $this->requestAs($actor, $method, $route->uri(), ['force' => true, 'confirmed' => true, 'school_year_id' => $this->ids['schoolYearId']]); - $this->assertStatusIn($response, [400, 401, 403, 404, 405, 409, 419, 422], $method . ' ' . $route->uri()); + $this->assertStatusIn($response, [400, 401, 403, 404, 405, 409, 419, 422], $method.' '.$route->uri()); } } } diff --git a/tests/Feature/Api/V1/FullSurface/ApiBatch14AccountEnumerationAndIdentityPrivacyContractTest.php b/tests/Feature/Api/V1/FullSurface/ApiBatch14AccountEnumerationAndIdentityPrivacyContractTest.php index eeff406e..a6af47d2 100644 --- a/tests/Feature/Api/V1/FullSurface/ApiBatch14AccountEnumerationAndIdentityPrivacyContractTest.php +++ b/tests/Feature/Api/V1/FullSurface/ApiBatch14AccountEnumerationAndIdentityPrivacyContractTest.php @@ -6,13 +6,15 @@ use Tests\Feature\Api\V1\FullSurface\Support\FullSurfaceE2EContractCase; class ApiBatch14AccountEnumerationAndIdentityPrivacyContractTest extends FullSurfaceE2EContractCase { -public function test_identity_recovery_routes_do_not_reveal_whether_user_exists(): void + public function test_identity_recovery_routes_do_not_reveal_whether_user_exists(): void { $routes = $this->apiRoutesContainingAny(['forgot', 'password', 'reset', 'verify', 'verification', 'recovery']); foreach (array_slice($routes, 0, 30) as $route) { $method = $this->primaryMethod($route); - if (! in_array($method, ['POST', 'PUT', 'PATCH'], true)) { continue; } - foreach ([$this->admin->email, 'missing-user-' . uniqid() . '@example.test'] as $email) { + if (! in_array($method, ['POST', 'PUT', 'PATCH'], true)) { + continue; + } + foreach ([$this->admin->email, 'missing-user-'.uniqid().'@example.test'] as $email) { $response = $this->json($method, $this->materializePath($route->uri()), ['email' => $email, 'token' => 'invalid-token', 'password' => 'NewPassword123!', 'password_confirmation' => 'NewPassword123!']); $this->assertControlled($response, $method, $route->uri()); $body = strtolower($response->getContent()); @@ -31,7 +33,7 @@ public function test_identity_recovery_routes_do_not_reveal_whether_user_exists( $this->assertControlled($response, $method, $route->uri()); $body = strtolower($response->getContent()); foreach (['password', 'remember_token', 'two_factor', 'api_token', 'provider_token', 'secret'] as $forbidden) { - $this->assertStringNotContainsString($forbidden, $body, $route->uri() . ' leaked identity internals.'); + $this->assertStringNotContainsString($forbidden, $body, $route->uri().' leaked identity internals.'); } } } diff --git a/tests/Feature/Api/V1/FullSurface/ApiBatch14AdminDelegationApprovalWorkflowContractTest.php b/tests/Feature/Api/V1/FullSurface/ApiBatch14AdminDelegationApprovalWorkflowContractTest.php index bb444c69..32b8eec1 100644 --- a/tests/Feature/Api/V1/FullSurface/ApiBatch14AdminDelegationApprovalWorkflowContractTest.php +++ b/tests/Feature/Api/V1/FullSurface/ApiBatch14AdminDelegationApprovalWorkflowContractTest.php @@ -6,7 +6,7 @@ use Tests\Feature\Api\V1\FullSurface\Support\FullSurfaceE2EContractCase; class ApiBatch14AdminDelegationApprovalWorkflowContractTest extends FullSurfaceE2EContractCase { -public function test_approval_and_delegation_routes_reject_forged_approval_metadata(): void + public function test_approval_and_delegation_routes_reject_forged_approval_metadata(): void { foreach (array_slice($this->apiRoutesContainingAny(['approve', 'approval', 'delegate', 'delegation', 'authorize', 'permission', 'role', 'impersonate']), 0, 50) as $route) { $method = $this->primaryMethod($route); @@ -20,10 +20,12 @@ public function test_approval_and_delegation_routes_reject_forged_approval_metad { foreach (array_slice($this->apiRoutesContainingAny(['approve', 'approval', 'delegate', 'permission', 'role']), 0, 35) as $route) { $method = $this->primaryMethod($route); - if (! in_array($method, ['POST', 'PUT', 'PATCH', 'DELETE'], true)) { continue; } + if (! in_array($method, ['POST', 'PUT', 'PATCH', 'DELETE'], true)) { + continue; + } foreach ([$this->teacher, $this->parent, $this->studentUser] as $actor) { $response = $this->requestAs($actor, $method, $route->uri(), ['approved' => true, 'permissions' => ['*'], 'role' => 'administrator', 'force' => true]); - $this->assertStatusIn($response, [400, 401, 403, 404, 405, 409, 419, 422], $method . ' ' . $route->uri()); + $this->assertStatusIn($response, [400, 401, 403, 404, 405, 409, 419, 422], $method.' '.$route->uri()); } } } diff --git a/tests/Feature/Api/V1/FullSurface/ApiBatch14ApiConsumerContractCompatibilityTest.php b/tests/Feature/Api/V1/FullSurface/ApiBatch14ApiConsumerContractCompatibilityTest.php index b61722d2..01135515 100644 --- a/tests/Feature/Api/V1/FullSurface/ApiBatch14ApiConsumerContractCompatibilityTest.php +++ b/tests/Feature/Api/V1/FullSurface/ApiBatch14ApiConsumerContractCompatibilityTest.php @@ -6,15 +6,17 @@ use Tests\Feature\Api\V1\FullSurface\Support\FullSurfaceE2EContractCase; class ApiBatch14ApiConsumerContractCompatibilityTest extends FullSurfaceE2EContractCase { -public function test_core_identity_contracts_remain_compatible_for_web_mobile_and_api_clients(): void + public function test_core_identity_contracts_remain_compatible_for_web_mobile_and_api_clients(): void { foreach (array_slice($this->apiRoutesContainingAny(['login', 'auth/me', 'me', 'frontend', 'profile']), 0, 25) as $route) { $method = $this->primaryMethod($route); foreach ([['Accept' => 'application/json', 'X-Client' => 'web'], ['Accept' => 'application/json', 'X-Client' => 'mobile', 'X-App-Version' => '1.0.0'], ['Accept' => 'application/vnd.alrahma.v1+json', 'X-Client' => 'api-consumer']] as $headers) { $response = $this->withHeaders($headers)->actingAs($this->actorFor($route->uri()), 'api')->json($method, $this->materializePath($route->uri()), $this->payloadFor($method, $route->uri())); $this->assertControlled($response, $method, $route->uri()); - $this->assertJsonResponseWhenSuccessful($response, $method . ' ' . $route->uri()); - if ($response->isSuccessful()) { $this->assertStringNotContainsString('undefined index', strtolower($response->getContent())); } + $this->assertJsonResponseWhenSuccessful($response, $method.' '.$route->uri()); + if ($response->isSuccessful()) { + $this->assertStringNotContainsString('undefined index', strtolower($response->getContent())); + } } } } @@ -23,13 +25,15 @@ public function test_core_identity_contracts_remain_compatible_for_web_mobile_an { foreach (array_slice($this->apiRoutesContainingAny(['login', 'auth/login']), 0, 10) as $route) { $method = $this->primaryMethod($route); - if ($method !== 'POST') { continue; } + if ($method !== 'POST') { + continue; + } $response = $this->json($method, $this->materializePath($route->uri()), ['email' => $this->admin->email, 'password' => 'password']); $this->assertControlled($response, $method, $route->uri()); if ($response->isSuccessful() && $this->isJsonString($response->getContent())) { $data = $response->json(); - $this->assertTrue(isset($data['token']) || isset($data['access_token']), $route->uri() . ' should expose token/access_token when login succeeds.'); - $this->assertArrayHasKey('user', $data, $route->uri() . ' should expose user when login succeeds.'); + $this->assertTrue(isset($data['token']) || isset($data['access_token']), $route->uri().' should expose token/access_token when login succeeds.'); + $this->assertArrayHasKey('user', $data, $route->uri().' should expose user when login succeeds.'); } } } diff --git a/tests/Feature/Api/V1/FullSurface/ApiBatch14AttendanceDeviceAndKioskAbuseContractTest.php b/tests/Feature/Api/V1/FullSurface/ApiBatch14AttendanceDeviceAndKioskAbuseContractTest.php index 1286d88a..4b9220d0 100644 --- a/tests/Feature/Api/V1/FullSurface/ApiBatch14AttendanceDeviceAndKioskAbuseContractTest.php +++ b/tests/Feature/Api/V1/FullSurface/ApiBatch14AttendanceDeviceAndKioskAbuseContractTest.php @@ -6,7 +6,7 @@ use Tests\Feature\Api\V1\FullSurface\Support\FullSurfaceE2EContractCase; class ApiBatch14AttendanceDeviceAndKioskAbuseContractTest extends FullSurfaceE2EContractCase { -public function test_kiosk_scanner_and_attendance_routes_reject_forged_device_identity(): void + public function test_kiosk_scanner_and_attendance_routes_reject_forged_device_identity(): void { foreach (array_slice($this->apiRoutesContainingAny(['attendance', 'scanner', 'scan', 'kiosk', 'badge', 'dismissal', 'late']), 0, 55) as $route) { $method = $this->primaryMethod($route); @@ -22,10 +22,12 @@ public function test_kiosk_scanner_and_attendance_routes_reject_forged_device_id { foreach (array_slice($this->apiRoutesContainingAny(['attendance', 'scan', 'dismissal', 'late']), 0, 30) as $route) { $method = $this->primaryMethod($route); - if (! in_array($method, ['POST', 'PUT', 'PATCH'], true)) { continue; } + if (! in_array($method, ['POST', 'PUT', 'PATCH'], true)) { + continue; + } foreach ([$this->studentUser, $this->parent] as $actor) { $response = $this->requestAs($actor, $method, $route->uri(), ['student_id' => $this->ids['studentId'], 'status' => 'present', 'verified_by_device' => true, 'override_reason' => 'parent approved']); - $this->assertStatusIn($response, [400, 401, 403, 404, 405, 409, 419, 422], $method . ' ' . $route->uri()); + $this->assertStatusIn($response, [400, 401, 403, 404, 405, 409, 419, 422], $method.' '.$route->uri()); } } } diff --git a/tests/Feature/Api/V1/FullSurface/ApiBatch14AuditTrailTamperResistanceContractTest.php b/tests/Feature/Api/V1/FullSurface/ApiBatch14AuditTrailTamperResistanceContractTest.php index b40a8c8e..b7c58626 100644 --- a/tests/Feature/Api/V1/FullSurface/ApiBatch14AuditTrailTamperResistanceContractTest.php +++ b/tests/Feature/Api/V1/FullSurface/ApiBatch14AuditTrailTamperResistanceContractTest.php @@ -6,7 +6,7 @@ use Tests\Feature\Api\V1\FullSurface\Support\FullSurfaceE2EContractCase; class ApiBatch14AuditTrailTamperResistanceContractTest extends FullSurfaceE2EContractCase { -public function test_audit_and_log_routes_reject_client_supplied_actor_and_timestamp_fields(): void + public function test_audit_and_log_routes_reject_client_supplied_actor_and_timestamp_fields(): void { foreach (array_slice($this->apiRoutesContainingAny(['audit', 'log', 'activity', 'history', 'trace']), 0, 35) as $route) { $method = $this->primaryMethod($route); @@ -22,10 +22,12 @@ public function test_audit_and_log_routes_reject_client_supplied_actor_and_times { foreach (array_slice($this->apiRoutesContainingAny(['audit', 'log', 'history', 'activity']), 0, 25) as $route) { $method = $this->primaryMethod($route); - if (! in_array($method, ['POST', 'PUT', 'PATCH', 'DELETE'], true)) { continue; } + if (! in_array($method, ['POST', 'PUT', 'PATCH', 'DELETE'], true)) { + continue; + } foreach ([$this->parent, $this->teacher, $this->studentUser] as $actor) { $response = $this->requestAs($actor, $method, $route->uri(), ['action' => 'clear', 'purge' => true, 'rewrite' => true]); - $this->assertStatusIn($response, [400, 401, 403, 404, 405, 409, 419, 422], $method . ' ' . $route->uri()); + $this->assertStatusIn($response, [400, 401, 403, 404, 405, 409, 419, 422], $method.' '.$route->uri()); } } } diff --git a/tests/Feature/Api/V1/FullSurface/ApiBatch14BulkAssignmentCollisionContractTest.php b/tests/Feature/Api/V1/FullSurface/ApiBatch14BulkAssignmentCollisionContractTest.php index 4f1e3342..e9a874db 100644 --- a/tests/Feature/Api/V1/FullSurface/ApiBatch14BulkAssignmentCollisionContractTest.php +++ b/tests/Feature/Api/V1/FullSurface/ApiBatch14BulkAssignmentCollisionContractTest.php @@ -6,11 +6,13 @@ use Tests\Feature\Api\V1\FullSurface\Support\FullSurfaceE2EContractCase; class ApiBatch14BulkAssignmentCollisionContractTest extends FullSurfaceE2EContractCase { -public function test_bulk_assignment_routes_handle_duplicate_and_conflicting_ids_cleanly(): void + public function test_bulk_assignment_routes_handle_duplicate_and_conflicting_ids_cleanly(): void { foreach (array_slice($this->apiRoutesContainingAny(['assign', 'assignment', 'enroll', 'bulk', 'batch', 'class', 'teacher', 'student']), 0, 50) as $route) { $method = $this->primaryMethod($route); - if (! in_array($method, ['POST', 'PUT', 'PATCH'], true)) { continue; } + if (! in_array($method, ['POST', 'PUT', 'PATCH'], true)) { + continue; + } $response = $this->requestAs($this->actorFor($route->uri()), $method, $route->uri(), $this->payloadFor($method, $route->uri()) + [ 'student_ids' => [$this->ids['studentId'], $this->ids['studentId'], 99999999], 'teacher_ids' => [$this->teacher->id, $this->teacher->id, $this->parent->id], 'class_section_ids' => [$this->ids['classSectionId'], $this->ids['classSectionId'], 99999999], 'parent_ids' => [$this->ids['parentId'], $this->ids['parentId'], 99999999], 'force' => true, 'overwrite' => true, @@ -27,10 +29,12 @@ public function test_bulk_assignment_routes_handle_duplicate_and_conflicting_ids { foreach (array_slice($this->apiRoutesContainingAny(['assign', 'bulk', 'batch', 'class-sections', 'teacher-class', 'enroll']), 0, 30) as $route) { $method = $this->primaryMethod($route); - if (! in_array($method, ['POST', 'PUT', 'PATCH', 'DELETE'], true)) { continue; } + if (! in_array($method, ['POST', 'PUT', 'PATCH', 'DELETE'], true)) { + continue; + } foreach ([$this->parent, $this->studentUser] as $actor) { $response = $this->requestAs($actor, $method, $route->uri(), ['student_ids' => [$this->ids['studentId']], 'teacher_ids' => [$this->teacher->id], 'class_section_ids' => [$this->ids['classSectionId']], 'all' => true, 'force' => true]); - $this->assertStatusIn($response, [400, 401, 403, 404, 405, 409, 419, 422], $method . ' ' . $route->uri()); + $this->assertStatusIn($response, [400, 401, 403, 404, 405, 409, 419, 422], $method.' '.$route->uri()); } } } diff --git a/tests/Feature/Api/V1/FullSurface/ApiBatch14CrossModuleInvariantRiskRegisterTest.php b/tests/Feature/Api/V1/FullSurface/ApiBatch14CrossModuleInvariantRiskRegisterTest.php index 05081135..5e38210d 100644 --- a/tests/Feature/Api/V1/FullSurface/ApiBatch14CrossModuleInvariantRiskRegisterTest.php +++ b/tests/Feature/Api/V1/FullSurface/ApiBatch14CrossModuleInvariantRiskRegisterTest.php @@ -6,7 +6,7 @@ use Tests\Feature\Api\V1\FullSurface\Support\FullSurfaceE2EContractCase; class ApiBatch14CrossModuleInvariantRiskRegisterTest extends FullSurfaceE2EContractCase { -public function test_batch14_route_families_have_cross_module_risk_coverage(): void + public function test_batch14_route_families_have_cross_module_risk_coverage(): void { $riskFamilies = [ 'session-cookie-boundary' => ['auth', 'login', 'frontend'], @@ -22,7 +22,7 @@ public function test_batch14_route_families_have_cross_module_risk_coverage(): v ]; foreach ($riskFamilies as $name => $needles) { - $this->assertNotEmpty($this->apiRoutesContainingAny($needles), 'Batch 14 expected route coverage for ' . $name); + $this->assertNotEmpty($this->apiRoutesContainingAny($needles), 'Batch 14 expected route coverage for '.$name); } } @@ -32,7 +32,7 @@ public function test_batch14_route_families_have_cross_module_risk_coverage(): v $mutations = array_filter($this->apiRoutesContainingAny([$family]), function ($route): bool { return in_array($this->primaryMethod($route), ['POST', 'PUT', 'PATCH', 'DELETE'], true); }); - $this->assertNotEmpty($mutations, 'Batch 14 expected mutating route family: ' . $family); + $this->assertNotEmpty($mutations, 'Batch 14 expected mutating route family: '.$family); } } } diff --git a/tests/Feature/Api/V1/FullSurface/ApiBatch14DataLifecycleArchivePurgeContractTest.php b/tests/Feature/Api/V1/FullSurface/ApiBatch14DataLifecycleArchivePurgeContractTest.php index 825ff17a..db86628b 100644 --- a/tests/Feature/Api/V1/FullSurface/ApiBatch14DataLifecycleArchivePurgeContractTest.php +++ b/tests/Feature/Api/V1/FullSurface/ApiBatch14DataLifecycleArchivePurgeContractTest.php @@ -6,14 +6,15 @@ use Tests\Feature\Api\V1\FullSurface\Support\FullSurfaceE2EContractCase; class ApiBatch14DataLifecycleArchivePurgeContractTest extends FullSurfaceE2EContractCase { -public function test_archive_purge_and_retention_routes_require_safe_controlled_requests(): void + public function test_archive_purge_and_retention_routes_require_safe_controlled_requests(): void { foreach (array_slice($this->apiRoutesContainingAny(['archive', 'restore', 'purge', 'erase', 'retention', 'delete', 'destroy']), 0, 50) as $route) { $method = $this->primaryMethod($route); $response = $this->requestAs($this->actorFor($route->uri()), $method, $route->uri(), $this->payloadFor($method, $route->uri()) + ['ids' => [$this->ids['studentId'], 99999999, '../../.env'], 'hard_delete' => true, 'purge' => true, 'cascade' => true, 'include_audit_logs' => true, 'reason' => '', 'confirmed' => false]); $this->assertControlled($response, $method, $route->uri()); $body = strtolower($response->getContent()); - $this->assertStringNotContainsString('sqlstate', $body); $this->assertStringNotContainsString('foreign key constraint', $body); + $this->assertStringNotContainsString('sqlstate', $body); + $this->assertStringNotContainsString('foreign key constraint', $body); } } @@ -21,10 +22,12 @@ public function test_archive_purge_and_retention_routes_require_safe_controlled_ { foreach (array_slice($this->apiRoutesContainingAny(['purge', 'erase', 'restore', 'archive', 'delete']), 0, 35) as $route) { $method = $this->primaryMethod($route); - if (! in_array($method, ['POST', 'PUT', 'PATCH', 'DELETE'], true)) { continue; } + if (! in_array($method, ['POST', 'PUT', 'PATCH', 'DELETE'], true)) { + continue; + } foreach ([$this->teacher, $this->parent, $this->studentUser] as $actor) { $response = $this->requestAs($actor, $method, $route->uri(), ['ids' => [$this->ids['studentId']], 'hard_delete' => true, 'purge' => true, 'global' => true]); - $this->assertStatusIn($response, [400, 401, 403, 404, 405, 409, 419, 422], $method . ' ' . $route->uri()); + $this->assertStatusIn($response, [400, 401, 403, 404, 405, 409, 419, 422], $method.' '.$route->uri()); } } } diff --git a/tests/Feature/Api/V1/FullSurface/ApiBatch14FinanceReconciliationAndRefundAbuseContractTest.php b/tests/Feature/Api/V1/FullSurface/ApiBatch14FinanceReconciliationAndRefundAbuseContractTest.php index c83be0f7..a96f3b74 100644 --- a/tests/Feature/Api/V1/FullSurface/ApiBatch14FinanceReconciliationAndRefundAbuseContractTest.php +++ b/tests/Feature/Api/V1/FullSurface/ApiBatch14FinanceReconciliationAndRefundAbuseContractTest.php @@ -6,16 +6,18 @@ use Tests\Feature\Api\V1\FullSurface\Support\FullSurfaceE2EContractCase; class ApiBatch14FinanceReconciliationAndRefundAbuseContractTest extends FullSurfaceE2EContractCase { -public function test_finance_reconciliation_routes_reject_mismatched_invoice_parent_and_payment_fields(): void + public function test_finance_reconciliation_routes_reject_mismatched_invoice_parent_and_payment_fields(): void { foreach (array_slice($this->apiRoutesContainingAny(['finance', 'invoice', 'payment', 'refund', 'transaction', 'ledger', 'reconcile']), 0, 55) as $route) { $method = $this->primaryMethod($route); $response = $this->requestAs($this->actorFor($route->uri()), $method, $route->uri(), $this->payloadFor($method, $route->uri()) + [ - 'invoice_id' => $this->ids['invoiceId'], 'parent_id' => 99999999, 'student_id' => 99999999, 'payment_id' => 99999999, 'refund_id' => 99999999, 'transaction_id' => 'provider-' . uniqid(), 'provider_transaction_id' => 'provider-' . uniqid(), 'amount' => -99999.99, 'net_amount' => -99999.99, 'fee_amount' => -99999.99, 'currency' => 'XXX', 'reconciled' => true, 'force_posted' => true, + 'invoice_id' => $this->ids['invoiceId'], 'parent_id' => 99999999, 'student_id' => 99999999, 'payment_id' => 99999999, 'refund_id' => 99999999, 'transaction_id' => 'provider-'.uniqid(), 'provider_transaction_id' => 'provider-'.uniqid(), 'amount' => -99999.99, 'net_amount' => -99999.99, 'fee_amount' => -99999.99, 'currency' => 'XXX', 'reconciled' => true, 'force_posted' => true, ]); $this->assertControlled($response, $method, $route->uri()); $body = strtolower($response->getContent()); - $this->assertStringNotContainsString('sqlstate', $body); $this->assertStringNotContainsString('duplicate entry', $body); $this->assertStringNotContainsString('foreign key constraint', $body); + $this->assertStringNotContainsString('sqlstate', $body); + $this->assertStringNotContainsString('duplicate entry', $body); + $this->assertStringNotContainsString('foreign key constraint', $body); } } @@ -23,9 +25,11 @@ public function test_finance_reconciliation_routes_reject_mismatched_invoice_par { foreach (array_slice($this->apiRoutesContainingAny(['refund', 'reconcile', 'ledger', 'payment']), 0, 35) as $route) { $method = $this->primaryMethod($route); - if (! in_array($method, ['POST', 'PUT', 'PATCH', 'DELETE'], true)) { continue; } + if (! in_array($method, ['POST', 'PUT', 'PATCH', 'DELETE'], true)) { + continue; + } $response = $this->requestAs($this->parent, $method, $route->uri(), ['invoice_id' => $this->ids['invoiceId'], 'amount' => 100.00, 'approved' => true, 'reconciled' => true, 'refund_now' => true]); - $this->assertStatusIn($response, [400, 401, 403, 404, 405, 409, 419, 422], $method . ' ' . $route->uri()); + $this->assertStatusIn($response, [400, 401, 403, 404, 405, 409, 419, 422], $method.' '.$route->uri()); } } } diff --git a/tests/Feature/Api/V1/FullSurface/ApiBatch14NotificationTemplateInjectionContractTest.php b/tests/Feature/Api/V1/FullSurface/ApiBatch14NotificationTemplateInjectionContractTest.php index 3c4dede5..7546f928 100644 --- a/tests/Feature/Api/V1/FullSurface/ApiBatch14NotificationTemplateInjectionContractTest.php +++ b/tests/Feature/Api/V1/FullSurface/ApiBatch14NotificationTemplateInjectionContractTest.php @@ -6,17 +6,21 @@ use Tests\Feature\Api\V1\FullSurface\Support\FullSurfaceE2EContractCase; class ApiBatch14NotificationTemplateInjectionContractTest extends FullSurfaceE2EContractCase { -public function test_notification_and_template_routes_reject_template_injection_payloads(): void + public function test_notification_and_template_routes_reject_template_injection_payloads(): void { foreach (array_slice($this->apiRoutesContainingAny(['notification', 'notify', 'message', 'email', 'whatsapp', 'template', 'broadcast']), 0, 55) as $route) { $method = $this->primaryMethod($route); - if (! in_array($method, ['POST', 'PUT', 'PATCH'], true)) { continue; } + if (! in_array($method, ['POST', 'PUT', 'PATCH'], true)) { + continue; + } $response = $this->requestAs($this->actorFor($route->uri()), $method, $route->uri(), $this->payloadFor($method, $route->uri()) + [ 'subject' => '{{ config("app.key") }}', 'message' => '{{ $user->password }}', 'body' => '${jndi:ldap://127.0.0.1/a}', 'template' => '@php echo env("APP_KEY"); @endphp', 'channel' => 'all', 'recipients' => ['*'], 'phone' => "+15551234567\r\nBcc: attacker@example.test", 'email' => "victim@example.test\r\nBcc: attacker@example.test", ]); $this->assertControlled($response, $method, $route->uri()); $body = strtolower($response->getContent()); - $this->assertStringNotContainsString('app_key', $body); $this->assertStringNotContainsString('document.cookie', $body); $this->assertStringNotContainsString('jndi:ldap', $body); + $this->assertStringNotContainsString('app_key', $body); + $this->assertStringNotContainsString('document.cookie', $body); + $this->assertStringNotContainsString('jndi:ldap', $body); } } @@ -24,10 +28,12 @@ public function test_notification_and_template_routes_reject_template_injection_ { foreach (array_slice($this->apiRoutesContainingAny(['broadcast', 'notification', 'email', 'whatsapp']), 0, 30) as $route) { $method = $this->primaryMethod($route); - if (! in_array($method, ['POST', 'PUT', 'PATCH'], true)) { continue; } + if (! in_array($method, ['POST', 'PUT', 'PATCH'], true)) { + continue; + } foreach ([$this->parent, $this->teacher, $this->studentUser] as $actor) { $response = $this->requestAs($actor, $method, $route->uri(), ['channel' => 'all', 'recipients' => ['all_school'], 'template_id' => 1, 'force_send' => true]); - $this->assertStatusIn($response, [400, 401, 403, 404, 405, 409, 419, 422], $method . ' ' . $route->uri()); + $this->assertStatusIn($response, [400, 401, 403, 404, 405, 409, 419, 422], $method.' '.$route->uri()); } } } diff --git a/tests/Feature/Api/V1/FullSurface/ApiBatch14PrintExportTamperingContractTest.php b/tests/Feature/Api/V1/FullSurface/ApiBatch14PrintExportTamperingContractTest.php index 6be2e9a7..32552660 100644 --- a/tests/Feature/Api/V1/FullSurface/ApiBatch14PrintExportTamperingContractTest.php +++ b/tests/Feature/Api/V1/FullSurface/ApiBatch14PrintExportTamperingContractTest.php @@ -6,7 +6,7 @@ use Tests\Feature\Api\V1\FullSurface\Support\FullSurfaceE2EContractCase; class ApiBatch14PrintExportTamperingContractTest extends FullSurfaceE2EContractCase { -public function test_print_export_and_document_generation_routes_reject_tampered_scope_fields(): void + public function test_print_export_and_document_generation_routes_reject_tampered_scope_fields(): void { foreach (array_slice($this->apiRoutesContainingAny(['print', 'export', 'download', 'certificate', 'badge', 'receipt', 'transcript', 'report-card']), 0, 50) as $route) { $method = $this->primaryMethod($route); @@ -15,7 +15,9 @@ public function test_print_export_and_document_generation_routes_reject_tampered ]); $this->assertControlled($response, $method, $route->uri()); $body = strtolower($response->getContent()); - foreach (['app_key', 'laravel.log', 'password', 'remember_token', 'sqlstate'] as $forbidden) { $this->assertStringNotContainsString($forbidden, $body); } + foreach (['app_key', 'laravel.log', 'password', 'remember_token', 'sqlstate'] as $forbidden) { + $this->assertStringNotContainsString($forbidden, $body); + } } } @@ -25,7 +27,7 @@ public function test_print_export_and_document_generation_routes_reject_tampered $method = $this->primaryMethod($route); foreach ([$this->parent, $this->teacher, $this->studentUser] as $actor) { $response = $this->requestAs($actor, $method, $route->uri(), ['scope' => 'all_school', 'all_students' => true, 'include_finance' => true, 'include_guardians' => true, 'format' => 'csv']); - $this->assertStatusIn($response, [200, 204, 302, 400, 401, 403, 404, 405, 409, 419, 422], $method . ' ' . $route->uri()); + $this->assertStatusIn($response, [200, 204, 302, 400, 401, 403, 404, 405, 409, 419, 422], $method.' '.$route->uri()); $this->assertStringNotContainsString('remember_token', strtolower($response->getContent())); } } diff --git a/tests/Feature/Api/V1/FullSurface/ApiBatch14RegressionMegaSweepTest.php b/tests/Feature/Api/V1/FullSurface/ApiBatch14RegressionMegaSweepTest.php index 33cb73da..3a7f2415 100644 --- a/tests/Feature/Api/V1/FullSurface/ApiBatch14RegressionMegaSweepTest.php +++ b/tests/Feature/Api/V1/FullSurface/ApiBatch14RegressionMegaSweepTest.php @@ -6,7 +6,7 @@ use Tests\Feature\Api\V1\FullSurface\Support\FullSurfaceE2EContractCase; class ApiBatch14RegressionMegaSweepTest extends FullSurfaceE2EContractCase { -public function test_batch14_mega_sweep_keeps_new_risk_payloads_under_controlled_failure(): void + public function test_batch14_mega_sweep_keeps_new_risk_payloads_under_controlled_failure(): void { $families = [ ['auth', 'login', 'password', 'verify'], ['users', 'roles', 'permissions', 'profile'], ['students', 'parents', 'family', 'guardian'], @@ -26,7 +26,9 @@ public function test_batch14_mega_sweep_keeps_new_risk_payloads_under_controlled $response = $this->requestAs($this->actorFor($route->uri()), $method, $route->uri(), $payload); $this->assertControlled($response, $method, $route->uri()); $body = strtolower($response->getContent()); - foreach (['sqlstate', 'app_key', 'db_password', 'remember_token', 'stack trace', 'laravel.log'] as $forbidden) { $this->assertStringNotContainsString($forbidden, $body); } + foreach (['sqlstate', 'app_key', 'db_password', 'remember_token', 'stack trace', 'laravel.log'] as $forbidden) { + $this->assertStringNotContainsString($forbidden, $body); + } } } } diff --git a/tests/Feature/Api/V1/FullSurface/ApiBatch14RelationshipOwnershipMatrixExpansionTest.php b/tests/Feature/Api/V1/FullSurface/ApiBatch14RelationshipOwnershipMatrixExpansionTest.php index 2c12014a..b4f81e04 100644 --- a/tests/Feature/Api/V1/FullSurface/ApiBatch14RelationshipOwnershipMatrixExpansionTest.php +++ b/tests/Feature/Api/V1/FullSurface/ApiBatch14RelationshipOwnershipMatrixExpansionTest.php @@ -6,15 +6,19 @@ use Tests\Feature\Api\V1\FullSurface\Support\FullSurfaceE2EContractCase; class ApiBatch14RelationshipOwnershipMatrixExpansionTest extends FullSurfaceE2EContractCase { -public function test_relationship_owner_override_fields_are_not_trusted_across_route_families(): void + public function test_relationship_owner_override_fields_are_not_trusted_across_route_families(): void { foreach (array_slice($this->apiRoutesContainingAny(['student', 'parent', 'family', 'invoice', 'attendance', 'score', 'guardian', 'authorized']), 0, 60) as $route) { $method = $this->primaryMethod($route); - if (! in_array($method, ['POST', 'PUT', 'PATCH', 'DELETE'], true)) { continue; } + if (! in_array($method, ['POST', 'PUT', 'PATCH', 'DELETE'], true)) { + continue; + } foreach ([$this->parent, $this->teacher] as $actor) { $response = $this->requestAs($actor, $method, $route->uri(), $this->payloadFor($method, $route->uri()) + ['student_ids' => [$this->ids['studentId'], 99999999], 'parent_id' => 99999999, 'family_id' => 99999999, 'guardian_id' => 99999999, 'owner_id' => $this->admin->id, 'owner_type' => 'App\\Models\\User', 'created_by' => $this->admin->id, 'scope' => 'global']); $this->assertControlled($response, $method, $route->uri()); - $body = strtolower($response->getContent()); $this->assertStringNotContainsString('sqlstate', $body); $this->assertStringNotContainsString('remember_token', $body); + $body = strtolower($response->getContent()); + $this->assertStringNotContainsString('sqlstate', $body); + $this->assertStringNotContainsString('remember_token', $body); } } } @@ -24,7 +28,7 @@ public function test_relationship_owner_override_fields_are_not_trusted_across_r foreach (array_slice($this->apiRoutesContainingAny(['parent', 'family', 'guardian', 'invoice', 'attendance', 'report-card']), 0, 40) as $route) { $method = $this->primaryMethod($route); $response = $this->requestAs($this->studentUser, $method, $route->uri(), ['student_id' => 99999999, 'parent_id' => 99999999, 'family_id' => 99999999, 'include' => 'guardians,invoices,attendance,notes']); - $this->assertStatusIn($response, [200, 204, 302, 400, 401, 403, 404, 405, 409, 419, 422], $method . ' ' . $route->uri()); + $this->assertStatusIn($response, [200, 204, 302, 400, 401, 403, 404, 405, 409, 419, 422], $method.' '.$route->uri()); $this->assertStringNotContainsString('remember_token', strtolower($response->getContent())); } } diff --git a/tests/Feature/Api/V1/FullSurface/ApiBatch14SessionCookieCsrfBoundaryContractTest.php b/tests/Feature/Api/V1/FullSurface/ApiBatch14SessionCookieCsrfBoundaryContractTest.php index 2073b4dc..6a7a1c45 100644 --- a/tests/Feature/Api/V1/FullSurface/ApiBatch14SessionCookieCsrfBoundaryContractTest.php +++ b/tests/Feature/Api/V1/FullSurface/ApiBatch14SessionCookieCsrfBoundaryContractTest.php @@ -6,7 +6,7 @@ use Tests\Feature\Api\V1\FullSurface\Support\FullSurfaceE2EContractCase; class ApiBatch14SessionCookieCsrfBoundaryContractTest extends FullSurfaceE2EContractCase { -public function test_api_auth_routes_do_not_depend_on_browser_session_cookies(): void + public function test_api_auth_routes_do_not_depend_on_browser_session_cookies(): void { foreach (array_slice($this->apiRoutesContainingAny(['auth', 'login', 'logout', 'me', 'profile', 'frontend']), 0, 40) as $route) { $method = $this->primaryMethod($route); @@ -26,7 +26,7 @@ public function test_api_auth_routes_do_not_depend_on_browser_session_cookies(): $response = $this->withCookie('laravel_session', 'fake-admin-browser-session') ->withHeaders(['X-Requested-With' => 'XMLHttpRequest', 'X-CSRF-TOKEN' => 'fake-csrf', 'X-User-Id' => (string) $this->admin->id]) ->json($method, $this->materializePath($route->uri()), $this->payloadFor($method, $route->uri())); - $this->assertStatusIn($response, [401, 403, 404, 405, 419, 422], $method . ' ' . $route->uri()); + $this->assertStatusIn($response, [401, 403, 404, 405, 419, 422], $method.' '.$route->uri()); $this->assertStringNotContainsString('sqlstate', strtolower($response->getContent())); } } diff --git a/tests/Feature/Api/V1/FullSurface/ApiBatch14StoragePathAndSignedUrlContractTest.php b/tests/Feature/Api/V1/FullSurface/ApiBatch14StoragePathAndSignedUrlContractTest.php index ce080f4f..b925e4a1 100644 --- a/tests/Feature/Api/V1/FullSurface/ApiBatch14StoragePathAndSignedUrlContractTest.php +++ b/tests/Feature/Api/V1/FullSurface/ApiBatch14StoragePathAndSignedUrlContractTest.php @@ -6,14 +6,16 @@ use Tests\Feature\Api\V1\FullSurface\Support\FullSurfaceE2EContractCase; class ApiBatch14StoragePathAndSignedUrlContractTest extends FullSurfaceE2EContractCase { -public function test_file_and_signed_url_routes_reject_path_and_disk_overrides(): void + public function test_file_and_signed_url_routes_reject_path_and_disk_overrides(): void { foreach (array_slice($this->apiRoutesContainingAny(['file', 'media', 'upload', 'download', 'export', 'import', 'attachment', 'document', 'signed']), 0, 55) as $route) { $method = $this->primaryMethod($route); $response = $this->requestAs($this->actorFor($route->uri()), $method, $route->uri(), $this->payloadFor($method, $route->uri()) + ['path' => '../../.env', 'filename' => '../../storage/logs/laravel.log', 'disk' => 'local', 'storage_disk' => 's3://private-bucket', 'url' => 'file:///etc/passwd', 'expires' => '2099-01-01', 'signature' => 'forged-signature', 'mime_type' => 'text/x-php']); $this->assertControlled($response, $method, $route->uri()); $body = strtolower($response->getContent()); - foreach (['app_key', 'db_password', 'laravel.log', '/etc/passwd', 'begin rsa private key'] as $forbidden) { $this->assertStringNotContainsString($forbidden, $body); } + foreach (['app_key', 'db_password', 'laravel.log', '/etc/passwd', 'begin rsa private key'] as $forbidden) { + $this->assertStringNotContainsString($forbidden, $body); + } } } @@ -21,8 +23,8 @@ public function test_file_and_signed_url_routes_reject_path_and_disk_overrides() { foreach (array_slice($this->apiRoutesContainingAny(['download', 'signed', 'public', 'media', 'file']), 0, 35) as $route) { $method = $this->primaryMethod($route); - $response = $this->json($method, $this->materializePath($route->uri()) . '?signature=forged&expires=1&path=../../.env'); - $this->assertStatusIn($response, [200, 204, 302, 400, 401, 403, 404, 405, 410, 419, 422], $method . ' ' . $route->uri()); + $response = $this->json($method, $this->materializePath($route->uri()).'?signature=forged&expires=1&path=../../.env'); + $this->assertStatusIn($response, [200, 204, 302, 400, 401, 403, 404, 405, 410, 419, 422], $method.' '.$route->uri()); $this->assertStringNotContainsString('app_key', strtolower($response->getContent())); } } diff --git a/tests/Feature/Api/V1/FullSurface/ApiBatch15AdminImpersonationSessionBoundaryContractTest.php b/tests/Feature/Api/V1/FullSurface/ApiBatch15AdminImpersonationSessionBoundaryContractTest.php index adc9fc2b..41b7118d 100644 --- a/tests/Feature/Api/V1/FullSurface/ApiBatch15AdminImpersonationSessionBoundaryContractTest.php +++ b/tests/Feature/Api/V1/FullSurface/ApiBatch15AdminImpersonationSessionBoundaryContractTest.php @@ -6,7 +6,6 @@ use Tests\Feature\Api\V1\FullSurface\Support\FullSurfaceE2EContractCase; class ApiBatch15AdminImpersonationSessionBoundaryContractTest extends FullSurfaceE2EContractCase { - public function test_batch15_impersonation_and_acting_as_routes_require_real_admin_context_not_payload_claims(): void { $routes = $this->apiRoutesContainingAny(['impersonate', 'acting-as', 'act-as', 'switch-user', 'delegate', 'behalf']); @@ -28,5 +27,4 @@ class ApiBatch15AdminImpersonationSessionBoundaryContractTest extends FullSurfac $this->assertStringNotContainsString('access_token', strtolower($parentResponse->getContent())); } } - } diff --git a/tests/Feature/Api/V1/FullSurface/ApiBatch15AuthTokenRotationAndRevocationContractTest.php b/tests/Feature/Api/V1/FullSurface/ApiBatch15AuthTokenRotationAndRevocationContractTest.php index eed4e194..e770e195 100644 --- a/tests/Feature/Api/V1/FullSurface/ApiBatch15AuthTokenRotationAndRevocationContractTest.php +++ b/tests/Feature/Api/V1/FullSurface/ApiBatch15AuthTokenRotationAndRevocationContractTest.php @@ -6,7 +6,6 @@ use Tests\Feature\Api\V1\FullSurface\Support\FullSurfaceE2EContractCase; class ApiBatch15AuthTokenRotationAndRevocationContractTest extends FullSurfaceE2EContractCase { - public function test_batch15_token_rotation_revocation_and_identity_routes_do_not_accept_stale_or_forged_tokens(): void { $routes = $this->apiRoutesContainingAny(['auth', 'login', 'logout', 'refresh', 'token', 'me', 'profile', 'session']); @@ -46,5 +45,4 @@ class ApiBatch15AuthTokenRotationAndRevocationContractTest extends FullSurfaceE2 } } } - } diff --git a/tests/Feature/Api/V1/FullSurface/ApiBatch15BackgroundCommandAndSchedulerContractTest.php b/tests/Feature/Api/V1/FullSurface/ApiBatch15BackgroundCommandAndSchedulerContractTest.php index f45d2915..37e0d85b 100644 --- a/tests/Feature/Api/V1/FullSurface/ApiBatch15BackgroundCommandAndSchedulerContractTest.php +++ b/tests/Feature/Api/V1/FullSurface/ApiBatch15BackgroundCommandAndSchedulerContractTest.php @@ -6,7 +6,6 @@ use Tests\Feature\Api\V1\FullSurface\Support\FullSurfaceE2EContractCase; class ApiBatch15BackgroundCommandAndSchedulerContractTest extends FullSurfaceE2EContractCase { - public function test_batch15_command_scheduler_and_job_trigger_routes_are_admin_only_and_idempotent(): void { $routes = $this->apiRoutesContainingAny(['command', 'schedule', 'scheduler', 'cron', 'job', 'queue', 'sync', 'recalculate', 'cleanup', 'send']); @@ -30,5 +29,4 @@ class ApiBatch15BackgroundCommandAndSchedulerContractTest extends FullSurfaceE2E $this->assertControlled($adminResponse, $method, $uri); } } - } diff --git a/tests/Feature/Api/V1/FullSurface/ApiBatch15ConsentPrivacyAndDataSubjectRightsContractTest.php b/tests/Feature/Api/V1/FullSurface/ApiBatch15ConsentPrivacyAndDataSubjectRightsContractTest.php index 9d18ee5d..90315bc5 100644 --- a/tests/Feature/Api/V1/FullSurface/ApiBatch15ConsentPrivacyAndDataSubjectRightsContractTest.php +++ b/tests/Feature/Api/V1/FullSurface/ApiBatch15ConsentPrivacyAndDataSubjectRightsContractTest.php @@ -6,7 +6,6 @@ use Tests\Feature\Api\V1\FullSurface\Support\FullSurfaceE2EContractCase; class ApiBatch15ConsentPrivacyAndDataSubjectRightsContractTest extends FullSurfaceE2EContractCase { - public function test_batch15_privacy_consent_erasure_and_export_routes_are_admin_or_owner_scoped(): void { $routes = $this->apiRoutesContainingAny(['privacy', 'consent', 'erase', 'erasure', 'delete-account', 'export-data', 'data-subject', 'retention']); @@ -44,5 +43,4 @@ class ApiBatch15ConsentPrivacyAndDataSubjectRightsContractTest extends FullSurfa $this->assertStringNotContainsString('db_password', strtolower($response->getContent())); } } - } diff --git a/tests/Feature/Api/V1/FullSurface/ApiBatch15CrossSchoolIdentityCollisionContractTest.php b/tests/Feature/Api/V1/FullSurface/ApiBatch15CrossSchoolIdentityCollisionContractTest.php index c9c1c5bd..3e4cb03c 100644 --- a/tests/Feature/Api/V1/FullSurface/ApiBatch15CrossSchoolIdentityCollisionContractTest.php +++ b/tests/Feature/Api/V1/FullSurface/ApiBatch15CrossSchoolIdentityCollisionContractTest.php @@ -6,7 +6,6 @@ use Tests\Feature\Api\V1\FullSurface\Support\FullSurfaceE2EContractCase; class ApiBatch15CrossSchoolIdentityCollisionContractTest extends FullSurfaceE2EContractCase { - public function test_batch15_duplicate_external_identifiers_and_cross_school_aliases_fail_cleanly(): void { $routes = $this->apiRoutesContainingAny(['students', 'users', 'parents', 'family', 'guardian', 'enrollment', 'registration']); @@ -31,5 +30,4 @@ class ApiBatch15CrossSchoolIdentityCollisionContractTest extends FullSurfaceE2EC $this->assertStringNotContainsString('duplicate entry', strtolower($response->getContent())); } } - } diff --git a/tests/Feature/Api/V1/FullSurface/ApiBatch15DataResidencyBackupAndRestoreContractTest.php b/tests/Feature/Api/V1/FullSurface/ApiBatch15DataResidencyBackupAndRestoreContractTest.php index c1680cbe..f86d06ab 100644 --- a/tests/Feature/Api/V1/FullSurface/ApiBatch15DataResidencyBackupAndRestoreContractTest.php +++ b/tests/Feature/Api/V1/FullSurface/ApiBatch15DataResidencyBackupAndRestoreContractTest.php @@ -6,7 +6,6 @@ use Tests\Feature\Api\V1\FullSurface\Support\FullSurfaceE2EContractCase; class ApiBatch15DataResidencyBackupAndRestoreContractTest extends FullSurfaceE2EContractCase { - public function test_batch15_backup_restore_residency_and_snapshot_routes_do_not_expose_or_mutate_for_low_privilege_users(): void { $routes = $this->apiRoutesContainingAny(['backup', 'restore', 'snapshot', 'residency', 'tenant', 'database', 'dump', 'archive']); @@ -32,5 +31,4 @@ class ApiBatch15DataResidencyBackupAndRestoreContractTest extends FullSurfaceE2E } } } - } diff --git a/tests/Feature/Api/V1/FullSurface/ApiBatch15DomainEventOutboxAndNotificationConsistencyContractTest.php b/tests/Feature/Api/V1/FullSurface/ApiBatch15DomainEventOutboxAndNotificationConsistencyContractTest.php index f27096a8..0f7b2b7e 100644 --- a/tests/Feature/Api/V1/FullSurface/ApiBatch15DomainEventOutboxAndNotificationConsistencyContractTest.php +++ b/tests/Feature/Api/V1/FullSurface/ApiBatch15DomainEventOutboxAndNotificationConsistencyContractTest.php @@ -6,7 +6,6 @@ use Tests\Feature\Api\V1\FullSurface\Support\FullSurfaceE2EContractCase; class ApiBatch15DomainEventOutboxAndNotificationConsistencyContractTest extends FullSurfaceE2EContractCase { - public function test_batch15_event_outbox_and_notification_trigger_routes_reject_forged_event_metadata(): void { $routes = $this->apiRoutesContainingAny(['event', 'outbox', 'notification', 'broadcast', 'message', 'email', 'whatsapp', 'webhook']); @@ -28,5 +27,4 @@ class ApiBatch15DomainEventOutboxAndNotificationConsistencyContractTest extends $this->assertControlled($response, $method, $uri); } } - } diff --git a/tests/Feature/Api/V1/FullSurface/ApiBatch15FeatureFlagExperimentAndRolloutContractTest.php b/tests/Feature/Api/V1/FullSurface/ApiBatch15FeatureFlagExperimentAndRolloutContractTest.php index 90b1e66c..e3aaa287 100644 --- a/tests/Feature/Api/V1/FullSurface/ApiBatch15FeatureFlagExperimentAndRolloutContractTest.php +++ b/tests/Feature/Api/V1/FullSurface/ApiBatch15FeatureFlagExperimentAndRolloutContractTest.php @@ -6,7 +6,6 @@ use Tests\Feature\Api\V1\FullSurface\Support\FullSurfaceE2EContractCase; class ApiBatch15FeatureFlagExperimentAndRolloutContractTest extends FullSurfaceE2EContractCase { - public function test_batch15_feature_flag_and_rollout_routes_reject_low_privilege_override_payloads(): void { $routes = $this->apiRoutesContainingAny(['feature', 'flag', 'experiment', 'rollout', 'settings', 'configuration']); @@ -28,5 +27,4 @@ class ApiBatch15FeatureFlagExperimentAndRolloutContractTest extends FullSurfaceE $this->assertControlled($response, $method, $uri); } } - } diff --git a/tests/Feature/Api/V1/FullSurface/ApiBatch15FormRequestAuthorizationCoverageContractTest.php b/tests/Feature/Api/V1/FullSurface/ApiBatch15FormRequestAuthorizationCoverageContractTest.php index 4eac3aa3..6882af99 100644 --- a/tests/Feature/Api/V1/FullSurface/ApiBatch15FormRequestAuthorizationCoverageContractTest.php +++ b/tests/Feature/Api/V1/FullSurface/ApiBatch15FormRequestAuthorizationCoverageContractTest.php @@ -6,7 +6,6 @@ use Tests\Feature\Api\V1\FullSurface\Support\FullSurfaceE2EContractCase; class ApiBatch15FormRequestAuthorizationCoverageContractTest extends FullSurfaceE2EContractCase { - public function test_batch15_mutating_routes_do_not_allow_public_or_low_privilege_form_request_bypass(): void { foreach (array_slice($this->apiRoutes(), 0, 120) as $route) { @@ -29,5 +28,4 @@ class ApiBatch15FormRequestAuthorizationCoverageContractTest extends FullSurface $this->assertControlled($response, $method, $uri); } } - } diff --git a/tests/Feature/Api/V1/FullSurface/ApiBatch15GlobalSearchAndDirectoryEnumerationContractTest.php b/tests/Feature/Api/V1/FullSurface/ApiBatch15GlobalSearchAndDirectoryEnumerationContractTest.php index dc5b1331..8025c9ec 100644 --- a/tests/Feature/Api/V1/FullSurface/ApiBatch15GlobalSearchAndDirectoryEnumerationContractTest.php +++ b/tests/Feature/Api/V1/FullSurface/ApiBatch15GlobalSearchAndDirectoryEnumerationContractTest.php @@ -6,7 +6,6 @@ use Tests\Feature\Api\V1\FullSurface\Support\FullSurfaceE2EContractCase; class ApiBatch15GlobalSearchAndDirectoryEnumerationContractTest extends FullSurfaceE2EContractCase { - public function test_batch15_global_search_directory_and_lookup_routes_resist_enumeration_inputs(): void { $routes = $this->apiRoutesContainingAny(['search', 'lookup', 'directory', 'autocomplete', 'users', 'students', 'parents']); @@ -27,5 +26,4 @@ class ApiBatch15GlobalSearchAndDirectoryEnumerationContractTest extends FullSurf } } } - } diff --git a/tests/Feature/Api/V1/FullSurface/ApiBatch15LocalizationContentDirectionAndEncodingContractTest.php b/tests/Feature/Api/V1/FullSurface/ApiBatch15LocalizationContentDirectionAndEncodingContractTest.php index 50580682..3035cbfa 100644 --- a/tests/Feature/Api/V1/FullSurface/ApiBatch15LocalizationContentDirectionAndEncodingContractTest.php +++ b/tests/Feature/Api/V1/FullSurface/ApiBatch15LocalizationContentDirectionAndEncodingContractTest.php @@ -6,7 +6,6 @@ use Tests\Feature\Api\V1\FullSurface\Support\FullSurfaceE2EContractCase; class ApiBatch15LocalizationContentDirectionAndEncodingContractTest extends FullSurfaceE2EContractCase { - public function test_batch15_rtl_unicode_and_encoding_payloads_remain_controlled_across_text_routes(): void { $routes = $this->apiRoutesContainingAny(['message', 'contact', 'support', 'notification', 'student', 'parent', 'settings', 'certificate']); @@ -31,5 +30,4 @@ class ApiBatch15LocalizationContentDirectionAndEncodingContractTest extends Full $this->assertStringNotContainsString('malformed utf-8', strtolower($response->getContent())); } } - } diff --git a/tests/Feature/Api/V1/FullSurface/ApiBatch15MetadataFilteringAndSparseFieldsetSecurityContractTest.php b/tests/Feature/Api/V1/FullSurface/ApiBatch15MetadataFilteringAndSparseFieldsetSecurityContractTest.php index 5ff26a3b..906c5aca 100644 --- a/tests/Feature/Api/V1/FullSurface/ApiBatch15MetadataFilteringAndSparseFieldsetSecurityContractTest.php +++ b/tests/Feature/Api/V1/FullSurface/ApiBatch15MetadataFilteringAndSparseFieldsetSecurityContractTest.php @@ -6,7 +6,6 @@ use Tests\Feature\Api\V1\FullSurface\Support\FullSurfaceE2EContractCase; class ApiBatch15MetadataFilteringAndSparseFieldsetSecurityContractTest extends FullSurfaceE2EContractCase { - public function test_batch15_sparse_fieldsets_metadata_and_embeds_do_not_reveal_sensitive_associations(): void { $routes = $this->apiRoutesContainingAny(['users', 'students', 'parents', 'family', 'finance', 'attendance', 'reports', 'dashboard']); @@ -31,5 +30,4 @@ class ApiBatch15MetadataFilteringAndSparseFieldsetSecurityContractTest extends F } } } - } diff --git a/tests/Feature/Api/V1/FullSurface/ApiBatch15RateLimitAndAbusePatternContractTest.php b/tests/Feature/Api/V1/FullSurface/ApiBatch15RateLimitAndAbusePatternContractTest.php index 11d8f6a4..3a7c7c74 100644 --- a/tests/Feature/Api/V1/FullSurface/ApiBatch15RateLimitAndAbusePatternContractTest.php +++ b/tests/Feature/Api/V1/FullSurface/ApiBatch15RateLimitAndAbusePatternContractTest.php @@ -6,7 +6,6 @@ use Tests\Feature\Api\V1\FullSurface\Support\FullSurfaceE2EContractCase; class ApiBatch15RateLimitAndAbusePatternContractTest extends FullSurfaceE2EContractCase { - public function test_batch15_repeated_sensitive_requests_return_controlled_responses_without_leaking_internals(): void { $routes = $this->apiRoutesContainingAny(['login', 'password', 'verify', 'contact', 'support', 'message', 'scanner', 'badge']); @@ -16,7 +15,7 @@ class ApiBatch15RateLimitAndAbusePatternContractTest extends FullSurfaceE2EContr $uri = $route->uri(); for ($attempt = 1; $attempt <= 3; $attempt++) { - $response = $this->withHeader('X-Forwarded-For', '203.0.113.' . $attempt) + $response = $this->withHeader('X-Forwarded-For', '203.0.113.'.$attempt) ->requestAs($this->actorFor($uri), $method, $uri, $this->payloadFor($method, $uri) + [ 'email' => 'rate-limit-'.$attempt.'@example.test', 'password' => 'wrong-password', @@ -31,5 +30,4 @@ class ApiBatch15RateLimitAndAbusePatternContractTest extends FullSurfaceE2EContr } } } - } diff --git a/tests/Feature/Api/V1/FullSurface/ApiBatch15RegressionUltraSweepTest.php b/tests/Feature/Api/V1/FullSurface/ApiBatch15RegressionUltraSweepTest.php index c81337be..79ee779c 100644 --- a/tests/Feature/Api/V1/FullSurface/ApiBatch15RegressionUltraSweepTest.php +++ b/tests/Feature/Api/V1/FullSurface/ApiBatch15RegressionUltraSweepTest.php @@ -6,7 +6,6 @@ use Tests\Feature\Api\V1\FullSurface\Support\FullSurfaceE2EContractCase; class ApiBatch15RegressionUltraSweepTest extends FullSurfaceE2EContractCase { - public function test_batch15_ultra_sweep_keeps_cross_domain_hostile_payloads_under_controlled_responses(): void { $families = [ @@ -43,5 +42,4 @@ class ApiBatch15RegressionUltraSweepTest extends FullSurfaceE2EContractCase } } } - } diff --git a/tests/Feature/Api/V1/FullSurface/ApiBatch15ReportDrilldownAndAggregatePrivacyContractTest.php b/tests/Feature/Api/V1/FullSurface/ApiBatch15ReportDrilldownAndAggregatePrivacyContractTest.php index afdb971a..3ab667b2 100644 --- a/tests/Feature/Api/V1/FullSurface/ApiBatch15ReportDrilldownAndAggregatePrivacyContractTest.php +++ b/tests/Feature/Api/V1/FullSurface/ApiBatch15ReportDrilldownAndAggregatePrivacyContractTest.php @@ -6,7 +6,6 @@ use Tests\Feature\Api\V1\FullSurface\Support\FullSurfaceE2EContractCase; class ApiBatch15ReportDrilldownAndAggregatePrivacyContractTest extends FullSurfaceE2EContractCase { - public function test_batch15_report_drilldown_filters_do_not_allow_scope_escape_or_sensitive_field_inclusion(): void { $routes = $this->apiRoutesContainingAny(['report', 'analytics', 'dashboard', 'summary', 'drilldown', 'export']); @@ -34,5 +33,4 @@ class ApiBatch15ReportDrilldownAndAggregatePrivacyContractTest extends FullSurfa } } } - } diff --git a/tests/Feature/Api/V1/FullSurface/ApiBatch15RouteParameterPoisoningExpansionTest.php b/tests/Feature/Api/V1/FullSurface/ApiBatch15RouteParameterPoisoningExpansionTest.php index 0b9ffed2..113e109a 100644 --- a/tests/Feature/Api/V1/FullSurface/ApiBatch15RouteParameterPoisoningExpansionTest.php +++ b/tests/Feature/Api/V1/FullSurface/ApiBatch15RouteParameterPoisoningExpansionTest.php @@ -6,7 +6,6 @@ use Tests\Feature\Api\V1\FullSurface\Support\FullSurfaceE2EContractCase; class ApiBatch15RouteParameterPoisoningExpansionTest extends FullSurfaceE2EContractCase { - public function test_batch15_route_parameters_reject_encoded_path_script_and_type_confusion_payloads_cleanly(): void { $poisoned = ['..%2F..%2F.env', '%00', '', '1 or 1=1', str_repeat('9', 80), '٠١٢٣', 'null', '[]']; @@ -18,7 +17,7 @@ class ApiBatch15RouteParameterPoisoningExpansionTest extends FullSurfaceE2EContr $method = $this->primaryMethod($route); foreach (array_slice($poisoned, 0, 4) as $value) { - $path = '/' . preg_replace('/\{[^}]+\}/', $value, $route->uri()); + $path = '/'.preg_replace('/\{[^}]+\}/', $value, $route->uri()); $this->actingAs($this->actorFor($route->uri()), 'api'); $response = $this->json($method, $path, $this->payloadFor($method, $route->uri())); $this->assertControlled($response, $method, $route->uri()); @@ -26,5 +25,4 @@ class ApiBatch15RouteParameterPoisoningExpansionTest extends FullSurfaceE2EContr } } } - } diff --git a/tests/Feature/Api/V1/FullSurface/ApiBatch15StudentSafetyIncidentEscalationContractTest.php b/tests/Feature/Api/V1/FullSurface/ApiBatch15StudentSafetyIncidentEscalationContractTest.php index 390e3284..fe7ee79c 100644 --- a/tests/Feature/Api/V1/FullSurface/ApiBatch15StudentSafetyIncidentEscalationContractTest.php +++ b/tests/Feature/Api/V1/FullSurface/ApiBatch15StudentSafetyIncidentEscalationContractTest.php @@ -6,7 +6,6 @@ use Tests\Feature\Api\V1\FullSurface\Support\FullSurfaceE2EContractCase; class ApiBatch15StudentSafetyIncidentEscalationContractTest extends FullSurfaceE2EContractCase { - public function test_batch15_safety_incident_escalation_routes_are_not_mutable_by_parents_or_students(): void { $routes = $this->apiRoutesContainingAny(['incident', 'violation', 'safety', 'alert', 'flag', 'dismissal', 'late', 'absence', 'attendance']); @@ -29,5 +28,4 @@ class ApiBatch15StudentSafetyIncidentEscalationContractTest extends FullSurfaceE } } } - } diff --git a/tests/Feature/Api/V1/FullSurface/ApiBatch17AcademicPrerequisiteAndDependencyContractTest.php b/tests/Feature/Api/V1/FullSurface/ApiBatch17AcademicPrerequisiteAndDependencyContractTest.php index 8d3041fd..a43f9cb2 100644 --- a/tests/Feature/Api/V1/FullSurface/ApiBatch17AcademicPrerequisiteAndDependencyContractTest.php +++ b/tests/Feature/Api/V1/FullSurface/ApiBatch17AcademicPrerequisiteAndDependencyContractTest.php @@ -4,7 +4,6 @@ namespace Tests\Feature\Api\V1\FullSurface; use Tests\Feature\Api\V1\FullSurface\Support\FullSurfaceE2EContractCase; - class ApiBatch17AcademicPrerequisiteAndDependencyContractTest extends FullSurfaceE2EContractCase { public function test_academic_actions_do_not_skip_required_prerequisites_or_locks(): void diff --git a/tests/Feature/Api/V1/FullSurface/ApiBatch17ApiContractDiffAndCriticalKeyRegisterTest.php b/tests/Feature/Api/V1/FullSurface/ApiBatch17ApiContractDiffAndCriticalKeyRegisterTest.php index 3b302de2..367cce78 100644 --- a/tests/Feature/Api/V1/FullSurface/ApiBatch17ApiContractDiffAndCriticalKeyRegisterTest.php +++ b/tests/Feature/Api/V1/FullSurface/ApiBatch17ApiContractDiffAndCriticalKeyRegisterTest.php @@ -4,7 +4,6 @@ namespace Tests\Feature\Api\V1\FullSurface; use Tests\Feature\Api\V1\FullSurface\Support\FullSurfaceE2EContractCase; - class ApiBatch17ApiContractDiffAndCriticalKeyRegisterTest extends FullSurfaceE2EContractCase { public function test_critical_identity_finance_attendance_and_academic_payloads_keep_contract_anchors(): void diff --git a/tests/Feature/Api/V1/FullSurface/ApiBatch17AttendanceTamperWindowContractTest.php b/tests/Feature/Api/V1/FullSurface/ApiBatch17AttendanceTamperWindowContractTest.php index ed581198..310e4209 100644 --- a/tests/Feature/Api/V1/FullSurface/ApiBatch17AttendanceTamperWindowContractTest.php +++ b/tests/Feature/Api/V1/FullSurface/ApiBatch17AttendanceTamperWindowContractTest.php @@ -4,7 +4,6 @@ namespace Tests\Feature\Api\V1\FullSurface; use Tests\Feature\Api\V1\FullSurface\Support\FullSurfaceE2EContractCase; - class ApiBatch17AttendanceTamperWindowContractTest extends FullSurfaceE2EContractCase { public function test_attendance_cutoff_backdate_future_and_reason_requirements_are_controlled(): void diff --git a/tests/Feature/Api/V1/FullSurface/ApiBatch17CommunicationRecipientExpansionContractTest.php b/tests/Feature/Api/V1/FullSurface/ApiBatch17CommunicationRecipientExpansionContractTest.php index 9161b259..d8c90dea 100644 --- a/tests/Feature/Api/V1/FullSurface/ApiBatch17CommunicationRecipientExpansionContractTest.php +++ b/tests/Feature/Api/V1/FullSurface/ApiBatch17CommunicationRecipientExpansionContractTest.php @@ -4,7 +4,6 @@ namespace Tests\Feature\Api\V1\FullSurface; use Tests\Feature\Api\V1\FullSurface\Support\FullSurfaceE2EContractCase; - class ApiBatch17CommunicationRecipientExpansionContractTest extends FullSurfaceE2EContractCase { public function test_message_broadcast_and_notification_recipient_scope_cannot_be_expanded_by_payload(): void diff --git a/tests/Feature/Api/V1/FullSurface/ApiBatch17DataLineageAndSourceAttributionContractTest.php b/tests/Feature/Api/V1/FullSurface/ApiBatch17DataLineageAndSourceAttributionContractTest.php index 68d988da..feaea17d 100644 --- a/tests/Feature/Api/V1/FullSurface/ApiBatch17DataLineageAndSourceAttributionContractTest.php +++ b/tests/Feature/Api/V1/FullSurface/ApiBatch17DataLineageAndSourceAttributionContractTest.php @@ -4,7 +4,6 @@ namespace Tests\Feature\Api\V1\FullSurface; use Tests\Feature\Api\V1\FullSurface\Support\FullSurfaceE2EContractCase; - class ApiBatch17DataLineageAndSourceAttributionContractTest extends FullSurfaceE2EContractCase { public function test_client_supplied_lineage_and_audit_source_fields_are_not_authoritative(): void diff --git a/tests/Feature/Api/V1/FullSurface/ApiBatch17ExportColumnLevelSecurityContractTest.php b/tests/Feature/Api/V1/FullSurface/ApiBatch17ExportColumnLevelSecurityContractTest.php index 66fb484b..a42ee2d2 100644 --- a/tests/Feature/Api/V1/FullSurface/ApiBatch17ExportColumnLevelSecurityContractTest.php +++ b/tests/Feature/Api/V1/FullSurface/ApiBatch17ExportColumnLevelSecurityContractTest.php @@ -4,7 +4,6 @@ namespace Tests\Feature\Api\V1\FullSurface; use Tests\Feature\Api\V1\FullSurface\Support\FullSurfaceE2EContractCase; - class ApiBatch17ExportColumnLevelSecurityContractTest extends FullSurfaceE2EContractCase { public function test_export_field_selection_cannot_request_secrets_or_cross_scope_columns(): void diff --git a/tests/Feature/Api/V1/FullSurface/ApiBatch17ExternalIdAndNaturalKeyCollisionContractTest.php b/tests/Feature/Api/V1/FullSurface/ApiBatch17ExternalIdAndNaturalKeyCollisionContractTest.php index 604058ce..3917548a 100644 --- a/tests/Feature/Api/V1/FullSurface/ApiBatch17ExternalIdAndNaturalKeyCollisionContractTest.php +++ b/tests/Feature/Api/V1/FullSurface/ApiBatch17ExternalIdAndNaturalKeyCollisionContractTest.php @@ -4,7 +4,6 @@ namespace Tests\Feature\Api\V1\FullSurface; use Tests\Feature\Api\V1\FullSurface\Support\FullSurfaceE2EContractCase; - class ApiBatch17ExternalIdAndNaturalKeyCollisionContractTest extends FullSurfaceE2EContractCase { public function test_external_ids_student_numbers_and_emails_are_not_cross_domain_authority(): void diff --git a/tests/Feature/Api/V1/FullSurface/ApiBatch17FinancialPeriodCloseAndAdjustmentContractTest.php b/tests/Feature/Api/V1/FullSurface/ApiBatch17FinancialPeriodCloseAndAdjustmentContractTest.php index 832ca735..f56b36b0 100644 --- a/tests/Feature/Api/V1/FullSurface/ApiBatch17FinancialPeriodCloseAndAdjustmentContractTest.php +++ b/tests/Feature/Api/V1/FullSurface/ApiBatch17FinancialPeriodCloseAndAdjustmentContractTest.php @@ -4,7 +4,6 @@ namespace Tests\Feature\Api\V1\FullSurface; use Tests\Feature\Api\V1\FullSurface\Support\FullSurfaceE2EContractCase; - class ApiBatch17FinancialPeriodCloseAndAdjustmentContractTest extends FullSurfaceE2EContractCase { public function test_closed_period_adjustments_writeoffs_and_credit_transfers_are_guarded(): void diff --git a/tests/Feature/Api/V1/FullSurface/ApiBatch17InventoryAssetCustodyContractTest.php b/tests/Feature/Api/V1/FullSurface/ApiBatch17InventoryAssetCustodyContractTest.php index 70c8db71..1fc865aa 100644 --- a/tests/Feature/Api/V1/FullSurface/ApiBatch17InventoryAssetCustodyContractTest.php +++ b/tests/Feature/Api/V1/FullSurface/ApiBatch17InventoryAssetCustodyContractTest.php @@ -4,7 +4,6 @@ namespace Tests\Feature\Api\V1\FullSurface; use Tests\Feature\Api\V1\FullSurface\Support\FullSurfaceE2EContractCase; - class ApiBatch17InventoryAssetCustodyContractTest extends FullSurfaceE2EContractCase { public function test_inventory_asset_custody_transfers_and_disposal_are_not_client_authorized(): void diff --git a/tests/Feature/Api/V1/FullSurface/ApiBatch17MultiGuardBoundaryContractTest.php b/tests/Feature/Api/V1/FullSurface/ApiBatch17MultiGuardBoundaryContractTest.php index dba62eb6..f9add2c4 100644 --- a/tests/Feature/Api/V1/FullSurface/ApiBatch17MultiGuardBoundaryContractTest.php +++ b/tests/Feature/Api/V1/FullSurface/ApiBatch17MultiGuardBoundaryContractTest.php @@ -4,7 +4,6 @@ namespace Tests\Feature\Api\V1\FullSurface; use Tests\Feature\Api\V1\FullSurface\Support\FullSurfaceE2EContractCase; - class ApiBatch17MultiGuardBoundaryContractTest extends FullSurfaceE2EContractCase { public function test_api_guard_boundaries_are_not_crossed_by_session_or_web_identity(): void diff --git a/tests/Feature/Api/V1/FullSurface/ApiBatch17PartialFailureErrorDetailRedactionContractTest.php b/tests/Feature/Api/V1/FullSurface/ApiBatch17PartialFailureErrorDetailRedactionContractTest.php index 6bf3f05b..9d3ec409 100644 --- a/tests/Feature/Api/V1/FullSurface/ApiBatch17PartialFailureErrorDetailRedactionContractTest.php +++ b/tests/Feature/Api/V1/FullSurface/ApiBatch17PartialFailureErrorDetailRedactionContractTest.php @@ -4,7 +4,6 @@ namespace Tests\Feature\Api\V1\FullSurface; use Tests\Feature\Api\V1\FullSurface\Support\FullSurfaceE2EContractCase; - class ApiBatch17PartialFailureErrorDetailRedactionContractTest extends FullSurfaceE2EContractCase { public function test_bulk_partial_failure_details_are_actionable_without_leaking_database_or_secret_details(): void diff --git a/tests/Feature/Api/V1/FullSurface/ApiBatch17PerResourceRateLimitIdentityContractTest.php b/tests/Feature/Api/V1/FullSurface/ApiBatch17PerResourceRateLimitIdentityContractTest.php index 5b35561d..c00e1cbf 100644 --- a/tests/Feature/Api/V1/FullSurface/ApiBatch17PerResourceRateLimitIdentityContractTest.php +++ b/tests/Feature/Api/V1/FullSurface/ApiBatch17PerResourceRateLimitIdentityContractTest.php @@ -4,7 +4,6 @@ namespace Tests\Feature\Api\V1\FullSurface; use Tests\Feature\Api\V1\FullSurface\Support\FullSurfaceE2EContractCase; - class ApiBatch17PerResourceRateLimitIdentityContractTest extends FullSurfaceE2EContractCase { public function test_repeated_sensitive_actions_are_scoped_by_actor_resource_and_operation(): void diff --git a/tests/Feature/Api/V1/FullSurface/ApiBatch17RegressionOmegaSweepTest.php b/tests/Feature/Api/V1/FullSurface/ApiBatch17RegressionOmegaSweepTest.php index 06a72b3f..8208f7fc 100644 --- a/tests/Feature/Api/V1/FullSurface/ApiBatch17RegressionOmegaSweepTest.php +++ b/tests/Feature/Api/V1/FullSurface/ApiBatch17RegressionOmegaSweepTest.php @@ -4,7 +4,6 @@ namespace Tests\Feature\Api\V1\FullSurface; use Tests\Feature\Api\V1\FullSurface\Support\FullSurfaceE2EContractCase; - class ApiBatch17RegressionOmegaSweepTest extends FullSurfaceE2EContractCase { public function test_batch17_omega_sweep_combines_headers_payloads_queries_and_route_parameters(): void diff --git a/tests/Feature/Api/V1/FullSurface/ApiBatch17SearchRankingAndPermissionFilterContractTest.php b/tests/Feature/Api/V1/FullSurface/ApiBatch17SearchRankingAndPermissionFilterContractTest.php index e0683e5f..412cf0aa 100644 --- a/tests/Feature/Api/V1/FullSurface/ApiBatch17SearchRankingAndPermissionFilterContractTest.php +++ b/tests/Feature/Api/V1/FullSurface/ApiBatch17SearchRankingAndPermissionFilterContractTest.php @@ -4,7 +4,6 @@ namespace Tests\Feature\Api\V1\FullSurface; use Tests\Feature\Api\V1\FullSurface\Support\FullSurfaceE2EContractCase; - class ApiBatch17SearchRankingAndPermissionFilterContractTest extends FullSurfaceE2EContractCase { public function test_search_autocomplete_and_lookup_do_not_bypass_permission_filters(): void diff --git a/tests/Feature/Api/V1/FullSurface/ApiBatch17StudentIdentityMergeSplitContractTest.php b/tests/Feature/Api/V1/FullSurface/ApiBatch17StudentIdentityMergeSplitContractTest.php index d564a982..ed8dd426 100644 --- a/tests/Feature/Api/V1/FullSurface/ApiBatch17StudentIdentityMergeSplitContractTest.php +++ b/tests/Feature/Api/V1/FullSurface/ApiBatch17StudentIdentityMergeSplitContractTest.php @@ -4,7 +4,6 @@ namespace Tests\Feature\Api\V1\FullSurface; use Tests\Feature\Api\V1\FullSurface\Support\FullSurfaceE2EContractCase; - class ApiBatch17StudentIdentityMergeSplitContractTest extends FullSurfaceE2EContractCase { public function test_merge_split_and_duplicate_student_identity_routes_reject_unsafe_payloads(): void diff --git a/tests/Feature/Api/V1/FullSurface/ApiBatch17ZeroTrustInternalHeaderContractTest.php b/tests/Feature/Api/V1/FullSurface/ApiBatch17ZeroTrustInternalHeaderContractTest.php index 6b6f30ab..a68dab8a 100644 --- a/tests/Feature/Api/V1/FullSurface/ApiBatch17ZeroTrustInternalHeaderContractTest.php +++ b/tests/Feature/Api/V1/FullSurface/ApiBatch17ZeroTrustInternalHeaderContractTest.php @@ -4,7 +4,6 @@ namespace Tests\Feature\Api\V1\FullSurface; use Tests\Feature\Api\V1\FullSurface\Support\FullSurfaceE2EContractCase; - class ApiBatch17ZeroTrustInternalHeaderContractTest extends FullSurfaceE2EContractCase { public function test_internal_proxy_and_service_headers_do_not_grant_privilege(): void diff --git a/tests/Feature/Api/V1/FullSurface/ApiBatch18ApiKeyServiceAccountAndMachineIdentityContractTest.php b/tests/Feature/Api/V1/FullSurface/ApiBatch18ApiKeyServiceAccountAndMachineIdentityContractTest.php index 3e083430..6028a31a 100644 --- a/tests/Feature/Api/V1/FullSurface/ApiBatch18ApiKeyServiceAccountAndMachineIdentityContractTest.php +++ b/tests/Feature/Api/V1/FullSurface/ApiBatch18ApiKeyServiceAccountAndMachineIdentityContractTest.php @@ -4,7 +4,6 @@ namespace Tests\Feature\Api\V1\FullSurface; use Tests\Feature\Api\V1\FullSurface\Support\FullSurfaceE2EContractCase; - class ApiBatch18ApiKeyServiceAccountAndMachineIdentityContractTest extends FullSurfaceE2EContractCase { public function test_batch18_apikeyserviceaccountandmachineidentity_stays_controlled(): void diff --git a/tests/Feature/Api/V1/FullSurface/ApiBatch18CanonicalIdentifierImmutabilityContractTest.php b/tests/Feature/Api/V1/FullSurface/ApiBatch18CanonicalIdentifierImmutabilityContractTest.php index ffeef331..36e7ce0e 100644 --- a/tests/Feature/Api/V1/FullSurface/ApiBatch18CanonicalIdentifierImmutabilityContractTest.php +++ b/tests/Feature/Api/V1/FullSurface/ApiBatch18CanonicalIdentifierImmutabilityContractTest.php @@ -4,7 +4,6 @@ namespace Tests\Feature\Api\V1\FullSurface; use Tests\Feature\Api\V1\FullSurface\Support\FullSurfaceE2EContractCase; - class ApiBatch18CanonicalIdentifierImmutabilityContractTest extends FullSurfaceE2EContractCase { public function test_batch18_canonicalidentifierimmutability_stays_controlled(): void diff --git a/tests/Feature/Api/V1/FullSurface/ApiBatch18ClassCapacityWaitlistAndScheduleConflictContractTest.php b/tests/Feature/Api/V1/FullSurface/ApiBatch18ClassCapacityWaitlistAndScheduleConflictContractTest.php index 8820229b..ad7554e1 100644 --- a/tests/Feature/Api/V1/FullSurface/ApiBatch18ClassCapacityWaitlistAndScheduleConflictContractTest.php +++ b/tests/Feature/Api/V1/FullSurface/ApiBatch18ClassCapacityWaitlistAndScheduleConflictContractTest.php @@ -4,7 +4,6 @@ namespace Tests\Feature\Api\V1\FullSurface; use Tests\Feature\Api\V1\FullSurface\Support\FullSurfaceE2EContractCase; - class ApiBatch18ClassCapacityWaitlistAndScheduleConflictContractTest extends FullSurfaceE2EContractCase { public function test_batch18_classcapacitywaitlistandscheduleconflict_stays_controlled(): void diff --git a/tests/Feature/Api/V1/FullSurface/ApiBatch18ConsentRevocationAndCommunicationComplianceContractTest.php b/tests/Feature/Api/V1/FullSurface/ApiBatch18ConsentRevocationAndCommunicationComplianceContractTest.php index 8d1bfdb4..23a881f4 100644 --- a/tests/Feature/Api/V1/FullSurface/ApiBatch18ConsentRevocationAndCommunicationComplianceContractTest.php +++ b/tests/Feature/Api/V1/FullSurface/ApiBatch18ConsentRevocationAndCommunicationComplianceContractTest.php @@ -4,7 +4,6 @@ namespace Tests\Feature\Api\V1\FullSurface; use Tests\Feature\Api\V1\FullSurface\Support\FullSurfaceE2EContractCase; - class ApiBatch18ConsentRevocationAndCommunicationComplianceContractTest extends FullSurfaceE2EContractCase { public function test_batch18_consentrevocationandcommunicationcompliance_stays_controlled(): void diff --git a/tests/Feature/Api/V1/FullSurface/ApiBatch18CrossModuleBusinessInvariantRegisterTest.php b/tests/Feature/Api/V1/FullSurface/ApiBatch18CrossModuleBusinessInvariantRegisterTest.php index eff26c3d..3425d02b 100644 --- a/tests/Feature/Api/V1/FullSurface/ApiBatch18CrossModuleBusinessInvariantRegisterTest.php +++ b/tests/Feature/Api/V1/FullSurface/ApiBatch18CrossModuleBusinessInvariantRegisterTest.php @@ -4,7 +4,6 @@ namespace Tests\Feature\Api\V1\FullSurface; use Tests\Feature\Api\V1\FullSurface\Support\FullSurfaceE2EContractCase; - class ApiBatch18CrossModuleBusinessInvariantRegisterTest extends FullSurfaceE2EContractCase { public function test_batch18_business_invariant_route_families_remain_present(): void diff --git a/tests/Feature/Api/V1/FullSurface/ApiBatch18ExportWatermarkAndAuditabilityContractTest.php b/tests/Feature/Api/V1/FullSurface/ApiBatch18ExportWatermarkAndAuditabilityContractTest.php index d427bfe3..ca413d4d 100644 --- a/tests/Feature/Api/V1/FullSurface/ApiBatch18ExportWatermarkAndAuditabilityContractTest.php +++ b/tests/Feature/Api/V1/FullSurface/ApiBatch18ExportWatermarkAndAuditabilityContractTest.php @@ -4,7 +4,6 @@ namespace Tests\Feature\Api\V1\FullSurface; use Tests\Feature\Api\V1\FullSurface\Support\FullSurfaceE2EContractCase; - class ApiBatch18ExportWatermarkAndAuditabilityContractTest extends FullSurfaceE2EContractCase { public function test_batch18_exportwatermarkandauditability_stays_controlled(): void diff --git a/tests/Feature/Api/V1/FullSurface/ApiBatch18FeatureEntitlementAndLicenseBoundaryContractTest.php b/tests/Feature/Api/V1/FullSurface/ApiBatch18FeatureEntitlementAndLicenseBoundaryContractTest.php index 635c1f65..6de1c613 100644 --- a/tests/Feature/Api/V1/FullSurface/ApiBatch18FeatureEntitlementAndLicenseBoundaryContractTest.php +++ b/tests/Feature/Api/V1/FullSurface/ApiBatch18FeatureEntitlementAndLicenseBoundaryContractTest.php @@ -4,7 +4,6 @@ namespace Tests\Feature\Api\V1\FullSurface; use Tests\Feature\Api\V1\FullSurface\Support\FullSurfaceE2EContractCase; - class ApiBatch18FeatureEntitlementAndLicenseBoundaryContractTest extends FullSurfaceE2EContractCase { public function test_batch18_featureentitlementandlicenseboundary_stays_controlled(): void diff --git a/tests/Feature/Api/V1/FullSurface/ApiBatch18FileVirusScanAndQuarantineContractTest.php b/tests/Feature/Api/V1/FullSurface/ApiBatch18FileVirusScanAndQuarantineContractTest.php index e2168257..1658af58 100644 --- a/tests/Feature/Api/V1/FullSurface/ApiBatch18FileVirusScanAndQuarantineContractTest.php +++ b/tests/Feature/Api/V1/FullSurface/ApiBatch18FileVirusScanAndQuarantineContractTest.php @@ -4,7 +4,6 @@ namespace Tests\Feature\Api\V1\FullSurface; use Tests\Feature\Api\V1\FullSurface\Support\FullSurfaceE2EContractCase; - class ApiBatch18FileVirusScanAndQuarantineContractTest extends FullSurfaceE2EContractCase { public function test_batch18_filevirusscanandquarantine_stays_controlled(): void diff --git a/tests/Feature/Api/V1/FullSurface/ApiBatch18FinanceChargebackDisputeContractTest.php b/tests/Feature/Api/V1/FullSurface/ApiBatch18FinanceChargebackDisputeContractTest.php index 916498ae..c4fa4962 100644 --- a/tests/Feature/Api/V1/FullSurface/ApiBatch18FinanceChargebackDisputeContractTest.php +++ b/tests/Feature/Api/V1/FullSurface/ApiBatch18FinanceChargebackDisputeContractTest.php @@ -4,7 +4,6 @@ namespace Tests\Feature\Api\V1\FullSurface; use Tests\Feature\Api\V1\FullSurface\Support\FullSurfaceE2EContractCase; - class ApiBatch18FinanceChargebackDisputeContractTest extends FullSurfaceE2EContractCase { public function test_batch18_financechargebackdispute_stays_controlled(): void diff --git a/tests/Feature/Api/V1/FullSurface/ApiBatch18OrphanedRecordAndCascadeBoundaryContractTest.php b/tests/Feature/Api/V1/FullSurface/ApiBatch18OrphanedRecordAndCascadeBoundaryContractTest.php index 0346ccbb..89e3f6d0 100644 --- a/tests/Feature/Api/V1/FullSurface/ApiBatch18OrphanedRecordAndCascadeBoundaryContractTest.php +++ b/tests/Feature/Api/V1/FullSurface/ApiBatch18OrphanedRecordAndCascadeBoundaryContractTest.php @@ -4,7 +4,6 @@ namespace Tests\Feature\Api\V1\FullSurface; use Tests\Feature\Api\V1\FullSurface\Support\FullSurfaceE2EContractCase; - class ApiBatch18OrphanedRecordAndCascadeBoundaryContractTest extends FullSurfaceE2EContractCase { public function test_batch18_orphanedrecordandcascadeboundary_stays_controlled(): void diff --git a/tests/Feature/Api/V1/FullSurface/ApiBatch18PrivilegeBoundaryAfterRoleMutationContractTest.php b/tests/Feature/Api/V1/FullSurface/ApiBatch18PrivilegeBoundaryAfterRoleMutationContractTest.php index de26f526..1fb1388f 100644 --- a/tests/Feature/Api/V1/FullSurface/ApiBatch18PrivilegeBoundaryAfterRoleMutationContractTest.php +++ b/tests/Feature/Api/V1/FullSurface/ApiBatch18PrivilegeBoundaryAfterRoleMutationContractTest.php @@ -4,7 +4,6 @@ namespace Tests\Feature\Api\V1\FullSurface; use Tests\Feature\Api\V1\FullSurface\Support\FullSurfaceE2EContractCase; - class ApiBatch18PrivilegeBoundaryAfterRoleMutationContractTest extends FullSurfaceE2EContractCase { public function test_batch18_privilegeboundaryafterrolemutation_stays_controlled(): void diff --git a/tests/Feature/Api/V1/FullSurface/ApiBatch18RegressionFinalitySweepTest.php b/tests/Feature/Api/V1/FullSurface/ApiBatch18RegressionFinalitySweepTest.php index e63327f3..b7f1bd47 100644 --- a/tests/Feature/Api/V1/FullSurface/ApiBatch18RegressionFinalitySweepTest.php +++ b/tests/Feature/Api/V1/FullSurface/ApiBatch18RegressionFinalitySweepTest.php @@ -4,7 +4,6 @@ namespace Tests\Feature\Api\V1\FullSurface; use Tests\Feature\Api\V1\FullSurface\Support\FullSurfaceE2EContractCase; - class ApiBatch18RegressionFinalitySweepTest extends FullSurfaceE2EContractCase { public function test_batch18_combined_hostile_payload_sweep_stays_controlled(): void diff --git a/tests/Feature/Api/V1/FullSurface/ApiBatch18ReportDrillthroughAndRowLevelSecurityContractTest.php b/tests/Feature/Api/V1/FullSurface/ApiBatch18ReportDrillthroughAndRowLevelSecurityContractTest.php index 9a01ac71..5023a51a 100644 --- a/tests/Feature/Api/V1/FullSurface/ApiBatch18ReportDrillthroughAndRowLevelSecurityContractTest.php +++ b/tests/Feature/Api/V1/FullSurface/ApiBatch18ReportDrillthroughAndRowLevelSecurityContractTest.php @@ -4,7 +4,6 @@ namespace Tests\Feature\Api\V1\FullSurface; use Tests\Feature\Api\V1\FullSurface\Support\FullSurfaceE2EContractCase; - class ApiBatch18ReportDrillthroughAndRowLevelSecurityContractTest extends FullSurfaceE2EContractCase { public function test_batch18_reportdrillthroughandrowlevelsecurity_stays_controlled(): void diff --git a/tests/Feature/Api/V1/FullSurface/ApiBatch18StudentMedicalAndSafetyDataMinimizationContractTest.php b/tests/Feature/Api/V1/FullSurface/ApiBatch18StudentMedicalAndSafetyDataMinimizationContractTest.php index 555f1961..8df5684a 100644 --- a/tests/Feature/Api/V1/FullSurface/ApiBatch18StudentMedicalAndSafetyDataMinimizationContractTest.php +++ b/tests/Feature/Api/V1/FullSurface/ApiBatch18StudentMedicalAndSafetyDataMinimizationContractTest.php @@ -4,7 +4,6 @@ namespace Tests\Feature\Api\V1\FullSurface; use Tests\Feature\Api\V1\FullSurface\Support\FullSurfaceE2EContractCase; - class ApiBatch18StudentMedicalAndSafetyDataMinimizationContractTest extends FullSurfaceE2EContractCase { public function test_batch18_studentmedicalandsafetydataminimization_stays_controlled(): void diff --git a/tests/Feature/Api/V1/FullSurface/ApiBatch18TemporalAuditAndTimezoneForgeryContractTest.php b/tests/Feature/Api/V1/FullSurface/ApiBatch18TemporalAuditAndTimezoneForgeryContractTest.php index 606e560f..1ad161ab 100644 --- a/tests/Feature/Api/V1/FullSurface/ApiBatch18TemporalAuditAndTimezoneForgeryContractTest.php +++ b/tests/Feature/Api/V1/FullSurface/ApiBatch18TemporalAuditAndTimezoneForgeryContractTest.php @@ -4,7 +4,6 @@ namespace Tests\Feature\Api\V1\FullSurface; use Tests\Feature\Api\V1\FullSurface\Support\FullSurfaceE2EContractCase; - class ApiBatch18TemporalAuditAndTimezoneForgeryContractTest extends FullSurfaceE2EContractCase { public function test_batch18_temporalauditandtimezoneforgery_stays_controlled(): void diff --git a/tests/Feature/Api/V1/FullSurface/ApiBatch18TwoPersonApprovalAndSegregationContractTest.php b/tests/Feature/Api/V1/FullSurface/ApiBatch18TwoPersonApprovalAndSegregationContractTest.php index f4e7c064..6171148b 100644 --- a/tests/Feature/Api/V1/FullSurface/ApiBatch18TwoPersonApprovalAndSegregationContractTest.php +++ b/tests/Feature/Api/V1/FullSurface/ApiBatch18TwoPersonApprovalAndSegregationContractTest.php @@ -4,7 +4,6 @@ namespace Tests\Feature\Api\V1\FullSurface; use Tests\Feature\Api\V1\FullSurface\Support\FullSurfaceE2EContractCase; - class ApiBatch18TwoPersonApprovalAndSegregationContractTest extends FullSurfaceE2EContractCase { public function test_batch18_twopersonapprovalandsegregation_stays_controlled(): void diff --git a/tests/Feature/Api/V1/FullSurface/ApiBatch18WebhookOrderingAndOutOfSequenceContractTest.php b/tests/Feature/Api/V1/FullSurface/ApiBatch18WebhookOrderingAndOutOfSequenceContractTest.php index d52c6050..b44e7702 100644 --- a/tests/Feature/Api/V1/FullSurface/ApiBatch18WebhookOrderingAndOutOfSequenceContractTest.php +++ b/tests/Feature/Api/V1/FullSurface/ApiBatch18WebhookOrderingAndOutOfSequenceContractTest.php @@ -4,7 +4,6 @@ namespace Tests\Feature\Api\V1\FullSurface; use Tests\Feature\Api\V1\FullSurface\Support\FullSurfaceE2EContractCase; - class ApiBatch18WebhookOrderingAndOutOfSequenceContractTest extends FullSurfaceE2EContractCase { public function test_batch18_webhookorderingandoutofsequence_stays_controlled(): void diff --git a/tests/Feature/Api/V1/FullSurface/ApiBatch19AggregateDashboardScopeContractTest.php b/tests/Feature/Api/V1/FullSurface/ApiBatch19AggregateDashboardScopeContractTest.php index cecb1136..c8331d4a 100644 --- a/tests/Feature/Api/V1/FullSurface/ApiBatch19AggregateDashboardScopeContractTest.php +++ b/tests/Feature/Api/V1/FullSurface/ApiBatch19AggregateDashboardScopeContractTest.php @@ -4,7 +4,6 @@ namespace Tests\Feature\Api\V1\FullSurface; use Tests\Feature\Api\V1\FullSurface\Support\FullSurfaceE2EContractCase; - class ApiBatch19AggregateDashboardScopeContractTest extends FullSurfaceE2EContractCase { public function test_batch19_aggregate_dashboard_scope_stays_controlled(): void diff --git a/tests/Feature/Api/V1/FullSurface/ApiBatch19AppendOnlyAuditLedgerContractTest.php b/tests/Feature/Api/V1/FullSurface/ApiBatch19AppendOnlyAuditLedgerContractTest.php index 1cb4a723..e949c40d 100644 --- a/tests/Feature/Api/V1/FullSurface/ApiBatch19AppendOnlyAuditLedgerContractTest.php +++ b/tests/Feature/Api/V1/FullSurface/ApiBatch19AppendOnlyAuditLedgerContractTest.php @@ -4,7 +4,6 @@ namespace Tests\Feature\Api\V1\FullSurface; use Tests\Feature\Api\V1\FullSurface\Support\FullSurfaceE2EContractCase; - class ApiBatch19AppendOnlyAuditLedgerContractTest extends FullSurfaceE2EContractCase { public function test_batch19_append_only_audit_ledger_stays_controlled(): void diff --git a/tests/Feature/Api/V1/FullSurface/ApiBatch19AttendanceExcuseDocumentLifecycleContractTest.php b/tests/Feature/Api/V1/FullSurface/ApiBatch19AttendanceExcuseDocumentLifecycleContractTest.php index aa433b98..d2a664e0 100644 --- a/tests/Feature/Api/V1/FullSurface/ApiBatch19AttendanceExcuseDocumentLifecycleContractTest.php +++ b/tests/Feature/Api/V1/FullSurface/ApiBatch19AttendanceExcuseDocumentLifecycleContractTest.php @@ -4,7 +4,6 @@ namespace Tests\Feature\Api\V1\FullSurface; use Tests\Feature\Api\V1\FullSurface\Support\FullSurfaceE2EContractCase; - class ApiBatch19AttendanceExcuseDocumentLifecycleContractTest extends FullSurfaceE2EContractCase { public function test_batch19_attendance_excuse_document_lifecycle_stays_controlled(): void diff --git a/tests/Feature/Api/V1/FullSurface/ApiBatch19AttributeBasedAccessConditionContractTest.php b/tests/Feature/Api/V1/FullSurface/ApiBatch19AttributeBasedAccessConditionContractTest.php index 5661d445..1fa564dc 100644 --- a/tests/Feature/Api/V1/FullSurface/ApiBatch19AttributeBasedAccessConditionContractTest.php +++ b/tests/Feature/Api/V1/FullSurface/ApiBatch19AttributeBasedAccessConditionContractTest.php @@ -4,7 +4,6 @@ namespace Tests\Feature\Api\V1\FullSurface; use Tests\Feature\Api\V1\FullSurface\Support\FullSurfaceE2EContractCase; - class ApiBatch19AttributeBasedAccessConditionContractTest extends FullSurfaceE2EContractCase { public function test_batch19_attribute_based_access_condition_stays_controlled(): void diff --git a/tests/Feature/Api/V1/FullSurface/ApiBatch19BackupRestoreIsolationContractTest.php b/tests/Feature/Api/V1/FullSurface/ApiBatch19BackupRestoreIsolationContractTest.php index 6e57b836..37843e1c 100644 --- a/tests/Feature/Api/V1/FullSurface/ApiBatch19BackupRestoreIsolationContractTest.php +++ b/tests/Feature/Api/V1/FullSurface/ApiBatch19BackupRestoreIsolationContractTest.php @@ -4,7 +4,6 @@ namespace Tests\Feature\Api\V1\FullSurface; use Tests\Feature\Api\V1\FullSurface\Support\FullSurfaceE2EContractCase; - class ApiBatch19BackupRestoreIsolationContractTest extends FullSurfaceE2EContractCase { public function test_batch19_backup_restore_isolation_stays_controlled(): void diff --git a/tests/Feature/Api/V1/FullSurface/ApiBatch19ClassTransferTranscriptContinuityContractTest.php b/tests/Feature/Api/V1/FullSurface/ApiBatch19ClassTransferTranscriptContinuityContractTest.php index 806debf8..e4c1a9d8 100644 --- a/tests/Feature/Api/V1/FullSurface/ApiBatch19ClassTransferTranscriptContinuityContractTest.php +++ b/tests/Feature/Api/V1/FullSurface/ApiBatch19ClassTransferTranscriptContinuityContractTest.php @@ -4,7 +4,6 @@ namespace Tests\Feature\Api\V1\FullSurface; use Tests\Feature\Api\V1\FullSurface\Support\FullSurfaceE2EContractCase; - class ApiBatch19ClassTransferTranscriptContinuityContractTest extends FullSurfaceE2EContractCase { public function test_batch19_class_transfer_transcript_continuity_stays_controlled(): void diff --git a/tests/Feature/Api/V1/FullSurface/ApiBatch19CommunicationBounceAndSuppressionWebhookContractTest.php b/tests/Feature/Api/V1/FullSurface/ApiBatch19CommunicationBounceAndSuppressionWebhookContractTest.php index 92ed2541..e24080e2 100644 --- a/tests/Feature/Api/V1/FullSurface/ApiBatch19CommunicationBounceAndSuppressionWebhookContractTest.php +++ b/tests/Feature/Api/V1/FullSurface/ApiBatch19CommunicationBounceAndSuppressionWebhookContractTest.php @@ -4,7 +4,6 @@ namespace Tests\Feature\Api\V1\FullSurface; use Tests\Feature\Api\V1\FullSurface\Support\FullSurfaceE2EContractCase; - class ApiBatch19CommunicationBounceAndSuppressionWebhookContractTest extends FullSurfaceE2EContractCase { public function test_batch19_communication_bounce_and_suppression_webhook_stays_controlled(): void diff --git a/tests/Feature/Api/V1/FullSurface/ApiBatch19CustodyRestrictionPickupAuthorizationContractTest.php b/tests/Feature/Api/V1/FullSurface/ApiBatch19CustodyRestrictionPickupAuthorizationContractTest.php index 3e2b96bc..b0635cd9 100644 --- a/tests/Feature/Api/V1/FullSurface/ApiBatch19CustodyRestrictionPickupAuthorizationContractTest.php +++ b/tests/Feature/Api/V1/FullSurface/ApiBatch19CustodyRestrictionPickupAuthorizationContractTest.php @@ -4,7 +4,6 @@ namespace Tests\Feature\Api\V1\FullSurface; use Tests\Feature\Api\V1\FullSurface\Support\FullSurfaceE2EContractCase; - class ApiBatch19CustodyRestrictionPickupAuthorizationContractTest extends FullSurfaceE2EContractCase { public function test_batch19_custody_restriction_pickup_authorization_stays_controlled(): void diff --git a/tests/Feature/Api/V1/FullSurface/ApiBatch19NestedRelationshipRedactionContractTest.php b/tests/Feature/Api/V1/FullSurface/ApiBatch19NestedRelationshipRedactionContractTest.php index 27e97868..ae7eedb2 100644 --- a/tests/Feature/Api/V1/FullSurface/ApiBatch19NestedRelationshipRedactionContractTest.php +++ b/tests/Feature/Api/V1/FullSurface/ApiBatch19NestedRelationshipRedactionContractTest.php @@ -4,7 +4,6 @@ namespace Tests\Feature\Api\V1\FullSurface; use Tests\Feature\Api\V1\FullSurface\Support\FullSurfaceE2EContractCase; - class ApiBatch19NestedRelationshipRedactionContractTest extends FullSurfaceE2EContractCase { public function test_batch19_nested_relationship_redaction_stays_controlled(): void diff --git a/tests/Feature/Api/V1/FullSurface/ApiBatch19OperationalAndPrivacyInvariantRegisterTest.php b/tests/Feature/Api/V1/FullSurface/ApiBatch19OperationalAndPrivacyInvariantRegisterTest.php index 001cfd57..c717611a 100644 --- a/tests/Feature/Api/V1/FullSurface/ApiBatch19OperationalAndPrivacyInvariantRegisterTest.php +++ b/tests/Feature/Api/V1/FullSurface/ApiBatch19OperationalAndPrivacyInvariantRegisterTest.php @@ -4,7 +4,6 @@ namespace Tests\Feature\Api\V1\FullSurface; use Tests\Feature\Api\V1\FullSurface\Support\FullSurfaceE2EContractCase; - class ApiBatch19OperationalAndPrivacyInvariantRegisterTest extends FullSurfaceE2EContractCase { public function test_batch19_operational_and_privacy_route_families_remain_present(): void diff --git a/tests/Feature/Api/V1/FullSurface/ApiBatch19PaymentAllocationAndBalanceContractTest.php b/tests/Feature/Api/V1/FullSurface/ApiBatch19PaymentAllocationAndBalanceContractTest.php index dc498b1c..bb82d802 100644 --- a/tests/Feature/Api/V1/FullSurface/ApiBatch19PaymentAllocationAndBalanceContractTest.php +++ b/tests/Feature/Api/V1/FullSurface/ApiBatch19PaymentAllocationAndBalanceContractTest.php @@ -4,7 +4,6 @@ namespace Tests\Feature\Api\V1\FullSurface; use Tests\Feature\Api\V1\FullSurface\Support\FullSurfaceE2EContractCase; - class ApiBatch19PaymentAllocationAndBalanceContractTest extends FullSurfaceE2EContractCase { public function test_batch19_payment_allocation_and_balance_stays_controlled(): void diff --git a/tests/Feature/Api/V1/FullSurface/ApiBatch19RefundMethodAndSettlementBoundaryContractTest.php b/tests/Feature/Api/V1/FullSurface/ApiBatch19RefundMethodAndSettlementBoundaryContractTest.php index 69c1774e..c52b9d16 100644 --- a/tests/Feature/Api/V1/FullSurface/ApiBatch19RefundMethodAndSettlementBoundaryContractTest.php +++ b/tests/Feature/Api/V1/FullSurface/ApiBatch19RefundMethodAndSettlementBoundaryContractTest.php @@ -4,7 +4,6 @@ namespace Tests\Feature\Api\V1\FullSurface; use Tests\Feature\Api\V1\FullSurface\Support\FullSurfaceE2EContractCase; - class ApiBatch19RefundMethodAndSettlementBoundaryContractTest extends FullSurfaceE2EContractCase { public function test_batch19_refund_method_and_settlement_boundary_stays_controlled(): void diff --git a/tests/Feature/Api/V1/FullSurface/ApiBatch19RegressionContinuitySweepTest.php b/tests/Feature/Api/V1/FullSurface/ApiBatch19RegressionContinuitySweepTest.php index ceebf25b..b6902363 100644 --- a/tests/Feature/Api/V1/FullSurface/ApiBatch19RegressionContinuitySweepTest.php +++ b/tests/Feature/Api/V1/FullSurface/ApiBatch19RegressionContinuitySweepTest.php @@ -4,7 +4,6 @@ namespace Tests\Feature\Api\V1\FullSurface; use Tests\Feature\Api\V1\FullSurface\Support\FullSurfaceE2EContractCase; - class ApiBatch19RegressionContinuitySweepTest extends FullSurfaceE2EContractCase { public function test_batch19_continuity_and_privacy_sweep_stays_controlled(): void diff --git a/tests/Feature/Api/V1/FullSurface/ApiBatch19ReportCacheInvalidationContractTest.php b/tests/Feature/Api/V1/FullSurface/ApiBatch19ReportCacheInvalidationContractTest.php index 0ffa56a0..898cf6bf 100644 --- a/tests/Feature/Api/V1/FullSurface/ApiBatch19ReportCacheInvalidationContractTest.php +++ b/tests/Feature/Api/V1/FullSurface/ApiBatch19ReportCacheInvalidationContractTest.php @@ -4,7 +4,6 @@ namespace Tests\Feature\Api\V1\FullSurface; use Tests\Feature\Api\V1\FullSurface\Support\FullSurfaceE2EContractCase; - class ApiBatch19ReportCacheInvalidationContractTest extends FullSurfaceE2EContractCase { public function test_batch19_report_cache_invalidation_stays_controlled(): void diff --git a/tests/Feature/Api/V1/FullSurface/ApiBatch19RevokedDeletedUserTokenBoundaryContractTest.php b/tests/Feature/Api/V1/FullSurface/ApiBatch19RevokedDeletedUserTokenBoundaryContractTest.php index e6f17b69..3d85a265 100644 --- a/tests/Feature/Api/V1/FullSurface/ApiBatch19RevokedDeletedUserTokenBoundaryContractTest.php +++ b/tests/Feature/Api/V1/FullSurface/ApiBatch19RevokedDeletedUserTokenBoundaryContractTest.php @@ -4,7 +4,6 @@ namespace Tests\Feature\Api\V1\FullSurface; use Tests\Feature\Api\V1\FullSurface\Support\FullSurfaceE2EContractCase; - class ApiBatch19RevokedDeletedUserTokenBoundaryContractTest extends FullSurfaceE2EContractCase { public function test_batch19_revoked_deleted_user_token_boundary_stays_controlled(): void diff --git a/tests/Feature/Api/V1/FullSurface/ApiBatch19TeacherSubstitutionCoverageContractTest.php b/tests/Feature/Api/V1/FullSurface/ApiBatch19TeacherSubstitutionCoverageContractTest.php index 3f9d029e..dafd1e84 100644 --- a/tests/Feature/Api/V1/FullSurface/ApiBatch19TeacherSubstitutionCoverageContractTest.php +++ b/tests/Feature/Api/V1/FullSurface/ApiBatch19TeacherSubstitutionCoverageContractTest.php @@ -4,7 +4,6 @@ namespace Tests\Feature\Api\V1\FullSurface; use Tests\Feature\Api\V1\FullSurface\Support\FullSurfaceE2EContractCase; - class ApiBatch19TeacherSubstitutionCoverageContractTest extends FullSurfaceE2EContractCase { public function test_batch19_teacher_substitution_coverage_stays_controlled(): void diff --git a/tests/Feature/Api/V1/FullSurface/ApiBatch19TemporaryUrlExpirationRevocationContractTest.php b/tests/Feature/Api/V1/FullSurface/ApiBatch19TemporaryUrlExpirationRevocationContractTest.php index a148c868..b1e13ac5 100644 --- a/tests/Feature/Api/V1/FullSurface/ApiBatch19TemporaryUrlExpirationRevocationContractTest.php +++ b/tests/Feature/Api/V1/FullSurface/ApiBatch19TemporaryUrlExpirationRevocationContractTest.php @@ -4,7 +4,6 @@ namespace Tests\Feature\Api\V1\FullSurface; use Tests\Feature\Api\V1\FullSurface\Support\FullSurfaceE2EContractCase; - class ApiBatch19TemporaryUrlExpirationRevocationContractTest extends FullSurfaceE2EContractCase { public function test_batch19_temporary_url_expiration_revocation_stays_controlled(): void diff --git a/tests/Feature/Api/V1/FullSurface/ApiBatch19ThirdPartySyncCanonicalizationContractTest.php b/tests/Feature/Api/V1/FullSurface/ApiBatch19ThirdPartySyncCanonicalizationContractTest.php index 7390fc20..78f7f3e4 100644 --- a/tests/Feature/Api/V1/FullSurface/ApiBatch19ThirdPartySyncCanonicalizationContractTest.php +++ b/tests/Feature/Api/V1/FullSurface/ApiBatch19ThirdPartySyncCanonicalizationContractTest.php @@ -4,7 +4,6 @@ namespace Tests\Feature\Api\V1\FullSurface; use Tests\Feature\Api\V1\FullSurface\Support\FullSurfaceE2EContractCase; - class ApiBatch19ThirdPartySyncCanonicalizationContractTest extends FullSurfaceE2EContractCase { public function test_batch19_third_party_sync_canonicalization_stays_controlled(): void diff --git a/tests/Feature/Api/V1/FullSurface/ApiBatch20AdminAuditExportRedactionContractTest.php b/tests/Feature/Api/V1/FullSurface/ApiBatch20AdminAuditExportRedactionContractTest.php index 8c936589..4a714112 100644 --- a/tests/Feature/Api/V1/FullSurface/ApiBatch20AdminAuditExportRedactionContractTest.php +++ b/tests/Feature/Api/V1/FullSurface/ApiBatch20AdminAuditExportRedactionContractTest.php @@ -4,7 +4,6 @@ namespace Tests\Feature\Api\V1\FullSurface; use Tests\Feature\Api\V1\FullSurface\Support\FullSurfaceE2EContractCase; - class ApiBatch20AdminAuditExportRedactionContractTest extends FullSurfaceE2EContractCase { public function test_batch20_admin_audit_export_redaction_stays_controlled(): void diff --git a/tests/Feature/Api/V1/FullSurface/ApiBatch20ApiSunsetDeprecationAndCompatibilityContractTest.php b/tests/Feature/Api/V1/FullSurface/ApiBatch20ApiSunsetDeprecationAndCompatibilityContractTest.php index 67fa59c7..4261a4d4 100644 --- a/tests/Feature/Api/V1/FullSurface/ApiBatch20ApiSunsetDeprecationAndCompatibilityContractTest.php +++ b/tests/Feature/Api/V1/FullSurface/ApiBatch20ApiSunsetDeprecationAndCompatibilityContractTest.php @@ -4,7 +4,6 @@ namespace Tests\Feature\Api\V1\FullSurface; use Tests\Feature\Api\V1\FullSurface\Support\FullSurfaceE2EContractCase; - class ApiBatch20ApiSunsetDeprecationAndCompatibilityContractTest extends FullSurfaceE2EContractCase { public function test_batch20_api_sunset_deprecation_and_compatibility_stays_controlled(): void diff --git a/tests/Feature/Api/V1/FullSurface/ApiBatch20AttendanceGeofenceAndDeviceTrustContractTest.php b/tests/Feature/Api/V1/FullSurface/ApiBatch20AttendanceGeofenceAndDeviceTrustContractTest.php index 85ad6419..6e2a93b3 100644 --- a/tests/Feature/Api/V1/FullSurface/ApiBatch20AttendanceGeofenceAndDeviceTrustContractTest.php +++ b/tests/Feature/Api/V1/FullSurface/ApiBatch20AttendanceGeofenceAndDeviceTrustContractTest.php @@ -4,7 +4,6 @@ namespace Tests\Feature\Api\V1\FullSurface; use Tests\Feature\Api\V1\FullSurface\Support\FullSurfaceE2EContractCase; - class ApiBatch20AttendanceGeofenceAndDeviceTrustContractTest extends FullSurfaceE2EContractCase { public function test_batch20_attendance_geofence_and_device_trust_stays_controlled(): void diff --git a/tests/Feature/Api/V1/FullSurface/ApiBatch20CashDrawerManualPaymentIntegrityContractTest.php b/tests/Feature/Api/V1/FullSurface/ApiBatch20CashDrawerManualPaymentIntegrityContractTest.php index 7e8d0a44..f2b78a9c 100644 --- a/tests/Feature/Api/V1/FullSurface/ApiBatch20CashDrawerManualPaymentIntegrityContractTest.php +++ b/tests/Feature/Api/V1/FullSurface/ApiBatch20CashDrawerManualPaymentIntegrityContractTest.php @@ -4,7 +4,6 @@ namespace Tests\Feature\Api\V1\FullSurface; use Tests\Feature\Api\V1\FullSurface\Support\FullSurfaceE2EContractCase; - class ApiBatch20CashDrawerManualPaymentIntegrityContractTest extends FullSurfaceE2EContractCase { public function test_batch20_cash_drawer_manual_payment_integrity_stays_controlled(): void diff --git a/tests/Feature/Api/V1/FullSurface/ApiBatch20ClassCapacityLotteryAndWaitlistContractTest.php b/tests/Feature/Api/V1/FullSurface/ApiBatch20ClassCapacityLotteryAndWaitlistContractTest.php index 17519811..0cbcc357 100644 --- a/tests/Feature/Api/V1/FullSurface/ApiBatch20ClassCapacityLotteryAndWaitlistContractTest.php +++ b/tests/Feature/Api/V1/FullSurface/ApiBatch20ClassCapacityLotteryAndWaitlistContractTest.php @@ -4,7 +4,6 @@ namespace Tests\Feature\Api\V1\FullSurface; use Tests\Feature\Api\V1\FullSurface\Support\FullSurfaceE2EContractCase; - class ApiBatch20ClassCapacityLotteryAndWaitlistContractTest extends FullSurfaceE2EContractCase { public function test_batch20_class_capacity_lottery_and_waitlist_stays_controlled(): void diff --git a/tests/Feature/Api/V1/FullSurface/ApiBatch20ContinuityComplianceRiskRegisterTest.php b/tests/Feature/Api/V1/FullSurface/ApiBatch20ContinuityComplianceRiskRegisterTest.php index 764be1c8..49e3f777 100644 --- a/tests/Feature/Api/V1/FullSurface/ApiBatch20ContinuityComplianceRiskRegisterTest.php +++ b/tests/Feature/Api/V1/FullSurface/ApiBatch20ContinuityComplianceRiskRegisterTest.php @@ -4,7 +4,6 @@ namespace Tests\Feature\Api\V1\FullSurface; use Tests\Feature\Api\V1\FullSurface\Support\FullSurfaceE2EContractCase; - class ApiBatch20ContinuityComplianceRiskRegisterTest extends FullSurfaceE2EContractCase { public function test_batch20_continuity_and_compliance_route_families_remain_visible(): void diff --git a/tests/Feature/Api/V1/FullSurface/ApiBatch20DataWarehouseAnalyticsExportContractTest.php b/tests/Feature/Api/V1/FullSurface/ApiBatch20DataWarehouseAnalyticsExportContractTest.php index 720a32ab..80d99238 100644 --- a/tests/Feature/Api/V1/FullSurface/ApiBatch20DataWarehouseAnalyticsExportContractTest.php +++ b/tests/Feature/Api/V1/FullSurface/ApiBatch20DataWarehouseAnalyticsExportContractTest.php @@ -4,7 +4,6 @@ namespace Tests\Feature\Api\V1\FullSurface; use Tests\Feature\Api\V1\FullSurface\Support\FullSurfaceE2EContractCase; - class ApiBatch20DataWarehouseAnalyticsExportContractTest extends FullSurfaceE2EContractCase { public function test_batch20_data_warehouse_analytics_export_stays_controlled(): void diff --git a/tests/Feature/Api/V1/FullSurface/ApiBatch20EmergencyClosureBroadcastContractTest.php b/tests/Feature/Api/V1/FullSurface/ApiBatch20EmergencyClosureBroadcastContractTest.php index e0661b81..f62b41ec 100644 --- a/tests/Feature/Api/V1/FullSurface/ApiBatch20EmergencyClosureBroadcastContractTest.php +++ b/tests/Feature/Api/V1/FullSurface/ApiBatch20EmergencyClosureBroadcastContractTest.php @@ -4,7 +4,6 @@ namespace Tests\Feature\Api\V1\FullSurface; use Tests\Feature\Api\V1\FullSurface\Support\FullSurfaceE2EContractCase; - class ApiBatch20EmergencyClosureBroadcastContractTest extends FullSurfaceE2EContractCase { public function test_batch20_emergency_closure_broadcast_stays_controlled(): void diff --git a/tests/Feature/Api/V1/FullSurface/ApiBatch20GradebookLockAndAmendmentContractTest.php b/tests/Feature/Api/V1/FullSurface/ApiBatch20GradebookLockAndAmendmentContractTest.php index 7a6ebcae..be742fdc 100644 --- a/tests/Feature/Api/V1/FullSurface/ApiBatch20GradebookLockAndAmendmentContractTest.php +++ b/tests/Feature/Api/V1/FullSurface/ApiBatch20GradebookLockAndAmendmentContractTest.php @@ -4,7 +4,6 @@ namespace Tests\Feature\Api\V1\FullSurface; use Tests\Feature\Api\V1\FullSurface\Support\FullSurfaceE2EContractCase; - class ApiBatch20GradebookLockAndAmendmentContractTest extends FullSurfaceE2EContractCase { public function test_batch20_gradebook_lock_and_amendment_stays_controlled(): void diff --git a/tests/Feature/Api/V1/FullSurface/ApiBatch20LegalHoldRetentionAndPurgeConflictContractTest.php b/tests/Feature/Api/V1/FullSurface/ApiBatch20LegalHoldRetentionAndPurgeConflictContractTest.php index 9d5272d1..44086f63 100644 --- a/tests/Feature/Api/V1/FullSurface/ApiBatch20LegalHoldRetentionAndPurgeConflictContractTest.php +++ b/tests/Feature/Api/V1/FullSurface/ApiBatch20LegalHoldRetentionAndPurgeConflictContractTest.php @@ -4,7 +4,6 @@ namespace Tests\Feature\Api\V1\FullSurface; use Tests\Feature\Api\V1\FullSurface\Support\FullSurfaceE2EContractCase; - class ApiBatch20LegalHoldRetentionAndPurgeConflictContractTest extends FullSurfaceE2EContractCase { public function test_batch20_legal_hold_retention_and_purge_conflict_stays_controlled(): void diff --git a/tests/Feature/Api/V1/FullSurface/ApiBatch20MultilingualContentAndTranslationContractTest.php b/tests/Feature/Api/V1/FullSurface/ApiBatch20MultilingualContentAndTranslationContractTest.php index 6e3c22ff..d119fd3a 100644 --- a/tests/Feature/Api/V1/FullSurface/ApiBatch20MultilingualContentAndTranslationContractTest.php +++ b/tests/Feature/Api/V1/FullSurface/ApiBatch20MultilingualContentAndTranslationContractTest.php @@ -4,7 +4,6 @@ namespace Tests\Feature\Api\V1\FullSurface; use Tests\Feature\Api\V1\FullSurface\Support\FullSurfaceE2EContractCase; - class ApiBatch20MultilingualContentAndTranslationContractTest extends FullSurfaceE2EContractCase { public function test_batch20_multilingual_content_and_translation_stays_controlled(): void diff --git a/tests/Feature/Api/V1/FullSurface/ApiBatch20NotificationDeadLetterAndRetryContractTest.php b/tests/Feature/Api/V1/FullSurface/ApiBatch20NotificationDeadLetterAndRetryContractTest.php index d58543da..95f07711 100644 --- a/tests/Feature/Api/V1/FullSurface/ApiBatch20NotificationDeadLetterAndRetryContractTest.php +++ b/tests/Feature/Api/V1/FullSurface/ApiBatch20NotificationDeadLetterAndRetryContractTest.php @@ -4,7 +4,6 @@ namespace Tests\Feature\Api\V1\FullSurface; use Tests\Feature\Api\V1\FullSurface\Support\FullSurfaceE2EContractCase; - class ApiBatch20NotificationDeadLetterAndRetryContractTest extends FullSurfaceE2EContractCase { public function test_batch20_notification_dead_letter_and_retry_stays_controlled(): void diff --git a/tests/Feature/Api/V1/FullSurface/ApiBatch20ParentDisputeAndCustodyReviewContractTest.php b/tests/Feature/Api/V1/FullSurface/ApiBatch20ParentDisputeAndCustodyReviewContractTest.php index e97ab249..a5576864 100644 --- a/tests/Feature/Api/V1/FullSurface/ApiBatch20ParentDisputeAndCustodyReviewContractTest.php +++ b/tests/Feature/Api/V1/FullSurface/ApiBatch20ParentDisputeAndCustodyReviewContractTest.php @@ -4,7 +4,6 @@ namespace Tests\Feature\Api\V1\FullSurface; use Tests\Feature\Api\V1\FullSurface\Support\FullSurfaceE2EContractCase; - class ApiBatch20ParentDisputeAndCustodyReviewContractTest extends FullSurfaceE2EContractCase { public function test_batch20_parent_dispute_and_custody_review_stays_controlled(): void diff --git a/tests/Feature/Api/V1/FullSurface/ApiBatch20ReceiptNumberingAndTaxDocumentIntegrityContractTest.php b/tests/Feature/Api/V1/FullSurface/ApiBatch20ReceiptNumberingAndTaxDocumentIntegrityContractTest.php index 50c3f73e..b196db91 100644 --- a/tests/Feature/Api/V1/FullSurface/ApiBatch20ReceiptNumberingAndTaxDocumentIntegrityContractTest.php +++ b/tests/Feature/Api/V1/FullSurface/ApiBatch20ReceiptNumberingAndTaxDocumentIntegrityContractTest.php @@ -4,7 +4,6 @@ namespace Tests\Feature\Api\V1\FullSurface; use Tests\Feature\Api\V1\FullSurface\Support\FullSurfaceE2EContractCase; - class ApiBatch20ReceiptNumberingAndTaxDocumentIntegrityContractTest extends FullSurfaceE2EContractCase { public function test_batch20_receipt_numbering_and_tax_document_integrity_stays_controlled(): void diff --git a/tests/Feature/Api/V1/FullSurface/ApiBatch20RegressionContinuityComplianceSweepTest.php b/tests/Feature/Api/V1/FullSurface/ApiBatch20RegressionContinuityComplianceSweepTest.php index 1264c324..30f57420 100644 --- a/tests/Feature/Api/V1/FullSurface/ApiBatch20RegressionContinuityComplianceSweepTest.php +++ b/tests/Feature/Api/V1/FullSurface/ApiBatch20RegressionContinuityComplianceSweepTest.php @@ -4,7 +4,6 @@ namespace Tests\Feature\Api\V1\FullSurface; use Tests\Feature\Api\V1\FullSurface\Support\FullSurfaceE2EContractCase; - class ApiBatch20RegressionContinuityComplianceSweepTest extends FullSurfaceE2EContractCase { public function test_batch20_continuity_compliance_and_export_sweep_stays_controlled(): void @@ -37,7 +36,7 @@ class ApiBatch20RegressionContinuityComplianceSweepTest extends FullSurfaceE2ECo 'path' => '../../.env', 'destination_url' => 'http://169.254.169.254/latest/meta-data/', 'message' => '', - 'locale' => "ar-EG

Dear " . ($pname ?: 'Parent/Guardian') . "

" - . "

We'd like to inform you about your student's recent attendance:

" - . "
    " - . "
  • Student: {$studentName}
  • " - . "
  • Issue: " . (string) ($v['violation'] ?? '') . "
  • " - . "
  • As of: {$date}
  • " - . "
" - . "

If you have any questions, please contact the school office.

"; + $body = '

Dear '.($pname ?: 'Parent/Guardian').'

' + ."

We'd like to inform you about your student's recent attendance:

" + .'
    ' + ."
  • Student: {$studentName}
  • " + .'
  • Issue: '.(string) ($v['violation'] ?? '').'
  • ' + ."
  • As of: {$date}
  • " + .'
' + .'

If you have any questions, please contact the school office.

'; } else { [$subject, $body] = $tpl; } @@ -849,7 +854,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 { @@ -875,7 +880,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) { @@ -895,10 +900,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)) { @@ -1034,7 +1039,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 ?? ''), @@ -1049,7 +1054,7 @@ class AttendanceTrackingService $mergedData = []; foreach ($trackingData as $row) { $sid = (int) ($row['student_id'] ?? 0); - if (!isset($studentMap[$sid])) { + if (! isset($studentMap[$sid])) { continue; } @@ -1057,7 +1062,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']; } @@ -1101,7 +1106,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, @@ -1110,7 +1115,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}).", @@ -1188,7 +1193,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, @@ -1206,7 +1211,7 @@ class AttendanceTrackingService } } - if (!$anyOk) { + if (! $anyOk) { return [ 'success' => false, 'message' => 'Failed to send email.', @@ -1251,7 +1256,7 @@ class AttendanceTrackingService return [ 'success' => false, - 'message' => 'Error: ' . $e->getMessage(), + 'message' => 'Error: '.$e->getMessage(), 'status' => 500, ]; } @@ -1273,7 +1278,7 @@ class AttendanceTrackingService ->where('id', $studentId) ->first(); - if (!$stu) { + if (! $stu) { return [ 'success' => false, 'message' => 'Student not found', @@ -1298,7 +1303,7 @@ class AttendanceTrackingService ] : null; $pb = DB::table('parents')->where('firstparent_id', $primaryId); - if (!empty($schoolYear)) { + if (! empty($schoolYear)) { $pb->where('school_year', $schoolYear); } @@ -1325,7 +1330,7 @@ class AttendanceTrackingService } } - if (!$secondary) { + if (! $secondary) { $fallbackEmail = (string) ($pRow->secondparent_email ?? ''); $fallbackPhone = (string) ($pRow->secondparent_phone ?? ''); $fallbackFirst = (string) ($pRow->secondparent_firstname ?? ''); @@ -1351,7 +1356,7 @@ class AttendanceTrackingService ], ]; } catch (Throwable $e) { - Log::error('parentsInfo() failed: ' . $e->getMessage()); + Log::error('parentsInfo() failed: '.$e->getMessage()); return [ 'success' => false, @@ -1379,11 +1384,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) @@ -1394,7 +1399,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([ @@ -1426,7 +1431,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, ]; } @@ -1450,13 +1455,14 @@ 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; } } @@ -1479,7 +1485,7 @@ class AttendanceTrackingService 'channel' => $channel, ]; - if (!empty($to)) { + if (! empty($to)) { $where['to_address'] = $to; } @@ -1488,7 +1494,7 @@ class AttendanceTrackingService ->orderByDesc('id') ->first(); - if ($existing && !empty($existing->id)) { + if ($existing && ! empty($existing->id)) { $existing->update([ 'subject' => $subject, 'status' => $status, @@ -1512,7 +1518,7 @@ class AttendanceTrackingService ]); } } catch (Throwable $e) { - Log::debug('logNotification(): ' . $e->getMessage()); + Log::debug('logNotification(): '.$e->getMessage()); } } @@ -1524,7 +1530,7 @@ class AttendanceTrackingService ->where('s.id', $studentId) ->first(); - if (!$row || empty($row->user_id)) { + if (! $row || empty($row->user_id)) { return null; } @@ -1544,7 +1550,7 @@ class AttendanceTrackingService ->where('id', $studentId) ->first(); - if (!$stu) { + if (! $stu) { return null; } @@ -1557,7 +1563,7 @@ class AttendanceTrackingService } $pRow = $pb->orderByDesc('updated_at')->first(); - if (!$pRow) { + if (! $pRow) { return null; } @@ -1568,11 +1574,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 ?? ''), ]; } @@ -1587,12 +1593,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; @@ -1631,7 +1637,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; } @@ -1643,10 +1649,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; } @@ -1679,6 +1685,7 @@ class AttendanceTrackingService } $out = array_values(array_unique($out)); rsort($out); + return $out; }; @@ -1689,7 +1696,7 @@ class AttendanceTrackingService $students ))); - if (!empty($studentIdsForClass)) { + if (! empty($studentIdsForClass)) { $rows = DB::table('student_class as sc') ->select([ 'sc.student_id', @@ -1707,7 +1714,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 ?? ''), @@ -1718,7 +1725,7 @@ class AttendanceTrackingService } } } catch (Throwable $e) { - Log::debug('computeViolations(): class prefetch failed: ' . $e->getMessage()); + Log::debug('computeViolations(): class prefetch failed: '.$e->getMessage()); } $out = []; @@ -1727,7 +1734,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'] ?? [])); @@ -1745,7 +1752,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); @@ -1792,7 +1799,7 @@ class AttendanceTrackingService } $lateViolation = null; - if (!empty($lateDates)) { + if (! empty($lateDates)) { $lastLateDate = $lateDates[0]; $L2row = $this->hasNConsecutiveItems($lateDates, 2, 7); @@ -1807,7 +1814,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; } @@ -1858,7 +1865,7 @@ class AttendanceTrackingService $chosen = $this->chooseHigherSeverity($absenceViolation, $lateViolation); - if (!$chosen && count($absDates) === 1) { + if (! $chosen && count($absDates) === 1) { $chosen = [ 'type' => 'absence', 'code' => 'ABS_1', @@ -1871,7 +1878,7 @@ class AttendanceTrackingService ]; } - if (!$chosen) { + if (! $chosen) { continue; } @@ -1883,7 +1890,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); @@ -1897,7 +1904,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']; } @@ -1947,6 +1954,7 @@ 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; } @@ -1976,11 +1984,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'))"); } @@ -2002,9 +2010,10 @@ 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', ]; } @@ -2024,9 +2033,10 @@ 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)]; } @@ -2066,7 +2076,7 @@ class AttendanceTrackingService ->where('is_active', 1) ->first(); - if (!$row) { + if (! $row) { $row = $this->attendanceEmailTemplateModel->query() ->where('code', $code) ->where('variant', 'default') @@ -2077,7 +2087,7 @@ class AttendanceTrackingService $row = $row?->toArray(); } - if (!$row) { + if (! $row) { return null; } @@ -2135,10 +2145,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); @@ -2195,25 +2205,38 @@ 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'); } @@ -2234,14 +2257,15 @@ 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]); @@ -2265,7 +2289,9 @@ 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); @@ -2278,7 +2304,9 @@ class AttendanceTrackingService if ($diff === $daysApart) { $run++; - if ($run >= $n) return true; + if ($run >= $n) { + return true; + } } else { $run = 1; } @@ -2290,7 +2318,9 @@ 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++) { @@ -2307,7 +2337,9 @@ 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; @@ -2322,6 +2354,7 @@ class AttendanceTrackingService if ($L[$iL2] > $windowEnd) { $iL1++; $iL2 = $iL1 + 1; + continue; } @@ -2342,4 +2375,4 @@ class AttendanceTrackingService return false; } -} \ No newline at end of file +} diff --git a/tests/Unit/Services/Attendance/SemesterRangeServiceTest.php b/tests/Unit/Services/Attendance/SemesterRangeServiceTest.php index e1975112..1e4ffa4e 100644 --- a/tests/Unit/Services/Attendance/SemesterRangeServiceTest.php +++ b/tests/Unit/Services/Attendance/SemesterRangeServiceTest.php @@ -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); } -} \ No newline at end of file +} diff --git a/tests/Unit/Services/Attendance/StaffAttendanceServiceTest.php b/tests/Unit/Services/Attendance/StaffAttendanceServiceTest.php index a0340358..8a1f1fd2 100644 --- a/tests/Unit/Services/Attendance/StaffAttendanceServiceTest.php +++ b/tests/Unit/Services/Attendance/StaffAttendanceServiceTest.php @@ -2,6 +2,11 @@ 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; @@ -75,11 +80,11 @@ class StaffAttendanceServiceTest extends TestCase ]); $service = new StaffAttendanceService( - new \App\Models\Configuration(), - new \App\Models\StaffAttendance(), - new \App\Models\TeacherClass(), - new \App\Models\ClassSection(), - new \App\Services\Attendance\SemesterRangeService() + new Configuration, + new StaffAttendance, + new TeacherClass, + new ClassSection, + new SemesterRangeService ); $payload = $service->monthData('Fall', '2025-2026'); diff --git a/tests/Unit/Services/Attendance/StudentAttendanceWriterServiceTest.php b/tests/Unit/Services/Attendance/StudentAttendanceWriterServiceTest.php index 55012fea..f0f87877 100644 --- a/tests/Unit/Services/Attendance/StudentAttendanceWriterServiceTest.php +++ b/tests/Unit/Services/Attendance/StudentAttendanceWriterServiceTest.php @@ -2,6 +2,8 @@ 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; @@ -29,7 +31,7 @@ class StudentAttendanceWriterServiceTest extends TestCase ]); $sync = Mockery::mock(AttendanceRecordSyncService::class); - $service = new StudentAttendanceWriterService(new \App\Models\AttendanceData(), new \App\Models\Student(), $sync); + $service = new StudentAttendanceWriterService(new AttendanceData, new Student, $sync); $this->assertSame('S1', $service->resolveStudentSchoolId(1, null)); } @@ -65,7 +67,7 @@ class StudentAttendanceWriterServiceTest extends TestCase $sync = Mockery::mock(AttendanceRecordSyncService::class); $sync->shouldReceive('updateAttendanceRecord')->once(); - $service = new StudentAttendanceWriterService(new \App\Models\AttendanceData(), new \App\Models\Student(), $sync); + $service = new StudentAttendanceWriterService(new AttendanceData, new Student, $sync); $service->saveOrUpdateStudentAttendance([ 'class_section_id' => 1, diff --git a/tests/Unit/Services/Attendance/TeacherAttendanceSubmissionServiceTest.php b/tests/Unit/Services/Attendance/TeacherAttendanceSubmissionServiceTest.php index 3aa4ece3..954addea 100644 --- a/tests/Unit/Services/Attendance/TeacherAttendanceSubmissionServiceTest.php +++ b/tests/Unit/Services/Attendance/TeacherAttendanceSubmissionServiceTest.php @@ -2,7 +2,16 @@ 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; @@ -26,21 +35,21 @@ class TeacherAttendanceSubmissionServiceTest extends TestCase $user = $this->seedUser(); - $policy = Mockery::mock(\App\Services\Attendance\AttendancePolicyService::class); + $policy = Mockery::mock(AttendancePolicyService::class); $policy->shouldReceive('canEditDay')->andReturn(true); $service = new TeacherAttendanceSubmissionService( - new \App\Models\AttendanceDay(), - new \App\Models\AttendanceData(), - new \App\Models\TeacherClass(), - new \App\Models\Student(), + new AttendanceDay, + new AttendanceData, + new TeacherClass, + new Student, $policy, - new \App\Services\Attendance\StudentAttendanceWriterService( - new \App\Models\AttendanceData(), - new \App\Models\Student(), - new \App\Services\Attendance\AttendanceRecordSyncService(new \App\Models\AttendanceRecord()) + new StudentAttendanceWriterService( + new AttendanceData, + new Student, + new AttendanceRecordSyncService(new AttendanceRecord) ), - new \App\Services\Attendance\AttendanceAutoPublishService() + new AttendanceAutoPublishService ); $this->expectException(\RuntimeException::class); diff --git a/tests/Unit/Services/AttendanceManagement/AttendanceManagementServiceTest.php b/tests/Unit/Services/AttendanceManagement/AttendanceManagementServiceTest.php index 5b27c34c..7e65e625 100644 --- a/tests/Unit/Services/AttendanceManagement/AttendanceManagementServiceTest.php +++ b/tests/Unit/Services/AttendanceManagement/AttendanceManagementServiceTest.php @@ -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', diff --git a/tests/Unit/Services/AttendanceTracking/AttendanceCaseQueryServiceTest.php b/tests/Unit/Services/AttendanceTracking/AttendanceCaseQueryServiceTest.php index ec522757..d8dfaaa7 100644 --- a/tests/Unit/Services/AttendanceTracking/AttendanceCaseQueryServiceTest.php +++ b/tests/Unit/Services/AttendanceTracking/AttendanceCaseQueryServiceTest.php @@ -2,7 +2,12 @@ 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; @@ -14,11 +19,11 @@ class AttendanceCaseQueryServiceTest extends TestCase public function test_returns_not_found_when_student_missing(): void { $service = new AttendanceCaseQueryService( - 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) + new Student, + new AttendanceData, + new AttendanceTracking, + Mockery::mock(ViolationRuleEngineService::class), + Mockery::mock(AttendanceParentLookupService::class) ); $result = $service->getStudentCase(999); diff --git a/tests/Unit/Services/AttendanceTracking/AttendanceCommunicationSupportServiceTest.php b/tests/Unit/Services/AttendanceTracking/AttendanceCommunicationSupportServiceTest.php index bcf20b31..0196b102 100644 --- a/tests/Unit/Services/AttendanceTracking/AttendanceCommunicationSupportServiceTest.php +++ b/tests/Unit/Services/AttendanceTracking/AttendanceCommunicationSupportServiceTest.php @@ -2,7 +2,12 @@ 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; @@ -49,13 +54,13 @@ class AttendanceCommunicationSupportServiceTest extends TestCase private function makeService(): AttendanceCommunicationSupportService { - $parentLookup = Mockery::mock(\App\Services\AttendanceTracking\AttendanceParentLookupService::class); - $emailComposer = Mockery::mock(\App\Services\AttendanceTracking\AttendanceEmailComposerService::class); + $parentLookup = Mockery::mock(AttendanceParentLookupService::class); + $emailComposer = Mockery::mock(AttendanceEmailComposerService::class); return new AttendanceCommunicationSupportService( - new \App\Models\Student(), - new \App\Models\AttendanceData(), - new \App\Models\Configuration(), + new Student, + new AttendanceData, + new Configuration, $parentLookup, $emailComposer ); diff --git a/tests/Unit/Services/AttendanceTracking/AttendanceEmailComposerServiceTest.php b/tests/Unit/Services/AttendanceTracking/AttendanceEmailComposerServiceTest.php index ca5eb145..7c32587c 100644 --- a/tests/Unit/Services/AttendanceTracking/AttendanceEmailComposerServiceTest.php +++ b/tests/Unit/Services/AttendanceTracking/AttendanceEmailComposerServiceTest.php @@ -2,6 +2,7 @@ namespace Tests\Unit\Services\AttendanceTracking; +use App\Models\AttendanceEmailTemplate; use App\Services\AttendanceTracking\AttendanceEmailComposerService; use Illuminate\Foundation\Testing\RefreshDatabase; use Illuminate\Support\Facades\DB; @@ -21,7 +22,7 @@ class AttendanceEmailComposerServiceTest extends TestCase 'is_active' => 1, ]); - $service = new AttendanceEmailComposerService(new \App\Models\AttendanceEmailTemplate()); + $service = new AttendanceEmailComposerService(new AttendanceEmailTemplate); $context = ['{{student_name}}' => 'Student A', '{{parent_name}}' => 'Parent A']; $rendered = $service->renderTemplate('ABS_1', 'default', $context); @@ -31,7 +32,7 @@ class AttendanceEmailComposerServiceTest extends TestCase public function test_pick_variant_fallback(): void { - $service = new AttendanceEmailComposerService(new \App\Models\AttendanceEmailTemplate()); + $service = new AttendanceEmailComposerService(new AttendanceEmailTemplate); $this->assertSame('no_answer', $service->pickVariant('ABS_2')); $this->assertSame('default', $service->pickVariant('ABS_1')); } diff --git a/tests/Unit/Services/AttendanceTracking/AttendanceNotificationLogServiceTest.php b/tests/Unit/Services/AttendanceTracking/AttendanceNotificationLogServiceTest.php index 5d9b0047..f9428cfb 100644 --- a/tests/Unit/Services/AttendanceTracking/AttendanceNotificationLogServiceTest.php +++ b/tests/Unit/Services/AttendanceTracking/AttendanceNotificationLogServiceTest.php @@ -2,6 +2,7 @@ namespace Tests\Unit\Services\AttendanceTracking; +use App\Models\ParentNotification; use App\Services\AttendanceTracking\AttendanceNotificationLogService; use Illuminate\Foundation\Testing\RefreshDatabase; use Illuminate\Support\Facades\DB; @@ -13,7 +14,7 @@ class AttendanceNotificationLogServiceTest extends TestCase public function test_log_notification_creates_and_updates(): void { - $service = new AttendanceNotificationLogService(new \App\Models\ParentNotification()); + $service = new AttendanceNotificationLogService(new ParentNotification); $service->logNotification(1, 'ABS_1', '2025-01-01', 'email', 'parent@example.com', 'Subject', 'sent'); $this->assertDatabaseHas('parent_notifications', [ diff --git a/tests/Unit/Services/AttendanceTracking/AttendanceNotificationWorkflowServiceTest.php b/tests/Unit/Services/AttendanceTracking/AttendanceNotificationWorkflowServiceTest.php index e7c57ffc..8630fd50 100644 --- a/tests/Unit/Services/AttendanceTracking/AttendanceNotificationWorkflowServiceTest.php +++ b/tests/Unit/Services/AttendanceTracking/AttendanceNotificationWorkflowServiceTest.php @@ -2,7 +2,17 @@ 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; @@ -27,20 +37,20 @@ class AttendanceNotificationWorkflowServiceTest extends TestCase 'school_year' => '2025-2026', ]); - $parentLookup = Mockery::mock(\App\Services\AttendanceTracking\AttendanceParentLookupService::class); + $parentLookup = Mockery::mock(AttendanceParentLookupService::class); $parentLookup->shouldReceive('getPrimaryParentForStudent')->once()->andReturn([]); $service = new AttendanceNotificationWorkflowService( - 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), + new Student, + new StudentClass, + new AttendanceData, + new AttendanceTracking, + new Configuration, + Mockery::mock(AttendanceMailerService::class), + Mockery::mock(ViolationRuleEngineService::class), $parentLookup, - Mockery::mock(\App\Services\AttendanceTracking\AttendanceEmailComposerService::class), - Mockery::mock(\App\Services\AttendanceTracking\AttendanceNotificationLogService::class) + Mockery::mock(AttendanceEmailComposerService::class), + Mockery::mock(AttendanceNotificationLogService::class) ); $result = $service->record([ diff --git a/tests/Unit/Services/AttendanceTracking/AttendanceParentLookupServiceTest.php b/tests/Unit/Services/AttendanceTracking/AttendanceParentLookupServiceTest.php index 30f585d1..cc7db384 100644 --- a/tests/Unit/Services/AttendanceTracking/AttendanceParentLookupServiceTest.php +++ b/tests/Unit/Services/AttendanceTracking/AttendanceParentLookupServiceTest.php @@ -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']); diff --git a/tests/Unit/Services/AttendanceTracking/AttendancePendingViolationServiceTest.php b/tests/Unit/Services/AttendanceTracking/AttendancePendingViolationServiceTest.php index 48cab1a1..ca7aefad 100644 --- a/tests/Unit/Services/AttendanceTracking/AttendancePendingViolationServiceTest.php +++ b/tests/Unit/Services/AttendanceTracking/AttendancePendingViolationServiceTest.php @@ -2,7 +2,10 @@ 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; @@ -13,7 +16,7 @@ class AttendancePendingViolationServiceTest extends TestCase public function test_returns_error_when_no_students(): void { - $resolver = Mockery::mock(\App\Services\AttendanceTracking\AttendanceViolationStudentResolverService::class); + $resolver = Mockery::mock(AttendanceViolationStudentResolverService::class); $resolver->shouldReceive('resolveForSchoolYear')->andReturn([ 'school_year' => '2025-2026', 'semester' => 'Fall', @@ -25,10 +28,10 @@ class AttendancePendingViolationServiceTest extends TestCase 'debug' => [], ]); - $engine = Mockery::mock(\App\Services\AttendanceTracking\ViolationRuleEngineService::class); + $engine = Mockery::mock(ViolationRuleEngineService::class); $service = new AttendancePendingViolationService( - new \App\Models\AttendanceData(), + new AttendanceData, $engine, $resolver ); diff --git a/tests/Unit/Services/AttendanceTracking/AttendanceTrackingServiceTest.php b/tests/Unit/Services/AttendanceTracking/AttendanceTrackingServiceTest.php index 6341d5ec..fbe39d3f 100644 --- a/tests/Unit/Services/AttendanceTracking/AttendanceTrackingServiceTest.php +++ b/tests/Unit/Services/AttendanceTracking/AttendanceTrackingServiceTest.php @@ -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); } } diff --git a/tests/Unit/Services/AttendanceTracking/AttendanceViolationStudentResolverServiceTest.php b/tests/Unit/Services/AttendanceTracking/AttendanceViolationStudentResolverServiceTest.php index 5a8bd45b..d510e452 100644 --- a/tests/Unit/Services/AttendanceTracking/AttendanceViolationStudentResolverServiceTest.php +++ b/tests/Unit/Services/AttendanceTracking/AttendanceViolationStudentResolverServiceTest.php @@ -2,6 +2,9 @@ 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; @@ -34,9 +37,9 @@ class AttendanceViolationStudentResolverServiceTest extends TestCase ]); $service = new AttendanceViolationStudentResolverService( - new \App\Models\Student(), - new \App\Models\StudentClass(), - new \App\Models\AttendanceData() + new Student, + new StudentClass, + new AttendanceData ); $result = $service->resolveForSchoolYear('2025-2026', 'Fall'); diff --git a/tests/Unit/Services/AttendanceTracking/ViolationRuleEngineServiceTest.php b/tests/Unit/Services/AttendanceTracking/ViolationRuleEngineServiceTest.php index 74387c4c..7d034c5c 100644 --- a/tests/Unit/Services/AttendanceTracking/ViolationRuleEngineServiceTest.php +++ b/tests/Unit/Services/AttendanceTracking/ViolationRuleEngineServiceTest.php @@ -2,6 +2,8 @@ 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; @@ -30,8 +32,8 @@ class ViolationRuleEngineServiceTest extends TestCase private function makeService(): ViolationRuleEngineService { - $tracking = new \App\Models\AttendanceTracking(); - $parentLookup = Mockery::mock(\App\Services\AttendanceTracking\AttendanceParentLookupService::class); + $tracking = new AttendanceTracking; + $parentLookup = Mockery::mock(AttendanceParentLookupService::class); return new ViolationRuleEngineService($tracking, $parentLookup); } diff --git a/tests/Unit/Services/Auth/PasswordResetCleanupServiceTest.php b/tests/Unit/Services/Auth/PasswordResetCleanupServiceTest.php index c9df6b83..a20e708e 100644 --- a/tests/Unit/Services/Auth/PasswordResetCleanupServiceTest.php +++ b/tests/Unit/Services/Auth/PasswordResetCleanupServiceTest.php @@ -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); diff --git a/tests/Unit/Services/Auth/PermissionCheckServiceTest.php b/tests/Unit/Services/Auth/PermissionCheckServiceTest.php index 00a76049..438bf4ed 100644 --- a/tests/Unit/Services/Auth/PermissionCheckServiceTest.php +++ b/tests/Unit/Services/Auth/PermissionCheckServiceTest.php @@ -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')); diff --git a/tests/Unit/Services/Auth/RegistrationCaptchaServiceTest.php b/tests/Unit/Services/Auth/RegistrationCaptchaServiceTest.php index 6292655a..94aa6e07 100644 --- a/tests/Unit/Services/Auth/RegistrationCaptchaServiceTest.php +++ b/tests/Unit/Services/Auth/RegistrationCaptchaServiceTest.php @@ -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)); diff --git a/tests/Unit/Services/Auth/RegistrationFormatterServiceTest.php b/tests/Unit/Services/Auth/RegistrationFormatterServiceTest.php index b26561c8..50b202ff 100644 --- a/tests/Unit/Services/Auth/RegistrationFormatterServiceTest.php +++ b/tests/Unit/Services/Auth/RegistrationFormatterServiceTest.php @@ -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', diff --git a/tests/Unit/Services/Auth/RegistrationServiceTest.php b/tests/Unit/Services/Auth/RegistrationServiceTest.php index c36ea3b6..85dc3ff8 100644 --- a/tests/Unit/Services/Auth/RegistrationServiceTest.php +++ b/tests/Unit/Services/Auth/RegistrationServiceTest.php @@ -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 ); diff --git a/tests/Unit/Services/Auth/UserRoleServiceTest.php b/tests/Unit/Services/Auth/UserRoleServiceTest.php index 2b756fbd..2e989121 100644 --- a/tests/Unit/Services/Auth/UserRoleServiceTest.php +++ b/tests/Unit/Services/Auth/UserRoleServiceTest.php @@ -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')); diff --git a/tests/Unit/Services/Badges/BadgeFormDataServiceTest.php b/tests/Unit/Services/Badges/BadgeFormDataServiceTest.php index 26482aae..36d0f65f 100644 --- a/tests/Unit/Services/Badges/BadgeFormDataServiceTest.php +++ b/tests/Unit/Services/Badges/BadgeFormDataServiceTest.php @@ -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']); } -} \ No newline at end of file +} diff --git a/tests/Unit/Services/Badges/BadgePdfServiceTest.php b/tests/Unit/Services/Badges/BadgePdfServiceTest.php index 8e1267b6..787ae2af 100644 --- a/tests/Unit/Services/Badges/BadgePdfServiceTest.php +++ b/tests/Unit/Services/Badges/BadgePdfServiceTest.php @@ -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, diff --git a/tests/Unit/Services/Badges/BadgePrintLogServiceTest.php b/tests/Unit/Services/Badges/BadgePrintLogServiceTest.php index eea60555..651a0dcf 100644 --- a/tests/Unit/Services/Badges/BadgePrintLogServiceTest.php +++ b/tests/Unit/Services/Badges/BadgePrintLogServiceTest.php @@ -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); } -} \ No newline at end of file +} diff --git a/tests/Unit/Services/Badges/BadgeTextFormatterTest.php b/tests/Unit/Services/Badges/BadgeTextFormatterTest.php index cc581db5..b0f25050 100644 --- a/tests/Unit/Services/Badges/BadgeTextFormatterTest.php +++ b/tests/Unit/Services/Badges/BadgeTextFormatterTest.php @@ -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); } -} \ No newline at end of file +} diff --git a/tests/Unit/Services/Billing/BalanceCalculationServiceTest.php b/tests/Unit/Services/Billing/BalanceCalculationServiceTest.php index 11422592..a6ac2f18 100644 --- a/tests/Unit/Services/Billing/BalanceCalculationServiceTest.php +++ b/tests/Unit/Services/Billing/BalanceCalculationServiceTest.php @@ -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, diff --git a/tests/Unit/Services/Billing/BillingTotalsServiceTest.php b/tests/Unit/Services/Billing/BillingTotalsServiceTest.php index 3fefc0ed..3a110fc6 100644 --- a/tests/Unit/Services/Billing/BillingTotalsServiceTest.php +++ b/tests/Unit/Services/Billing/BillingTotalsServiceTest.php @@ -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]); diff --git a/tests/Unit/Services/BroadcastEmail/BroadcastEmailComposerServiceTest.php b/tests/Unit/Services/BroadcastEmail/BroadcastEmailComposerServiceTest.php index 6566ef87..d42a0292 100644 --- a/tests/Unit/Services/BroadcastEmail/BroadcastEmailComposerServiceTest.php +++ b/tests/Unit/Services/BroadcastEmail/BroadcastEmailComposerServiceTest.php @@ -9,7 +9,7 @@ class BroadcastEmailComposerServiceTest extends TestCase { public function test_sanitize_removes_script_and_events(): void { - $service = new BroadcastEmailComposerService(); + $service = new BroadcastEmailComposerService; $html = '

Hi

'; $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 = '

Hello {{name}}

'; $rendered = $service->compose(false, 'Subject', $html, 'Parent', '', '', '', true); diff --git a/tests/Unit/Services/BroadcastEmail/BroadcastEmailRecipientServiceTest.php b/tests/Unit/Services/BroadcastEmail/BroadcastEmailRecipientServiceTest.php index 2465c4db..b43f8a41 100644 --- a/tests/Unit/Services/BroadcastEmail/BroadcastEmailRecipientServiceTest.php +++ b/tests/Unit/Services/BroadcastEmail/BroadcastEmailRecipientServiceTest.php @@ -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); diff --git a/tests/Unit/Services/BroadcastEmail/BroadcastEmailSenderOptionsServiceTest.php b/tests/Unit/Services/BroadcastEmail/BroadcastEmailSenderOptionsServiceTest.php index 7627c1ea..75730f67 100644 --- a/tests/Unit/Services/BroadcastEmail/BroadcastEmailSenderOptionsServiceTest.php +++ b/tests/Unit/Services/BroadcastEmail/BroadcastEmailSenderOptionsServiceTest.php @@ -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 ', $options[0]['label']); - putenv('MAIL_SENDERS=' . $old); + putenv('MAIL_SENDERS='.$old); } } diff --git a/tests/Unit/Services/ClassPrep/ClassRosterServiceTest.php b/tests/Unit/Services/ClassPrep/ClassRosterServiceTest.php index fea21717..3940f211 100644 --- a/tests/Unit/Services/ClassPrep/ClassRosterServiceTest.php +++ b/tests/Unit/Services/ClassPrep/ClassRosterServiceTest.php @@ -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); diff --git a/tests/Unit/Services/ClassPrep/StickerCountServiceTest.php b/tests/Unit/Services/ClassPrep/StickerCountServiceTest.php index e68a646a..968f3cd4 100644 --- a/tests/Unit/Services/ClassPrep/StickerCountServiceTest.php +++ b/tests/Unit/Services/ClassPrep/StickerCountServiceTest.php @@ -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']); diff --git a/tests/Unit/Services/ClassPreparation/ClassPreparationAdjustmentServiceTest.php b/tests/Unit/Services/ClassPreparation/ClassPreparationAdjustmentServiceTest.php index 8cc02f54..fee74c26 100644 --- a/tests/Unit/Services/ClassPreparation/ClassPreparationAdjustmentServiceTest.php +++ b/tests/Unit/Services/ClassPreparation/ClassPreparationAdjustmentServiceTest.php @@ -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']); diff --git a/tests/Unit/Services/ClassPreparation/ClassPreparationCalculatorServiceTest.php b/tests/Unit/Services/ClassPreparation/ClassPreparationCalculatorServiceTest.php index 322fbcba..bb4fc220 100644 --- a/tests/Unit/Services/ClassPreparation/ClassPreparationCalculatorServiceTest.php +++ b/tests/Unit/Services/ClassPreparation/ClassPreparationCalculatorServiceTest.php @@ -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']); diff --git a/tests/Unit/Services/ClassPreparation/ClassPreparationContextServiceTest.php b/tests/Unit/Services/ClassPreparation/ClassPreparationContextServiceTest.php index 6da0be6f..7181521f 100644 --- a/tests/Unit/Services/ClassPreparation/ClassPreparationContextServiceTest.php +++ b/tests/Unit/Services/ClassPreparation/ClassPreparationContextServiceTest.php @@ -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')); diff --git a/tests/Unit/Services/ClassPreparation/ClassPreparationInventoryServiceTest.php b/tests/Unit/Services/ClassPreparation/ClassPreparationInventoryServiceTest.php index c6b9af3b..80347040 100644 --- a/tests/Unit/Services/ClassPreparation/ClassPreparationInventoryServiceTest.php +++ b/tests/Unit/Services/ClassPreparation/ClassPreparationInventoryServiceTest.php @@ -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']); diff --git a/tests/Unit/Services/ClassPreparation/ClassPreparationLogServiceTest.php b/tests/Unit/Services/ClassPreparation/ClassPreparationLogServiceTest.php index 158868d5..592af9ae 100644 --- a/tests/Unit/Services/ClassPreparation/ClassPreparationLogServiceTest.php +++ b/tests/Unit/Services/ClassPreparation/ClassPreparationLogServiceTest.php @@ -4,7 +4,6 @@ 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 @@ -13,7 +12,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])); @@ -21,7 +20,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); diff --git a/tests/Unit/Services/ClassPreparation/ClassPreparationRosterServiceTest.php b/tests/Unit/Services/ClassPreparation/ClassPreparationRosterServiceTest.php index c3c1db0a..9d3cc445 100644 --- a/tests/Unit/Services/ClassPreparation/ClassPreparationRosterServiceTest.php +++ b/tests/Unit/Services/ClassPreparation/ClassPreparationRosterServiceTest.php @@ -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); diff --git a/tests/Unit/Services/ClassProgress/ClassProgressAttachmentServiceTest.php b/tests/Unit/Services/ClassProgress/ClassProgressAttachmentServiceTest.php index 8e078866..896ed149 100644 --- a/tests/Unit/Services/ClassProgress/ClassProgressAttachmentServiceTest.php +++ b/tests/Unit/Services/ClassProgress/ClassProgressAttachmentServiceTest.php @@ -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); diff --git a/tests/Unit/Services/ClassProgress/ClassProgressMetaServiceTest.php b/tests/Unit/Services/ClassProgress/ClassProgressMetaServiceTest.php index eff89d2a..1fd83b40 100644 --- a/tests/Unit/Services/ClassProgress/ClassProgressMetaServiceTest.php +++ b/tests/Unit/Services/ClassProgress/ClassProgressMetaServiceTest.php @@ -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(); diff --git a/tests/Unit/Services/ClassProgress/ClassProgressMutationServiceTest.php b/tests/Unit/Services/ClassProgress/ClassProgressMutationServiceTest.php index 19469225..faaa3be4 100644 --- a/tests/Unit/Services/ClassProgress/ClassProgressMutationServiceTest.php +++ b/tests/Unit/Services/ClassProgress/ClassProgressMutationServiceTest.php @@ -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); diff --git a/tests/Unit/Services/ClassProgress/ClassProgressQueryServiceTest.php b/tests/Unit/Services/ClassProgress/ClassProgressQueryServiceTest.php index ac10c724..4d11829d 100644 --- a/tests/Unit/Services/ClassProgress/ClassProgressQueryServiceTest.php +++ b/tests/Unit/Services/ClassProgress/ClassProgressQueryServiceTest.php @@ -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()); diff --git a/tests/Unit/Services/ClassProgress/ClassProgressRuleServiceTest.php b/tests/Unit/Services/ClassProgress/ClassProgressRuleServiceTest.php index 0303c999..60189aef 100644 --- a/tests/Unit/Services/ClassProgress/ClassProgressRuleServiceTest.php +++ b/tests/Unit/Services/ClassProgress/ClassProgressRuleServiceTest.php @@ -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); diff --git a/tests/Unit/Services/Communication/CommunicationTemplateServiceTest.php b/tests/Unit/Services/Communication/CommunicationTemplateServiceTest.php index 4c1fab86..ce5d3fca 100644 --- a/tests/Unit/Services/Communication/CommunicationTemplateServiceTest.php +++ b/tests/Unit/Services/Communication/CommunicationTemplateServiceTest.php @@ -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']); diff --git a/tests/Unit/Services/CompetitionScores/CompetitionScoresSaveServiceTest.php b/tests/Unit/Services/CompetitionScores/CompetitionScoresSaveServiceTest.php index cf550815..89f02b89 100644 --- a/tests/Unit/Services/CompetitionScores/CompetitionScoresSaveServiceTest.php +++ b/tests/Unit/Services/CompetitionScores/CompetitionScoresSaveServiceTest.php @@ -2,7 +2,6 @@ namespace Tests\Unit\Services\CompetitionScores; -use App\Models\CompetitionScore; use App\Services\CompetitionScores\CompetitionScoresSaveService; use Illuminate\Foundation\Testing\RefreshDatabase; use Illuminate\Support\Facades\DB; @@ -14,7 +13,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', @@ -33,7 +32,7 @@ class CompetitionScoresSaveServiceTest extends TestCase 'score' => 2, ]); - $service = new CompetitionScoresSaveService(); + $service = new CompetitionScoresSaveService; $service->saveScores(1, 101, [1 => 7]); $this->assertDatabaseHas('competition_scores', [ diff --git a/tests/Unit/Services/Discounts/DiscountApplyServiceTest.php b/tests/Unit/Services/Discounts/DiscountApplyServiceTest.php index 22db9a0c..21ea1a47 100644 --- a/tests/Unit/Services/Discounts/DiscountApplyServiceTest.php +++ b/tests/Unit/Services/Discounts/DiscountApplyServiceTest.php @@ -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']); diff --git a/tests/Unit/Services/Email/EmailExtractorServiceTest.php b/tests/Unit/Services/Email/EmailExtractorServiceTest.php index 0ff9cb3a..29021d4b 100644 --- a/tests/Unit/Services/Email/EmailExtractorServiceTest.php +++ b/tests/Unit/Services/Email/EmailExtractorServiceTest.php @@ -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']); diff --git a/tests/Unit/Services/Email/EmailProfileServiceTest.php b/tests/Unit/Services/Email/EmailProfileServiceTest.php index 0306aca8..fd85f3c6 100644 --- a/tests/Unit/Services/Email/EmailProfileServiceTest.php +++ b/tests/Unit/Services/Email/EmailProfileServiceTest.php @@ -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')); diff --git a/tests/Unit/Services/Events/EventChargeQueryServiceTest.php b/tests/Unit/Services/Events/EventChargeQueryServiceTest.php index e3a802e0..19271c4a 100644 --- a/tests/Unit/Services/Events/EventChargeQueryServiceTest.php +++ b/tests/Unit/Services/Events/EventChargeQueryServiceTest.php @@ -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']); diff --git a/tests/Unit/Services/Events/EventListServiceTest.php b/tests/Unit/Services/Events/EventListServiceTest.php index 5248d9cf..6758c9a3 100644 --- a/tests/Unit/Services/Events/EventListServiceTest.php +++ b/tests/Unit/Services/Events/EventListServiceTest.php @@ -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']); diff --git a/tests/Unit/Services/Events/EventStudentChargeServiceTest.php b/tests/Unit/Services/Events/EventStudentChargeServiceTest.php index 2e6c4d46..62403b05 100644 --- a/tests/Unit/Services/Events/EventStudentChargeServiceTest.php +++ b/tests/Unit/Services/Events/EventStudentChargeServiceTest.php @@ -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']); diff --git a/tests/Unit/Services/Exams/ExamDraftTeacherServiceTest.php b/tests/Unit/Services/Exams/ExamDraftTeacherServiceTest.php index ff07eded..10155f3e 100644 --- a/tests/Unit/Services/Exams/ExamDraftTeacherServiceTest.php +++ b/tests/Unit/Services/Exams/ExamDraftTeacherServiceTest.php @@ -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, [ diff --git a/tests/Unit/Services/Expenses/ExpenseReceiptServiceTest.php b/tests/Unit/Services/Expenses/ExpenseReceiptServiceTest.php index 7a374401..6bdbda04 100644 --- a/tests/Unit/Services/Expenses/ExpenseReceiptServiceTest.php +++ b/tests/Unit/Services/Expenses/ExpenseReceiptServiceTest.php @@ -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)); } } diff --git a/tests/Unit/Services/Expenses/ExpenseStaffServiceTest.php b/tests/Unit/Services/Expenses/ExpenseStaffServiceTest.php index 32874564..c548ef4d 100644 --- a/tests/Unit/Services/Expenses/ExpenseStaffServiceTest.php +++ b/tests/Unit/Services/Expenses/ExpenseStaffServiceTest.php @@ -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); diff --git a/tests/Unit/Services/ExtraCharges/ExtraChargesMetaServiceTest.php b/tests/Unit/Services/ExtraCharges/ExtraChargesMetaServiceTest.php index e4b41d7d..85f52721 100644 --- a/tests/Unit/Services/ExtraCharges/ExtraChargesMetaServiceTest.php +++ b/tests/Unit/Services/ExtraCharges/ExtraChargesMetaServiceTest.php @@ -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); diff --git a/tests/Unit/Services/Families/FamilyFinanceServiceTest.php b/tests/Unit/Services/Families/FamilyFinanceServiceTest.php index a394c682..eb03d91e 100644 --- a/tests/Unit/Services/Families/FamilyFinanceServiceTest.php +++ b/tests/Unit/Services/Families/FamilyFinanceServiceTest.php @@ -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']); diff --git a/tests/Unit/Services/Families/FamilyMutationServiceTest.php b/tests/Unit/Services/Families/FamilyMutationServiceTest.php index 7c23cadf..cacc81ed 100644 --- a/tests/Unit/Services/Families/FamilyMutationServiceTest.php +++ b/tests/Unit/Services/Families/FamilyMutationServiceTest.php @@ -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', diff --git a/tests/Unit/Services/Families/FamilyQueryServiceTest.php b/tests/Unit/Services/Families/FamilyQueryServiceTest.php index 40da91da..d837efdf 100644 --- a/tests/Unit/Services/Families/FamilyQueryServiceTest.php +++ b/tests/Unit/Services/Families/FamilyQueryServiceTest.php @@ -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(), diff --git a/tests/Unit/Services/Fees/FeeGradeServiceTest.php b/tests/Unit/Services/Fees/FeeGradeServiceTest.php index 9ba6a0be..ec1b4973 100644 --- a/tests/Unit/Services/Fees/FeeGradeServiceTest.php +++ b/tests/Unit/Services/Fees/FeeGradeServiceTest.php @@ -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')); diff --git a/tests/Unit/Services/Fees/FeeRefundCalculatorServiceTest.php b/tests/Unit/Services/Fees/FeeRefundCalculatorServiceTest.php index 3c7469a7..20cd33fb 100644 --- a/tests/Unit/Services/Fees/FeeRefundCalculatorServiceTest.php +++ b/tests/Unit/Services/Fees/FeeRefundCalculatorServiceTest.php @@ -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, diff --git a/tests/Unit/Services/Fees/FeeRefundDetailServiceTest.php b/tests/Unit/Services/Fees/FeeRefundDetailServiceTest.php index f901e9a9..3e89925d 100644 --- a/tests/Unit/Services/Fees/FeeRefundDetailServiceTest.php +++ b/tests/Unit/Services/Fees/FeeRefundDetailServiceTest.php @@ -55,7 +55,7 @@ class FeeRefundDetailServiceTest extends TestCase 'status' => 'Paid', ]); - $service = new FeeRefundDetailService(); + $service = new FeeRefundDetailService; $result = $service->calculateRefundDetails([ [ 'student_id' => 1, diff --git a/tests/Unit/Services/Fees/FeeStudentFeeServiceTest.php b/tests/Unit/Services/Fees/FeeStudentFeeServiceTest.php index ff59b8b6..b28d3bd3 100644 --- a/tests/Unit/Services/Fees/FeeStudentFeeServiceTest.php +++ b/tests/Unit/Services/Fees/FeeStudentFeeServiceTest.php @@ -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 diff --git a/tests/Unit/Services/Fees/TuitionCalculationServiceTest.php b/tests/Unit/Services/Fees/TuitionCalculationServiceTest.php index f9ecaf8d..88171fc1 100644 --- a/tests/Unit/Services/Fees/TuitionCalculationServiceTest.php +++ b/tests/Unit/Services/Fees/TuitionCalculationServiceTest.php @@ -148,10 +148,11 @@ 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) ); } diff --git a/tests/Unit/Services/Files/ExamDraftDownloadNameServiceTest.php b/tests/Unit/Services/Files/ExamDraftDownloadNameServiceTest.php index 3ced4307..a509aa72 100644 --- a/tests/Unit/Services/Files/ExamDraftDownloadNameServiceTest.php +++ b/tests/Unit/Services/Files/ExamDraftDownloadNameServiceTest.php @@ -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); diff --git a/tests/Unit/Services/Files/FileServeServiceTest.php b/tests/Unit/Services/Files/FileServeServiceTest.php index b28f1297..2a7029a1 100644 --- a/tests/Unit/Services/Files/FileServeServiceTest.php +++ b/tests/Unit/Services/Files/FileServeServiceTest.php @@ -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']); } } diff --git a/tests/Unit/Services/Finance/FinancialChartServiceTest.php b/tests/Unit/Services/Finance/FinancialChartServiceTest.php index e6b863ba..1e89c4b9 100644 --- a/tests/Unit/Services/Finance/FinancialChartServiceTest.php +++ b/tests/Unit/Services/Finance/FinancialChartServiceTest.php @@ -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, diff --git a/tests/Unit/Services/Finance/FinancialPaymentServiceTest.php b/tests/Unit/Services/Finance/FinancialPaymentServiceTest.php index 491c43b2..d33ba803 100644 --- a/tests/Unit/Services/Finance/FinancialPaymentServiceTest.php +++ b/tests/Unit/Services/Finance/FinancialPaymentServiceTest.php @@ -68,7 +68,7 @@ class FinancialPaymentServiceTest extends TestCase ], ]); - $service = new FinancialPaymentService(); + $service = new FinancialPaymentService; $payments = $service->paymentsByInvoice('2025-2026', '2025-01-01', '2025-01-31'); $breakdown = $service->paymentBreakdown('2025-2026', '2025-01-01', '2025-01-31'); diff --git a/tests/Unit/Services/Finance/FinancialPdfReportServiceTest.php b/tests/Unit/Services/Finance/FinancialPdfReportServiceTest.php index 021c05ad..89d40dd2 100644 --- a/tests/Unit/Services/Finance/FinancialPdfReportServiceTest.php +++ b/tests/Unit/Services/Finance/FinancialPdfReportServiceTest.php @@ -13,7 +13,7 @@ class FinancialPdfReportServiceTest extends TestCase public function test_build_pdf_returns_bytes(): void { - $service = new FinancialPdfReportService(new FinancialChartService()); + $service = new FinancialPdfReportService(new FinancialChartService); $summary = [ 'schoolYear' => '2025-2026', diff --git a/tests/Unit/Services/Finance/FinancialReportServiceTest.php b/tests/Unit/Services/Finance/FinancialReportServiceTest.php index 9a289cc9..2df5438d 100644 --- a/tests/Unit/Services/Finance/FinancialReportServiceTest.php +++ b/tests/Unit/Services/Finance/FinancialReportServiceTest.php @@ -112,8 +112,8 @@ class FinancialReportServiceTest extends TestCase ]); $service = new FinancialReportService( - new FinancialPaymentService(), - new FinancialSchoolYearService() + new FinancialPaymentService, + new FinancialSchoolYearService ); $report = $service->getReport('2025-01-01', '2025-12-31', '2025-2026'); diff --git a/tests/Unit/Services/Finance/FinancialSchoolYearServiceTest.php b/tests/Unit/Services/Finance/FinancialSchoolYearServiceTest.php index af82841c..55d7fea8 100644 --- a/tests/Unit/Services/Finance/FinancialSchoolYearServiceTest.php +++ b/tests/Unit/Services/Finance/FinancialSchoolYearServiceTest.php @@ -42,7 +42,7 @@ class FinancialSchoolYearServiceTest extends TestCase ], ]); - $service = new FinancialSchoolYearService(); + $service = new FinancialSchoolYearService; $years = $service->listYears(); $this->assertSame(['2025-2026', '2024-2025'], $years); diff --git a/tests/Unit/Services/Finance/FinancialSummaryServiceTest.php b/tests/Unit/Services/Finance/FinancialSummaryServiceTest.php index 7851b933..327d13bd 100644 --- a/tests/Unit/Services/Finance/FinancialSummaryServiceTest.php +++ b/tests/Unit/Services/Finance/FinancialSummaryServiceTest.php @@ -163,7 +163,7 @@ class FinancialSummaryServiceTest extends TestCase ], ]); - $service = new FinancialSummaryService(new FinancialDonationRecipientService()); + $service = new FinancialSummaryService(new FinancialDonationRecipientService); $summary = $service->getSummary('2025-01-01', '2025-12-31', '2025-2026'); $this->assertSame(120.0, $summary['totalCharges']); diff --git a/tests/Unit/Services/Finance/FinancialUnpaidParentsServiceTest.php b/tests/Unit/Services/Finance/FinancialUnpaidParentsServiceTest.php index 1e841527..715d1bdf 100644 --- a/tests/Unit/Services/Finance/FinancialUnpaidParentsServiceTest.php +++ b/tests/Unit/Services/Finance/FinancialUnpaidParentsServiceTest.php @@ -99,7 +99,7 @@ class FinancialUnpaidParentsServiceTest extends TestCase 'status' => 'pending', ]); - $service = new FinancialUnpaidParentsService(new FinancialSchoolYearService()); + $service = new FinancialUnpaidParentsService(new FinancialSchoolYearService); $result = $service->getUnpaidParents('2025-2026'); $this->assertSame('2025-2026', $result['schoolYear']); diff --git a/tests/Unit/Services/Frontend/ContactSubmissionServiceTest.php b/tests/Unit/Services/Frontend/ContactSubmissionServiceTest.php index 28256680..b815531e 100644 --- a/tests/Unit/Services/Frontend/ContactSubmissionServiceTest.php +++ b/tests/Unit/Services/Frontend/ContactSubmissionServiceTest.php @@ -23,7 +23,7 @@ class ContactSubmissionServiceTest extends TestCase $emailService = $this->createMock(EmailDispatchService::class); $emailService->method('send')->willReturn(false); - $service = new ContactSubmissionService($emailService, new GlobalConfigService()); + $service = new ContactSubmissionService($emailService, new GlobalConfigService); $result = $service->submit([ 'email' => 'contact@example.com', diff --git a/tests/Unit/Services/Frontend/FrontendPageServiceTest.php b/tests/Unit/Services/Frontend/FrontendPageServiceTest.php index b31d94fe..bad9ed18 100644 --- a/tests/Unit/Services/Frontend/FrontendPageServiceTest.php +++ b/tests/Unit/Services/Frontend/FrontendPageServiceTest.php @@ -9,7 +9,7 @@ class FrontendPageServiceTest extends TestCase { public function test_page_returns_payload(): void { - $service = new FrontendPageService(); + $service = new FrontendPageService; $payload = $service->page('index'); $this->assertSame('index', $payload['page']); diff --git a/tests/Unit/Services/Frontend/StaticPageServiceTest.php b/tests/Unit/Services/Frontend/StaticPageServiceTest.php index 0ac31223..2ab401ae 100644 --- a/tests/Unit/Services/Frontend/StaticPageServiceTest.php +++ b/tests/Unit/Services/Frontend/StaticPageServiceTest.php @@ -12,9 +12,9 @@ class StaticPageServiceTest extends TestCase { $dir = public_path('html'); File::ensureDirectoryExists($dir); - File::put($dir . '/terms_of_service.html', '

Terms

'); + File::put($dir.'/terms_of_service.html', '

Terms

'); - $service = new StaticPageService(); + $service = new StaticPageService; $result = $service->load('terms_of_service.html', 'terms_of_service'); $this->assertTrue($result['ok']); @@ -24,7 +24,7 @@ class StaticPageServiceTest extends TestCase public function test_load_missing_returns_error(): void { - $service = new StaticPageService(); + $service = new StaticPageService; $result = $service->load('missing.html', 'missing'); $this->assertFalse($result['ok']); diff --git a/tests/Unit/Services/Grading/GradingBelowSixtyServiceTest.php b/tests/Unit/Services/Grading/GradingBelowSixtyServiceTest.php index 1981b4f8..06ebf76d 100644 --- a/tests/Unit/Services/Grading/GradingBelowSixtyServiceTest.php +++ b/tests/Unit/Services/Grading/GradingBelowSixtyServiceTest.php @@ -45,7 +45,7 @@ class GradingBelowSixtyServiceTest extends TestCase 'semester_score' => 55, ]); - $service = new GradingBelowSixtyService(); + $service = new GradingBelowSixtyService; $rows = $service->listRows('2025-2026', 'Fall'); $this->assertCount(1, $rows); diff --git a/tests/Unit/Services/Grading/GradingLockServiceTest.php b/tests/Unit/Services/Grading/GradingLockServiceTest.php index 2cf59bb0..e7b96b30 100644 --- a/tests/Unit/Services/Grading/GradingLockServiceTest.php +++ b/tests/Unit/Services/Grading/GradingLockServiceTest.php @@ -22,7 +22,7 @@ class GradingLockServiceTest extends TestCase 'school_year' => '2025-2026', ]); - $service = new GradingLockService(); + $service = new GradingLockService; $locked = $service->toggle(1, 'Fall', '2025-2026', 99); $this->assertTrue($locked); @@ -55,7 +55,7 @@ class GradingLockServiceTest extends TestCase ], ]); - $service = new GradingLockService(); + $service = new GradingLockService; $count = $service->lockAll('Fall', '2025-2026', 99); $this->assertSame(2, $count); diff --git a/tests/Unit/Services/Grading/HomeworkTrackingServiceTest.php b/tests/Unit/Services/Grading/HomeworkTrackingServiceTest.php index 88ffb099..43a094c0 100644 --- a/tests/Unit/Services/Grading/HomeworkTrackingServiceTest.php +++ b/tests/Unit/Services/Grading/HomeworkTrackingServiceTest.php @@ -72,7 +72,7 @@ class HomeworkTrackingServiceTest extends TestCase 'updated_at' => now(), ]); - $service = new HomeworkTrackingService(new HomeworkTrackingCalendarService()); + $service = new HomeworkTrackingService(new HomeworkTrackingCalendarService); $result = $service->report('Fall', '2025-2026', 1); $this->assertNotEmpty($result['teachers']); diff --git a/tests/Unit/Services/Incidents/IncidentAnalysisServiceTest.php b/tests/Unit/Services/Incidents/IncidentAnalysisServiceTest.php index 36f98db5..44ab9d71 100644 --- a/tests/Unit/Services/Incidents/IncidentAnalysisServiceTest.php +++ b/tests/Unit/Services/Incidents/IncidentAnalysisServiceTest.php @@ -48,7 +48,7 @@ class IncidentAnalysisServiceTest extends TestCase ], ]); - $service = new IncidentAnalysisService(new IncidentLookupService()); + $service = new IncidentAnalysisService(new IncidentLookupService); $result = $service->analyze('2025-2026', 'Fall'); $this->assertCount(1, $result); diff --git a/tests/Unit/Services/Incidents/IncidentHistoryServiceTest.php b/tests/Unit/Services/Incidents/IncidentHistoryServiceTest.php index f4a0e8dd..6b0f3f40 100644 --- a/tests/Unit/Services/Incidents/IncidentHistoryServiceTest.php +++ b/tests/Unit/Services/Incidents/IncidentHistoryServiceTest.php @@ -57,7 +57,7 @@ class IncidentHistoryServiceTest extends TestCase 'school_year' => '2025-2026', ]); - $service = new IncidentHistoryService(new IncidentLookupService()); + $service = new IncidentHistoryService(new IncidentLookupService); $rows = $service->processed('2025-2026', 'Fall'); $this->assertSame('5A', $rows[0]['grade']); diff --git a/tests/Unit/Services/Incidents/IncidentLookupServiceTest.php b/tests/Unit/Services/Incidents/IncidentLookupServiceTest.php index 20332c33..f76ebfd5 100644 --- a/tests/Unit/Services/Incidents/IncidentLookupServiceTest.php +++ b/tests/Unit/Services/Incidents/IncidentLookupServiceTest.php @@ -42,7 +42,7 @@ class IncidentLookupServiceTest extends TestCase 'school_year' => '2025-2026', ]); - $service = new IncidentLookupService(); + $service = new IncidentLookupService; $grades = $service->gradeOptionsWithActiveStudents('2025-2026'); $this->assertSame([['id' => 10, 'name' => '1A']], $grades); @@ -71,7 +71,7 @@ class IncidentLookupServiceTest extends TestCase 'school_year' => '2025-2026', ]); - $service = new IncidentLookupService(); + $service = new IncidentLookupService; $map = $service->updaterNameMap([1]); $this->assertSame('Jane Doe', $map[1]); diff --git a/tests/Unit/Services/Inventory/InventoryCategoryServiceTest.php b/tests/Unit/Services/Inventory/InventoryCategoryServiceTest.php index 3c6fad94..9da498da 100644 --- a/tests/Unit/Services/Inventory/InventoryCategoryServiceTest.php +++ b/tests/Unit/Services/Inventory/InventoryCategoryServiceTest.php @@ -13,7 +13,7 @@ class InventoryCategoryServiceTest extends TestCase public function test_create_category(): void { - $service = new InventoryCategoryService(); + $service = new InventoryCategoryService; $result = $service->create([ 'type' => 'office', 'name' => 'Supplies', @@ -32,7 +32,7 @@ class InventoryCategoryServiceTest extends TestCase 'updated_at' => now(), ]); - $service = new InventoryCategoryService(); + $service = new InventoryCategoryService; $result = $service->update($categoryId, ['name' => 'Updated']); $this->assertTrue($result['ok']); diff --git a/tests/Unit/Services/Inventory/InventoryItemServiceTest.php b/tests/Unit/Services/Inventory/InventoryItemServiceTest.php index 56e063d1..83b45b45 100644 --- a/tests/Unit/Services/Inventory/InventoryItemServiceTest.php +++ b/tests/Unit/Services/Inventory/InventoryItemServiceTest.php @@ -17,7 +17,7 @@ class InventoryItemServiceTest extends TestCase { $this->seedConfig(); $categoryId = $this->seedCategory(); - $service = new InventoryItemService(new InventoryContextService(), new InventoryMovementService(new InventoryContextService())); + $service = new InventoryItemService(new InventoryContextService, new InventoryMovementService(new InventoryContextService)); $result = $service->create([ 'type' => 'classroom', @@ -36,7 +36,7 @@ class InventoryItemServiceTest extends TestCase $this->seedConfig(); $categoryId = $this->seedCategory(); $itemId = $this->seedItem($categoryId); - $service = new InventoryItemService(new InventoryContextService(), new InventoryMovementService(new InventoryContextService())); + $service = new InventoryItemService(new InventoryContextService, new InventoryMovementService(new InventoryContextService)); $result = $service->update($itemId, ['name' => 'Updated'], 1); diff --git a/tests/Unit/Services/Inventory/InventoryMovementServiceTest.php b/tests/Unit/Services/Inventory/InventoryMovementServiceTest.php index f5b30e49..35a9299c 100644 --- a/tests/Unit/Services/Inventory/InventoryMovementServiceTest.php +++ b/tests/Unit/Services/Inventory/InventoryMovementServiceTest.php @@ -16,7 +16,7 @@ class InventoryMovementServiceTest extends TestCase { $this->seedConfig(); $itemId = $this->seedItem(); - $service = new InventoryMovementService(new InventoryContextService()); + $service = new InventoryMovementService(new InventoryContextService); $result = $service->create([ 'item_id' => $itemId, diff --git a/tests/Unit/Services/Inventory/InventorySummaryServiceTest.php b/tests/Unit/Services/Inventory/InventorySummaryServiceTest.php index 8b1f7dfe..b02ef8dd 100644 --- a/tests/Unit/Services/Inventory/InventorySummaryServiceTest.php +++ b/tests/Unit/Services/Inventory/InventorySummaryServiceTest.php @@ -17,7 +17,7 @@ class InventorySummaryServiceTest extends TestCase $this->seedConfig(); $this->seedItem(); - $service = new InventorySummaryService(new InventoryContextService()); + $service = new InventorySummaryService(new InventoryContextService); $result = $service->summary('classroom', '2025-2026'); $this->assertNotEmpty($result['items']); diff --git a/tests/Unit/Services/Inventory/InventoryTeacherDistributionServiceTest.php b/tests/Unit/Services/Inventory/InventoryTeacherDistributionServiceTest.php index ebe74742..dc1187f2 100644 --- a/tests/Unit/Services/Inventory/InventoryTeacherDistributionServiceTest.php +++ b/tests/Unit/Services/Inventory/InventoryTeacherDistributionServiceTest.php @@ -19,8 +19,8 @@ class InventoryTeacherDistributionServiceTest extends TestCase $userId = $this->seedUser(); $service = new InventoryTeacherDistributionService( - new InventoryContextService(), - new InventoryMovementService(new InventoryContextService()) + new InventoryContextService, + new InventoryMovementService(new InventoryContextService) ); $result = $service->formData($userId, null, null); @@ -41,8 +41,8 @@ class InventoryTeacherDistributionServiceTest extends TestCase $bookId = $this->seedItem($categoryId); $service = new InventoryTeacherDistributionService( - new InventoryContextService(), - new InventoryMovementService(new InventoryContextService()) + new InventoryContextService, + new InventoryMovementService(new InventoryContextService) ); $result = $service->distribute($userId, [ diff --git a/tests/Unit/Services/Inventory/SupplierServiceTest.php b/tests/Unit/Services/Inventory/SupplierServiceTest.php index a3192461..598617d4 100644 --- a/tests/Unit/Services/Inventory/SupplierServiceTest.php +++ b/tests/Unit/Services/Inventory/SupplierServiceTest.php @@ -13,7 +13,7 @@ class SupplierServiceTest extends TestCase public function test_create_supplier(): void { - $service = new SupplierService(); + $service = new SupplierService; $result = $service->create([ 'name' => 'Supplier A', 'email' => 'a@example.com', @@ -31,7 +31,7 @@ class SupplierServiceTest extends TestCase 'updated_at' => now(), ]); - $service = new SupplierService(); + $service = new SupplierService; $result = $service->update($id, ['name' => 'Supplier Updated']); $this->assertTrue($result['ok']); diff --git a/tests/Unit/Services/Inventory/SupplyCategoryServiceTest.php b/tests/Unit/Services/Inventory/SupplyCategoryServiceTest.php index 738b810d..392d021d 100644 --- a/tests/Unit/Services/Inventory/SupplyCategoryServiceTest.php +++ b/tests/Unit/Services/Inventory/SupplyCategoryServiceTest.php @@ -13,7 +13,7 @@ class SupplyCategoryServiceTest extends TestCase public function test_create_category(): void { - $service = new SupplyCategoryService(); + $service = new SupplyCategoryService; $result = $service->create(['name' => 'Paper']); $this->assertTrue($result['ok']); @@ -28,7 +28,7 @@ class SupplyCategoryServiceTest extends TestCase 'updated_at' => now(), ]); - $service = new SupplyCategoryService(); + $service = new SupplyCategoryService; $result = $service->update($id, ['name' => 'Pens Updated']); $this->assertTrue($result['ok']); diff --git a/tests/Unit/Services/Invoices/InvoiceConfigServiceTest.php b/tests/Unit/Services/Invoices/InvoiceConfigServiceTest.php index 4e5631f8..e1d26dc9 100644 --- a/tests/Unit/Services/Invoices/InvoiceConfigServiceTest.php +++ b/tests/Unit/Services/Invoices/InvoiceConfigServiceTest.php @@ -24,7 +24,7 @@ class InvoiceConfigServiceTest extends TestCase ['config_key' => 'refund_deadline', 'config_value' => '2025-10-01'], ]); - $service = new InvoiceConfigService(); + $service = new InvoiceConfigService; $this->assertSame('2025-2026', $service->getSchoolYear()); $this->assertSame('Fall', $service->getSemester()); diff --git a/tests/Unit/Services/Invoices/InvoiceGenerationServiceTest.php b/tests/Unit/Services/Invoices/InvoiceGenerationServiceTest.php index cfc9dad0..f62e1f77 100644 --- a/tests/Unit/Services/Invoices/InvoiceGenerationServiceTest.php +++ b/tests/Unit/Services/Invoices/InvoiceGenerationServiceTest.php @@ -77,7 +77,7 @@ class InvoiceGenerationServiceTest extends TestCase 'school_year' => '2025-2026', ]); - $config = new InvoiceConfigService(); + $config = new InvoiceConfigService; $grades = new InvoiceGradeService($config->getGradeFee()); $tuition = new InvoiceTuitionService( $grades, diff --git a/tests/Unit/Services/Invoices/InvoiceManagementServiceTest.php b/tests/Unit/Services/Invoices/InvoiceManagementServiceTest.php index 65029f1a..ce37da82 100644 --- a/tests/Unit/Services/Invoices/InvoiceManagementServiceTest.php +++ b/tests/Unit/Services/Invoices/InvoiceManagementServiceTest.php @@ -107,7 +107,7 @@ class InvoiceManagementServiceTest extends TestCase 'refund_paid_amount' => 5, ]); - $service = new InvoiceManagementService(new InvoiceConfigService()); + $service = new InvoiceManagementService(new InvoiceConfigService); $data = $service->getManagementData('2025-2026'); $this->assertSame('2025-2026', $data['schoolYear']); diff --git a/tests/Unit/Services/Invoices/InvoicePaymentServiceTest.php b/tests/Unit/Services/Invoices/InvoicePaymentServiceTest.php index 801c7160..cc678175 100644 --- a/tests/Unit/Services/Invoices/InvoicePaymentServiceTest.php +++ b/tests/Unit/Services/Invoices/InvoicePaymentServiceTest.php @@ -53,7 +53,7 @@ class InvoicePaymentServiceTest extends TestCase 'refund_paid_amount' => 5, ]); - $service = new InvoicePaymentService(new InvoiceConfigService()); + $service = new InvoicePaymentService(new InvoiceConfigService); $data = $service->getParentInvoiceSummary(10, '2025-2026'); $this->assertSame('2025-2026', $data['selectedYear']); diff --git a/tests/Unit/Services/Invoices/InvoicePdfServiceTest.php b/tests/Unit/Services/Invoices/InvoicePdfServiceTest.php index 2d7f2657..43a55950 100644 --- a/tests/Unit/Services/Invoices/InvoicePdfServiceTest.php +++ b/tests/Unit/Services/Invoices/InvoicePdfServiceTest.php @@ -89,7 +89,7 @@ class InvoicePdfServiceTest extends TestCase 'school_year' => '2025-2026', ]); - $config = new InvoiceConfigService(); + $config = new InvoiceConfigService; $grades = new InvoiceGradeService($config->getGradeFee()); $service = new InvoicePdfService($config, $grades); diff --git a/tests/Unit/Services/Messaging/MessageCommandServiceTest.php b/tests/Unit/Services/Messaging/MessageCommandServiceTest.php index 85e8f6e1..15f15c19 100644 --- a/tests/Unit/Services/Messaging/MessageCommandServiceTest.php +++ b/tests/Unit/Services/Messaging/MessageCommandServiceTest.php @@ -23,7 +23,7 @@ class MessageCommandServiceTest extends TestCase $sender = $this->createUser('sender@example.com'); $recipient = $this->createUser('recipient@example.com'); - $service = new MessageCommandService(new GlobalConfigService()); + $service = new MessageCommandService(new GlobalConfigService); $message = $service->create($sender->id, [ 'recipient_id' => $recipient->id, 'subject' => 'Subject', @@ -41,7 +41,7 @@ class MessageCommandServiceTest extends TestCase { $sender = $this->createUser('sender2@example.com'); - $service = new MessageCommandService(new GlobalConfigService()); + $service = new MessageCommandService(new GlobalConfigService); $this->expectException(\RuntimeException::class); $service->create($sender->id, [ diff --git a/tests/Unit/Services/Messaging/MessageQueryServiceTest.php b/tests/Unit/Services/Messaging/MessageQueryServiceTest.php index 33677cef..c4d5bc19 100644 --- a/tests/Unit/Services/Messaging/MessageQueryServiceTest.php +++ b/tests/Unit/Services/Messaging/MessageQueryServiceTest.php @@ -31,7 +31,7 @@ class MessageQueryServiceTest extends TestCase 'school_year' => '2025-2026', ]); - $service = new MessageQueryService(); + $service = new MessageQueryService; $paginator = $service->inbox($recipient->id, [], 1, 20); $this->assertSame(1, $paginator->total()); diff --git a/tests/Unit/Services/Messaging/MessageRecipientServiceTest.php b/tests/Unit/Services/Messaging/MessageRecipientServiceTest.php index 6019804a..6b206534 100644 --- a/tests/Unit/Services/Messaging/MessageRecipientServiceTest.php +++ b/tests/Unit/Services/Messaging/MessageRecipientServiceTest.php @@ -28,7 +28,7 @@ class MessageRecipientServiceTest extends TestCase 'updated_at' => now(), ]); - $service = new MessageRecipientService(); + $service = new MessageRecipientService; $rows = $service->teachers(); $this->assertSame($teacher->id, $rows[0]['id']); @@ -82,7 +82,7 @@ class MessageRecipientServiceTest extends TestCase 'school_year' => '2025-2026', ]); - $service = new MessageRecipientService(); + $service = new MessageRecipientService; $rows = $service->parents(); $ids = array_column($rows, 'id'); diff --git a/tests/Unit/Services/Navigation/NavBuilderServiceTest.php b/tests/Unit/Services/Navigation/NavBuilderServiceTest.php index 26f6bb7a..476e4b39 100644 --- a/tests/Unit/Services/Navigation/NavBuilderServiceTest.php +++ b/tests/Unit/Services/Navigation/NavBuilderServiceTest.php @@ -2,8 +2,8 @@ namespace Tests\Unit\Services\Navigation; -use App\Services\Navigation\NavBuilderService; use App\Services\Navigation\NavbarService; +use App\Services\Navigation\NavBuilderService; use Illuminate\Foundation\Testing\RefreshDatabase; use Illuminate\Support\Facades\DB; use Tests\TestCase; @@ -19,7 +19,7 @@ class NavBuilderServiceTest extends TestCase 'priority' => 1, ]); - $service = new NavBuilderService(new NavbarService()); + $service = new NavBuilderService(new NavbarService); $result = $service->save([ 'label' => 'Dashboard', 'url' => 'dashboard', @@ -45,7 +45,7 @@ class NavBuilderServiceTest extends TestCase 'is_enabled' => 1, ]); - $service = new NavBuilderService(new NavbarService()); + $service = new NavBuilderService(new NavbarService); $service->reorder([$id => 5]); $this->assertDatabaseHas('nav_items', [ diff --git a/tests/Unit/Services/Navigation/NavbarServiceTest.php b/tests/Unit/Services/Navigation/NavbarServiceTest.php index 525122aa..8ec5ec1c 100644 --- a/tests/Unit/Services/Navigation/NavbarServiceTest.php +++ b/tests/Unit/Services/Navigation/NavbarServiceTest.php @@ -38,7 +38,7 @@ class NavbarServiceTest extends TestCase ['role_id' => $roleId, 'nav_item_id' => $childId], ]); - $service = new NavbarService(); + $service = new NavbarService; $menu = $service->getMenuForRoles(['admin']); $this->assertCount(1, $menu); diff --git a/tests/Unit/Services/Notifications/NotificationActiveServiceTest.php b/tests/Unit/Services/Notifications/NotificationActiveServiceTest.php index c3ca62e0..670fbec3 100644 --- a/tests/Unit/Services/Notifications/NotificationActiveServiceTest.php +++ b/tests/Unit/Services/Notifications/NotificationActiveServiceTest.php @@ -38,7 +38,7 @@ class NotificationActiveServiceTest extends TestCase ], ]); - $service = new NotificationActiveService(); + $service = new NotificationActiveService; $result = $service->list('parent'); $this->assertCount(1, $result['notifications']); diff --git a/tests/Unit/Services/Notifications/NotificationCleanupServiceTest.php b/tests/Unit/Services/Notifications/NotificationCleanupServiceTest.php index 82878abe..056670e5 100644 --- a/tests/Unit/Services/Notifications/NotificationCleanupServiceTest.php +++ b/tests/Unit/Services/Notifications/NotificationCleanupServiceTest.php @@ -42,7 +42,7 @@ class NotificationCleanupServiceTest extends TestCase ], ]); - $service = new NotificationCleanupService(); + $service = new NotificationCleanupService; $deleted = $service->cleanupExpired(); $this->assertSame(1, $deleted); diff --git a/tests/Unit/Services/Notifications/NotificationDeletedServiceTest.php b/tests/Unit/Services/Notifications/NotificationDeletedServiceTest.php index c54b3481..ecd537db 100644 --- a/tests/Unit/Services/Notifications/NotificationDeletedServiceTest.php +++ b/tests/Unit/Services/Notifications/NotificationDeletedServiceTest.php @@ -24,7 +24,7 @@ class NotificationDeletedServiceTest extends TestCase 'updated_at' => now(), ]); - $service = new NotificationDeletedService(); + $service = new NotificationDeletedService; $result = $service->list(); $this->assertCount(1, $result['notifications']); diff --git a/tests/Unit/Services/Notifications/NotificationDispatchServiceTest.php b/tests/Unit/Services/Notifications/NotificationDispatchServiceTest.php index b3dba708..4eb945a2 100644 --- a/tests/Unit/Services/Notifications/NotificationDispatchServiceTest.php +++ b/tests/Unit/Services/Notifications/NotificationDispatchServiceTest.php @@ -12,7 +12,7 @@ class NotificationDispatchServiceTest extends TestCase { Log::spy(); - $service = new NotificationDispatchService(); + $service = new NotificationDispatchService; $service->sendEmail('parent@example.com', 'Subject', 'Message'); $service->sendSms('5551112222', 'Message'); diff --git a/tests/Unit/Services/Notifications/NotificationManagementServiceTest.php b/tests/Unit/Services/Notifications/NotificationManagementServiceTest.php index f3da6090..70abbfb9 100644 --- a/tests/Unit/Services/Notifications/NotificationManagementServiceTest.php +++ b/tests/Unit/Services/Notifications/NotificationManagementServiceTest.php @@ -23,7 +23,7 @@ class NotificationManagementServiceTest extends TestCase 'updated_at' => now(), ]); - $service = new NotificationManagementService(); + $service = new NotificationManagementService; $result = $service->update(1, ['title' => 'New']); $this->assertTrue($result['ok']); @@ -35,7 +35,7 @@ class NotificationManagementServiceTest extends TestCase public function test_update_returns_false_when_missing(): void { - $service = new NotificationManagementService(); + $service = new NotificationManagementService; $result = $service->update(999, ['title' => 'New']); $this->assertFalse($result['ok']); @@ -53,7 +53,7 @@ class NotificationManagementServiceTest extends TestCase 'updated_at' => now(), ]); - $service = new NotificationManagementService(); + $service = new NotificationManagementService; $this->assertTrue($service->delete(1)); $this->assertSoftDeleted('notifications', ['id' => 1]); diff --git a/tests/Unit/Services/Notifications/NotificationReadServiceTest.php b/tests/Unit/Services/Notifications/NotificationReadServiceTest.php index 5506cbaf..de459ad5 100644 --- a/tests/Unit/Services/Notifications/NotificationReadServiceTest.php +++ b/tests/Unit/Services/Notifications/NotificationReadServiceTest.php @@ -19,7 +19,7 @@ class NotificationReadServiceTest extends TestCase 'is_read' => 0, ]); - $service = new NotificationReadService(); + $service = new NotificationReadService; $result = $service->markRead(1, 1); $this->assertTrue($result); @@ -32,7 +32,7 @@ class NotificationReadServiceTest extends TestCase public function test_mark_read_returns_false_when_missing(): void { - $service = new NotificationReadService(); + $service = new NotificationReadService; $this->assertFalse($service->markRead(1, 99)); } diff --git a/tests/Unit/Services/Notifications/NotificationRecipientServiceTest.php b/tests/Unit/Services/Notifications/NotificationRecipientServiceTest.php index 338d4e2b..7e94c4a0 100644 --- a/tests/Unit/Services/Notifications/NotificationRecipientServiceTest.php +++ b/tests/Unit/Services/Notifications/NotificationRecipientServiceTest.php @@ -46,7 +46,7 @@ class NotificationRecipientServiceTest extends TestCase 'role_id' => 1, ]); - $service = new NotificationRecipientService(); + $service = new NotificationRecipientService; $rows = $service->getRecipients('parent'); $this->assertCount(1, $rows); diff --git a/tests/Unit/Services/Notifications/NotificationShowServiceTest.php b/tests/Unit/Services/Notifications/NotificationShowServiceTest.php index 38650730..a28194a2 100644 --- a/tests/Unit/Services/Notifications/NotificationShowServiceTest.php +++ b/tests/Unit/Services/Notifications/NotificationShowServiceTest.php @@ -30,7 +30,7 @@ class NotificationShowServiceTest extends TestCase 'is_read' => 0, ]); - $service = new NotificationShowService(); + $service = new NotificationShowService; $row = $service->getForUser(1, 1); $this->assertNotNull($row); @@ -39,7 +39,7 @@ class NotificationShowServiceTest extends TestCase public function test_get_for_user_returns_null_when_missing(): void { - $service = new NotificationShowService(); + $service = new NotificationShowService; $row = $service->getForUser(1, 99); $this->assertNull($row); diff --git a/tests/Unit/Services/Notifications/NotificationUserListServiceTest.php b/tests/Unit/Services/Notifications/NotificationUserListServiceTest.php index b3709f3b..14e3491e 100644 --- a/tests/Unit/Services/Notifications/NotificationUserListServiceTest.php +++ b/tests/Unit/Services/Notifications/NotificationUserListServiceTest.php @@ -49,7 +49,7 @@ class NotificationUserListServiceTest extends TestCase ], ]); - $service = new NotificationUserListService(); + $service = new NotificationUserListService; $result = $service->listForUser(1, [ 'read' => true, 'per_page' => 10, diff --git a/tests/Unit/Services/Notifications/UserNotificationDispatchServiceTest.php b/tests/Unit/Services/Notifications/UserNotificationDispatchServiceTest.php index 5b024cbb..d59d9b1c 100644 --- a/tests/Unit/Services/Notifications/UserNotificationDispatchServiceTest.php +++ b/tests/Unit/Services/Notifications/UserNotificationDispatchServiceTest.php @@ -34,7 +34,7 @@ class UserNotificationDispatchServiceTest extends TestCase 'school_year' => '2025-2026', ]); - $service = new UserNotificationDispatchService(); + $service = new UserNotificationDispatchService; $result = $service->notifyUser(1, 'Title', 'Message', ['in_app'], 'registration', 'parent'); $this->assertTrue($result['ok']); @@ -50,7 +50,7 @@ class UserNotificationDispatchServiceTest extends TestCase public function test_notify_user_rejects_invalid_user(): void { - $service = new UserNotificationDispatchService(); + $service = new UserNotificationDispatchService; $result = $service->notifyUser(0, 'Title', 'Message'); $this->assertFalse($result['ok']); diff --git a/tests/Unit/Services/Parents/ParentAttendanceReportServiceTest.php b/tests/Unit/Services/Parents/ParentAttendanceReportServiceTest.php index 3321c784..326e42d0 100644 --- a/tests/Unit/Services/Parents/ParentAttendanceReportServiceTest.php +++ b/tests/Unit/Services/Parents/ParentAttendanceReportServiceTest.php @@ -25,9 +25,9 @@ class ParentAttendanceReportServiceTest extends TestCase $this->seedStudent($parentId); $service = new ParentAttendanceReportService( - new ParentConfigService(), - new ParentAttendanceReportCalendarService(new ParentConfigService()), - new SemesterRangeService(new SemesterConfigService()) + new ParentConfigService, + new ParentAttendanceReportCalendarService(new ParentConfigService), + new SemesterRangeService(new SemesterConfigService) ); $data = $service->formData($parentId); diff --git a/tests/Unit/Services/Parents/ParentAttendanceServiceTest.php b/tests/Unit/Services/Parents/ParentAttendanceServiceTest.php index 7ed2a824..b9dd229e 100644 --- a/tests/Unit/Services/Parents/ParentAttendanceServiceTest.php +++ b/tests/Unit/Services/Parents/ParentAttendanceServiceTest.php @@ -2,7 +2,6 @@ namespace Tests\Unit\Services\Parents; -use App\Models\User; use App\Services\Parents\ParentAttendanceService; use App\Services\Parents\ParentConfigService; use Illuminate\Foundation\Testing\RefreshDatabase; @@ -64,7 +63,7 @@ class ParentAttendanceServiceTest extends TestCase 'school_year' => '2025-2026', ]); - $service = new ParentAttendanceService(new ParentConfigService()); + $service = new ParentAttendanceService(new ParentConfigService); $result = $service->listAttendance($parentId, '2025-2026'); $this->assertSame('2025-2026', $result['selectedYear']); diff --git a/tests/Unit/Services/Parents/ParentConfigServiceTest.php b/tests/Unit/Services/Parents/ParentConfigServiceTest.php index 3bf0c70d..839eeda0 100644 --- a/tests/Unit/Services/Parents/ParentConfigServiceTest.php +++ b/tests/Unit/Services/Parents/ParentConfigServiceTest.php @@ -24,7 +24,7 @@ class ParentConfigServiceTest extends TestCase ['config_key' => 'fall_semester_start', 'config_value' => '2025-09-05'], ]); - $service = new ParentConfigService(); + $service = new ParentConfigService; $context = $service->context(); $this->assertSame('2025-2026', $context['school_year']); diff --git a/tests/Unit/Services/Parents/ParentEmergencyContactServiceTest.php b/tests/Unit/Services/Parents/ParentEmergencyContactServiceTest.php index c6bf969c..bf662c44 100644 --- a/tests/Unit/Services/Parents/ParentEmergencyContactServiceTest.php +++ b/tests/Unit/Services/Parents/ParentEmergencyContactServiceTest.php @@ -18,7 +18,7 @@ class ParentEmergencyContactServiceTest extends TestCase $this->seedConfig(); $parentId = $this->seedParent(); - $service = new ParentEmergencyContactService(new ParentConfigService(), new PhoneFormatterService()); + $service = new ParentEmergencyContactService(new ParentConfigService, new PhoneFormatterService); $contact = $service->store($parentId, [ 'name' => 'Sam Contact', 'cellphone' => '1234567890', diff --git a/tests/Unit/Services/Parents/ParentEnrollmentServiceTest.php b/tests/Unit/Services/Parents/ParentEnrollmentServiceTest.php index 83b0aa1c..07a467f0 100644 --- a/tests/Unit/Services/Parents/ParentEnrollmentServiceTest.php +++ b/tests/Unit/Services/Parents/ParentEnrollmentServiceTest.php @@ -42,7 +42,7 @@ class ParentEnrollmentServiceTest extends TestCase 'is_withdrawn' => 0, ]); - $service = new ParentEnrollmentService(new ParentConfigService()); + $service = new ParentEnrollmentService(new ParentConfigService); $result = $service->overview($parentId, '2025-2026'); $this->assertSame('2025-2026', $result['selectedYear']); diff --git a/tests/Unit/Services/Parents/ParentInvoiceServiceTest.php b/tests/Unit/Services/Parents/ParentInvoiceServiceTest.php index 510a736c..59961f00 100644 --- a/tests/Unit/Services/Parents/ParentInvoiceServiceTest.php +++ b/tests/Unit/Services/Parents/ParentInvoiceServiceTest.php @@ -34,7 +34,7 @@ class ParentInvoiceServiceTest extends TestCase 'issue_date' => '2025-09-02', ]); - $service = new ParentInvoiceService(); + $service = new ParentInvoiceService; $rows = $service->listInvoices($parentId); $this->assertCount(1, $rows); diff --git a/tests/Unit/Services/Parents/ParentRegistrationServiceTest.php b/tests/Unit/Services/Parents/ParentRegistrationServiceTest.php index fbb9eecd..a34842e4 100644 --- a/tests/Unit/Services/Parents/ParentRegistrationServiceTest.php +++ b/tests/Unit/Services/Parents/ParentRegistrationServiceTest.php @@ -18,7 +18,7 @@ class ParentRegistrationServiceTest extends TestCase $this->seedConfig(); $parentId = $this->seedParent(); - $service = new ParentRegistrationService(new ParentConfigService(), new SchoolIdService()); + $service = new ParentRegistrationService(new ParentConfigService, new SchoolIdService); $result = $service->register($parentId, [ [ 'firstname' => 'Omar', diff --git a/tests/Unit/Services/Payments/PaymentBalanceServiceTest.php b/tests/Unit/Services/Payments/PaymentBalanceServiceTest.php index 5fa4e6d7..87e71d1b 100644 --- a/tests/Unit/Services/Payments/PaymentBalanceServiceTest.php +++ b/tests/Unit/Services/Payments/PaymentBalanceServiceTest.php @@ -27,7 +27,7 @@ class PaymentBalanceServiceTest extends TestCase 'status' => 'Pending', ]); - $service = new PaymentBalanceService(); + $service = new PaymentBalanceService; $updated = $service->updateBalance(1, 40); $this->assertTrue($updated); diff --git a/tests/Unit/Services/Payments/PaymentEnrollmentEventServiceTest.php b/tests/Unit/Services/Payments/PaymentEnrollmentEventServiceTest.php index 4225103d..12b0890c 100644 --- a/tests/Unit/Services/Payments/PaymentEnrollmentEventServiceTest.php +++ b/tests/Unit/Services/Payments/PaymentEnrollmentEventServiceTest.php @@ -14,7 +14,7 @@ class PaymentEnrollmentEventServiceTest extends TestCase private function makeService(): PaymentEnrollmentEventService { - return new PaymentEnrollmentEventService(new ApplicationUrlService()); + return new PaymentEnrollmentEventService(new ApplicationUrlService); } public function test_build_event_data_requires_parent(): void diff --git a/tests/Unit/Services/Payments/PaymentLookupServiceTest.php b/tests/Unit/Services/Payments/PaymentLookupServiceTest.php index a47e0fd1..03afbc4b 100644 --- a/tests/Unit/Services/Payments/PaymentLookupServiceTest.php +++ b/tests/Unit/Services/Payments/PaymentLookupServiceTest.php @@ -41,7 +41,7 @@ class PaymentLookupServiceTest extends TestCase 'status' => 'Pending', ]); - $service = new PaymentLookupService(); + $service = new PaymentLookupService; $results = $service->getByParent(10, '2025-2026'); $this->assertCount(1, $results); diff --git a/tests/Unit/Services/Payments/PaymentNotificationDispatchServiceTest.php b/tests/Unit/Services/Payments/PaymentNotificationDispatchServiceTest.php index 74b9c2ba..07345f48 100644 --- a/tests/Unit/Services/Payments/PaymentNotificationDispatchServiceTest.php +++ b/tests/Unit/Services/Payments/PaymentNotificationDispatchServiceTest.php @@ -18,7 +18,7 @@ class PaymentNotificationDispatchServiceTest extends TestCase ['id' => 2002, 'config_key' => 'semester', 'config_value' => 'Fall'], ]); - $service = new PaymentNotificationDispatchService(); + $service = new PaymentNotificationDispatchService; $service->notifyUser(10, 'Payment Reminder', 'Reminder body', ['in_app']); $this->assertDatabaseHas('notifications', [ diff --git a/tests/Unit/Services/Payments/PaymentPlanServiceTest.php b/tests/Unit/Services/Payments/PaymentPlanServiceTest.php index 119ffa30..489dcd70 100644 --- a/tests/Unit/Services/Payments/PaymentPlanServiceTest.php +++ b/tests/Unit/Services/Payments/PaymentPlanServiceTest.php @@ -53,7 +53,7 @@ class PaymentPlanServiceTest extends TestCase 'semester' => 'Fall', ]); - $service = new PaymentPlanService(); + $service = new PaymentPlanService; $payment = $service->createPlan([ 'parent_id' => 10, 'invoice_id' => 1, diff --git a/tests/Unit/Services/Payments/PaymentTransactionServiceTest.php b/tests/Unit/Services/Payments/PaymentTransactionServiceTest.php index aaf648ea..9551e94d 100644 --- a/tests/Unit/Services/Payments/PaymentTransactionServiceTest.php +++ b/tests/Unit/Services/Payments/PaymentTransactionServiceTest.php @@ -3,8 +3,8 @@ namespace Tests\Unit\Services\Payments; use App\Services\Payments\PaymentTransactionService; -use Illuminate\Foundation\Testing\RefreshDatabase; use Illuminate\Database\Schema\Blueprint; +use Illuminate\Foundation\Testing\RefreshDatabase; use Illuminate\Support\Facades\DB; use Illuminate\Support\Facades\Schema; use Tests\TestCase; @@ -15,13 +15,13 @@ class PaymentTransactionServiceTest extends TestCase public function test_create_and_update_transaction(): void { - if (!Schema::hasColumn('payment_transactions', 'payment_reference')) { + if (! Schema::hasColumn('payment_transactions', 'payment_reference')) { Schema::table('payment_transactions', function (Blueprint $table) { $table->string('payment_reference')->nullable(); }); } - if (!Schema::hasColumn('payment_transactions', 'is_full_payment')) { + if (! Schema::hasColumn('payment_transactions', 'is_full_payment')) { Schema::table('payment_transactions', function (Blueprint $table) { $table->boolean('is_full_payment')->default(0); }); @@ -41,7 +41,7 @@ class PaymentTransactionServiceTest extends TestCase 'status' => 'Pending', ]); - $service = new PaymentTransactionService(); + $service = new PaymentTransactionService; $transaction = $service->create([ 'transaction_id' => 'TXN-1', 'payment_id' => 1, diff --git a/tests/Unit/Services/Payments/PaypalPaymentServiceTest.php b/tests/Unit/Services/Payments/PaypalPaymentServiceTest.php index a7a93778..a52fd31c 100644 --- a/tests/Unit/Services/Payments/PaypalPaymentServiceTest.php +++ b/tests/Unit/Services/Payments/PaypalPaymentServiceTest.php @@ -14,7 +14,7 @@ class PaypalPaymentServiceTest extends TestCase private function makeService(): PaypalPaymentService { - return new PaypalPaymentService(new ApplicationUrlService()); + return new PaypalPaymentService(new ApplicationUrlService); } public function test_create_payment_falls_back_to_hosted_when_sdk_missing(): void diff --git a/tests/Unit/Services/Payments/PaypalTransactionsServiceTest.php b/tests/Unit/Services/Payments/PaypalTransactionsServiceTest.php index 2e2759fb..9457959c 100644 --- a/tests/Unit/Services/Payments/PaypalTransactionsServiceTest.php +++ b/tests/Unit/Services/Payments/PaypalTransactionsServiceTest.php @@ -37,7 +37,7 @@ class PaypalTransactionsServiceTest extends TestCase 'created_at' => '2025-01-02 00:00:00', ]); - $service = new PaypalTransactionsService(); + $service = new PaypalTransactionsService; $results = $service->listAll('ABC'); $this->assertCount(1, $results); diff --git a/tests/Unit/Services/Policy/PolicyContentServiceTest.php b/tests/Unit/Services/Policy/PolicyContentServiceTest.php index 1c12b85a..702c1266 100644 --- a/tests/Unit/Services/Policy/PolicyContentServiceTest.php +++ b/tests/Unit/Services/Policy/PolicyContentServiceTest.php @@ -12,7 +12,7 @@ class PolicyContentServiceTest extends TestCase public function test_get_policy_returns_content(): void { - $service = new PolicyContentService(); + $service = new PolicyContentService; $result = $service->getPolicy('school'); $this->assertSame('school', $result['type']); diff --git a/tests/Unit/Services/Promotions/LevelProgressionServiceTest.php b/tests/Unit/Services/Promotions/LevelProgressionServiceTest.php index f20daddd..58e1dc0b 100644 --- a/tests/Unit/Services/Promotions/LevelProgressionServiceTest.php +++ b/tests/Unit/Services/Promotions/LevelProgressionServiceTest.php @@ -14,7 +14,7 @@ class LevelProgressionServiceTest extends TestCase public function test_seed_defaults_creates_baseline_levels(): void { - $service = new LevelProgressionService(); + $service = new LevelProgressionService; $created = $service->seedDefaults(); $this->assertGreaterThan(0, $created); $this->assertSame($created, LevelProgression::query()->count()); @@ -35,7 +35,7 @@ class LevelProgressionServiceTest extends TestCase ['id' => 8, 'class_name' => 'Grade 4', 'school_year' => '2025-2026', 'semester' => 'Fall', 'capacity' => 0], ]); - $service = new LevelProgressionService(); + $service = new LevelProgressionService; $service->seedDefaults(); $resolved = $service->resolveByCurrentClassId(7); @@ -53,7 +53,7 @@ class LevelProgressionServiceTest extends TestCase ['id' => 101, 'class_name' => 'Custom B', 'school_year' => '2025-2026', 'semester' => 'Fall', 'capacity' => 0], ]); - $service = new LevelProgressionService(); + $service = new LevelProgressionService; $resolved = $service->resolveByCurrentClassId(100); $this->assertNotNull($resolved); @@ -64,7 +64,7 @@ class LevelProgressionServiceTest extends TestCase public function test_upsert_mapping_updates_existing_row(): void { - $service = new LevelProgressionService(); + $service = new LevelProgressionService; $service->upsertMapping([ 'current_level_name' => 'Grade 5', 'next_level_name' => 'Grade 6', diff --git a/tests/Unit/Services/Promotions/PromotionEligibilityServiceTest.php b/tests/Unit/Services/Promotions/PromotionEligibilityServiceTest.php index 3d34fb9c..3f155996 100644 --- a/tests/Unit/Services/Promotions/PromotionEligibilityServiceTest.php +++ b/tests/Unit/Services/Promotions/PromotionEligibilityServiceTest.php @@ -2,6 +2,7 @@ namespace Tests\Unit\Services\Promotions; +use App\Models\LevelProgression; use App\Models\StudentPromotionRecord; use App\Services\Promotions\LevelProgressionService; use App\Services\Promotions\PromotionAuditService; @@ -75,7 +76,7 @@ class PromotionEligibilityServiceTest extends TestCase $this->seedDecision($studentId, '2025-2026', 90.0, 'promoted'); // Mark Youth as terminal - \App\Models\LevelProgression::query()->where('current_level_name', 'Youth')->update(['is_terminal' => 1]); + LevelProgression::query()->where('current_level_name', 'Youth')->update(['is_terminal' => 1]); $service = $this->makeService(); $record = $service->evaluateStudent($studentId, '2025-2026', 1); @@ -108,9 +109,10 @@ class PromotionEligibilityServiceTest extends TestCase private function makeService(): PromotionEligibilityService { - $progression = new LevelProgressionService(); - $audit = new PromotionAuditService(); + $progression = new LevelProgressionService; + $audit = new PromotionAuditService; $status = new PromotionStatusService($audit); + return new PromotionEligibilityService($progression, $status, $audit); } @@ -173,4 +175,4 @@ class PromotionEligibilityServiceTest extends TestCase 'updated_at' => now(), ]); } -} \ No newline at end of file +} diff --git a/tests/Unit/Services/Promotions/PromotionEnrollmentServiceTest.php b/tests/Unit/Services/Promotions/PromotionEnrollmentServiceTest.php index efa98d9c..27c627c8 100644 --- a/tests/Unit/Services/Promotions/PromotionEnrollmentServiceTest.php +++ b/tests/Unit/Services/Promotions/PromotionEnrollmentServiceTest.php @@ -100,7 +100,7 @@ class PromotionEnrollmentServiceTest extends TestCase */ private function bootstrap(): array { - $audit = new PromotionAuditService(); + $audit = new PromotionAuditService; $service = new PromotionEnrollmentService(new PromotionStatusService($audit), $audit); $parentId = (int) DB::table('users')->insertGetId([ diff --git a/tests/Unit/Services/Promotions/PromotionStatusServiceTest.php b/tests/Unit/Services/Promotions/PromotionStatusServiceTest.php index c4e157f6..b21db466 100644 --- a/tests/Unit/Services/Promotions/PromotionStatusServiceTest.php +++ b/tests/Unit/Services/Promotions/PromotionStatusServiceTest.php @@ -82,7 +82,7 @@ class PromotionStatusServiceTest extends TestCase private function makeService(): PromotionStatusService { - return new PromotionStatusService(new PromotionAuditService()); + return new PromotionStatusService(new PromotionAuditService); } private function makeRecord(string $status): StudentPromotionRecord diff --git a/tests/Unit/Services/PurchaseOrders/PurchaseOrderCreateServiceTest.php b/tests/Unit/Services/PurchaseOrders/PurchaseOrderCreateServiceTest.php index 332dd0ab..adc7a63d 100644 --- a/tests/Unit/Services/PurchaseOrders/PurchaseOrderCreateServiceTest.php +++ b/tests/Unit/Services/PurchaseOrders/PurchaseOrderCreateServiceTest.php @@ -16,7 +16,7 @@ class PurchaseOrderCreateServiceTest extends TestCase $supplierId = DB::table('suppliers')->insertGetId(['name' => 'Alpha Supplies']); $supplyId = DB::table('supplies')->insertGetId(['name' => 'Paper', 'unit' => 'box', 'qty_on_hand' => 0]); - $service = new PurchaseOrderCreateService(); + $service = new PurchaseOrderCreateService; $po = $service->create([ 'po_number' => 'PO-500', 'supplier_id' => $supplierId, diff --git a/tests/Unit/Services/PurchaseOrders/PurchaseOrderReceiveServiceTest.php b/tests/Unit/Services/PurchaseOrders/PurchaseOrderReceiveServiceTest.php index f560dd72..8cce2db6 100644 --- a/tests/Unit/Services/PurchaseOrders/PurchaseOrderReceiveServiceTest.php +++ b/tests/Unit/Services/PurchaseOrders/PurchaseOrderReceiveServiceTest.php @@ -39,7 +39,7 @@ class PurchaseOrderReceiveServiceTest extends TestCase 'unit_cost' => 3, ]); - $service = new PurchaseOrderReceiveService(); + $service = new PurchaseOrderReceiveService; $result = $service->receive($po->id, [ ['id' => $item->id, 'quantity' => 3], ], 'tester@example.com'); diff --git a/tests/Unit/Services/PurchaseOrders/PurchaseOrderStatusServiceTest.php b/tests/Unit/Services/PurchaseOrders/PurchaseOrderStatusServiceTest.php index 37d0532c..33e0e63c 100644 --- a/tests/Unit/Services/PurchaseOrders/PurchaseOrderStatusServiceTest.php +++ b/tests/Unit/Services/PurchaseOrders/PurchaseOrderStatusServiceTest.php @@ -22,7 +22,7 @@ class PurchaseOrderStatusServiceTest extends TestCase 'total' => 0, ]); - $service = new PurchaseOrderStatusService(); + $service = new PurchaseOrderStatusService; $updated = $service->cancel($po->id); $this->assertSame(PurchaseOrder::STATUS_CANCELED, $updated->status); diff --git a/tests/Unit/Services/Refunds/RefundDecisionServiceTest.php b/tests/Unit/Services/Refunds/RefundDecisionServiceTest.php index 298f6054..5842e1ec 100644 --- a/tests/Unit/Services/Refunds/RefundDecisionServiceTest.php +++ b/tests/Unit/Services/Refunds/RefundDecisionServiceTest.php @@ -26,7 +26,7 @@ class RefundDecisionServiceTest extends TestCase 'updated_at' => now(), ]); - $service = new RefundDecisionService(); + $service = new RefundDecisionService; $result = $service->approve(1, 2); $this->assertTrue($result['ok']); diff --git a/tests/Unit/Services/Refunds/RefundNotificationServiceTest.php b/tests/Unit/Services/Refunds/RefundNotificationServiceTest.php index 4e687932..ef0a8957 100644 --- a/tests/Unit/Services/Refunds/RefundNotificationServiceTest.php +++ b/tests/Unit/Services/Refunds/RefundNotificationServiceTest.php @@ -15,7 +15,7 @@ class RefundNotificationServiceTest extends TestCase { Log::spy(); - $service = new RefundNotificationService(); + $service = new RefundNotificationService; $service->notifyPending(10, 50.25, 'https://example.test/login'); Log::shouldHaveReceived('info')->once(); diff --git a/tests/Unit/Services/Refunds/RefundOverpaymentServiceTest.php b/tests/Unit/Services/Refunds/RefundOverpaymentServiceTest.php index 7b648b67..c6ee418f 100644 --- a/tests/Unit/Services/Refunds/RefundOverpaymentServiceTest.php +++ b/tests/Unit/Services/Refunds/RefundOverpaymentServiceTest.php @@ -45,7 +45,7 @@ class RefundOverpaymentServiceTest extends TestCase 'status' => 'Paid', ]); - $service = new RefundOverpaymentService(new RefundNotificationService()); + $service = new RefundOverpaymentService(new RefundNotificationService); $result = $service->recalculate(false, null, 1); $this->assertNotEmpty($result['created_ids']); diff --git a/tests/Unit/Services/Refunds/RefundPayoutServiceTest.php b/tests/Unit/Services/Refunds/RefundPayoutServiceTest.php index 5c5cf9cf..2ad40c0f 100644 --- a/tests/Unit/Services/Refunds/RefundPayoutServiceTest.php +++ b/tests/Unit/Services/Refunds/RefundPayoutServiceTest.php @@ -27,7 +27,7 @@ class RefundPayoutServiceTest extends TestCase 'updated_at' => now(), ]); - $service = new RefundPayoutService(); + $service = new RefundPayoutService; $result = $service->recordPayment(1, [ 'paid_amount' => 40, 'payment_method' => 'Cash', diff --git a/tests/Unit/Services/Refunds/RefundPolicyServiceTest.php b/tests/Unit/Services/Refunds/RefundPolicyServiceTest.php index 593e1c63..6d4ecd1a 100644 --- a/tests/Unit/Services/Refunds/RefundPolicyServiceTest.php +++ b/tests/Unit/Services/Refunds/RefundPolicyServiceTest.php @@ -2,10 +2,10 @@ namespace Tests\Unit\Services\Refunds; +use App\Services\Discounts\DiscountInvoiceService; +use App\Services\Fees\FeeRefundDetailService; use App\Services\Refunds\RefundInvoiceAdjustmentService; use App\Services\Refunds\RefundPolicyService; -use App\Services\Fees\FeeRefundDetailService; -use App\Services\Discounts\DiscountInvoiceService; use Illuminate\Foundation\Testing\RefreshDatabase; use Illuminate\Support\Facades\DB; use Tests\TestCase; @@ -84,8 +84,8 @@ class RefundPolicyServiceTest extends TestCase ]); $service = new RefundPolicyService( - new FeeRefundDetailService(), - new RefundInvoiceAdjustmentService(new DiscountInvoiceService()) + new FeeRefundDetailService, + new RefundInvoiceAdjustmentService(new DiscountInvoiceService) ); $result = $service->processWithdrawalRefund(10, 1, '2025-2026', 'Fall', 1); diff --git a/tests/Unit/Services/Refunds/RefundQueryServiceTest.php b/tests/Unit/Services/Refunds/RefundQueryServiceTest.php index 3e144468..90de4355 100644 --- a/tests/Unit/Services/Refunds/RefundQueryServiceTest.php +++ b/tests/Unit/Services/Refunds/RefundQueryServiceTest.php @@ -40,7 +40,7 @@ class RefundQueryServiceTest extends TestCase ], ]); - $service = new RefundQueryService(); + $service = new RefundQueryService; $result = $service->list(['status' => 'Approved', 'per_page' => 10, 'page' => 1]); $this->assertSame(1, $result['pagination']['total']); diff --git a/tests/Unit/Services/Refunds/RefundRequestServiceTest.php b/tests/Unit/Services/Refunds/RefundRequestServiceTest.php index 4ec216a9..12f97147 100644 --- a/tests/Unit/Services/Refunds/RefundRequestServiceTest.php +++ b/tests/Unit/Services/Refunds/RefundRequestServiceTest.php @@ -2,13 +2,13 @@ namespace Tests\Unit\Services\Refunds; +use App\Services\Discounts\DiscountInvoiceService; use App\Services\Fees\FeeRefundDetailService; use App\Services\Refunds\RefundInvoiceAdjustmentService; use App\Services\Refunds\RefundNotificationService; use App\Services\Refunds\RefundPolicyService; use App\Services\Refunds\RefundRequestService; use App\Services\Refunds\RefundSummaryService; -use App\Services\Discounts\DiscountInvoiceService; use Illuminate\Foundation\Testing\RefreshDatabase; use Illuminate\Support\Facades\DB; use Tests\TestCase; @@ -60,11 +60,11 @@ class RefundRequestServiceTest extends TestCase $service = new RefundRequestService( new RefundPolicyService( - new FeeRefundDetailService(), - new RefundInvoiceAdjustmentService(new DiscountInvoiceService()) + new FeeRefundDetailService, + new RefundInvoiceAdjustmentService(new DiscountInvoiceService) ), - new RefundSummaryService(), - new RefundNotificationService() + new RefundSummaryService, + new RefundNotificationService ); $result = $service->requestRefund([ diff --git a/tests/Unit/Services/Refunds/RefundSummaryServiceTest.php b/tests/Unit/Services/Refunds/RefundSummaryServiceTest.php index 04cfb10b..582c9dd2 100644 --- a/tests/Unit/Services/Refunds/RefundSummaryServiceTest.php +++ b/tests/Unit/Services/Refunds/RefundSummaryServiceTest.php @@ -53,7 +53,7 @@ class RefundSummaryServiceTest extends TestCase 'updated_at' => now(), ]); - $service = new RefundSummaryService(); + $service = new RefundSummaryService; $summary = $service->getParentFinancialSummary(10, '2025-2026', 'Fall'); $this->assertSame(100.0, $summary['total_invoiced']); diff --git a/tests/Unit/Services/Reimbursements/ReimbursementBatchAssignmentServiceTest.php b/tests/Unit/Services/Reimbursements/ReimbursementBatchAssignmentServiceTest.php index 7d4e2ee5..c3bb951f 100644 --- a/tests/Unit/Services/Reimbursements/ReimbursementBatchAssignmentServiceTest.php +++ b/tests/Unit/Services/Reimbursements/ReimbursementBatchAssignmentServiceTest.php @@ -54,7 +54,7 @@ class ReimbursementBatchAssignmentServiceTest extends TestCase 'status' => 'approved', ]); - $service = new ReimbursementBatchAssignmentService(new ReimbursementLookupService()); + $service = new ReimbursementBatchAssignmentService(new ReimbursementLookupService); $result = $service->updateAssignment(100, 10, 1, null, '2025-2026', 'Fall'); $this->assertSame(10, $result['batch_id']); diff --git a/tests/Unit/Services/Reimbursements/ReimbursementBatchServiceTest.php b/tests/Unit/Services/Reimbursements/ReimbursementBatchServiceTest.php index 632d2bed..aea494f5 100644 --- a/tests/Unit/Services/Reimbursements/ReimbursementBatchServiceTest.php +++ b/tests/Unit/Services/Reimbursements/ReimbursementBatchServiceTest.php @@ -13,7 +13,7 @@ class ReimbursementBatchServiceTest extends TestCase public function test_create_batch_assigns_sequence_and_title(): void { - $service = new ReimbursementBatchService(new ReimbursementLookupService()); + $service = new ReimbursementBatchService(new ReimbursementLookupService); $batch = $service->createBatch(null, 1, '2025-2026', 'Fall'); diff --git a/tests/Unit/Services/Reimbursements/ReimbursementDonationServiceTest.php b/tests/Unit/Services/Reimbursements/ReimbursementDonationServiceTest.php index a6a1cbcc..14136f1c 100644 --- a/tests/Unit/Services/Reimbursements/ReimbursementDonationServiceTest.php +++ b/tests/Unit/Services/Reimbursements/ReimbursementDonationServiceTest.php @@ -59,7 +59,7 @@ class ReimbursementDonationServiceTest extends TestCase 'assigned_at' => now(), ]); - $service = new ReimbursementDonationService(); + $service = new ReimbursementDonationService; $service->markDonation(200, 1); $this->assertDatabaseHas('expenses', [ diff --git a/tests/Unit/Services/Reimbursements/ReimbursementRecipientServiceTest.php b/tests/Unit/Services/Reimbursements/ReimbursementRecipientServiceTest.php index a9665b59..b33e3140 100644 --- a/tests/Unit/Services/Reimbursements/ReimbursementRecipientServiceTest.php +++ b/tests/Unit/Services/Reimbursements/ReimbursementRecipientServiceTest.php @@ -66,7 +66,7 @@ class ReimbursementRecipientServiceTest extends TestCase ['user_id' => 2, 'role_id' => 2], ]); - $service = new ReimbursementRecipientService(); + $service = new ReimbursementRecipientService; $recipients = $service->recipientOptions(); $ids = array_map(static fn ($row) => (int) ($row['id'] ?? 0), $recipients); diff --git a/tests/Unit/Services/Reports/ReportCards/ReportCardServiceTest.php b/tests/Unit/Services/Reports/ReportCards/ReportCardServiceTest.php index b5f9d6d1..d87395b4 100644 --- a/tests/Unit/Services/Reports/ReportCards/ReportCardServiceTest.php +++ b/tests/Unit/Services/Reports/ReportCards/ReportCardServiceTest.php @@ -16,7 +16,7 @@ class ReportCardServiceTest extends TestCase $this->seedConfig(); $this->seedReportCardData(); - $service = new ReportCardService(); + $service = new ReportCardService; $result = $service->meta('2025-2026', 'Fall'); $this->assertNotEmpty($result['schoolYears']); @@ -30,7 +30,7 @@ class ReportCardServiceTest extends TestCase $this->seedConfig(); $data = $this->seedReportCardData(); - $service = new ReportCardService(); + $service = new ReportCardService; $result = $service->completeness('2025-2026', 'Fall', $data['class_section_id']); $this->assertTrue($result['ok']); @@ -44,7 +44,7 @@ class ReportCardServiceTest extends TestCase $data = $this->seedReportCardData(); $this->seedAcknowledgement($data['student_id']); - $service = new ReportCardService(); + $service = new ReportCardService; $result = $service->acknowledgement($data['student_id'], '2025-2026', 'Fall'); $this->assertSame('Parent One', $result['signed_name']); @@ -55,7 +55,7 @@ class ReportCardServiceTest extends TestCase $this->seedConfig(); $data = $this->seedReportCardData(); - $service = new ReportCardService(); + $service = new ReportCardService; $result = $service->generateSingleReport($data['student_id'], [ 'school_year' => '2025-2026', 'semester' => 'Fall', @@ -71,7 +71,7 @@ class ReportCardServiceTest extends TestCase $this->seedConfig(); $studentId = $this->seedStudentOnly(); - $service = new ReportCardService(); + $service = new ReportCardService; $result = $service->generateSingleReport($studentId, [ 'school_year' => '2025-2026', 'semester' => 'Fall', diff --git a/tests/Unit/Services/Reports/SlipPrinterFormatterServiceTest.php b/tests/Unit/Services/Reports/SlipPrinterFormatterServiceTest.php index f15deeb5..6690d156 100644 --- a/tests/Unit/Services/Reports/SlipPrinterFormatterServiceTest.php +++ b/tests/Unit/Services/Reports/SlipPrinterFormatterServiceTest.php @@ -9,13 +9,13 @@ class SlipPrinterFormatterServiceTest extends TestCase { public function test_to_db_date_parses_us_format(): void { - $service = new SlipPrinterFormatterService(); + $service = new SlipPrinterFormatterService; $this->assertSame('2025-09-01', $service->toDbDate('09/01/2025')); } public function test_to_db_time_parses_string(): void { - $service = new SlipPrinterFormatterService(); + $service = new SlipPrinterFormatterService; $this->assertSame('08:15:00', $service->toDbTime('8:15 AM')); } } diff --git a/tests/Unit/Services/Reports/Stickers/StickerPresetServiceTest.php b/tests/Unit/Services/Reports/Stickers/StickerPresetServiceTest.php index f1aeaa7c..041e5e0b 100644 --- a/tests/Unit/Services/Reports/Stickers/StickerPresetServiceTest.php +++ b/tests/Unit/Services/Reports/Stickers/StickerPresetServiceTest.php @@ -9,7 +9,7 @@ class StickerPresetServiceTest extends TestCase { public function test_presets_include_alias_fields(): void { - $service = new StickerPresetService(); + $service = new StickerPresetService; $presets = $service->presets(); $this->assertNotEmpty($presets); diff --git a/tests/Unit/Services/Reports/Stickers/StickerPrintServiceTest.php b/tests/Unit/Services/Reports/Stickers/StickerPrintServiceTest.php index ac3acb0b..1a6b5096 100644 --- a/tests/Unit/Services/Reports/Stickers/StickerPrintServiceTest.php +++ b/tests/Unit/Services/Reports/Stickers/StickerPrintServiceTest.php @@ -16,7 +16,7 @@ class StickerPrintServiceTest extends TestCase public function test_generate_returns_pdf_content(): void { $this->seedData(); - $service = new StickerPrintService(new StickerQueryService(new StickerContextService())); + $service = new StickerPrintService(new StickerQueryService(new StickerContextService)); $result = $service->generate([ 'student_id' => 100, @@ -30,7 +30,7 @@ class StickerPrintServiceTest extends TestCase public function test_generate_returns_error_when_no_students(): void { - $service = new StickerPrintService(new StickerQueryService(new StickerContextService())); + $service = new StickerPrintService(new StickerQueryService(new StickerContextService)); $result = $service->generate([ 'class_section_id' => 999, diff --git a/tests/Unit/Services/Reports/Stickers/StickerQueryServiceTest.php b/tests/Unit/Services/Reports/Stickers/StickerQueryServiceTest.php index 95c949fb..446a532c 100644 --- a/tests/Unit/Services/Reports/Stickers/StickerQueryServiceTest.php +++ b/tests/Unit/Services/Reports/Stickers/StickerQueryServiceTest.php @@ -15,7 +15,7 @@ class StickerQueryServiceTest extends TestCase public function test_list_students_by_class_returns_rows(): void { $this->seedData(); - $service = new StickerQueryService(new StickerContextService()); + $service = new StickerQueryService(new StickerContextService); $students = $service->listStudentsByClass(200, '2025-2026'); @@ -28,7 +28,7 @@ class StickerQueryServiceTest extends TestCase $this->seedData(); $this->seedYouthClass(); - $service = new StickerQueryService(new StickerContextService()); + $service = new StickerQueryService(new StickerContextService); $students = $service->listStudentsForPrintAll('2025-2026'); $this->assertCount(1, $students); diff --git a/tests/Unit/Services/Roles/PermissionCrudServiceTest.php b/tests/Unit/Services/Roles/PermissionCrudServiceTest.php index d56462a7..34ae85a6 100644 --- a/tests/Unit/Services/Roles/PermissionCrudServiceTest.php +++ b/tests/Unit/Services/Roles/PermissionCrudServiceTest.php @@ -13,7 +13,7 @@ class PermissionCrudServiceTest extends TestCase public function test_create_and_update_permission(): void { - $service = new PermissionCrudService(); + $service = new PermissionCrudService; $permission = $service->create([ 'name' => 'manage_roles', 'description' => 'Manage roles', diff --git a/tests/Unit/Services/Roles/RoleAssignmentServiceTest.php b/tests/Unit/Services/Roles/RoleAssignmentServiceTest.php index e826162d..efc01a41 100644 --- a/tests/Unit/Services/Roles/RoleAssignmentServiceTest.php +++ b/tests/Unit/Services/Roles/RoleAssignmentServiceTest.php @@ -2,7 +2,6 @@ namespace Tests\Unit\Services\Roles; -use App\Models\User; use App\Services\Roles\RoleAssignmentService; use Illuminate\Foundation\Testing\RefreshDatabase; use Illuminate\Support\Facades\DB; @@ -42,7 +41,7 @@ class RoleAssignmentServiceTest extends TestCase 'updated_at' => now(), ]); - $service = new RoleAssignmentService(new RoleStaffService()); + $service = new RoleAssignmentService(new RoleStaffService); $result = $service->assignRoles($userId, [$roleId], $userId); $this->assertTrue($result['ok']); diff --git a/tests/Unit/Services/Roles/RoleCrudServiceTest.php b/tests/Unit/Services/Roles/RoleCrudServiceTest.php index ad2db6da..551bc851 100644 --- a/tests/Unit/Services/Roles/RoleCrudServiceTest.php +++ b/tests/Unit/Services/Roles/RoleCrudServiceTest.php @@ -13,7 +13,7 @@ class RoleCrudServiceTest extends TestCase public function test_create_and_update_role(): void { - $service = new RoleCrudService(); + $service = new RoleCrudService; $role = $service->create([ 'name' => 'admin', 'slug' => 'admin', diff --git a/tests/Unit/Services/Roles/RoleDashboardServiceTest.php b/tests/Unit/Services/Roles/RoleDashboardServiceTest.php index 95933bf9..c9d0af5d 100644 --- a/tests/Unit/Services/Roles/RoleDashboardServiceTest.php +++ b/tests/Unit/Services/Roles/RoleDashboardServiceTest.php @@ -24,7 +24,7 @@ class RoleDashboardServiceTest extends TestCase 'updated_at' => now(), ]); - $service = new RoleDashboardService(); + $service = new RoleDashboardService; $route = $service->bestDashboardRouteFor(['admin']); $this->assertSame('administrator/administratordashboard', $route); diff --git a/tests/Unit/Services/Roles/RolePermissionServiceTest.php b/tests/Unit/Services/Roles/RolePermissionServiceTest.php index 0be8754a..d8a45412 100644 --- a/tests/Unit/Services/Roles/RolePermissionServiceTest.php +++ b/tests/Unit/Services/Roles/RolePermissionServiceTest.php @@ -30,7 +30,7 @@ class RolePermissionServiceTest extends TestCase 'updated_at' => now(), ]); - $service = new RolePermissionService(); + $service = new RolePermissionService; $service->saveRolePermissions($roleId, [ $permissionId => ['create' => true, 'read' => true], ]); diff --git a/tests/Unit/Services/Roles/RoleQueryServiceTest.php b/tests/Unit/Services/Roles/RoleQueryServiceTest.php index 299387a6..ed556dcf 100644 --- a/tests/Unit/Services/Roles/RoleQueryServiceTest.php +++ b/tests/Unit/Services/Roles/RoleQueryServiceTest.php @@ -24,7 +24,7 @@ class RoleQueryServiceTest extends TestCase 'updated_at' => now(), ]); - $service = new RoleQueryService(); + $service = new RoleQueryService; $page = $service->listRoles([]); $this->assertCount(1, $page->items()); @@ -65,7 +65,7 @@ class RoleQueryServiceTest extends TestCase 'role_id' => $roleId, ]); - $service = new RoleQueryService(); + $service = new RoleQueryService; $page = $service->listUsersWithRoles([]); $this->assertCount(1, $page->items()); diff --git a/tests/Unit/Services/Roles/RoleSwitchServiceTest.php b/tests/Unit/Services/Roles/RoleSwitchServiceTest.php index 09632972..5cef9855 100644 --- a/tests/Unit/Services/Roles/RoleSwitchServiceTest.php +++ b/tests/Unit/Services/Roles/RoleSwitchServiceTest.php @@ -47,7 +47,7 @@ class RoleSwitchServiceTest extends TestCase 'role_id' => $roleId, ]); - $service = new RoleSwitchService(new RoleDashboardService()); + $service = new RoleSwitchService(new RoleDashboardService); $roles = $service->getUserRoleNames($userId); $this->assertSame(['admin'], $roles); diff --git a/tests/Unit/Services/School/SemesterSelectionServiceTest.php b/tests/Unit/Services/School/SemesterSelectionServiceTest.php index 9f997568..a255f83d 100644 --- a/tests/Unit/Services/School/SemesterSelectionServiceTest.php +++ b/tests/Unit/Services/School/SemesterSelectionServiceTest.php @@ -13,7 +13,7 @@ class SemesterSelectionServiceTest extends TestCase public function test_selected_teacher_semester_prefers_session_value(): void { - $service = new SemesterSelectionService(); + $service = new SemesterSelectionService; $this->assertSame('Fall', $service->selectedTeacherSemester()); } @@ -24,7 +24,7 @@ class SemesterSelectionServiceTest extends TestCase ['config_key' => 'semester', 'config_value' => 'Fall'], ]); - $service = new SemesterSelectionService(); + $service = new SemesterSelectionService; $this->assertSame('Fall', $service->selectedTeacherSemester()); } diff --git a/tests/Unit/Services/SchoolIds/SchoolIdGenerationServiceTest.php b/tests/Unit/Services/SchoolIds/SchoolIdGenerationServiceTest.php index 4823f673..99a21044 100644 --- a/tests/Unit/Services/SchoolIds/SchoolIdGenerationServiceTest.php +++ b/tests/Unit/Services/SchoolIds/SchoolIdGenerationServiceTest.php @@ -16,7 +16,7 @@ class SchoolIdGenerationServiceTest extends TestCase $schoolId = $service->generateStudentId(); $year = date('y'); - $this->assertStringStartsWith('STU' . $year, $schoolId); + $this->assertStringStartsWith('STU'.$year, $schoolId); $this->assertSame(10, strlen($schoolId)); } diff --git a/tests/Unit/Services/Scores/AttendanceCalculatorTest.php b/tests/Unit/Services/Scores/AttendanceCalculatorTest.php index 9ae89c1e..f40d1558 100644 --- a/tests/Unit/Services/Scores/AttendanceCalculatorTest.php +++ b/tests/Unit/Services/Scores/AttendanceCalculatorTest.php @@ -32,7 +32,7 @@ class AttendanceCalculatorTest extends TestCase 'school_year' => '2024-2025', ]); - $service = new AttendanceCalculator(new AttendanceRecord(), new Configuration(), new CalendarEvent()); + $service = new AttendanceCalculator(new AttendanceRecord, new Configuration, new CalendarEvent); $result = $service->calculate(10, 'Fall', '2024-2025'); $this->assertSame(90.0, $result['attendance_score']); diff --git a/tests/Unit/Services/Scores/ExamScoreServiceTest.php b/tests/Unit/Services/Scores/ExamScoreServiceTest.php index 991e58a1..50f4d27d 100644 --- a/tests/Unit/Services/Scores/ExamScoreServiceTest.php +++ b/tests/Unit/Services/Scores/ExamScoreServiceTest.php @@ -38,7 +38,7 @@ class ExamScoreServiceTest extends TestCase ], ]); - $service = new ExamScoreService(new ScoreTermService()); + $service = new ExamScoreService(new ScoreTermService); $result = $service->list('midterm_exam', 3, 'Fall', '2025-2026'); $this->assertCount(1, $result['students']); @@ -50,7 +50,7 @@ class ExamScoreServiceTest extends TestCase $this->seedStudent(1, 'SCH1', 'Leo', 'Park'); $this->seedStudentClass(1, 3, '2025-2026'); - $service = new ExamScoreService(new ScoreTermService()); + $service = new ExamScoreService(new ScoreTermService); $count = $service->update( 'midterm_exam', diff --git a/tests/Unit/Services/Scores/HomeworkCalculatorTest.php b/tests/Unit/Services/Scores/HomeworkCalculatorTest.php index cb47983f..fef915b5 100644 --- a/tests/Unit/Services/Scores/HomeworkCalculatorTest.php +++ b/tests/Unit/Services/Scores/HomeworkCalculatorTest.php @@ -47,7 +47,7 @@ class HomeworkCalculatorTest extends TestCase ], ]); - $service = new HomeworkCalculator(new Homework()); + $service = new HomeworkCalculator(new Homework); $result = $service->calculate(5, 'Fall', '2024-2025', 1); $this->assertSame(85.0, $result['homework_avg']); diff --git a/tests/Unit/Services/Scores/HomeworkScoreServiceTest.php b/tests/Unit/Services/Scores/HomeworkScoreServiceTest.php index 95bba07d..626ea21d 100644 --- a/tests/Unit/Services/Scores/HomeworkScoreServiceTest.php +++ b/tests/Unit/Services/Scores/HomeworkScoreServiceTest.php @@ -28,7 +28,7 @@ class HomeworkScoreServiceTest extends TestCase 'school_year' => '2025-2026', ]); - $service = new HomeworkScoreService(new ScoreTermService()); + $service = new HomeworkScoreService(new ScoreTermService); $result = $service->list(8, 'Fall', '2025-2026'); $this->assertSame([1], $result['headers']); @@ -40,7 +40,7 @@ class HomeworkScoreServiceTest extends TestCase $this->seedStudent(1, 'SCH1', 'Eli', 'Page'); $this->seedStudentClass(1, 8, '2025-2026'); - $service = new HomeworkScoreService(new ScoreTermService()); + $service = new HomeworkScoreService(new ScoreTermService); $nextIndex = $service->addColumn(8, 'Fall', '2025-2026', 99); diff --git a/tests/Unit/Services/Scores/ParticipationScoreServiceTest.php b/tests/Unit/Services/Scores/ParticipationScoreServiceTest.php index 56829a8b..d2b45283 100644 --- a/tests/Unit/Services/Scores/ParticipationScoreServiceTest.php +++ b/tests/Unit/Services/Scores/ParticipationScoreServiceTest.php @@ -28,7 +28,7 @@ class ParticipationScoreServiceTest extends TestCase 'school_year' => '2025-2026', ]); - $service = new ParticipationScoreService(new ScoreTermService()); + $service = new ParticipationScoreService(new ScoreTermService); $result = $service->list(9, 'Fall', '2025-2026'); $this->assertSame(87.0, $result['students'][0]['scores']['score']); @@ -50,7 +50,7 @@ class ParticipationScoreServiceTest extends TestCase 'school_year' => '2025-2026', ]); - $service = new ParticipationScoreService(new ScoreTermService()); + $service = new ParticipationScoreService(new ScoreTermService); $count = $service->update( 9, 'Fall', diff --git a/tests/Unit/Services/Scores/ProjectCalculatorTest.php b/tests/Unit/Services/Scores/ProjectCalculatorTest.php index f1e22a49..1eb2d71d 100644 --- a/tests/Unit/Services/Scores/ProjectCalculatorTest.php +++ b/tests/Unit/Services/Scores/ProjectCalculatorTest.php @@ -47,7 +47,7 @@ class ProjectCalculatorTest extends TestCase ], ]); - $service = new ProjectCalculator(new Project()); + $service = new ProjectCalculator(new Project); $result = $service->calculate(9, 'Fall', '2024-2025', 1); $this->assertSame(90.0, $result['project_avg']); diff --git a/tests/Unit/Services/Scores/ProjectScoreServiceTest.php b/tests/Unit/Services/Scores/ProjectScoreServiceTest.php index b27c8f7b..7c7f11b2 100644 --- a/tests/Unit/Services/Scores/ProjectScoreServiceTest.php +++ b/tests/Unit/Services/Scores/ProjectScoreServiceTest.php @@ -28,7 +28,7 @@ class ProjectScoreServiceTest extends TestCase 'school_year' => '2025-2026', ]); - $service = new ProjectScoreService(new ScoreTermService()); + $service = new ProjectScoreService(new ScoreTermService); $result = $service->list(10, 'Fall', '2025-2026'); $this->assertSame([1], $result['headers']); @@ -41,7 +41,7 @@ class ProjectScoreServiceTest extends TestCase $this->seedStudent(1, 'SCH1', 'Maya', 'Stone'); $this->seedStudentClass(1, 10, '2025-2026'); - $service = new ProjectScoreService(new ScoreTermService()); + $service = new ProjectScoreService(new ScoreTermService); $count = $service->update( 10, @@ -73,7 +73,7 @@ class ProjectScoreServiceTest extends TestCase $this->seedStudent(1, 'SCH1', 'Maya', 'Stone'); $this->seedStudentClass(1, 10, '2025-2026'); - $service = new ProjectScoreService(new ScoreTermService()); + $service = new ProjectScoreService(new ScoreTermService); $nextIndex = $service->addColumn(10, 'Fall', '2025-2026', 99); diff --git a/tests/Unit/Services/Scores/QuizCalculatorTest.php b/tests/Unit/Services/Scores/QuizCalculatorTest.php index 33dfd0d2..b53e217d 100644 --- a/tests/Unit/Services/Scores/QuizCalculatorTest.php +++ b/tests/Unit/Services/Scores/QuizCalculatorTest.php @@ -56,7 +56,7 @@ class QuizCalculatorTest extends TestCase ], ]); - $service = new QuizCalculator(); + $service = new QuizCalculator; $result = $service->calculate(7, 'Fall', '2024-2025', 2); $this->assertSame(80.0, $result['quiz_avg']); diff --git a/tests/Unit/Services/Scores/QuizScoreServiceTest.php b/tests/Unit/Services/Scores/QuizScoreServiceTest.php index 2be158e8..eccdfe42 100644 --- a/tests/Unit/Services/Scores/QuizScoreServiceTest.php +++ b/tests/Unit/Services/Scores/QuizScoreServiceTest.php @@ -28,7 +28,7 @@ class QuizScoreServiceTest extends TestCase 'school_year' => '2025-2026', ]); - $service = new QuizScoreService(new ScoreTermService()); + $service = new QuizScoreService(new ScoreTermService); $result = $service->list(5, 'Fall', '2025-2026'); $this->assertSame([2], $result['headers']); @@ -41,7 +41,7 @@ class QuizScoreServiceTest extends TestCase $this->seedStudent(1, 'SCH1', 'Nina', 'Kim'); $this->seedStudentClass(1, 5, '2025-2026'); - $service = new QuizScoreService(new ScoreTermService()); + $service = new QuizScoreService(new ScoreTermService); $count = $service->update( 5, @@ -73,7 +73,7 @@ class QuizScoreServiceTest extends TestCase $this->seedStudent(1, 'SCH1', 'Nina', 'Kim'); $this->seedStudentClass(1, 5, '2025-2026'); - $service = new QuizScoreService(new ScoreTermService()); + $service = new QuizScoreService(new ScoreTermService); $nextIndex = $service->addColumn(5, 'Fall', '2025-2026', 99); diff --git a/tests/Unit/Services/Scores/ScoreCommentServiceTest.php b/tests/Unit/Services/Scores/ScoreCommentServiceTest.php index 63a13f20..3656e81f 100644 --- a/tests/Unit/Services/Scores/ScoreCommentServiceTest.php +++ b/tests/Unit/Services/Scores/ScoreCommentServiceTest.php @@ -26,7 +26,7 @@ class ScoreCommentServiceTest extends TestCase 'comment' => 'Ava has shown consistent improvement and strong effort across assignments.', ]); - $service = new ScoreCommentService(new ScoreTermService()); + $service = new ScoreCommentService(new ScoreTermService); $result = $service->list('2', 'Fall', '2025-2026'); $this->assertCount(1, $result['students']); @@ -38,7 +38,7 @@ class ScoreCommentServiceTest extends TestCase { $this->seedStudent(1, 'SCH1', 'Ava', 'Lane'); - $service = new ScoreCommentService(new ScoreTermService()); + $service = new ScoreCommentService(new ScoreTermService); $result = $service->save( 2, 'Fall', diff --git a/tests/Unit/Services/Scores/ScoreDashboardServiceTest.php b/tests/Unit/Services/Scores/ScoreDashboardServiceTest.php index bc773bcc..688b098b 100644 --- a/tests/Unit/Services/Scores/ScoreDashboardServiceTest.php +++ b/tests/Unit/Services/Scores/ScoreDashboardServiceTest.php @@ -39,7 +39,7 @@ class ScoreDashboardServiceTest extends TestCase 'school_year' => '2025-2026', ]); - $service = new ScoreDashboardService(new ScoreTermService()); + $service = new ScoreDashboardService(new ScoreTermService); $result = $service->overview(1, null, 'Fall', '2025-2026'); $this->assertSame(10, $result['class_section_id']); @@ -62,7 +62,7 @@ class ScoreDashboardServiceTest extends TestCase 'school_year' => '2025-2026', ]); - $service = new ScoreDashboardService(new ScoreTermService()); + $service = new ScoreDashboardService(new ScoreTermService); $result = $service->viewStudentScores(7, '2025-2026'); $this->assertArrayHasKey('2025-2026', $result['scores']); diff --git a/tests/Unit/Services/Scores/ScorePredictorServiceTest.php b/tests/Unit/Services/Scores/ScorePredictorServiceTest.php index fca60523..7c050d27 100644 --- a/tests/Unit/Services/Scores/ScorePredictorServiceTest.php +++ b/tests/Unit/Services/Scores/ScorePredictorServiceTest.php @@ -64,8 +64,8 @@ class ScorePredictorServiceTest extends TestCase ]); $service = new ScorePredictorService( - new ScorePredictorDataService(), - new ScorePredictorRiskService() + new ScorePredictorDataService, + new ScorePredictorRiskService ); $result = $service->report('2025-2026', 1); diff --git a/tests/Unit/Services/Scores/ScoreTermServiceTest.php b/tests/Unit/Services/Scores/ScoreTermServiceTest.php index 8c3a6093..aa932f92 100644 --- a/tests/Unit/Services/Scores/ScoreTermServiceTest.php +++ b/tests/Unit/Services/Scores/ScoreTermServiceTest.php @@ -18,7 +18,7 @@ class ScoreTermServiceTest extends TestCase ['config_key' => 'school_year', 'config_value' => '2025-2026'], ]); - $service = new ScoreTermService(); + $service = new ScoreTermService; $this->assertSame('Fall', $service->semesterLabel(null)); $this->assertSame('2025-2026', $service->schoolYear(null)); @@ -26,7 +26,7 @@ class ScoreTermServiceTest extends TestCase public function test_normalize_and_variants(): void { - $service = new ScoreTermService(); + $service = new ScoreTermService; $this->assertSame('fall', $service->normalizeSemester('Semester 1')); $this->assertSame('spring', $service->normalizeSemester('2nd Semester')); diff --git a/tests/Unit/Services/Scores/SemesterScoreServiceTest.php b/tests/Unit/Services/Scores/SemesterScoreServiceTest.php index c699cf76..39740a91 100644 --- a/tests/Unit/Services/Scores/SemesterScoreServiceTest.php +++ b/tests/Unit/Services/Scores/SemesterScoreServiceTest.php @@ -2,11 +2,11 @@ namespace Tests\Unit\Services\Scores; -use App\Services\Scores\SemesterScoreService; use App\Services\Scores\AttendanceCalculator; use App\Services\Scores\HomeworkCalculator; use App\Services\Scores\ProjectCalculator; use App\Services\Scores\QuizCalculator; +use App\Services\Scores\SemesterScoreService; use Illuminate\Foundation\Testing\RefreshDatabase; use Illuminate\Support\Facades\DB; use Tests\TestCase; @@ -28,10 +28,10 @@ class SemesterScoreServiceTest extends TestCase ]); $service = new SemesterScoreService( - new FakeAttendanceCalculator(), - new FakeHomeworkCalculator(), - new FakeQuizCalculator(), - new FakeProjectCalculator() + new FakeAttendanceCalculator, + new FakeHomeworkCalculator, + new FakeQuizCalculator, + new FakeProjectCalculator ); $service->updateStudentScores([ @@ -61,9 +61,7 @@ class SemesterScoreServiceTest extends TestCase class FakeAttendanceCalculator extends AttendanceCalculator { - public function __construct() - { - } + public function __construct() {} public function calculate(int $studentId, string $semester, string $schoolYear, ?int $classSectionId = null): array { @@ -73,9 +71,7 @@ class FakeAttendanceCalculator extends AttendanceCalculator class FakeHomeworkCalculator extends HomeworkCalculator { - public function __construct() - { - } + public function __construct() {} public function calculate(int $studentId, string $semester, string $schoolYear, ?int $classSectionId = null): array { @@ -93,9 +89,7 @@ class FakeQuizCalculator extends QuizCalculator class FakeProjectCalculator extends ProjectCalculator { - public function __construct() - { - } + public function __construct() {} public function calculate(int $studentId, string $semester, string $schoolYear, ?int $classSectionId = null): array { diff --git a/tests/Unit/Services/Security/Pbkdf2HasherTest.php b/tests/Unit/Services/Security/Pbkdf2HasherTest.php index 49b7d223..c78f2acb 100644 --- a/tests/Unit/Services/Security/Pbkdf2HasherTest.php +++ b/tests/Unit/Services/Security/Pbkdf2HasherTest.php @@ -9,7 +9,7 @@ class Pbkdf2HasherTest extends TestCase { public function test_hash_and_verify(): void { - $hasher = new Pbkdf2Hasher(); + $hasher = new Pbkdf2Hasher; $hash = $hasher->hash('secret'); $this->assertTrue($hasher->verify('secret', $hash)); diff --git a/tests/Unit/Services/Semesters/SemesterConfigServiceTest.php b/tests/Unit/Services/Semesters/SemesterConfigServiceTest.php index 9f1976e5..78283d74 100644 --- a/tests/Unit/Services/Semesters/SemesterConfigServiceTest.php +++ b/tests/Unit/Services/Semesters/SemesterConfigServiceTest.php @@ -19,7 +19,7 @@ class SemesterConfigServiceTest extends TestCase ['id' => 3, 'config_key' => 'last_school_day', 'config_value' => '2026-06-01'], ]); - $service = new SemesterConfigService(); + $service = new SemesterConfigService; $this->assertSame('2025-09-01', $service->getFallStart()?->format('Y-m-d')); $this->assertSame('2026-01-20', $service->getSpringStart()?->format('Y-m-d')); $this->assertSame('2026-06-01', $service->getLastSchoolDay()?->format('Y-m-d')); diff --git a/tests/Unit/Services/Semesters/SemesterRangeServiceTest.php b/tests/Unit/Services/Semesters/SemesterRangeServiceTest.php index 58400bff..b2713c41 100644 --- a/tests/Unit/Services/Semesters/SemesterRangeServiceTest.php +++ b/tests/Unit/Services/Semesters/SemesterRangeServiceTest.php @@ -14,7 +14,7 @@ class SemesterRangeServiceTest extends TestCase public function test_get_school_year_range_falls_back_when_config_missing(): void { - $service = new SemesterRangeService(new SemesterConfigService()); + $service = new SemesterRangeService(new SemesterConfigService); [$start, $end] = $service->getSchoolYearRange('2025-2026'); $this->assertSame('2025-09-01', $start); @@ -29,7 +29,7 @@ class SemesterRangeServiceTest extends TestCase ['id' => 3, 'config_key' => 'last_school_day', 'config_value' => '2026-06-01'], ]); - $service = new SemesterRangeService(new SemesterConfigService()); + $service = new SemesterRangeService(new SemesterConfigService); $semester = $service->getSemesterForDate('2025-10-01'); $this->assertSame('Fall', $semester); @@ -37,7 +37,7 @@ class SemesterRangeServiceTest extends TestCase public function test_normalize_semester(): void { - $service = new SemesterRangeService(new SemesterConfigService()); + $service = new SemesterRangeService(new SemesterConfigService); $this->assertSame('Fall', $service->normalizeSemester('fall')); $this->assertSame('Spring', $service->normalizeSemester('SPRING')); diff --git a/tests/Unit/Services/Settings/ConfigurationServiceTest.php b/tests/Unit/Services/Settings/ConfigurationServiceTest.php index a69db2eb..521cbd18 100644 --- a/tests/Unit/Services/Settings/ConfigurationServiceTest.php +++ b/tests/Unit/Services/Settings/ConfigurationServiceTest.php @@ -12,7 +12,7 @@ class ConfigurationServiceTest extends TestCase public function test_store_creates_config(): void { - $service = new ConfigurationService(); + $service = new ConfigurationService; $config = $service->store([ 'config_key' => 'school_year', 'config_value' => '2025-2026', diff --git a/tests/Unit/Services/Settings/SchoolCalendar/SchoolCalendarContextServiceTest.php b/tests/Unit/Services/Settings/SchoolCalendar/SchoolCalendarContextServiceTest.php index b6d21722..0745351c 100644 --- a/tests/Unit/Services/Settings/SchoolCalendar/SchoolCalendarContextServiceTest.php +++ b/tests/Unit/Services/Settings/SchoolCalendar/SchoolCalendarContextServiceTest.php @@ -9,7 +9,7 @@ class SchoolCalendarContextServiceTest extends TestCase { public function test_event_types_returns_list(): void { - $service = new SchoolCalendarContextService(); + $service = new SchoolCalendarContextService; $types = $service->eventTypes(); $this->assertNotEmpty($types); diff --git a/tests/Unit/Services/Settings/SchoolCalendar/SchoolCalendarFormatterServiceTest.php b/tests/Unit/Services/Settings/SchoolCalendar/SchoolCalendarFormatterServiceTest.php index 9a07b15a..3d6ded5e 100644 --- a/tests/Unit/Services/Settings/SchoolCalendar/SchoolCalendarFormatterServiceTest.php +++ b/tests/Unit/Services/Settings/SchoolCalendar/SchoolCalendarFormatterServiceTest.php @@ -9,7 +9,7 @@ class SchoolCalendarFormatterServiceTest extends TestCase { public function test_format_event_sets_background_for_no_school(): void { - $service = new SchoolCalendarFormatterService(); + $service = new SchoolCalendarFormatterService; $formatted = $service->formatEvent([ 'id' => 1, 'title' => 'No School Day', @@ -27,7 +27,7 @@ class SchoolCalendarFormatterServiceTest extends TestCase public function test_format_event_normalizes_iso_dates_to_plain_calendar_date(): void { - $service = new SchoolCalendarFormatterService(); + $service = new SchoolCalendarFormatterService; $formatted = $service->formatEvent([ 'id' => 2, 'title' => 'First Day of School', diff --git a/tests/Unit/Services/Settings/SchoolCalendar/SchoolCalendarMeetingServiceTest.php b/tests/Unit/Services/Settings/SchoolCalendar/SchoolCalendarMeetingServiceTest.php index ddcde851..60ed77d3 100644 --- a/tests/Unit/Services/Settings/SchoolCalendar/SchoolCalendarMeetingServiceTest.php +++ b/tests/Unit/Services/Settings/SchoolCalendar/SchoolCalendarMeetingServiceTest.php @@ -48,7 +48,7 @@ class SchoolCalendarMeetingServiceTest extends TestCase ], ]); - $service = new SchoolCalendarMeetingService(); + $service = new SchoolCalendarMeetingService; $rows = $service->listMeetings('2025-2026', 'parent', 10); $this->assertCount(1, $rows); diff --git a/tests/Unit/Services/Settings/SchoolCalendar/SchoolCalendarMutationServiceTest.php b/tests/Unit/Services/Settings/SchoolCalendar/SchoolCalendarMutationServiceTest.php index de7d2afa..e9281d32 100644 --- a/tests/Unit/Services/Settings/SchoolCalendar/SchoolCalendarMutationServiceTest.php +++ b/tests/Unit/Services/Settings/SchoolCalendar/SchoolCalendarMutationServiceTest.php @@ -16,7 +16,7 @@ class SchoolCalendarMutationServiceTest extends TestCase public function test_create_persists_event(): void { - $context = new SchoolCalendarContextService(); + $context = new SchoolCalendarContextService; $notifier = Mockery::mock(SchoolCalendarNotificationService::class); $notifier->shouldReceive('notify')->andReturn([]); @@ -47,7 +47,7 @@ class SchoolCalendarMutationServiceTest extends TestCase 'notification_sent' => 0, ]); - $context = new SchoolCalendarContextService(); + $context = new SchoolCalendarContextService; $notifier = Mockery::mock(SchoolCalendarNotificationService::class); $notifier->shouldReceive('notify')->andReturn([]); diff --git a/tests/Unit/Services/Settings/SchoolCalendar/SchoolCalendarQueryServiceTest.php b/tests/Unit/Services/Settings/SchoolCalendar/SchoolCalendarQueryServiceTest.php index 5510dd39..246c3d06 100644 --- a/tests/Unit/Services/Settings/SchoolCalendar/SchoolCalendarQueryServiceTest.php +++ b/tests/Unit/Services/Settings/SchoolCalendar/SchoolCalendarQueryServiceTest.php @@ -44,7 +44,7 @@ class SchoolCalendarQueryServiceTest extends TestCase ], ]); - $service = new SchoolCalendarQueryService(); + $service = new SchoolCalendarQueryService; $events = $service->listEvents(['school_year' => '2025-2026']); $this->assertCount(1, $events); @@ -84,7 +84,7 @@ class SchoolCalendarQueryServiceTest extends TestCase ], ]); - $service = new SchoolCalendarQueryService(); + $service = new SchoolCalendarQueryService; $events = $service->listEvents(['school_year' => '2025-2026']); $filtered = $service->filterEventsForAudience($events, 'parent'); diff --git a/tests/Unit/Services/Settings/SettingsServiceTest.php b/tests/Unit/Services/Settings/SettingsServiceTest.php index 70d4c26d..d101ba51 100644 --- a/tests/Unit/Services/Settings/SettingsServiceTest.php +++ b/tests/Unit/Services/Settings/SettingsServiceTest.php @@ -2,10 +2,8 @@ namespace Tests\Unit\Services\Settings; -use App\Models\Setting; use App\Services\Settings\SettingsService; use Illuminate\Foundation\Testing\RefreshDatabase; -use Illuminate\Support\Facades\DB; use Tests\TestCase; class SettingsServiceTest extends TestCase @@ -14,7 +12,7 @@ class SettingsServiceTest extends TestCase public function test_update_persists_settings(): void { - $service = new SettingsService(); + $service = new SettingsService; $result = $service->update([ 'site_name' => 'Alrahma', diff --git a/tests/Unit/Services/Staff/StaffCommandServiceTest.php b/tests/Unit/Services/Staff/StaffCommandServiceTest.php index a2c23b2d..523c1993 100644 --- a/tests/Unit/Services/Staff/StaffCommandServiceTest.php +++ b/tests/Unit/Services/Staff/StaffCommandServiceTest.php @@ -15,7 +15,7 @@ class StaffCommandServiceTest extends TestCase public function test_create_requires_user(): void { - $service = new StaffCommandService(new GlobalConfigService()); + $service = new StaffCommandService(new GlobalConfigService); $this->expectException(\RuntimeException::class); $service->create([ @@ -44,7 +44,7 @@ class StaffCommandServiceTest extends TestCase 'school_year' => '2025-2026', ]); - $service = new StaffCommandService(new GlobalConfigService()); + $service = new StaffCommandService(new GlobalConfigService); $staff = $service->create([ 'user_id' => $user->id, 'firstname' => 'Staff', diff --git a/tests/Unit/Services/Staff/StaffQueryServiceTest.php b/tests/Unit/Services/Staff/StaffQueryServiceTest.php index 75626690..9e5fe4f6 100644 --- a/tests/Unit/Services/Staff/StaffQueryServiceTest.php +++ b/tests/Unit/Services/Staff/StaffQueryServiceTest.php @@ -41,7 +41,7 @@ class StaffQueryServiceTest extends TestCase 'school_year' => '2025-2026', ]); - $service = new StaffQueryService(new GlobalConfigService()); + $service = new StaffQueryService(new GlobalConfigService); $result = $service->paginate([], 1, 10); $this->assertSame(1, $result['paginator']->total()); diff --git a/tests/Unit/Services/Students/StudentAssignmentServiceTest.php b/tests/Unit/Services/Students/StudentAssignmentServiceTest.php index a0cb4dcc..c7b4010c 100644 --- a/tests/Unit/Services/Students/StudentAssignmentServiceTest.php +++ b/tests/Unit/Services/Students/StudentAssignmentServiceTest.php @@ -18,7 +18,7 @@ class StudentAssignmentServiceTest extends TestCase $studentId = $this->seedStudent(); $classSectionId = $this->seedClassSection(); - $service = new StudentAssignmentService(new StudentConfigService()); + $service = new StudentAssignmentService(new StudentConfigService); $result = $service->assignClasses($studentId, [$classSectionId], false); $this->assertTrue($result['ok']); @@ -44,7 +44,7 @@ class StudentAssignmentServiceTest extends TestCase 'updated_at' => now(), ]); - $service = new StudentAssignmentService(new StudentConfigService()); + $service = new StudentAssignmentService(new StudentConfigService); $result = $service->removeClass($studentId, $classSectionId); $this->assertTrue($result['ok']); diff --git a/tests/Unit/Services/Students/StudentProfileServiceTest.php b/tests/Unit/Services/Students/StudentProfileServiceTest.php index aabe1091..fb6d11b1 100644 --- a/tests/Unit/Services/Students/StudentProfileServiceTest.php +++ b/tests/Unit/Services/Students/StudentProfileServiceTest.php @@ -15,7 +15,7 @@ class StudentProfileServiceTest extends TestCase { $studentId = $this->seedStudent(); - $service = new StudentProfileService(); + $service = new StudentProfileService; $result = $service->updateStudent($studentId, [ 'firstname' => 'Lee', 'lastname' => 'Student', diff --git a/tests/Unit/Services/Students/StudentScoreCardServiceTest.php b/tests/Unit/Services/Students/StudentScoreCardServiceTest.php index 5dd26889..2e71ca52 100644 --- a/tests/Unit/Services/Students/StudentScoreCardServiceTest.php +++ b/tests/Unit/Services/Students/StudentScoreCardServiceTest.php @@ -33,7 +33,7 @@ class StudentScoreCardServiceTest extends TestCase 'created_at' => now(), ]); - $service = new StudentScoreCardService(); + $service = new StudentScoreCardService; $result = $service->scoreCard($studentId); $this->assertTrue($result['ok']); diff --git a/tests/Unit/Services/Subjects/SubjectCurriculumServiceTest.php b/tests/Unit/Services/Subjects/SubjectCurriculumServiceTest.php index 239a6dcc..f5c21017 100644 --- a/tests/Unit/Services/Subjects/SubjectCurriculumServiceTest.php +++ b/tests/Unit/Services/Subjects/SubjectCurriculumServiceTest.php @@ -14,7 +14,7 @@ class SubjectCurriculumServiceTest extends TestCase public function test_store_creates_entry(): void { $classId = $this->seedClass(); - $service = new SubjectCurriculumService(); + $service = new SubjectCurriculumService; $entry = $service->store([ 'class_id' => $classId, @@ -44,7 +44,7 @@ class SubjectCurriculumServiceTest extends TestCase 'updated_at' => now(), ]); - $service = new SubjectCurriculumService(); + $service = new SubjectCurriculumService; $entry = $service->update($entryId, [ 'class_id' => $classId, 'subject' => 'quran', diff --git a/tests/Unit/Services/Support/SupportRequestServiceTest.php b/tests/Unit/Services/Support/SupportRequestServiceTest.php index 087cab13..20122dd8 100644 --- a/tests/Unit/Services/Support/SupportRequestServiceTest.php +++ b/tests/Unit/Services/Support/SupportRequestServiceTest.php @@ -40,7 +40,7 @@ class SupportRequestServiceTest extends TestCase $mailer = $this->createMock(EmailDispatchService::class); $mailer->method('send')->willReturn(false); - $service = new SupportRequestService(new GlobalConfigService(), $mailer); + $service = new SupportRequestService(new GlobalConfigService, $mailer); $result = $service->create($user->id, [ 'subject' => 'Help', 'message' => 'Need assistance', diff --git a/tests/Unit/Services/System/CleanupSchedulerServiceTest.php b/tests/Unit/Services/System/CleanupSchedulerServiceTest.php index 3ee4a583..caff5e84 100644 --- a/tests/Unit/Services/System/CleanupSchedulerServiceTest.php +++ b/tests/Unit/Services/System/CleanupSchedulerServiceTest.php @@ -13,13 +13,13 @@ class CleanupSchedulerServiceTest extends TestCase { Cache::forget('last_cleanup_run'); - $service = new CleanupSchedulerService(); + $service = new CleanupSchedulerService; $this->assertTrue($service->shouldRun('last_cleanup_run', 2)); } public function test_mark_run_sets_cache(): void { - $service = new CleanupSchedulerService(); + $service = new CleanupSchedulerService; $service->markRun('last_cleanup_run'); $this->assertNotNull(Cache::get('last_cleanup_run')); @@ -29,7 +29,7 @@ class CleanupSchedulerServiceTest extends TestCase { Artisan::shouldReceive('call')->once()->with('users:delete-unverified'); - $service = new CleanupSchedulerService(); + $service = new CleanupSchedulerService; $service->runUnverifiedCleanup(); } } diff --git a/tests/Unit/Services/System/ConfigUpdateServiceTest.php b/tests/Unit/Services/System/ConfigUpdateServiceTest.php index 04c7ddef..0e025ffe 100644 --- a/tests/Unit/Services/System/ConfigUpdateServiceTest.php +++ b/tests/Unit/Services/System/ConfigUpdateServiceTest.php @@ -13,7 +13,7 @@ class ConfigUpdateServiceTest extends TestCase public function test_run_task_rejects_unknown_task(): void { - $service = new ConfigUpdateService(); + $service = new ConfigUpdateService; $result = $service->runTask('unknown', false, false, new DateTimeZone('UTC')); $this->assertFalse($result['ok']); @@ -21,7 +21,7 @@ class ConfigUpdateServiceTest extends TestCase public function test_run_task_sets_config_when_forced(): void { - $service = new ConfigUpdateService(); + $service = new ConfigUpdateService; $result = $service->runTask('enable_attendance_on', false, true, new DateTimeZone('UTC')); $this->assertTrue($result['ok']); diff --git a/tests/Unit/Services/System/GlobalConfigServiceTest.php b/tests/Unit/Services/System/GlobalConfigServiceTest.php index f9895332..158a00b9 100644 --- a/tests/Unit/Services/System/GlobalConfigServiceTest.php +++ b/tests/Unit/Services/System/GlobalConfigServiceTest.php @@ -18,7 +18,7 @@ class GlobalConfigServiceTest extends TestCase ['config_key' => 'school_year', 'config_value' => '2024-2025'], ]); - $service = new GlobalConfigService(); + $service = new GlobalConfigService; $this->assertSame('Fall', $service->getSemester()); $this->assertSame('2024-2025', $service->getSchoolYear()); diff --git a/tests/Unit/Services/System/TimeServiceTest.php b/tests/Unit/Services/System/TimeServiceTest.php index 00c24b1c..c06529a4 100644 --- a/tests/Unit/Services/System/TimeServiceTest.php +++ b/tests/Unit/Services/System/TimeServiceTest.php @@ -9,7 +9,7 @@ class TimeServiceTest extends TestCase { public function test_to_utc_and_to_local_round_trip(): void { - $service = new TimeService(); + $service = new TimeService; $utc = $service->toUtc('2025-01-01 12:00:00', 'America/New_York'); $local = $service->toLocal($utc, 'UTC', 'America/New_York'); diff --git a/tests/Unit/Services/Teachers/TeacherAbsenceServiceTest.php b/tests/Unit/Services/Teachers/TeacherAbsenceServiceTest.php index c626aea5..b03e6f63 100644 --- a/tests/Unit/Services/Teachers/TeacherAbsenceServiceTest.php +++ b/tests/Unit/Services/Teachers/TeacherAbsenceServiceTest.php @@ -2,9 +2,9 @@ namespace Tests\Unit\Services\Teachers; -use App\Services\Staff\StaffTimeOffLinkService; use App\Services\SemesterRangeService; use App\Services\Semesters\SemesterConfigService; +use App\Services\Staff\StaffTimeOffLinkService; use App\Services\Teachers\TeacherAbsenceService; use App\Services\Teachers\TeacherConfigService; use Carbon\Carbon; @@ -23,9 +23,9 @@ class TeacherAbsenceServiceTest extends TestCase $teacherId = $this->seedTeacherUser(); $service = new TeacherAbsenceService( - new TeacherConfigService(), - new SemesterRangeService(new SemesterConfigService()), - new StaffTimeOffLinkService() + new TeacherConfigService, + new SemesterRangeService(new SemesterConfigService), + new StaffTimeOffLinkService ); $data = $service->formData($teacherId); @@ -42,9 +42,9 @@ class TeacherAbsenceServiceTest extends TestCase $teacherId = $this->seedTeacherUser(); $service = new TeacherAbsenceService( - new TeacherConfigService(), - new SemesterRangeService(new SemesterConfigService()), - new StaffTimeOffLinkService() + new TeacherConfigService, + new SemesterRangeService(new SemesterConfigService), + new StaffTimeOffLinkService ); $date = $service->formData($teacherId)['available_dates'][0]; diff --git a/tests/Unit/Services/Teachers/TeacherAssignmentServiceTest.php b/tests/Unit/Services/Teachers/TeacherAssignmentServiceTest.php index 73344c38..aadd15d7 100644 --- a/tests/Unit/Services/Teachers/TeacherAssignmentServiceTest.php +++ b/tests/Unit/Services/Teachers/TeacherAssignmentServiceTest.php @@ -18,7 +18,7 @@ class TeacherAssignmentServiceTest extends TestCase $teacherId = $this->seedTeacherUser(); $classSectionId = $this->seedClassSection(); - $service = new TeacherAssignmentService(new TeacherConfigService()); + $service = new TeacherAssignmentService(new TeacherConfigService); $result = $service->assign([ 'teacher_id' => $teacherId, 'class_section_id' => $classSectionId, @@ -51,7 +51,7 @@ class TeacherAssignmentServiceTest extends TestCase 'updated_at' => now(), ]); - $service = new TeacherAssignmentService(new TeacherConfigService()); + $service = new TeacherAssignmentService(new TeacherConfigService); $result = $service->delete([ 'teacher_id' => $teacherId, 'class_section_id' => $classSectionId, diff --git a/tests/Unit/Services/Teachers/TeacherDashboardServiceTest.php b/tests/Unit/Services/Teachers/TeacherDashboardServiceTest.php index f8880cb2..cb210b59 100644 --- a/tests/Unit/Services/Teachers/TeacherDashboardServiceTest.php +++ b/tests/Unit/Services/Teachers/TeacherDashboardServiceTest.php @@ -20,7 +20,7 @@ class TeacherDashboardServiceTest extends TestCase $this->seedTeacherAssignment($teacherId, $classSectionId); $this->seedStudent($teacherId, $classSectionId); - $service = new TeacherDashboardService(new TeacherConfigService()); + $service = new TeacherDashboardService(new TeacherConfigService); $data = $service->classView($teacherId, $classSectionId); $this->assertSame($classSectionId, $data['active_class_section_id']); diff --git a/tests/Unit/Services/Ui/UiStyleServiceTest.php b/tests/Unit/Services/Ui/UiStyleServiceTest.php index 0cea331a..44d11bf9 100644 --- a/tests/Unit/Services/Ui/UiStyleServiceTest.php +++ b/tests/Unit/Services/Ui/UiStyleServiceTest.php @@ -29,7 +29,7 @@ class UiStyleServiceTest extends TestCase 'school_year' => '2025-2026', ]); - $service = new UiStyleService(); + $service = new UiStyleService; $prefs = $service->update($user->id, [ 'accent' => 'blue', 'menu' => 'custom', diff --git a/tests/Unit/Services/Users/InactiveUserCleanupServiceTest.php b/tests/Unit/Services/Users/InactiveUserCleanupServiceTest.php index 82fd3d16..2b09001a 100644 --- a/tests/Unit/Services/Users/InactiveUserCleanupServiceTest.php +++ b/tests/Unit/Services/Users/InactiveUserCleanupServiceTest.php @@ -40,7 +40,7 @@ class InactiveUserCleanupServiceTest extends TestCase 'role_id' => 1, ]); - $service = new InactiveUserCleanupService(); + $service = new InactiveUserCleanupService; $result = $service->cleanup(15); $this->assertSame(1, $result['deleted_users']); diff --git a/tests/Unit/Services/Users/UserListServiceTest.php b/tests/Unit/Services/Users/UserListServiceTest.php index 565d7a42..1efd9208 100644 --- a/tests/Unit/Services/Users/UserListServiceTest.php +++ b/tests/Unit/Services/Users/UserListServiceTest.php @@ -2,7 +2,6 @@ namespace Tests\Unit\Services\Users; -use App\Models\User; use App\Services\Users\UserListService; use Illuminate\Foundation\Testing\RefreshDatabase; use Illuminate\Support\Facades\DB; diff --git a/tests/Unit/Services/Whatsapp/WhatsappContactServiceTest.php b/tests/Unit/Services/Whatsapp/WhatsappContactServiceTest.php index 77152bf1..8cdabaf6 100644 --- a/tests/Unit/Services/Whatsapp/WhatsappContactServiceTest.php +++ b/tests/Unit/Services/Whatsapp/WhatsappContactServiceTest.php @@ -47,7 +47,7 @@ class WhatsappContactServiceTest extends TestCase 'updated_at' => now(), ]); - $service = new WhatsappContactService(new WhatsappContextService()); + $service = new WhatsappContactService(new WhatsappContextService); $contacts = $service->listParentContacts('2025-2026', 'Fall'); $this->assertCount(2, $contacts); diff --git a/tests/Unit/Services/Whatsapp/WhatsappContextServiceTest.php b/tests/Unit/Services/Whatsapp/WhatsappContextServiceTest.php index f4b7c0d5..1d05393e 100644 --- a/tests/Unit/Services/Whatsapp/WhatsappContextServiceTest.php +++ b/tests/Unit/Services/Whatsapp/WhatsappContextServiceTest.php @@ -13,7 +13,7 @@ class WhatsappContextServiceTest extends TestCase public function test_school_year_aliases_generate_expected_values(): void { - $service = new WhatsappContextService(); + $service = new WhatsappContextService; [$aliases, $canonical, $startYear] = $service->schoolYearAliases('2025/2026'); $this->assertContains('2025-2026', $aliases); @@ -33,7 +33,7 @@ class WhatsappContextServiceTest extends TestCase 'updated_at' => now(), ]); - $service = new WhatsappContextService(); + $service = new WhatsappContextService; $this->assertTrue($service->hasRosterForYear('2025-2026')); $this->assertFalse($service->hasRosterForYear('2030-2031')); diff --git a/tests/Unit/Services/Whatsapp/WhatsappInviteBundleServiceTest.php b/tests/Unit/Services/Whatsapp/WhatsappInviteBundleServiceTest.php index 903f151c..a8f399a6 100644 --- a/tests/Unit/Services/Whatsapp/WhatsappInviteBundleServiceTest.php +++ b/tests/Unit/Services/Whatsapp/WhatsappInviteBundleServiceTest.php @@ -77,7 +77,7 @@ class WhatsappInviteBundleServiceTest extends TestCase ], ]; - $service = new WhatsappInviteBundleService(); + $service = new WhatsappInviteBundleService; $bundle = $service->bundleForParent(10, $linkBySection, '2025-2026', 'Fall'); $this->assertNotNull($bundle); @@ -87,7 +87,7 @@ class WhatsappInviteBundleServiceTest extends TestCase public function test_consolidate_bundles_merges_by_parent(): void { - $service = new WhatsappInviteBundleService(); + $service = new WhatsappInviteBundleService; $merged = $service->consolidateBundles([ [ diff --git a/tests/Unit/Services/Whatsapp/WhatsappInviteNotificationServiceTest.php b/tests/Unit/Services/Whatsapp/WhatsappInviteNotificationServiceTest.php index 83eb0deb..f486bc75 100644 --- a/tests/Unit/Services/Whatsapp/WhatsappInviteNotificationServiceTest.php +++ b/tests/Unit/Services/Whatsapp/WhatsappInviteNotificationServiceTest.php @@ -14,7 +14,7 @@ class WhatsappInviteNotificationServiceTest extends TestCase public function test_dispatch_returns_error_when_no_listeners(): void { - $service = new WhatsappInviteNotificationService(); + $service = new WhatsappInviteNotificationService; $result = $service->dispatch([], '2025-2026', 'Fall'); $this->assertFalse($result['ok']); @@ -22,10 +22,9 @@ class WhatsappInviteNotificationServiceTest extends TestCase public function test_dispatch_triggers_event_when_listener_registered(): void { - Event::listen(WhatsappInvitesSend::class, static function () { - }); + Event::listen(WhatsappInvitesSend::class, static function () {}); - $service = new WhatsappInviteNotificationService(); + $service = new WhatsappInviteNotificationService; $result = $service->dispatch([[ 'parent' => ['id' => 10], 'emails' => ['parent1@example.com'], diff --git a/tests/Unit/Services/Whatsapp/WhatsappInviteServiceTest.php b/tests/Unit/Services/Whatsapp/WhatsappInviteServiceTest.php index 375a7a8e..b942eb0c 100644 --- a/tests/Unit/Services/Whatsapp/WhatsappInviteServiceTest.php +++ b/tests/Unit/Services/Whatsapp/WhatsappInviteServiceTest.php @@ -19,13 +19,12 @@ class WhatsappInviteServiceTest extends TestCase public function test_send_returns_success_for_parent_mode(): void { $this->seedInviteData(); - Event::listen(WhatsappInvitesSend::class, static function () { - }); + Event::listen(WhatsappInvitesSend::class, static function () {}); $service = new WhatsappInviteService( - new WhatsappContextService(), - new WhatsappInviteBundleService(), - new WhatsappInviteNotificationService() + new WhatsappContextService, + new WhatsappInviteBundleService, + new WhatsappInviteNotificationService ); $result = $service->send([ diff --git a/tests/Unit/Services/Whatsapp/WhatsappLinkServiceTest.php b/tests/Unit/Services/Whatsapp/WhatsappLinkServiceTest.php index 1ae3e506..8d0377b7 100644 --- a/tests/Unit/Services/Whatsapp/WhatsappLinkServiceTest.php +++ b/tests/Unit/Services/Whatsapp/WhatsappLinkServiceTest.php @@ -14,7 +14,7 @@ class WhatsappLinkServiceTest extends TestCase public function test_upsert_creates_link(): void { - $service = new WhatsappLinkService(new WhatsappContextService()); + $service = new WhatsappLinkService(new WhatsappContextService); $link = $service->upsert([ 'class_section_id' => 300, @@ -57,7 +57,7 @@ class WhatsappLinkServiceTest extends TestCase ], ]); - $service = new WhatsappLinkService(new WhatsappContextService()); + $service = new WhatsappLinkService(new WhatsappContextService); $page = $service->paginate(['active' => true]); $this->assertCount(1, $page->items()); diff --git a/tests/Unit/Services/Whatsapp/WhatsappMembershipServiceTest.php b/tests/Unit/Services/Whatsapp/WhatsappMembershipServiceTest.php index 6b122d10..5068c1e0 100644 --- a/tests/Unit/Services/Whatsapp/WhatsappMembershipServiceTest.php +++ b/tests/Unit/Services/Whatsapp/WhatsappMembershipServiceTest.php @@ -13,7 +13,7 @@ class WhatsappMembershipServiceTest extends TestCase public function test_update_membership_saves_primary(): void { - $service = new WhatsappMembershipService(); + $service = new WhatsappMembershipService; $result = $service->updateMembership([ 'class_section_id' => 200, @@ -36,7 +36,7 @@ class WhatsappMembershipServiceTest extends TestCase { Schema::shouldReceive('hasTable')->with('whatsapp_group_memberships')->andReturn(false); - $service = new WhatsappMembershipService(); + $service = new WhatsappMembershipService; $result = $service->updateMembership([ 'class_section_id' => 200, 'primary_id' => 10,