fix logic and tests, update docker CI file

This commit is contained in:
root
2026-03-09 15:58:44 -04:00
parent 1cb3573d4b
commit 79e44fe037
188 changed files with 1776 additions and 524 deletions
@@ -3,10 +3,11 @@
namespace App\Http\Controllers\Api\Administrator;
use App\Http\Controllers\Controller;
use App\Http\Requests\Administrator\SubmitAdministratorAbsenceRequest;
use App\Services\Administrator\AdministratorAbsenceService;
use Illuminate\Http\JsonResponse;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Auth;
use Illuminate\Support\Facades\Validator;
class AdministratorAbsenceController extends Controller
{
@@ -25,13 +26,42 @@ class AdministratorAbsenceController extends Controller
return response()->json($this->service->getAbsenceFormData($userId));
}
public function store(SubmitAdministratorAbsenceRequest $request): JsonResponse
public function store(Request $request): JsonResponse
{
$userId = (int) Auth::id();
if ($userId <= 0) {
return response()->json(['message' => 'Please log in first.'], 401);
}
$payload = $request->all();
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);
}
}
$validator = Validator::make($payload, [
'dates' => ['required', 'array', 'min:1'],
'dates.*' => ['required', 'date_format:Y-m-d'],
'reason_type' => ['nullable', 'string', 'max:100'],
'reason' => ['required', 'string', 'max:2000'],
], [
'dates.required' => 'At least one date is required.',
'dates.array' => 'Dates must be submitted as an array.',
'dates.min' => 'At least one date is required.',
'dates.*.date_format' => 'Each date must be in Y-m-d format.',
'reason.required' => 'Reason is required.',
]);
if ($validator->fails()) {
return response()->json([
'message' => 'Validation failed.',
'errors' => $validator->errors(),
], 422);
}
$request->merge($validator->validated());
$result = $this->service->submit($request, $userId);
return response()->json([
@@ -3,11 +3,12 @@
namespace App\Http\Controllers\Api\Administrator;
use App\Http\Controllers\Controller;
use App\Http\Requests\Administrator\UpdateEnrollmentStatusesRequest;
use App\Services\Administrator\AdministratorEnrollmentService;
use Illuminate\Http\JsonResponse;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Auth;
use Illuminate\Support\Facades\Validator;
use Illuminate\Validation\Rule;
class AdministratorEnrollmentController extends Controller
{
@@ -30,14 +31,53 @@ class AdministratorEnrollmentController extends Controller
return response()->json($this->service->showNewStudentsData());
}
public function updateStatuses(UpdateEnrollmentStatusesRequest $request): JsonResponse
public function updateStatuses(Request $request): JsonResponse
{
$editorUserId = (int) Auth::id();
if ($editorUserId <= 0) {
return response()->json(['message' => 'Please log in first.'], 401);
}
$data = $request->validated();
$payload = $request->all();
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)) {
$payload['enrollment_status'] = $request->query('enrollment_status');
}
$allowedStatuses = [
'admission under review',
'payment pending',
'enrolled',
'withdraw under review',
'refund pending',
'withdrawn',
'denied',
'waitlist',
];
$validator = Validator::make($payload, [
'enrollment_status' => ['required', 'array', 'min:1'],
'enrollment_status.*' => ['required', 'string', Rule::in($allowedStatuses)],
], [
'enrollment_status.required' => 'Enrollment statuses are required.',
'enrollment_status.array' => 'Enrollment statuses must be an array.',
'enrollment_status.min' => 'At least one enrollment status is required.',
'enrollment_status.*.in' => 'One or more enrollment statuses are invalid.',
]);
if ($validator->fails()) {
return response()->json([
'message' => 'Validation failed.',
'errors' => $validator->errors(),
], 422);
}
$data = $validator->validated();
$result = $this->service->updateStatuses(
(array) ($data['enrollment_status'] ?? []),
$editorUserId
@@ -3,10 +3,10 @@
namespace App\Http\Controllers\Api\Administrator;
use App\Http\Controllers\Controller;
use App\Http\Requests\Administrator\SaveAdminNotificationSubjectsRequest;
use App\Http\Requests\Administrator\SavePrintRecipientsRequest;
use App\Services\Administrator\AdministratorNotificationService;
use Illuminate\Http\JsonResponse;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Validator;
class AdministratorNotificationController extends Controller
{
@@ -20,9 +20,31 @@ class AdministratorNotificationController extends Controller
return response()->json($this->service->notificationsAlertsData());
}
public function saveAlerts(SaveAdminNotificationSubjectsRequest $request): JsonResponse
public function saveAlerts(Request $request): JsonResponse
{
$data = $request->validated();
$payload = $request->all();
if (!array_key_exists('subjects', $payload)) {
$json = json_decode($request->getContent() ?: '', true);
if (is_array($json)) {
$payload = array_merge($payload, $json);
}
}
$validator = Validator::make($payload, [
'subjects' => ['required', 'array'],
], [
'subjects.required' => 'Subjects payload is required.',
'subjects.array' => 'Subjects payload must be an array.',
]);
if ($validator->fails()) {
return response()->json([
'message' => 'Validation failed.',
'errors' => $validator->errors(),
], 422);
}
$data = $validator->validated();
$result = $this->service->saveNotificationSubjects(
(array) ($data['subjects'] ?? [])
);
@@ -35,9 +57,31 @@ class AdministratorNotificationController extends Controller
return response()->json($this->service->printNotificationRecipientsData());
}
public function savePrintRecipients(SavePrintRecipientsRequest $request): JsonResponse
public function savePrintRecipients(Request $request): JsonResponse
{
$data = $request->validated();
$payload = $request->all();
if (!array_key_exists('notify', $payload)) {
$json = json_decode($request->getContent() ?: '', true);
if (is_array($json)) {
$payload = array_merge($payload, $json);
}
}
$validator = Validator::make($payload, [
'notify' => ['required', 'array'],
], [
'notify.required' => 'Notify payload is required.',
'notify.array' => 'Notify payload must be an array.',
]);
if ($validator->fails()) {
return response()->json([
'message' => 'Validation failed.',
'errors' => $validator->errors(),
], 422);
}
$data = $validator->validated();
$result = $this->service->savePrintNotificationRecipients(
(array) ($data['notify'] ?? [])
);
@@ -3,10 +3,11 @@
namespace App\Http\Controllers\Api\Administrator;
use App\Http\Controllers\Controller;
use App\Http\Requests\Administrator\SendTeacherSubmissionNotificationsRequest;
use App\Services\Administrator\AdministratorTeacherSubmissionService;
use Illuminate\Http\JsonResponse;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Auth;
use Illuminate\Support\Facades\Validator;
class AdministratorTeacherSubmissionController extends Controller
{
@@ -20,13 +21,30 @@ class AdministratorTeacherSubmissionController extends Controller
return response()->json($this->service->report());
}
public function notify(SendTeacherSubmissionNotificationsRequest $request): JsonResponse
public function notify(Request $request): JsonResponse
{
$adminId = (int) Auth::id();
if ($adminId <= 0) {
return response()->json(['message' => 'Please log in first.'], 401);
}
$validator = Validator::make($request->all(), [
'notify' => ['required', 'array', 'min:1'],
'missing_items' => ['nullable', 'array'],
], [
'notify.required' => 'Select at least one teacher to notify.',
'notify.array' => 'Notify payload must be an array.',
'notify.min' => 'Select at least one teacher to notify.',
'missing_items.array' => 'Missing items payload must be an array.',
]);
if ($validator->fails()) {
return response()->json([
'message' => 'Validation failed.',
'errors' => $validator->errors(),
], 422);
}
$result = $this->service->sendNotifications($request, $adminId);
return response()->json([
@@ -3,12 +3,12 @@
namespace App\Http\Controllers\Api\Assignment;
use App\Http\Controllers\Controller;
use App\Http\Requests\Assignment\StoreAssignmentRequest;
use App\Http\Resources\Assignment\AssignmentOverviewResource;
use App\Http\Resources\Assignment\AssignmentSectionResource;
use App\Services\Assignment\AssignmentService;
use Illuminate\Http\JsonResponse;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Validator;
class AssignmentApiController extends Controller
{
@@ -29,10 +29,25 @@ class AssignmentApiController extends Controller
]);
}
public function store(StoreAssignmentRequest $request): JsonResponse
public function store(Request $request): JsonResponse
{
$validator = Validator::make($request->all(), [
'student_id' => ['required', 'integer', 'exists:students,id'],
'class_section_id' => ['required', 'integer', 'exists:classSection,class_section_id'],
'semester' => ['required', 'string', 'max:50'],
'school_year' => ['required', 'string', 'max:50'],
'description' => ['nullable', 'string'],
]);
if ($validator->fails()) {
return response()->json([
'message' => 'Validation failed.',
'errors' => $validator->errors(),
], 422);
}
$assignment = $this->assignmentService->storeAssignment(
data: $request->validated(),
data: $validator->validated(),
updatedBy: $request->user()?->id
);
@@ -3,11 +3,10 @@
namespace App\Http\Controllers\Api\Attendance;
use App\Http\Controllers\Controller;
use App\Http\Requests\AttendanceCommentTemplate\StoreAttendanceCommentTemplateRequest;
use App\Http\Requests\AttendanceCommentTemplate\UpdateAttendanceCommentTemplateRequest;
use App\Services\AttendanceCommentTemplateService;
use Illuminate\Http\JsonResponse;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Validator;
class AttendanceCommentTemplateController extends Controller
{
@@ -49,9 +48,28 @@ class AttendanceCommentTemplateController extends Controller
]);
}
public function store(StoreAttendanceCommentTemplateRequest $request): JsonResponse
public function store(Request $request): JsonResponse
{
$created = $this->service->create($request->validated());
$validator = Validator::make($request->all(), [
'min_score' => ['required', 'integer', 'min:0', 'max:100'],
'max_score' => ['required', 'integer', 'min:0', 'max:100', 'gte:min_score'],
'template_text' => ['required', 'string', 'max:5000'],
'is_active' => ['sometimes', 'boolean'],
]);
if ($validator->fails()) {
return response()->json([
'message' => 'Validation failed.',
'errors' => $validator->errors(),
], 422);
}
$data = $validator->validated();
if (!array_key_exists('is_active', $data)) {
$data['is_active'] = true;
}
$created = $this->service->create($data);
return response()->json([
'status' => 'success',
@@ -60,9 +78,31 @@ class AttendanceCommentTemplateController extends Controller
], 201);
}
public function update(UpdateAttendanceCommentTemplateRequest $request, int $id): JsonResponse
public function update(Request $request, int $id): JsonResponse
{
$updated = $this->service->update($id, $request->validated());
$validator = Validator::make($request->all(), [
'min_score' => ['sometimes', 'integer', 'min:0', 'max:100'],
'max_score' => ['sometimes', 'integer', 'min:0', 'max:100'],
'template_text' => ['sometimes', 'string', 'max:5000'],
'is_active' => ['sometimes', 'boolean'],
]);
$validator->after(function ($validator) use ($request) {
$min = $request->has('min_score') ? (int) $request->input('min_score') : null;
$max = $request->has('max_score') ? (int) $request->input('max_score') : null;
if ($min !== null && $max !== null && $max < $min) {
$validator->errors()->add('max_score', 'The max_score field must be greater than or equal to min_score.');
}
});
if ($validator->fails()) {
return response()->json([
'message' => 'Validation failed.',
'errors' => $validator->errors(),
], 422);
}
$updated = $this->service->update($id, $validator->validated());
return response()->json([
'status' => 'success',
@@ -3,14 +3,10 @@
namespace App\Http\Controllers\Api\AttendanceTracking;
use App\Http\Controllers\Controller;
use App\Http\Requests\AttendanceTracking\ComposeAttendanceEmailRequest;
use App\Http\Requests\AttendanceTracking\ParentsInfoRequest;
use App\Http\Requests\AttendanceTracking\RecordAttendanceTrackingRequest;
use App\Http\Requests\AttendanceTracking\SaveAttendanceNotificationNoteRequest;
use App\Http\Requests\AttendanceTracking\SendAttendanceManualEmailRequest;
use App\Services\AttendanceTracking\AttendanceTrackingService;
use Illuminate\Http\JsonResponse;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Validator;
class AttendanceTrackingController extends Controller
{
@@ -63,9 +59,26 @@ class AttendanceTrackingController extends Controller
);
}
public function record(RecordAttendanceTrackingRequest $request): JsonResponse
public function record(Request $request): JsonResponse
{
$result = $this->service->record($request->validated());
$validator = Validator::make($request->all(), [
'student_id' => ['required', 'integer', 'min:1'],
'date' => ['required', 'date_format:Y-m-d'],
'parent_email' => ['nullable', 'email'],
'parent_name' => ['nullable', 'string'],
'subject_type' => ['nullable', 'string'],
'semester' => ['nullable', 'string'],
'school_year' => ['nullable', 'string'],
]);
if ($validator->fails()) {
return response()->json([
'message' => 'Validation failed.',
'errors' => $validator->errors(),
], 422);
}
$result = $this->service->record($validator->validated());
return response()->json(
$result,
@@ -80,13 +93,28 @@ class AttendanceTrackingController extends Controller
return response()->json($result);
}
public function compose(ComposeAttendanceEmailRequest $request): JsonResponse
public function compose(Request $request): JsonResponse
{
$validator = Validator::make($request->query(), [
'student_id' => ['required', 'integer', 'min:1'],
'code' => ['nullable', 'string'],
'variant' => ['nullable', 'string'],
'incident_date' => ['nullable', 'date_format:Y-m-d'],
]);
if ($validator->fails()) {
return response()->json([
'message' => 'Validation failed.',
'errors' => $validator->errors(),
], 422);
}
$data = $validator->validated();
$result = $this->service->compose(
(int) $request->validated('student_id'),
(string) $request->validated('code', 'ABS_1'),
(string) $request->validated('variant', 'default'),
$request->validated('incident_date'),
(int) ($data['student_id'] ?? 0),
(string) ($data['code'] ?? 'ABS_1'),
(string) ($data['variant'] ?? 'default'),
$data['incident_date'] ?? null,
);
return response()->json(
@@ -95,9 +123,26 @@ class AttendanceTrackingController extends Controller
);
}
public function sendManualEmail(SendAttendanceManualEmailRequest $request): JsonResponse
public function sendManualEmail(Request $request): JsonResponse
{
$result = $this->service->sendEmailManual($request->validated());
$validator = Validator::make($request->all(), [
'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'],
]);
if ($validator->fails()) {
return response()->json([
'message' => 'Validation failed.',
'errors' => $validator->errors(),
], 422);
}
$result = $this->service->sendEmailManual($validator->validated());
return response()->json(
$result,
@@ -105,9 +150,21 @@ class AttendanceTrackingController extends Controller
);
}
public function parentsInfo(ParentsInfoRequest $request): JsonResponse
public function parentsInfo(Request $request): JsonResponse
{
$result = $this->service->parentsInfo((int) $request->validated('student_id'));
$validator = Validator::make($request->query(), [
'student_id' => ['required', 'integer', 'min:1'],
]);
if ($validator->fails()) {
return response()->json([
'message' => 'Validation failed.',
'errors' => $validator->errors(),
], 422);
}
$data = $validator->validated();
$result = $this->service->parentsInfo((int) ($data['student_id'] ?? 0));
return response()->json(
$result,
@@ -115,9 +172,26 @@ class AttendanceTrackingController extends Controller
);
}
public function saveNotificationNote(SaveAttendanceNotificationNoteRequest $request): JsonResponse
public function saveNotificationNote(Request $request): JsonResponse
{
$result = $this->service->saveNotificationNote($request->validated());
$validator = Validator::make($request->all(), [
'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'],
]);
if ($validator->fails()) {
return response()->json([
'message' => 'Validation failed.',
'errors' => $validator->errors(),
], 422);
}
$result = $this->service->saveNotificationNote($validator->validated());
return response()->json(
$result,
@@ -3,11 +3,12 @@
namespace App\Http\Controllers\Api\Auth;
use App\Http\Controllers\Api\BaseApiController;
use App\Http\Requests\Auth\RegisterRequest;
use App\Http\Resources\Auth\RegisterResource;
use App\Services\Auth\RegistrationCaptchaService;
use App\Services\Auth\RegistrationService;
use Illuminate\Http\JsonResponse;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Validator;
class RegisterController extends BaseApiController
{
@@ -28,7 +29,7 @@ class RegisterController extends BaseApiController
]);
}
public function store(RegisterRequest $request): JsonResponse
public function store(Request $request): JsonResponse
{
if (app()->runningUnitTests() && $request->header('X-Debug-Request') === '1') {
return response()->json([
@@ -43,7 +44,15 @@ class RegisterController extends BaseApiController
]);
}
$result = $this->service->register($request->validated());
$validator = Validator::make($request->all(), \App\Http\Requests\Auth\RegisterRequest::ruleset());
if ($validator->fails()) {
return response()->json([
'message' => 'Validation failed.',
'errors' => $validator->errors(),
], 422);
}
$result = $this->service->register($validator->validated());
if (empty($result['ok'])) {
$code = $result['code'] ?? 'registration_failed';
@@ -3,13 +3,12 @@
namespace App\Http\Controllers\Api\Badges;
use App\Http\Controllers\Controller;
use App\Http\Requests\Badges\BadgePrintStatusRequest;
use App\Http\Requests\Badges\GenerateBadgePdfRequest;
use App\Http\Requests\Badges\LogBadgePrintRequest;
use App\Services\Badges\BadgeFormDataService;
use App\Services\Badges\BadgePdfService;
use App\Services\Badges\BadgePrintLogService;
use Illuminate\Http\JsonResponse;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Validator;
use Throwable;
class BadgeController extends Controller
@@ -21,24 +20,41 @@ class BadgeController extends Controller
) {
}
public function generatePdf(GenerateBadgePdfRequest $request)
public function generatePdf(Request $request)
{
$validator = Validator::make($request->all(), [
'user_ids' => ['required', 'array', 'min:1'],
'user_ids.*' => ['integer', 'min:1'],
'school_year' => ['nullable', 'string', 'max:50'],
'roles' => ['nullable', 'array'],
'classes' => ['nullable', 'array'],
]);
if ($validator->fails()) {
return response()->json([
'message' => 'Validation failed.',
'errors' => $validator->errors(),
], 422);
}
$data = $validator->validated();
return $this->badgePdfService->generate(
userIds: $request->input('user_ids', []),
schoolYear: $request->input('school_year'),
rolesMap: $request->input('roles', []),
classesMap: $request->input('classes', []),
userIds: $data['user_ids'] ?? [],
schoolYear: $data['school_year'] ?? null,
rolesMap: $data['roles'] ?? [],
classesMap: $data['classes'] ?? [],
actorId: optional($request->user())->id
);
}
public function printStatus(BadgePrintStatusRequest $request): JsonResponse
public function printStatus(Request $request): JsonResponse
{
try {
return response()->json([
'ok' => true,
'data' => $this->badgePrintLogService->getStatus(
$request->normalizedUserIds(),
$this->parseUserIds($request),
$request->input('school_year')
),
]);
@@ -50,15 +66,32 @@ class BadgeController extends Controller
}
}
public function logPrint(LogBadgePrintRequest $request): JsonResponse
public function logPrint(Request $request): JsonResponse
{
try {
$validator = Validator::make($request->all(), [
'user_ids' => ['required', 'array', 'min:1'],
'user_ids.*' => ['integer', 'min:1'],
'school_year' => ['nullable', 'string', 'max:50'],
'roles' => ['nullable', 'array'],
'classes' => ['nullable', 'array'],
]);
if ($validator->fails()) {
return response()->json([
'message' => 'Validation failed.',
'errors' => $validator->errors(),
], 422);
}
$data = $validator->validated();
$inserted = $this->badgePrintLogService->log(
userIds: $request->input('user_ids', []),
userIds: $data['user_ids'] ?? [],
actorId: optional($request->user())->id,
schoolYear: $request->input('school_year'),
rolesMap: $request->input('roles', []),
classesMap: $request->input('classes', [])
schoolYear: $data['school_year'] ?? null,
rolesMap: $data['roles'] ?? [],
classesMap: $data['classes'] ?? []
);
return response()->json([
@@ -73,15 +106,30 @@ class BadgeController extends Controller
}
}
public function formData(BadgePrintStatusRequest $request): JsonResponse
public function formData(Request $request): JsonResponse
{
return response()->json([
'ok' => true,
'data' => $this->badgeFormDataService->build(
schoolYear: $request->input('school_year'),
selectedUserIds: $request->normalizedUserIds(),
selectedUserIds: $this->parseUserIds($request),
activeRole: $request->input('active_role')
),
]);
}
}
private function parseUserIds(Request $request): array
{
$userIds = $request->input('user_ids', $request->query('user_ids', []));
if (is_string($userIds)) {
$userIds = array_filter(array_map('trim', explode(',', $userIds)), 'strlen');
} elseif (!is_array($userIds)) {
$userIds = $userIds ? [$userIds] : [];
}
$userIds = array_values(array_unique(array_map(static fn ($v) => (int) $v, $userIds)));
return array_values(array_filter($userIds, static fn ($v) => $v > 0));
}
}
@@ -3,13 +3,13 @@
namespace App\Http\Controllers\Api\Finance;
use App\Http\Controllers\Api\BaseApiController;
use App\Http\Requests\Fees\FeeRefundRequest;
use App\Http\Requests\Fees\FeeTuitionTotalRequest;
use App\Http\Resources\Fees\FeeRefundResource;
use App\Http\Resources\Fees\FeeTuitionTotalResource;
use App\Services\Fees\FeeRefundCalculatorService;
use App\Services\Fees\FeeStudentFeeService;
use Illuminate\Http\JsonResponse;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Validator;
class FeeCalculationController extends BaseApiController
{
@@ -20,9 +20,26 @@ class FeeCalculationController extends BaseApiController
parent::__construct();
}
public function refund(FeeRefundRequest $request): JsonResponse
public function refund(Request $request): JsonResponse
{
$payload = $request->validated();
$validator = Validator::make($request->all(), [
'parent_id' => ['required', 'integer', 'min:1'],
'students' => ['required', 'array'],
'students.*.student_id' => ['nullable', 'integer'],
'students.*.class_section_id' => ['required', 'integer', 'min:1'],
'students.*.enrollment_status' => ['required', 'string', 'max:50'],
'students.*.admission_status' => ['required', 'string', 'max:50'],
'students.*.withdrawal_date' => ['nullable', 'date'],
]);
if ($validator->fails()) {
return response()->json([
'message' => 'Validation failed.',
'errors' => $validator->errors(),
], 422);
}
$payload = $validator->validated();
$result = $this->refunds->calculateRefund($payload['students'], (int) $payload['parent_id']);
return response()->json([
@@ -31,9 +48,22 @@ class FeeCalculationController extends BaseApiController
]);
}
public function tuitionTotal(FeeTuitionTotalRequest $request): JsonResponse
public function tuitionTotal(Request $request): JsonResponse
{
$payload = $request->validated();
$validator = Validator::make($request->all(), [
'students' => ['required', 'array'],
'students.*.class_section_id' => ['required', 'integer', 'min:1'],
'students.*.grade' => ['nullable', 'string', 'max:20'],
]);
if ($validator->fails()) {
return response()->json([
'message' => 'Validation failed.',
'errors' => $validator->errors(),
], 422);
}
$payload = $validator->validated();
$total = $this->tuition->totalTuitionFee($payload['students']);
return response()->json([
@@ -3,7 +3,6 @@
namespace App\Http\Controllers\Api\Finance;
use App\Http\Controllers\Api\BaseApiController;
use App\Http\Requests\Invoices\InvoiceGenerateRequest;
use App\Http\Requests\Invoices\InvoiceManagementRequest;
use App\Http\Requests\Invoices\InvoiceParentRequest;
use App\Http\Requests\Invoices\InvoiceStatusRequest;
@@ -15,6 +14,8 @@ use App\Services\Invoices\InvoiceManagementService;
use App\Services\Invoices\InvoicePaymentService;
use App\Services\Invoices\InvoicePdfService;
use Illuminate\Http\JsonResponse;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Validator;
use Symfony\Component\HttpFoundation\StreamedResponse;
class InvoiceController extends BaseApiController
@@ -44,9 +45,22 @@ class InvoiceController extends BaseApiController
]);
}
public function generate(InvoiceGenerateRequest $request): JsonResponse
public function generate(Request $request): JsonResponse
{
$payload = $request->validated();
$data = array_merge($request->query->all(), $request->all(), $request->json()->all());
$validator = Validator::make($data, [
'parent_id' => ['required', 'integer', 'exists:users,id'],
'school_year' => ['nullable', 'string', 'max:20'],
]);
if ($validator->fails()) {
return response()->json([
'message' => 'Validation failed.',
'errors' => $validator->errors(),
], 422);
}
$payload = $validator->validated();
$result = $this->generation->generateInvoice((int) $payload['parent_id'], $payload['school_year'] ?? null);
if (empty($result['ok'])) {
@@ -4,13 +4,13 @@ namespace App\Http\Controllers\Api\Finance;
use App\Http\Controllers\Api\BaseApiController;
use App\Http\Requests\Payments\PaymentByParentRequest;
use App\Http\Requests\Payments\PaymentCreateRequest;
use App\Http\Requests\Payments\PaymentUpdateBalanceRequest;
use App\Http\Resources\Payments\PaymentResource;
use App\Services\Payments\PaymentBalanceService;
use App\Services\Payments\PaymentLookupService;
use App\Services\Payments\PaymentPlanService;
use Illuminate\Http\JsonResponse;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Validator;
class PaymentController extends BaseApiController
{
@@ -22,9 +22,30 @@ class PaymentController extends BaseApiController
parent::__construct();
}
public function store(PaymentCreateRequest $request): JsonResponse
public function store(Request $request): JsonResponse
{
$payload = $request->validated();
$data = array_merge($request->query->all(), $request->all(), $request->json()->all());
$validator = Validator::make($data, [
'parent_id' => ['required', 'integer', 'exists:users,id'],
'invoice_id' => ['nullable', 'integer', 'exists:invoices,id'],
'total_amount' => ['required', 'numeric', 'min:0.01'],
'number_of_installments' => ['required', 'integer', 'min:1'],
'payment_date' => ['nullable', 'date'],
'payment_method' => ['nullable', 'string', 'max:50'],
'payment_type' => ['nullable', 'string', 'max:50'],
'status' => ['nullable', 'string', 'max:50'],
'semester' => ['nullable', 'string', 'max:50'],
'school_year' => ['nullable', 'string', 'max:50'],
]);
if ($validator->fails()) {
return response()->json([
'message' => 'Validation failed.',
'errors' => $validator->errors(),
], 422);
}
$payload = $validator->validated();
$payment = $this->planService->createPlan($payload);
return response()->json([
@@ -44,9 +65,21 @@ class PaymentController extends BaseApiController
]);
}
public function updateBalance(PaymentUpdateBalanceRequest $request, int $paymentId): JsonResponse
public function updateBalance(Request $request, int $paymentId): JsonResponse
{
$payload = $request->validated();
$data = array_merge($request->query->all(), $request->all(), $request->json()->all());
$validator = Validator::make($data, [
'paid_amount' => ['required', 'numeric', 'min:0.01'],
]);
if ($validator->fails()) {
return response()->json([
'message' => 'Validation failed.',
'errors' => $validator->errors(),
], 422);
}
$payload = $validator->validated();
$updated = $this->balanceService->updateBalance($paymentId, (float) $payload['paid_amount']);
if (!$updated) {
@@ -4,9 +4,10 @@ namespace App\Http\Controllers\Api\Finance;
use App\Http\Controllers\Api\BaseApiController;
use App\Http\Requests\Payments\PaymentEventChargesListRequest;
use App\Http\Requests\Payments\PaymentEventChargesStoreRequest;
use App\Services\Payments\PaymentEventChargesService;
use Illuminate\Http\JsonResponse;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Validator;
class PaymentEventChargesController extends BaseApiController
{
@@ -23,9 +24,29 @@ class PaymentEventChargesController extends BaseApiController
return response()->json(['ok' => true] + $data);
}
public function store(PaymentEventChargesStoreRequest $request): JsonResponse
public function store(Request $request): JsonResponse
{
$payload = $request->validated();
$data = array_merge($request->query->all(), $request->all(), $request->json()->all());
$validator = Validator::make($data, [
'parent_id' => ['required', 'integer', 'min:1'],
'event_name' => ['required', 'string', 'max:255'],
'description' => ['nullable', 'string', 'max:1000'],
'amount' => ['required', 'numeric', 'min:0'],
'semester' => ['required', 'string', 'max:50'],
'school_year' => ['required', 'string', 'max:50'],
'student_ids' => ['nullable', 'array'],
'student_ids.*' => ['integer', 'min:1'],
'student_id' => ['nullable', 'integer', 'min:1'],
]);
if ($validator->fails()) {
return response()->json([
'message' => 'Validation failed.',
'errors' => $validator->errors(),
], 422);
}
$payload = $validator->validated();
$userId = (int) (auth()->id() ?? 0);
$count = $this->service->addCharges($payload, $userId);
@@ -4,12 +4,11 @@ namespace App\Http\Controllers\Api\Finance;
use App\Http\Controllers\Api\BaseApiController;
use App\Http\Requests\Payments\PaymentManualEditRequest;
use App\Http\Requests\Payments\PaymentManualSearchRequest;
use App\Http\Requests\Payments\PaymentManualSuggestRequest;
use App\Http\Requests\Payments\PaymentManualUpdateRequest;
use App\Services\Payments\PaymentManualService;
use Illuminate\Http\JsonResponse;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Validator;
class PaymentManualController extends BaseApiController
{
@@ -18,9 +17,22 @@ class PaymentManualController extends BaseApiController
parent::__construct();
}
public function search(PaymentManualSearchRequest $request): JsonResponse
public function search(Request $request): JsonResponse
{
$payload = $request->validated();
$data = array_merge($request->query->all(), $request->all(), $request->json()->all());
$validator = Validator::make($data, [
'search_term' => ['nullable', 'string', 'max:255'],
'school_year' => ['nullable', 'string', 'max:50'],
]);
if ($validator->fails()) {
return response()->json([
'message' => 'Validation failed.',
'errors' => $validator->errors(),
], 422);
}
$payload = $validator->validated();
$data = $this->service->search(
(string) ($payload['search_term'] ?? ''),
$payload['school_year'] ?? null
@@ -29,9 +41,21 @@ class PaymentManualController extends BaseApiController
return response()->json(['ok' => true] + $data);
}
public function suggest(PaymentManualSuggestRequest $request): JsonResponse
public function suggest(Request $request): JsonResponse
{
$payload = $request->validated();
$data = array_merge($request->query->all(), $request->all(), $request->json()->all());
$validator = Validator::make($data, [
'q' => ['required', 'string', 'max:255'],
]);
if ($validator->fails()) {
return response()->json([
'message' => 'Validation failed.',
'errors' => $validator->errors(),
], 422);
}
$payload = $validator->validated();
$items = $this->service->suggest((string) ($payload['q'] ?? ''));
return response()->json(['ok' => true, 'items' => $items]);
@@ -3,11 +3,11 @@
namespace App\Http\Controllers\Api\Finance;
use App\Http\Controllers\Api\BaseApiController;
use App\Http\Requests\Payments\PaymentTransactionCreateRequest;
use App\Http\Resources\Payments\PaymentTransactionResource;
use App\Services\Payments\PaymentTransactionService;
use Illuminate\Http\JsonResponse;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Validator;
class PaymentTransactionController extends BaseApiController
{
@@ -16,9 +16,31 @@ class PaymentTransactionController extends BaseApiController
parent::__construct();
}
public function store(PaymentTransactionCreateRequest $request): JsonResponse
public function store(Request $request): JsonResponse
{
$payload = $request->validated();
$data = array_merge($request->query->all(), $request->all(), $request->json()->all());
$validator = Validator::make($data, [
'transaction_id' => ['required', 'string', 'max:100'],
'payment_id' => ['required', 'integer', 'min:1'],
'transaction_date' => ['nullable', 'date'],
'amount' => ['required', 'numeric', 'min:0.01'],
'payment_method' => ['required', 'string', 'max:50'],
'payment_status' => ['nullable', 'string', 'max:50'],
'transaction_fee' => ['nullable', 'numeric', 'min:0'],
'payment_reference' => ['nullable', 'string', 'max:100'],
'is_full_payment' => ['nullable', 'boolean'],
'school_year' => ['nullable', 'string', 'max:50'],
'semester' => ['nullable', 'string', 'max:50'],
]);
if ($validator->fails()) {
return response()->json([
'message' => 'Validation failed.',
'errors' => $validator->errors(),
], 422);
}
$payload = $validator->validated();
$transaction = $this->service->create($payload);
return response()->json([
@@ -4,8 +4,6 @@ namespace App\Http\Controllers\Api\Finance;
use App\Http\Controllers\Api\BaseApiController;
use App\Http\Requests\PurchaseOrders\PurchaseOrderIndexRequest;
use App\Http\Requests\PurchaseOrders\PurchaseOrderReceiveRequest;
use App\Http\Requests\PurchaseOrders\PurchaseOrderStoreRequest;
use App\Http\Resources\PurchaseOrders\PurchaseOrderItemResource;
use App\Http\Resources\PurchaseOrders\PurchaseOrderResource;
use App\Models\PurchaseOrder;
@@ -14,6 +12,8 @@ use App\Services\PurchaseOrders\PurchaseOrderQueryService;
use App\Services\PurchaseOrders\PurchaseOrderReceiveService;
use App\Services\PurchaseOrders\PurchaseOrderStatusService;
use Illuminate\Http\JsonResponse;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Validator;
use RuntimeException;
class PurchaseOrderController extends BaseApiController
@@ -63,9 +63,41 @@ class PurchaseOrderController extends BaseApiController
]);
}
public function store(PurchaseOrderStoreRequest $request): JsonResponse
public function store(Request $request): JsonResponse
{
$payload = $request->validated();
$data = array_merge($request->query->all(), $request->all(), $request->json()->all());
$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())],
'order_date' => ['nullable', 'date'],
'expected_date' => ['nullable', 'date'],
'notes' => ['nullable', 'string', 'max:5000'],
'items' => ['required', 'array', 'min:1'],
'items.*.supply_id' => ['required', 'integer', 'min:1', 'exists:supplies,id'],
'items.*.description' => ['nullable', 'string', 'max:1000'],
'items.*.quantity' => ['required', 'integer', 'min:1'],
'items.*.unit_cost' => ['required', 'numeric', 'min:0'],
]);
if ($validator->fails()) {
return response()->json([
'message' => 'Validation failed.',
'errors' => $validator->errors(),
], 422);
}
$payload = $validator->validated();
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) {
return response()->json([
'message' => 'Validation failed.',
'errors' => ['expected_date' => ['The expected date must be on or after the order date.']],
], 422);
}
}
try {
$po = $this->createService->create($payload);
@@ -81,9 +113,23 @@ class PurchaseOrderController extends BaseApiController
], 201);
}
public function receive(PurchaseOrderReceiveRequest $request, int $id): JsonResponse
public function receive(Request $request, int $id): JsonResponse
{
$payload = $request->validated();
$data = array_merge($request->query->all(), $request->all(), $request->json()->all());
$validator = Validator::make($data, [
'items' => ['required', 'array', 'min:1'],
'items.*.id' => ['required', 'integer', 'min:1'],
'items.*.quantity' => ['required', 'integer', 'min:1'],
]);
if ($validator->fails()) {
return response()->json([
'message' => 'Validation failed.',
'errors' => $validator->errors(),
], 422);
}
$payload = $validator->validated();
$po = PurchaseOrder::query()->find($id);
if (!$po) {
@@ -6,17 +6,13 @@ use App\Http\Controllers\Api\BaseApiController;
use App\Http\Requests\Files\FileNameRequest;
use App\Http\Requests\Reimbursements\ReimbursementBatchAdminFileRequest;
use App\Http\Requests\Reimbursements\ReimbursementBatchAssignmentRequest;
use App\Http\Requests\Reimbursements\ReimbursementBatchCreateRequest;
use App\Http\Requests\Reimbursements\ReimbursementBatchEmailRequest;
use App\Http\Requests\Reimbursements\ReimbursementBatchLockRequest;
use App\Http\Requests\Reimbursements\ReimbursementExportBatchRequest;
use App\Http\Requests\Reimbursements\ReimbursementExportRequest;
use App\Http\Requests\Reimbursements\ReimbursementIndexRequest;
use App\Http\Requests\Reimbursements\ReimbursementMarkDonationRequest;
use App\Http\Requests\Reimbursements\ReimbursementProcessRequest;
use App\Http\Requests\Reimbursements\ReimbursementStoreRequest;
use App\Http\Requests\Reimbursements\ReimbursementUnderProcessingRequest;
use App\Http\Requests\Reimbursements\ReimbursementUpdateRequest;
use App\Http\Resources\Reimbursements\ReimbursementBatchResource;
use App\Http\Resources\Reimbursements\ReimbursementExpenseResource;
use App\Http\Resources\Reimbursements\ReimbursementRecipientResource;
@@ -37,6 +33,7 @@ use App\Services\Reimbursements\ReimbursementQueryService;
use Illuminate\Http\JsonResponse;
use Illuminate\Http\Request;
use Illuminate\Http\Response;
use Illuminate\Support\Facades\Validator;
use RuntimeException;
class ReimbursementController extends BaseApiController
@@ -69,9 +66,21 @@ class ReimbursementController extends BaseApiController
]);
}
public function markDonation(ReimbursementMarkDonationRequest $request): JsonResponse
public function markDonation(Request $request): JsonResponse
{
$payload = $request->validated();
$data = array_merge($request->query->all(), $request->all(), $request->json()->all());
$validator = Validator::make($data, [
'expense_id' => ['required', 'integer', 'min:1'],
]);
if ($validator->fails()) {
return response()->json([
'message' => 'Validation failed.',
'errors' => $validator->errors(),
], 422);
}
$payload = $validator->validated();
$userId = (int) (auth()->id() ?? 0);
try {
@@ -87,9 +96,21 @@ class ReimbursementController extends BaseApiController
return response()->json(['ok' => true]);
}
public function createBatch(ReimbursementBatchCreateRequest $request): JsonResponse
public function createBatch(Request $request): JsonResponse
{
$payload = $request->validated();
$data = array_merge($request->query->all(), $request->all(), $request->json()->all());
$validator = Validator::make($data, [
'title' => ['nullable', 'string', 'max:255'],
]);
if ($validator->fails()) {
return response()->json([
'message' => 'Validation failed.',
'errors' => $validator->errors(),
], 422);
}
$payload = $validator->validated();
$userId = (int) (auth()->id() ?? 0);
$schoolYear = $this->context->getSchoolYear();
$semester = $this->context->getSemester();
@@ -289,9 +310,29 @@ class ReimbursementController extends BaseApiController
]);
}
public function store(ReimbursementStoreRequest $request): JsonResponse
public function store(Request $request): JsonResponse
{
$payload = $request->validated();
$data = array_merge($request->query->all(), $request->all(), $request->json()->all());
$validator = Validator::make($data, [
'amount' => ['required', 'numeric', 'gt:0'],
'reimbursed_to' => ['required', 'integer', 'min:1'],
'reimbursement_method' => ['required', 'in:Cash,Check'],
'check_number' => ['required_if:reimbursement_method,Check', 'nullable', 'string', 'max:50'],
'receipt' => ['required_if:reimbursement_method,Check', 'nullable', 'file', 'max:2048', 'mimes:jpg,jpeg,png,webp,gif,pdf'],
'expense_id' => ['nullable', 'integer', 'min:1'],
'description' => ['nullable', 'string'],
'school_year' => ['nullable', 'string', 'max:20'],
'semester' => ['nullable', 'string', 'max:20'],
]);
if ($validator->fails()) {
return response()->json([
'message' => 'Validation failed.',
'errors' => $validator->errors(),
], 422);
}
$payload = $validator->validated();
$userId = (int) (auth()->id() ?? 0);
$receiptName = null;
@@ -361,14 +402,32 @@ class ReimbursementController extends BaseApiController
]);
}
public function update(ReimbursementUpdateRequest $request, int $id): JsonResponse
public function update(Request $request, int $id): JsonResponse
{
$reimb = Reimbursement::query()->find($id);
if (!$reimb) {
return response()->json(['ok' => false, 'message' => 'Reimbursement not found.'], 404);
}
$payload = $request->validated();
$data = array_merge($request->query->all(), $request->all(), $request->json()->all());
$validator = Validator::make($data, [
'amount' => ['required', 'numeric', 'gt:0'],
'reimbursed_to' => ['required', 'integer', 'min:1'],
'reimbursement_method' => ['required', 'in:Cash,Check'],
'check_number' => ['required_if:reimbursement_method,Check', 'nullable', 'string', 'max:50'],
'receipt' => ['nullable', 'file', 'max:2048', 'mimes:jpg,jpeg,png,webp,gif,pdf'],
'remove_receipt' => ['nullable', 'boolean'],
'description' => ['nullable', 'string'],
]);
if ($validator->fails()) {
return response()->json([
'message' => 'Validation failed.',
'errors' => $validator->errors(),
], 422);
}
$payload = $validator->validated();
$userId = (int) (auth()->id() ?? 0);
$receiptName = $reimb->receipt_path;
@@ -7,7 +7,6 @@ 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\Requests\Incidents\IncidentStoreRequest;
use App\Http\Resources\Incidents\IncidentAnalysisStudentResource;
use App\Http\Resources\Incidents\IncidentGradeResource;
use App\Http\Resources\Incidents\IncidentResource;
@@ -16,6 +15,8 @@ use App\Services\Incidents\CurrentIncidentService;
use App\Services\Incidents\IncidentAnalysisService;
use App\Services\Incidents\IncidentHistoryService;
use Illuminate\Http\JsonResponse;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Validator;
class IncidentController extends BaseApiController
{
@@ -83,9 +84,26 @@ class IncidentController extends BaseApiController
]);
}
public function store(IncidentStoreRequest $request): JsonResponse
public function store(Request $request): JsonResponse
{
$payload = $request->validated();
$data = array_merge($request->query->all(), $request->all(), $request->json()->all());
$validator = Validator::make($data, [
'student_id' => ['required', 'integer', 'min:1'],
'grade' => ['required', 'integer', 'min:1'],
'incident' => ['required', 'string', 'max:255'],
'description' => ['required', 'string', 'max:2000'],
'school_year' => ['nullable', 'string', 'max:20'],
'semester' => ['nullable', 'string', 'max:20'],
]);
if ($validator->fails()) {
return response()->json([
'message' => 'Validation failed.',
'errors' => $validator->errors(),
], 422);
}
$payload = $validator->validated();
$result = $this->currentService->addIncident($payload, (int) (auth()->id() ?? 0));
if (empty($result['ok'])) {
@@ -109,9 +127,22 @@ class IncidentController extends BaseApiController
]);
}
public function close(IncidentCloseRequest $request, int $incidentId): JsonResponse
public function close(Request $request, int $incidentId): JsonResponse
{
$payload = $request->validated();
$data = array_merge($request->query->all(), $request->all(), $request->json()->all());
$validator = Validator::make($data, [
'state_description' => ['required', 'string', 'max:2000'],
'action_taken' => ['nullable', 'string', 'max:2000'],
]);
if ($validator->fails()) {
return response()->json([
'message' => 'Validation failed.',
'errors' => $validator->errors(),
], 422);
}
$payload = $validator->validated();
$result = $this->currentService->closeIncident(
$incidentId,
(string) $payload['state_description'],
@@ -3,13 +3,14 @@
namespace App\Http\Controllers\Api\Scores;
use App\Http\Controllers\Api\BaseApiController;
use App\Http\Requests\Scores\ScoreClassRequest;
use App\Http\Requests\Scores\ScoreUpdateRequest;
use App\Http\Resources\Scores\ScoreStudentResource;
use App\Models\Student;
use App\Services\Scores\ExamScoreService;
use App\Services\Scores\SemesterScoreService;
use Illuminate\Http\JsonResponse;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Validator;
class FinalController extends BaseApiController
{
@@ -20,9 +21,23 @@ class FinalController extends BaseApiController
parent::__construct();
}
public function index(ScoreClassRequest $request): JsonResponse
public function index(Request $request): JsonResponse
{
$payload = $request->validated();
$data = array_merge($request->query->all(), $request->all(), $request->json()->all());
$validator = Validator::make($data, [
'class_section_id' => ['required', 'integer', 'min:1'],
'semester' => ['nullable', 'string', 'max:50'],
'school_year' => ['nullable', 'string', 'max:50'],
]);
if ($validator->fails()) {
return response()->json([
'message' => 'Validation failed.',
'errors' => $validator->errors(),
], 422);
}
$payload = $validator->validated();
$data = $this->service->list('final_exam', (int) $payload['class_section_id'], $payload['semester'] ?? null, $payload['school_year'] ?? null);
return response()->json([
@@ -4,13 +4,14 @@ namespace App\Http\Controllers\Api\Scores;
use App\Http\Controllers\Api\BaseApiController;
use App\Http\Requests\Scores\ScoreAddColumnRequest;
use App\Http\Requests\Scores\ScoreClassRequest;
use App\Http\Requests\Scores\ScoreUpdateRequest;
use App\Http\Resources\Scores\ScoreStudentResource;
use App\Models\Student;
use App\Services\Scores\HomeworkScoreService;
use App\Services\Scores\SemesterScoreService;
use Illuminate\Http\JsonResponse;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Validator;
class HomeworkController extends BaseApiController
{
@@ -21,9 +22,23 @@ class HomeworkController extends BaseApiController
parent::__construct();
}
public function index(ScoreClassRequest $request): JsonResponse
public function index(Request $request): JsonResponse
{
$payload = $request->validated();
$data = array_merge($request->query->all(), $request->all(), $request->json()->all());
$validator = Validator::make($data, [
'class_section_id' => ['required', 'integer', 'min:1'],
'semester' => ['nullable', 'string', 'max:50'],
'school_year' => ['nullable', 'string', 'max:50'],
]);
if ($validator->fails()) {
return response()->json([
'message' => 'Validation failed.',
'errors' => $validator->errors(),
], 422);
}
$payload = $validator->validated();
$data = $this->service->list(
(int) $payload['class_section_id'],
$payload['semester'] ?? null,
@@ -3,13 +3,14 @@
namespace App\Http\Controllers\Api\Scores;
use App\Http\Controllers\Api\BaseApiController;
use App\Http\Requests\Scores\ScoreClassRequest;
use App\Http\Requests\Scores\ScoreUpdateRequest;
use App\Http\Resources\Scores\ScoreStudentResource;
use App\Models\Student;
use App\Services\Scores\ExamScoreService;
use App\Services\Scores\SemesterScoreService;
use Illuminate\Http\JsonResponse;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Validator;
class MidtermController extends BaseApiController
{
@@ -20,9 +21,23 @@ class MidtermController extends BaseApiController
parent::__construct();
}
public function index(ScoreClassRequest $request): JsonResponse
public function index(Request $request): JsonResponse
{
$payload = $request->validated();
$data = array_merge($request->query->all(), $request->all(), $request->json()->all());
$validator = Validator::make($data, [
'class_section_id' => ['required', 'integer', 'min:1'],
'semester' => ['nullable', 'string', 'max:50'],
'school_year' => ['nullable', 'string', 'max:50'],
]);
if ($validator->fails()) {
return response()->json([
'message' => 'Validation failed.',
'errors' => $validator->errors(),
], 422);
}
$payload = $validator->validated();
$data = $this->service->list('midterm_exam', (int) $payload['class_section_id'], $payload['semester'] ?? null, $payload['school_year'] ?? null);
return response()->json([
@@ -3,13 +3,14 @@
namespace App\Http\Controllers\Api\Scores;
use App\Http\Controllers\Api\BaseApiController;
use App\Http\Requests\Scores\ScoreClassRequest;
use App\Http\Requests\Scores\ScoreUpdateRequest;
use App\Http\Resources\Scores\ScoreStudentResource;
use App\Models\Student;
use App\Services\Scores\ParticipationScoreService;
use App\Services\Scores\SemesterScoreService;
use Illuminate\Http\JsonResponse;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Validator;
class ParticipationController extends BaseApiController
{
@@ -20,9 +21,23 @@ class ParticipationController extends BaseApiController
parent::__construct();
}
public function index(ScoreClassRequest $request): JsonResponse
public function index(Request $request): JsonResponse
{
$payload = $request->validated();
$data = array_merge($request->query->all(), $request->all(), $request->json()->all());
$validator = Validator::make($data, [
'class_section_id' => ['required', 'integer', 'min:1'],
'semester' => ['nullable', 'string', 'max:50'],
'school_year' => ['nullable', 'string', 'max:50'],
]);
if ($validator->fails()) {
return response()->json([
'message' => 'Validation failed.',
'errors' => $validator->errors(),
], 422);
}
$payload = $validator->validated();
$data = $this->service->list(
(int) $payload['class_section_id'],
$payload['semester'] ?? null,
@@ -4,13 +4,14 @@ namespace App\Http\Controllers\Api\Scores;
use App\Http\Controllers\Api\BaseApiController;
use App\Http\Requests\Scores\ScoreAddColumnRequest;
use App\Http\Requests\Scores\ScoreClassRequest;
use App\Http\Requests\Scores\ScoreUpdateRequest;
use App\Http\Resources\Scores\ScoreStudentResource;
use App\Models\Student;
use App\Services\Scores\ProjectScoreService;
use App\Services\Scores\SemesterScoreService;
use Illuminate\Http\JsonResponse;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Validator;
class ProjectController extends BaseApiController
{
@@ -21,9 +22,23 @@ class ProjectController extends BaseApiController
parent::__construct();
}
public function index(ScoreClassRequest $request): JsonResponse
public function index(Request $request): JsonResponse
{
$payload = $request->validated();
$data = array_merge($request->query->all(), $request->all(), $request->json()->all());
$validator = Validator::make($data, [
'class_section_id' => ['required', 'integer', 'min:1'],
'semester' => ['nullable', 'string', 'max:50'],
'school_year' => ['nullable', 'string', 'max:50'],
]);
if ($validator->fails()) {
return response()->json([
'message' => 'Validation failed.',
'errors' => $validator->errors(),
], 422);
}
$payload = $validator->validated();
$data = $this->service->list(
(int) $payload['class_section_id'],
$payload['semester'] ?? null,
@@ -4,13 +4,14 @@ namespace App\Http\Controllers\Api\Scores;
use App\Http\Controllers\Api\BaseApiController;
use App\Http\Requests\Scores\ScoreAddColumnRequest;
use App\Http\Requests\Scores\ScoreClassRequest;
use App\Http\Requests\Scores\ScoreUpdateRequest;
use App\Http\Resources\Scores\ScoreStudentResource;
use App\Models\Student;
use App\Services\Scores\QuizScoreService;
use App\Services\Scores\SemesterScoreService;
use Illuminate\Http\JsonResponse;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Validator;
class QuizController extends BaseApiController
{
@@ -21,9 +22,23 @@ class QuizController extends BaseApiController
parent::__construct();
}
public function index(ScoreClassRequest $request): JsonResponse
public function index(Request $request): JsonResponse
{
$payload = $request->validated();
$data = array_merge($request->query->all(), $request->all(), $request->json()->all());
$validator = Validator::make($data, [
'class_section_id' => ['required', 'integer', 'min:1'],
'semester' => ['nullable', 'string', 'max:50'],
'school_year' => ['nullable', 'string', 'max:50'],
]);
if ($validator->fails()) {
return response()->json([
'message' => 'Validation failed.',
'errors' => $validator->errors(),
], 422);
}
$payload = $validator->validated();
$data = $this->service->list(
(int) $payload['class_section_id'],
$payload['semester'] ?? null,
@@ -7,6 +7,39 @@ use Illuminate\Validation\Rule;
class UpdateEnrollmentStatusesRequest extends ApiFormRequest
{
public function validationData(): array
{
$data = $this->all();
if (array_key_exists('enrollment_status', $data)) {
return $data;
}
$raw = $this->getContent() ?: '';
if ($raw !== '') {
$json = json_decode($raw, true);
if (is_array($json)) {
return array_merge($data, $json);
}
parse_str($raw, $parsed);
if (is_array($parsed)) {
return array_merge($data, $parsed);
}
}
$queryString = (string) $this->server->get('QUERY_STRING', '');
if ($queryString !== '') {
$parsedQuery = [];
parse_str($queryString, $parsedQuery);
if (is_array($parsedQuery)) {
return array_merge($data, $parsedQuery);
}
}
return $data;
}
public function authorize(): bool
{
return auth()->check();
@@ -1,10 +1,10 @@
<?php
namespace App\Http\Requests;
namespace App\Http\Requests\Assignment;
use Illuminate\Foundation\Http\FormRequest;
use App\Http\Requests\ApiFormRequest;
class StoreAssignmentRequest extends FormRequest
class StoreAssignmentRequest extends ApiFormRequest
{
public function authorize(): bool
{
@@ -15,10 +15,10 @@ class StoreAssignmentRequest extends FormRequest
{
return [
'student_id' => ['required', 'integer', 'exists:students,id'],
'class_section_id' => ['required', 'integer', 'exists:class_sections,id'],
'class_section_id' => ['required', 'integer', 'exists:classSection,class_section_id'],
'semester' => ['required', 'string', 'max:50'],
'school_year' => ['required', 'string', 'max:50'],
'description' => ['nullable', 'string'],
];
}
}
}
@@ -2,9 +2,9 @@
namespace App\Http\Requests\AttendanceTracking;
use Illuminate\Foundation\Http\FormRequest;
use App\Http\Requests\ApiFormRequest;
class ComposeAttendanceEmailRequest extends FormRequest
class ComposeAttendanceEmailRequest extends ApiFormRequest
{
public function authorize(): bool
{
@@ -20,4 +20,4 @@ class ComposeAttendanceEmailRequest extends FormRequest
'incident_date' => ['nullable', 'date_format:Y-m-d'],
];
}
}
}
@@ -2,9 +2,9 @@
namespace App\Http\Requests\AttendanceTracking;
use Illuminate\Foundation\Http\FormRequest;
use App\Http\Requests\ApiFormRequest;
class ParentsInfoRequest extends FormRequest
class ParentsInfoRequest extends ApiFormRequest
{
public function authorize(): bool
{
@@ -24,4 +24,4 @@ class ParentsInfoRequest extends FormRequest
'student_id' => $this->query('student_id'),
]);
}
}
}
@@ -2,9 +2,9 @@
namespace App\Http\Requests\AttendanceTracking;
use Illuminate\Foundation\Http\FormRequest;
use App\Http\Requests\ApiFormRequest;
class RecordAttendanceTrackingRequest extends FormRequest
class RecordAttendanceTrackingRequest extends ApiFormRequest
{
public function authorize(): bool
{
@@ -23,4 +23,4 @@ class RecordAttendanceTrackingRequest extends FormRequest
'school_year' => ['nullable', 'string'],
];
}
}
}
@@ -2,9 +2,9 @@
namespace App\Http\Requests\AttendanceTracking;
use Illuminate\Foundation\Http\FormRequest;
use App\Http\Requests\ApiFormRequest;
class SaveAttendanceNotificationNoteRequest extends FormRequest
class SaveAttendanceNotificationNoteRequest extends ApiFormRequest
{
public function authorize(): bool
{
@@ -23,4 +23,4 @@ class SaveAttendanceNotificationNoteRequest extends FormRequest
'variant' => ['nullable', 'string'],
];
}
}
}
@@ -2,9 +2,9 @@
namespace App\Http\Requests\AttendanceTracking;
use Illuminate\Foundation\Http\FormRequest;
use App\Http\Requests\ApiFormRequest;
class SendAttendanceManualEmailRequest extends FormRequest
class SendAttendanceManualEmailRequest extends ApiFormRequest
{
public function authorize(): bool
{
@@ -23,4 +23,4 @@ class SendAttendanceManualEmailRequest extends FormRequest
'incident_date' => ['nullable', 'date_format:Y-m-d'],
];
}
}
}
@@ -1,6 +1,6 @@
<?php
namespace App\Http\Resources;
namespace App\Http\Resources\Assignment;
use Illuminate\Http\Request;
use Illuminate\Http\Resources\Json\JsonResource;
@@ -19,4 +19,4 @@ class AssignmentOverviewResource extends JsonResource
'current_school_year' => $this->current_school_year ?? null,
];
}
}
}
@@ -1,6 +1,6 @@
<?php
namespace App\Http\Resources;
namespace App\Http\Resources\Assignment;
use Illuminate\Http\Request;
use Illuminate\Http\Resources\Json\JsonResource;
@@ -20,4 +20,4 @@ class AssignmentSectionResource extends JsonResource
'description' => $this['description'] ?? '',
];
}
}
}
@@ -0,0 +1,25 @@
<?php
namespace App\Mail;
use Illuminate\Bus\Queueable;
use Illuminate\Mail\Mailable;
use Illuminate\Queue\SerializesModels;
class TeacherSubmissionReminderMail extends Mailable
{
use Queueable;
use SerializesModels;
public function __construct(
protected string $subjectLine,
protected string $htmlBody
) {
}
public function build(): self
{
return $this->subject($this->subjectLine)
->html($this->htmlBody);
}
}
+13 -12
View File
@@ -68,7 +68,7 @@ class AttendanceDay extends BaseModel
$q = static::query()
->select('id')
->where('class_section_id', $classSectionId)
->where('date', $date)
->whereDate('date', $date)
->where(function ($w) {
$w->where('status', 'published')
->orWhere('status', 'finalized'); // legacy
@@ -91,7 +91,7 @@ class AttendanceDay extends BaseModel
): ?self {
return static::query()
->where('class_section_id', $classSectionId)
->where('date', $date)
->whereDate('date', $date)
->where('semester', $semester)
->where('school_year', $schoolYear)
->first();
@@ -112,11 +112,12 @@ class AttendanceDay extends BaseModel
if ($row) return $row;
$now = now();
$normalizedDate = Carbon::parse($date)->toDateString();
try {
return static::create([
'class_section_id' => $classSectionId,
'date' => $date,
'date' => $normalizedDate,
'status' => 'draft',
'submitted_by' => null,
'submitted_at' => null,
@@ -142,15 +143,15 @@ class AttendanceDay extends BaseModel
/**
* DEPRECATED legacy finalize => submit (teacher submit).
*/
public static function finalize(int $id, int $userId): bool
public function finalize(int $userId): bool
{
return static::submit($id, $userId, null);
return $this->submit($userId, null);
}
/**
* Teacher submit: status => submitted (teacher locked, admin editable).
*/
public static function submit(int $id, int $userId, ?string $autoPublishAt = null): bool
public function submit(int $userId, ?string $autoPublishAt = null): bool
{
$now = now();
@@ -165,17 +166,17 @@ class AttendanceDay extends BaseModel
$payload['auto_publish_at'] = $autoPublishAt;
}
return static::query()->whereKey($id)->update($payload) >= 0;
return static::query()->whereKey($this->id)->update($payload) >= 0;
}
/**
* Admin publish (hard lock): status => published.
*/
public static function publish(int $id, int $userId): bool
public function publish(int $userId): bool
{
$now = now();
return static::query()->whereKey($id)->update([
return static::query()->whereKey($this->id)->update([
'status' => 'published',
'published_by' => $userId,
'published_at' => $now,
@@ -186,7 +187,7 @@ class AttendanceDay extends BaseModel
/**
* Admin reopen a day back to 'draft' (default) or 'submitted'.
*/
public static function reopen(int $id, int $userId, string $toStatus = 'draft', ?string $reason = null): bool
public function reopen(int $userId, string $toStatus = 'draft', ?string $reason = null): bool
{
if (!in_array($toStatus, ['draft', 'submitted'], true)) {
throw new \InvalidArgumentException('toStatus must be "draft" or "submitted".');
@@ -194,7 +195,7 @@ class AttendanceDay extends BaseModel
$now = now();
return static::query()->whereKey($id)->update([
return static::query()->whereKey($this->id)->update([
'status' => $toStatus,
'reopened_by' => $userId,
'reopened_at' => $now,
@@ -202,4 +203,4 @@ class AttendanceDay extends BaseModel
'updated_at' => $now,
]) >= 0;
}
}
}
+6 -4
View File
@@ -30,7 +30,7 @@ class AttendanceEmailTemplate extends BaseModel
* Fetch a template by code and variant, falling back to 'default' if needed.
* Active only.
*/
public static function getTemplate(string $code, string $variant = 'default'): ?self
public static function getTemplate(string $code, string $variant = 'default'): ?array
{
$row = static::query()
->where('code', $code)
@@ -39,13 +39,15 @@ class AttendanceEmailTemplate extends BaseModel
->first();
if ($row) {
return $row;
return $row->toArray();
}
return static::query()
$fallback = static::query()
->where('code', $code)
->where('variant', 'default')
->where('is_active', 1)
->first();
return $fallback?->toArray();
}
}
}
+13 -1
View File
@@ -3,10 +3,12 @@
namespace App\Models;
use App\Models\BaseModel;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Support\Facades\DB;
class ClassSection extends BaseModel
{
use HasFactory;
// CI table name is "classSection"
protected $table = 'classSection';
@@ -28,6 +30,16 @@ class ClassSection extends BaseModel
'class_section_id' => 'integer',
];
public function getSectionNameAttribute(): ?string
{
return $this->attributes['class_section_name'] ?? null;
}
public function setSectionNameAttribute($value): void
{
$this->attributes['class_section_name'] = $value;
}
/* =========================
* Relationships (optional)
* ========================= */
@@ -136,4 +148,4 @@ class ClassSection extends BaseModel
->get()
->toArray();
}
}
}
+6 -1
View File
@@ -28,6 +28,11 @@ class CommunicationLog extends BaseModel
'metadata',
];
public function getFillable(): array
{
return $this->fillable;
}
protected $casts = [
'student_id' => 'integer',
'family_id' => 'integer',
@@ -51,4 +56,4 @@ class CommunicationLog extends BaseModel
{
return $this->belongsTo(User::class, 'sent_by');
}
}
}
+17 -1
View File
@@ -23,8 +23,24 @@ class EmailTemplate extends BaseModel
protected $casts = [
'is_active' => 'boolean',
'updated_by' => 'integer',
];
public static function getTemplate(string $code, string $variant = 'default'): ?self
{
$row = static::query()
->where('code', $code)
->where('is_active', 1)
->where(function ($q) use ($variant) {
$q->where('variant', $variant)
->orWhere('variant', 'default');
})
->orderByRaw("CASE WHEN variant = ? THEN 0 ELSE 1 END", [$variant])
->first();
return $row;
}
/**
* Equivalent of CI getActiveTemplates():
* is_active=1, orderBy template_key ASC
@@ -48,4 +64,4 @@ class EmailTemplate extends BaseModel
->where('is_active', 1)
->first();
}
}
}
+6 -1
View File
@@ -40,6 +40,11 @@ class PurchaseOrder extends BaseModel
'deleted_at' => 'datetime',
];
public function getFillable(): array
{
return $this->fillable;
}
/* ============================================================
* Status (enum-like)
* ============================================================
@@ -157,4 +162,4 @@ class PurchaseOrder extends BaseModel
'notes' => ['nullable', 'string', 'max:5000'],
];
}
}
}
+6 -1
View File
@@ -30,6 +30,11 @@ class PurchaseOrderItem extends BaseModel
'updated_at' => 'datetime',
];
public function getFillable(): array
{
return $this->fillable;
}
/* ============================================================
* Relationships (optional but recommended)
* ============================================================
@@ -84,4 +89,4 @@ class PurchaseOrderItem extends BaseModel
'unit_cost' => [$updating ? 'sometimes' : 'required', 'numeric', 'min:0'],
];
}
}
}
+6 -1
View File
@@ -12,7 +12,7 @@ class Stats extends BaseModel
* If your stats table has created_at/updated_at, keep true (default).
* If it doesn't, set to false.
*/
public $timestamps = false;
public $timestamps = true;
protected $fillable = [
'students',
@@ -28,6 +28,11 @@ class Stats extends BaseModel
'users' => 'integer',
];
public function getFillable(): array
{
return $this->fillable;
}
/**
* CI-compatible: getStats()
* Returns all rows.
+4 -2
View File
@@ -4,12 +4,14 @@ 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;
use Illuminate\Support\Facades\DB;
class Student extends BaseModel
{
use HasFactory;
protected $table = 'students';
/**
@@ -38,7 +40,7 @@ class Student extends BaseModel
];
protected $casts = [
'school_id' => 'integer',
'school_id' => 'string',
'age' => 'integer',
'photo_consent' => 'boolean',
'is_new' => 'boolean',
@@ -392,4 +394,4 @@ class Student extends BaseModel
'is_active' => ['nullable', 'boolean'],
];
}
}
}
+4 -4
View File
@@ -279,7 +279,7 @@ class TeacherClass extends BaseModel
->join('classSection as cs', 'cs.class_section_id', '=', 'tc.class_section_id')
->where('tc.teacher_id', $teacherId)
->where('tc.school_year', $schoolYear)
->orderByRaw("FIELD(tc.position,'main','ta')")
->orderByRaw("CASE tc.position WHEN 'main' THEN 0 WHEN 'ta' THEN 1 ELSE 2 END")
->get()
->map(fn ($r) => (array) $r)
->all();
@@ -296,7 +296,7 @@ class TeacherClass extends BaseModel
->leftJoin('users as u', 'u.id', '=', 'tc.teacher_id')
->where('tc.class_section_id', $sectionCode)
->where('tc.school_year', $schoolYear)
->orderByRaw("FIELD(tc.position,'main','ta')")
->orderByRaw("CASE tc.position WHEN 'main' THEN 0 WHEN 'ta' THEN 1 ELSE 2 END")
->orderBy('u.firstname', 'asc')
->get()
->map(fn ($r) => (array) $r)
@@ -324,7 +324,7 @@ class TeacherClass extends BaseModel
->leftJoin('users as u', 'u.id', '=', 'tc.teacher_id')
->where('tc.school_year', $schoolYear)
->orderBy('tc.class_section_id', 'asc')
->orderByRaw("FIELD(tc.position,'main','ta')")
->orderByRaw("CASE tc.position WHEN 'main' THEN 0 WHEN 'ta' THEN 1 ELSE 2 END")
->orderBy('u.firstname', 'asc');
if (!empty($onlySectionCodes)) {
@@ -360,4 +360,4 @@ class TeacherClass extends BaseModel
'updated_by' => ['nullable', 'integer'],
];
}
}
}
+2
View File
@@ -3,6 +3,7 @@
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;
@@ -14,6 +15,7 @@ use Laravel\Sanctum\HasApiTokens;
class User extends Authenticatable
{
use HasApiTokens;
use HasFactory;
protected $table = 'users';
+20
View File
@@ -7,6 +7,9 @@ use App\Services\Attendance\AttendanceRecordSyncService;
use App\Services\Attendance\SemesterRangeService;
use App\Services\Attendance\StudentAttendanceWriterService;
use App\Services\Attendance\TeacherAttendanceSubmissionService;
use App\Services\Invoices\InvoiceConfigService;
use App\Services\Invoices\InvoiceGradeService;
use App\Services\Invoices\InvoiceTuitionService;
use Illuminate\Foundation\Http\FormRequest;
use Illuminate\Routing\Redirector;
use Illuminate\Support\ServiceProvider;
@@ -20,6 +23,23 @@ class AppServiceProvider extends ServiceProvider
$this->app->singleton(AttendanceRecordSyncService::class);
$this->app->singleton(StudentAttendanceWriterService::class);
$this->app->singleton(TeacherAttendanceSubmissionService::class);
$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(),
$config->getFirstStudentFee(),
$config->getSecondStudentFee(),
$config->getYouthFee(),
$config->getTimezone()
);
});
}
public function boot(): void
@@ -4,7 +4,7 @@ namespace App\Services\Administrator;
use App\Models\Enrollment;
use App\Models\Invoice;
use App\Models\RefundModel as Refund;
use App\Models\Refund;
use App\Services\FeeCalculationService;
class AdministratorEnrollmentRefundService
@@ -73,4 +73,4 @@ class AdministratorEnrollmentRefundService
'refundAmountByParent' => $refundAmountByParent,
];
}
}
}
@@ -2,6 +2,7 @@
namespace App\Services\Administrator;
use App\Mail\TeacherSubmissionReminderMail;
use App\Models\ClassSection;
use App\Models\TeacherSubmissionNotificationHistory;
use App\Models\User;
@@ -88,9 +89,7 @@ class TeacherSubmissionNotificationService
if (!empty($email) && filter_var($email, FILTER_VALIDATE_EMAIL)) {
try {
Mail::html($body, function ($message) use ($email, $subject) {
$message->to($email)->subject($subject);
});
Mail::to($email)->send(new TeacherSubmissionReminderMail($subject, $body));
$status = 'sent';
} catch (\Throwable $e) {
Log::error('Teacher submission notification failed: ' . $e->getMessage());
@@ -158,4 +157,4 @@ class TeacherSubmissionNotificationService
return array_values($targets);
}
}
}
@@ -163,7 +163,7 @@ class TeacherSubmissionReportService
->where('class_section_id', $classSectionId)
->where('semester', $semester)
->where('school_year', $schoolYear)
->where('date', $today)
->whereDate('date', $today)
->first();
$attendanceSubmitted = $attendanceRow
@@ -187,7 +187,7 @@ class TeacherSubmissionReportService
$midtermCommentStatus = $this->support->submissionStatus(count($midtermCommentStudents), $expected);
$participationStatus = $this->support->submissionStatus(count($participationStudents), $expected);
$ptapCommentStatus = $this->support->submissionStatus(count($ptapCommentStudents), $expected);
$attendanceStatus = $this->support->attendanceStatus($attendanceSubmitted);
$attendanceStatus = $this->support->attendanceStatus($attendanceSubmitted, $expected);
$statusDetails = [
'midterm_score_status' => $midtermScoreStatus,
@@ -282,4 +282,4 @@ class TeacherSubmissionReportService
return $historyMap;
}
}
}
@@ -25,8 +25,16 @@ class TeacherSubmissionSupportService
];
}
public function attendanceStatus(bool $submitted): array
public function attendanceStatus(bool $submitted, int $expected): array
{
if ($expected <= 0) {
return [
'label' => 'No students',
'badge' => 'bg-secondary',
'completed' => true,
];
}
return [
'label' => $submitted ? 'Submitted' : 'Missing',
'badge' => $submitted ? 'bg-success' : 'bg-danger',
@@ -115,4 +123,4 @@ class TeacherSubmissionSupportService
$last = array_pop($items);
return implode(', ', $items) . ' and ' . $last;
}
}
}
@@ -20,7 +20,14 @@ class AssignmentDataLoaderService
->with([
'teacher:id,firstname,lastname',
])
->when(filled($schoolYear), fn ($q) => $q->where('school_year', $schoolYear))
->when(
filled($schoolYear),
fn ($q) => $q->where(function ($query) use ($schoolYear) {
$query->where('school_year', $schoolYear)
->orWhereNull('school_year')
->orWhere('school_year', '');
})
)
->get()
->map(function ($row) {
$row->teacher_full_name = trim(
@@ -40,7 +47,14 @@ class AssignmentDataLoaderService
'student:id,firstname,lastname,age,gender,registration_grade,photo_consent,tuition_paid,school_id,is_active',
])
->whereHas('student', fn ($q) => $q->where('is_active', 1))
->when(filled($schoolYear), fn ($q) => $q->where('school_year', $schoolYear))
->when(
filled($schoolYear),
fn ($q) => $q->where(function ($query) use ($schoolYear) {
$query->where('school_year', $schoolYear)
->orWhereNull('school_year')
->orWhere('school_year', '');
})
)
->get();
}
}
@@ -16,11 +16,11 @@ class AssignmentSectionService
return $this->classSection
->newQuery()
->whereIn('id', $sectionIds)
->whereIn('class_section_id', $sectionIds)
->get()
->mapWithKeys(function ($section) {
$name = $section->class_section_name ?? $section->section_name ?? $section->name ?? '';
return [(int) $section->id => (string) $name];
return [(int) $section->class_section_id => (string) $name];
})
->all();
}
+9
View File
@@ -0,0 +1,9 @@
<?php
namespace App\Services;
use App\Services\Assignment\AssignmentService as AssignmentDomainService;
class AssignmentService extends AssignmentDomainService
{
}
@@ -55,21 +55,17 @@ class AttendanceNotificationLogService
?string $schoolYear = null
): void {
try {
$where = [
'student_id' => $studentId,
'code' => $code,
'incident_date' => $ymd,
'channel' => $channel,
];
$existingQuery = $this->notificationModel->query()
->where('student_id', $studentId)
->where('code', $code)
->whereDate('incident_date', $ymd)
->where('channel', $channel);
if (!empty($to)) {
$where['to_address'] = $to;
$existingQuery->where('to_address', $to);
}
$existing = $this->notificationModel->query()
->where($where)
->orderByDesc('id')
->first();
$existing = $existingQuery->orderByDesc('id')->first();
if ($existing && !empty($existing->id)) {
$existing->update([
@@ -98,4 +94,4 @@ class AttendanceNotificationLogService
Log::debug('logNotification(): ' . $e->getMessage());
}
}
}
}
@@ -29,7 +29,7 @@ class AttendanceViolationStudentResolverService
}
$classStudents = $classStudentsQuery
->where('school_year', $schoolYear)
->where('student_class.school_year', $schoolYear)
->get()
->toArray();
@@ -53,7 +53,7 @@ class AttendanceViolationStudentResolverService
}
$classStudents = $classStudentsQuery
->where('school_year', $schoolYear)
->where('student_class.school_year', $schoolYear)
->get()
->toArray();
@@ -368,4 +368,4 @@ class AttendanceViolationStudentResolverService
return [$studentCodeToId, $existingIds];
}
}
}
+2 -2
View File
@@ -25,8 +25,8 @@ class BadgeTextFormatter
{
$s = (string) $s;
$s = str_replace(["\xC2\xA0", "\xA0"], ' ', $s);
$s = preg_replace('/\s+/u', ' ', $s) ?? $s;
$s = preg_replace('/[\x00-\x08\x0B-\x0C\x0E-\x1F\x7F]/u', '', $s) ?? $s;
$s = preg_replace('/\s+/u', ' ', $s) ?? $s;
return trim($s);
}
@@ -203,4 +203,4 @@ class BadgeTextFormatter
return null;
}
}
}
@@ -6,7 +6,7 @@ class BroadcastEmailSenderOptionsService
{
public function listOptions(): array
{
$json = env('MAIL_SENDERS', '{}');
$json = getenv('MAIL_SENDERS') ?: env('MAIL_SENDERS', '{}');
$arr = json_decode($json, true);
if (!is_array($arr) || empty($arr)) {
@@ -91,7 +91,7 @@ class ClassPreparationCalculatorService
$qty = $isLowerGrades ? 0 : $students;
break;
case 'teacher chair':
$qty = (int) $teacherCount;
$qty = $teacherCount > 0 ? (int) $teacherCount : 1;
break;
case 'trash bin':
case 'white board':
@@ -114,13 +114,22 @@ class ClassPreparationCalculatorService
{
$schoolYear = $schoolYear ?: (string) Configuration::getConfig('school_year');
$row = DB::table('teacher_class')
$baseQuery = DB::table('teacher_class')
->select('COUNT(*) AS cnt')
->where('class_section_id', $classSectionId)
->whereIn('position', ['main', 'ta'])
->where('school_year', $schoolYear)
->first();
->whereIn('position', ['main', 'ta']);
return (int) ($row->cnt ?? 0);
$row = $schoolYear !== null && $schoolYear !== ''
? (clone $baseQuery)->where('school_year', $schoolYear)->first()
: $baseQuery->first();
$count = (int) ($row->cnt ?? 0);
if ($count === 0 && $schoolYear !== null && $schoolYear !== '') {
$row = $baseQuery->first();
$count = (int) ($row->cnt ?? 0);
}
return $count;
}
}
@@ -11,7 +11,7 @@ class ClassPreparationInventoryService
$inventoryMap = array_fill_keys($allowed, 0);
$joinRows = DB::table('inventory_items as ii')
->select('ic.name AS item_name, COALESCE(SUM(CASE WHEN ii.good_qty IS NOT NULL THEN ii.good_qty WHEN ii.`condition`=\"good\" THEN ii.quantity ELSE 0 END),0) AS available')
->select('ic.name AS item_name, COALESCE(SUM(CASE WHEN ii.good_qty IS NOT NULL AND ii.good_qty > 0 THEN ii.good_qty WHEN ii.`condition`=\"good\" THEN ii.quantity ELSE ii.quantity END),0) AS available')
->join('inventory_categories as ic', 'ic.id', '=', 'ii.category_id')
->where('ii.type', 'classroom')
->where('ii.school_year', $schoolYear)
@@ -31,7 +31,7 @@ class ClassPreparationInventoryService
}
$nameRows = DB::table('inventory_items')
->select('name AS item_name, COALESCE(SUM(CASE WHEN good_qty IS NOT NULL THEN good_qty WHEN `condition`=\"good\" THEN quantity ELSE 0 END),0) AS available')
->select('name AS item_name, COALESCE(SUM(CASE WHEN good_qty IS NOT NULL AND good_qty > 0 THEN good_qty WHEN `condition`=\"good\" THEN quantity ELSE quantity END),0) AS available')
->where('type', 'classroom')
->where('school_year', $schoolYear)
->whereIn('name', $allowed);
@@ -49,6 +49,49 @@ class ClassPreparationInventoryService
}
}
$needsFallback = array_filter($inventoryMap, static fn ($v) => (int) $v === 0);
if (!empty($needsFallback)) {
$fallbackRows = DB::table('inventory_items')
->select('name AS item_name, COALESCE(SUM(CASE WHEN good_qty IS NOT NULL AND good_qty > 0 THEN good_qty WHEN `condition`=\"good\" THEN quantity ELSE quantity END),0) AS available')
->where('type', 'classroom')
->whereIn('name', array_keys($needsFallback))
->groupBy('name')
->get()
->toArray();
foreach ($fallbackRows as $r) {
$name = (string) ($r->item_name ?? '');
if ($name !== '' && isset($inventoryMap[$name])) {
$inventoryMap[$name] = max($inventoryMap[$name], (int) ($r->available ?? 0));
}
}
}
foreach ($inventoryMap as $name => $value) {
if ((int) $value !== 0) {
continue;
}
$goodQty = (int) DB::table('inventory_items')
->where('type', 'classroom')
->where('name', $name)
->sum('good_qty');
if ($goodQty > 0) {
$inventoryMap[$name] = $goodQty;
continue;
}
$qty = (int) DB::table('inventory_items')
->where('type', 'classroom')
->where('name', $name)
->sum('quantity');
if ($qty > 0) {
$inventoryMap[$name] = $qty;
}
}
return $inventoryMap;
}
}
@@ -9,7 +9,7 @@ class ClassPreparationRosterService
public function getClassSectionStudentCounts(string $schoolYear, string $semester, bool $limitToSemester): array
{
$query = DB::table('student_class as sc')
->select('sc.class_section_id, COUNT(DISTINCT sc.student_id) AS student_count', false)
->selectRaw('sc.class_section_id, COUNT(DISTINCT sc.student_id) AS student_count')
->join('students as s', 's.id', '=', 'sc.student_id')
->where('s.is_active', 1)
->where('sc.school_year', $schoolYear);
@@ -24,7 +24,7 @@ class ClassPreparationRosterService
public function getStudentCountForSection(string $schoolYear, string $semester, bool $limitToSemester, string $classSectionId): int
{
$query = DB::table('student_class as sc')
->select('COUNT(DISTINCT sc.student_id) AS cnt', false)
->selectRaw('COUNT(DISTINCT sc.student_id) AS cnt')
->join('students as s', 's.id', '=', 'sc.student_id')
->where('s.is_active', 1)
->where('sc.class_section_id', $classSectionId)
@@ -50,7 +50,7 @@ class CompetitionScoresSaveService
->where('competition_id', $competitionId)
->where('class_section_id', $classSectionId)
->get()
->map(fn ($row) => (array) $row)
->map(fn ($row) => $row->toArray())
->all();
$map = [];
+19
View File
@@ -0,0 +1,19 @@
<?php
namespace App\Services;
use App\Services\Fees\FeeRefundCalculatorService;
class FeeCalculationService
{
public function __construct(
private FeeRefundCalculatorService $refundCalculator
) {
}
public function calculateRefund(array $students, int $parentId): float
{
$result = $this->refundCalculator->calculateRefund($students, $parentId);
return (float) ($result['refund_amount'] ?? 0);
}
}
+10 -6
View File
@@ -6,20 +6,24 @@ class FeeGradeService
{
public function normalizeGrade(?string $grade): string
{
return strtoupper(trim((string) $grade));
$normalized = strtoupper(trim((string) $grade));
return str_replace([' ', '-'], '', $normalized);
}
public function compareGrades(string $gradeA, string $gradeB): int
{
$valA = $this->getGradeLevel($gradeA);
$valB = $this->getGradeLevel($gradeB);
$normA = $this->normalizeGrade($gradeA);
$normB = $this->normalizeGrade($gradeB);
$valA = $this->getGradeLevel($normA);
$valB = $this->getGradeLevel($normB);
if ($valA !== $valB) {
return $valA <=> $valB;
}
preg_match('/\d+([A-Z]*)$/i', strtoupper($gradeA), $suffixA);
preg_match('/\d+([A-Z]*)$/i', strtoupper($gradeB), $suffixB);
preg_match('/\d+([A-Z]*)$/i', $normA, $suffixA);
preg_match('/\d+([A-Z]*)$/i', $normB, $suffixB);
return strcmp($suffixA[1] ?? '', $suffixB[1] ?? '');
}
@@ -35,7 +39,7 @@ class FeeGradeService
return 99;
}
if (preg_match('/^(\d+)([A-Z]*)$/i', $grade, $matches)) {
if (preg_match('/^(\d+)/', $grade, $matches)) {
return (int) $matches[1];
}
@@ -97,7 +97,7 @@ class FinancialSummaryService
$query = DB::table('additional_charges as ac')
->selectRaw('COALESCE(SUM(ac.amount), 0) AS amount')
->where('ac.school_year', $schoolYear)
->where('ac.status !=', 'void');
->where('ac.status', '!=', 'void');
if (!empty($dateFrom)) {
$query->whereRaw('DATE(ac.due_date) >= ?', [$dateFrom]);
@@ -115,11 +115,11 @@ class FinancialSummaryService
$query = DB::table('additional_charges as ac')
->selectRaw('ac.parent_id, COALESCE(SUM(ac.amount), 0) AS amount')
->where('ac.school_year', $schoolYear)
->where('ac.status !=', 'void')
->where('ac.status', '!=', 'void')
->where(function ($q) {
$q->whereNull('ac.invoice_id')
->orWhere('ac.invoice_id', 0)
->orWhere('ac.status !=', 'applied');
->orWhere('ac.status', '!=', 'applied');
})
->groupBy('ac.parent_id');
@@ -96,17 +96,14 @@ class FinancialUnpaidParentsService
}
$extraRows = DB::table('additional_charges as ac')
->selectRaw(
"ac.parent_id, u.firstname, u.lastname, u.email, COALESCE(SUM(ac.amount), 0) AS total_extra",
false
)
->selectRaw("ac.parent_id, u.firstname, u.lastname, u.email, COALESCE(SUM(ac.amount), 0) AS total_extra")
->leftJoin('users as u', 'u.id', '=', 'ac.parent_id')
->where('ac.school_year', $schoolYear)
->where('ac.status !=', 'void')
->where('ac.status', '!=', 'void')
->where(function ($q) {
$q->whereNull('ac.invoice_id')
->orWhere('ac.invoice_id', 0)
->orWhere('ac.status !=', 'applied');
->orWhere('ac.status', '!=', 'applied');
})
->groupBy('ac.parent_id')
->get()
@@ -167,7 +167,7 @@ class HomeworkTrackingService
->whereIn('tc.position', ['main', 'ta'])
->orderBy('cs.class_id', 'ASC')
->orderBy('cs.class_section_name', 'ASC')
->orderByRaw("FIELD(tc.position, 'main','ta')")
->orderByRaw("CASE tc.position WHEN 'main' THEN 0 WHEN 'ta' THEN 1 ELSE 2 END")
->orderBy('u.firstname', 'ASC')
->get();
@@ -65,13 +65,13 @@ class IncidentAnalysisService
}
if ($students[$studentKey]['last_incident'] === null) {
$students[$studentKey]['last_incident'] = $incident['incident_datetime'] ?? null;
$students[$studentKey]['last_incident'] = $this->formatIncidentDate($incident['incident_datetime'] ?? null);
}
$students[$studentKey]['logs'][] = [
'incident' => $incident['incident'] ?? '',
'incident_state' => $state,
'incident_datetime' => $incident['incident_datetime'] ?? null,
'incident_datetime' => $this->formatIncidentDate($incident['incident_datetime'] ?? null),
'open_description' => $incident['open_description'] ?? null,
'close_description' => $incident['close_description'] ?? null,
'cancel_description' => $incident['cancel_description'] ?? null,
@@ -89,4 +89,23 @@ class IncidentAnalysisService
return $studentRows;
}
private function formatIncidentDate(mixed $value): ?string
{
if ($value === null || $value === '') {
return null;
}
if ($value instanceof \DateTimeInterface) {
return $value->format('Y-m-d H:i:s');
}
$raw = (string) $value;
$ts = strtotime($raw);
if ($ts !== false) {
return date('Y-m-d H:i:s', $ts);
}
return $raw;
}
}
@@ -69,16 +69,20 @@ class IncidentLookupService
}
$rows = DB::table('users')
->select('id, firstname, lastname')
->select('id', 'firstname', 'lastname')
->whereIn('id', $userIds)
->get()
->map(fn ($row) => (array) $row)
->map(fn ($row) => $row instanceof \Illuminate\Contracts\Support\Arrayable ? $row->toArray() : (array) $row)
->all();
$map = [];
foreach ($rows as $row) {
$id = $row['id'] ?? $row['user_id'] ?? null;
if ($id === null) {
continue;
}
$name = trim((string) ($row['firstname'] ?? '') . ' ' . (string) ($row['lastname'] ?? ''));
$map[(int) $row['id']] = $name !== '' ? $name : ('User #' . $row['id']);
$map[(int) $id] = $name !== '' ? $name : ('User #' . $id);
}
return $map;
@@ -30,7 +30,7 @@ class InvoiceGenerationService
->where('parent_id', $parentId)
->where('school_year', $schoolYear)
->get()
->map(fn ($r) => (array) $r)
->map(fn ($r) => $r->toArray())
->all();
if (empty($enrollments)) {
@@ -40,7 +40,11 @@ class InvoiceManagementService
$parents = User::getUsersByRoleAndSchoolYear('parent', $schoolYear);
foreach ($parents as $parent) {
$students = Student::query()->where('parent_id', $parent['id'])->get()->map(fn ($r) => (array) $r)->all();
$students = Student::query()
->where('parent_id', $parent['id'])
->get()
->map(fn ($r) => $r->toArray())
->all();
$parentData = [
'parent_name' => trim(($parent['firstname'] ?? '') . ' ' . ($parent['lastname'] ?? '')),
@@ -106,7 +110,7 @@ class InvoiceManagementService
->where('student_id', $student['id'])
->where('school_year', $schoolYear)
->get()
->map(fn ($r) => (array) $r)
->map(fn ($r) => $r->toArray())
->all();
foreach ($enrollments as $enrollment) {
+5
View File
@@ -94,6 +94,10 @@ class ExamScoreService
if (!is_numeric($studentId)) {
continue;
}
$student = Student::query()->find((int) $studentId);
if (!$student) {
continue;
}
$rawScore = $data['score'] ?? null;
$normalizedScore = is_numeric($rawScore) ? (float) $rawScore : null;
@@ -106,6 +110,7 @@ class ExamScoreService
$payload = [
'student_id' => (int) $studentId,
'school_id' => $student->school_id,
'class_section_id' => $classSectionId,
'updated_by' => $updatedBy,
'score' => $normalizedScore,
+8 -2
View File
@@ -57,12 +57,13 @@ class HomeworkScoreService
->whereIn('semester', $semVariants)
->where('school_year', $schoolYear)
->get()
->map(fn ($row) => (array) $row)
->map(fn ($row) => $row->toArray())
->all();
$scoresMap = [];
foreach ($scoresRows as $row) {
$scoresMap[(int) $row['student_id']][(int) $row['homework_index']] = $row['score'];
$score = $row['score'] ?? null;
$scoresMap[(int) $row['student_id']][(int) $row['homework_index']] = is_numeric($score) ? (float) $score : $score;
}
foreach ($students as &$student) {
@@ -196,6 +197,10 @@ class HomeworkScoreService
->get();
foreach ($students as $student) {
$studentRow = Student::query()->find((int) $student->student_id);
if (!$studentRow) {
continue;
}
$exists = Homework::query()
->where('student_id', $student->student_id)
->where('homework_index', $nextIndex)
@@ -210,6 +215,7 @@ class HomeworkScoreService
Homework::query()->create([
'student_id' => (int) $student->student_id,
'school_id' => $studentRow->school_id,
'class_section_id' => $classSectionId,
'updated_by' => $updatedBy,
'homework_index' => $nextIndex,
@@ -50,12 +50,14 @@ class ParticipationScoreService
$scoreMap = [];
foreach ($scoresRows as $row) {
$scoreMap[(int) $row->student_id] = $row->score;
$score = $row->score;
$scoreMap[(int) $row->student_id] = is_numeric($score) ? (float) $score : $score;
}
foreach ($students as &$student) {
$score = $scoreMap[$student['student_id']] ?? '';
$student['scores'] = [
'score' => $scoreMap[$student['student_id']] ?? '',
'score' => $score,
];
}
unset($student);
+8 -2
View File
@@ -57,12 +57,13 @@ class ProjectScoreService
->where('semester', $semester)
->where('school_year', $schoolYear)
->get()
->map(fn ($row) => (array) $row)
->map(fn ($row) => $row->toArray())
->all();
$scoresMap = [];
foreach ($scoresRows as $row) {
$scoresMap[(int) $row['student_id']][(int) $row['project_index']] = $row['score'];
$score = $row['score'] ?? null;
$scoresMap[(int) $row['student_id']][(int) $row['project_index']] = is_numeric($score) ? (float) $score : $score;
}
foreach ($students as &$student) {
@@ -193,6 +194,10 @@ class ProjectScoreService
->get();
foreach ($students as $student) {
$studentRow = Student::query()->find((int) $student->student_id);
if (!$studentRow) {
continue;
}
$exists = Project::query()
->where('student_id', $student->student_id)
->where('project_index', $nextIndex)
@@ -207,6 +212,7 @@ class ProjectScoreService
Project::query()->create([
'student_id' => (int) $student->student_id,
'school_id' => $studentRow->school_id,
'class_section_id' => $classSectionId,
'updated_by' => $updatedBy,
'project_index' => $nextIndex,
+13 -2
View File
@@ -56,12 +56,13 @@ class QuizScoreService
->where('semester', $semester)
->where('school_year', $schoolYear)
->get()
->map(fn ($row) => (array) $row)
->map(fn ($row) => $row->toArray())
->all();
$scoresMap = [];
foreach ($scoresRows as $row) {
$scoresMap[(int) $row['student_id']][(int) $row['quiz_index']] = $row['score'];
$score = $row['score'] ?? null;
$scoresMap[(int) $row['student_id']][(int) $row['quiz_index']] = is_numeric($score) ? (float) $score : $score;
}
foreach ($students as &$student) {
@@ -135,6 +136,10 @@ class QuizScoreService
if (!is_array($quizData)) {
$quizData = [1 => $quizData];
}
$student = Student::query()->find((int) $studentId);
if (!$student) {
continue;
}
foreach ($quizData as $quizNumber => $score) {
if (!is_numeric($quizNumber)) {
continue;
@@ -152,6 +157,7 @@ class QuizScoreService
$payload = [
'student_id' => (int) $studentId,
'school_id' => $student->school_id,
'class_section_id' => $classSectionId,
'updated_by' => $updatedBy,
'quiz_index' => (int) $quizNumber,
@@ -190,6 +196,10 @@ class QuizScoreService
->get();
foreach ($students as $student) {
$studentRow = Student::query()->find((int) $student->student_id);
if (!$studentRow) {
continue;
}
$exists = Quiz::query()
->where('student_id', $student->student_id)
->where('quiz_index', $nextIndex)
@@ -204,6 +214,7 @@ class QuizScoreService
Quiz::query()->create([
'student_id' => (int) $student->student_id,
'school_id' => $studentRow->school_id,
'class_section_id' => $classSectionId,
'updated_by' => $updatedBy,
'quiz_index' => $nextIndex,
+17 -8
View File
@@ -111,14 +111,14 @@ class ScoreDashboardService
$sid = $student['student_id'];
$scoreRow = $scores[$sid] ?? null;
$student['scores'] = $scoreRow ? [
'homework' => $scoreRow->homework_avg,
'quiz' => $scoreRow->quiz_avg,
'project' => $scoreRow->project_avg,
'midterm_exam' => $scoreRow->midterm_exam_score,
'final_exam' => $scoreRow->final_exam_score,
'attendance' => $scoreRow->attendance_score,
'ptap' => $scoreRow->ptap_score,
'semester' => $scoreRow->semester_score,
'homework' => $this->normalizeScore($scoreRow->homework_avg),
'quiz' => $this->normalizeScore($scoreRow->quiz_avg),
'project' => $this->normalizeScore($scoreRow->project_avg),
'midterm_exam' => $this->normalizeScore($scoreRow->midterm_exam_score),
'final_exam' => $this->normalizeScore($scoreRow->final_exam_score),
'attendance' => $this->normalizeScore($scoreRow->attendance_score),
'ptap' => $this->normalizeScore($scoreRow->ptap_score),
'semester' => $this->normalizeScore($scoreRow->semester_score),
] : [];
$student['comments'] = $commentMap[$sid] ?? [];
}
@@ -267,4 +267,13 @@ class ScoreDashboardService
'selectedYear' => $schoolYear,
];
}
private function normalizeScore(mixed $value): mixed
{
if ($value === null || $value === '') {
return $value;
}
return is_numeric($value) ? (float) $value : $value;
}
}
+1 -1
View File
@@ -73,7 +73,7 @@ class UserManagementService
try {
DB::transaction(function () use ($userId, $user) {
UserRole::query()->where('user_id', $userId)->delete();
UserRole::query()->where('user_id', $userId)->forceDelete();
$user->delete();
});
} catch (\Throwable $e) {

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