update tests
This commit is contained in:
@@ -12,6 +12,7 @@ use App\Services\Auth\AuthSessionService;
|
||||
use Illuminate\Http\JsonResponse;
|
||||
use Illuminate\Http\Request;
|
||||
use Illuminate\Support\Facades\Auth;
|
||||
use PHPOpenSourceSaver\JWTAuth\Facades\JWTAuth;
|
||||
|
||||
/**
|
||||
* Session (cookie) endpoints for SPA — JSON alongside JWT on /api/login.
|
||||
@@ -141,12 +142,13 @@ class AuthSessionController extends Controller
|
||||
], 403);
|
||||
}
|
||||
|
||||
return response()->json([
|
||||
'status' => true,
|
||||
$loginPayload = $this->security->buildLoginResponse($fresh, JWTAuth::fromUser($fresh));
|
||||
|
||||
return response()->json(array_merge($loginPayload, [
|
||||
'requires_role_selection' => ($dest['kind'] ?? '') === 'select_role',
|
||||
'roles' => $dest['roles'] ?? null,
|
||||
'next_url' => $dest['redirect_url'] ?? $this->urls->docsHomeUrl(false),
|
||||
]);
|
||||
]));
|
||||
}
|
||||
|
||||
/** Closes the current web session. */
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
namespace App\Models;
|
||||
|
||||
use App\Models\BaseModel;
|
||||
use Illuminate\Database\Eloquent\Casts\Attribute;
|
||||
use Illuminate\Database\Eloquent\SoftDeletes;
|
||||
|
||||
class Notification extends BaseModel
|
||||
@@ -28,12 +29,40 @@ class Notification extends BaseModel
|
||||
];
|
||||
|
||||
protected $casts = [
|
||||
'scheduled_at' => 'datetime',
|
||||
'expires_at' => 'datetime',
|
||||
// If delivery_channels is JSON in DB:
|
||||
'delivery_channels' => 'array',
|
||||
'scheduled_at' => 'datetime',
|
||||
'expires_at' => 'datetime',
|
||||
];
|
||||
|
||||
/**
|
||||
* MySQL stores channels as SET('in_app','email','sms'); expose as array in PHP.
|
||||
*/
|
||||
protected function deliveryChannels(): Attribute
|
||||
{
|
||||
return Attribute::make(
|
||||
get: static function (?string $value): array {
|
||||
if ($value === null || $value === '') {
|
||||
return [];
|
||||
}
|
||||
|
||||
$trimmed = trim($value);
|
||||
if (str_starts_with($trimmed, '[')) {
|
||||
$decoded = json_decode($trimmed, true);
|
||||
|
||||
return is_array($decoded) ? array_values($decoded) : [];
|
||||
}
|
||||
|
||||
return array_values(array_filter(array_map('trim', explode(',', $value))));
|
||||
},
|
||||
set: static function ($value): string {
|
||||
if (is_array($value)) {
|
||||
return implode(',', array_values(array_filter(array_map('strval', $value))));
|
||||
}
|
||||
|
||||
return (string) $value;
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Equivalent of legacy getActiveNotifications()
|
||||
* Active = scheduled_at <= now AND (expires_at is null OR expires_at > now)
|
||||
|
||||
@@ -9,8 +9,8 @@ class PaymentTransaction extends BaseModel
|
||||
{
|
||||
protected $table = 'payment_transactions';
|
||||
|
||||
// ✅ legacy: useTimestamps = true (created_at/updated_at)
|
||||
public $timestamps = true;
|
||||
// Legacy table has no created_at/updated_at columns.
|
||||
public $timestamps = false;
|
||||
|
||||
protected $fillable = [
|
||||
'transaction_id',
|
||||
|
||||
@@ -13,7 +13,7 @@ class StudentAllergy extends BaseModel
|
||||
/**
|
||||
* legacy: timestamps disabled
|
||||
*/
|
||||
public $timestamps = true;
|
||||
public $timestamps = false;
|
||||
|
||||
protected $fillable = [
|
||||
'student_id',
|
||||
|
||||
@@ -13,7 +13,7 @@ class StudentMedicalCondition extends BaseModel
|
||||
/**
|
||||
* legacy: timestamps disabled
|
||||
*/
|
||||
public $timestamps = true;
|
||||
public $timestamps = false;
|
||||
|
||||
protected $fillable = [
|
||||
'student_id',
|
||||
|
||||
@@ -9,6 +9,7 @@ use App\Models\TeacherClass;
|
||||
use Carbon\Carbon;
|
||||
use Illuminate\Contracts\Auth\Authenticatable;
|
||||
use Illuminate\Support\Facades\DB;
|
||||
use Illuminate\Support\Facades\Schema;
|
||||
use RuntimeException;
|
||||
|
||||
class TeacherAttendanceSubmissionService
|
||||
@@ -171,6 +172,20 @@ class TeacherAttendanceSubmissionService
|
||||
$position = strtolower((string)($teacher['position'] ?? 'main'));
|
||||
$position = $position === 'ta' ? 'ta' : 'main';
|
||||
|
||||
$staffPayload = [
|
||||
'role_name' => 'Teacher',
|
||||
'status' => strtolower((string)($teachersData[$teacherId]['status'] ?? 'present')),
|
||||
'reason' => ($teachersData[$teacherId]['reason'] ?? null) ?: null,
|
||||
'updated_at' => now(),
|
||||
'created_at' => now(),
|
||||
];
|
||||
if (Schema::hasColumn('staff_attendance', 'class_section_id')) {
|
||||
$staffPayload['class_section_id'] = $classSectionId;
|
||||
}
|
||||
if (Schema::hasColumn('staff_attendance', 'position')) {
|
||||
$staffPayload['position'] = $position;
|
||||
}
|
||||
|
||||
DB::table('staff_attendance')->updateOrInsert(
|
||||
[
|
||||
'user_id' => $teacherId,
|
||||
@@ -178,15 +193,7 @@ class TeacherAttendanceSubmissionService
|
||||
'semester' => $semester,
|
||||
'school_year' => $schoolYear,
|
||||
],
|
||||
[
|
||||
'role_name' => 'Teacher',
|
||||
'class_section_id' => $classSectionId,
|
||||
'position' => $position,
|
||||
'status' => strtolower((string)($teachersData[$teacherId]['status'] ?? 'present')),
|
||||
'reason' => ($teachersData[$teacherId]['reason'] ?? null) ?: null,
|
||||
'updated_at' => now(),
|
||||
'created_at' => now(),
|
||||
]
|
||||
$staffPayload
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
@@ -129,7 +129,7 @@ class ApiLoginSecurityService
|
||||
/**
|
||||
* Top-level JSON body for API login success responses.
|
||||
*
|
||||
* @return array{status: true, token: string, user: array{id: int, name: string, roles: object, class_section_id: ?int, class_section_name: ?string}}
|
||||
* @return array{status: true, token: string, access_token: string, token_type: string, expires_in: int, user: array{id: int, name: string, firstname: ?string, lastname: ?string, email: ?string, roles: object, class_section_id: ?int, class_section_name: ?string}}
|
||||
*/
|
||||
public function buildLoginResponse(User $user, string $jwtToken): array
|
||||
{
|
||||
@@ -139,10 +139,19 @@ class ApiLoginSecurityService
|
||||
|
||||
return [
|
||||
'status' => true,
|
||||
// Keep both names. Older clients read `token`; JWT-aware clients often read
|
||||
// `access_token`. Removing either one just makes login fail in a new and
|
||||
// irritatingly avoidable way.
|
||||
'token' => $jwtToken,
|
||||
'access_token' => $jwtToken,
|
||||
'token_type' => 'bearer',
|
||||
'expires_in' => (int) config('jwt.ttl') * 60,
|
||||
'user' => [
|
||||
'id' => (int) $user->id,
|
||||
'name' => $fullName,
|
||||
'firstname' => $user->firstname,
|
||||
'lastname' => $user->lastname,
|
||||
'email' => $user->email,
|
||||
'roles' => $this->rolesToObjectMap($roleNames),
|
||||
'class_section_id' => $teacherContext['class_section_id'],
|
||||
'class_section_name' => $teacherContext['class_section_name'],
|
||||
|
||||
@@ -15,17 +15,17 @@ class ParentAttendanceReportCalendarService
|
||||
*/
|
||||
public function computeSundays(int $weeksBefore = 4, int $weeksAfter = 26): array
|
||||
{
|
||||
$today = new \DateTime('today');
|
||||
$today = now()->startOfDay();
|
||||
$dow = (int) $today->format('w');
|
||||
|
||||
$thisSunday = clone $today;
|
||||
$thisSunday = $today->copy();
|
||||
if ($dow !== 0) {
|
||||
$thisSunday->modify('last sunday');
|
||||
}
|
||||
|
||||
$default = clone $today;
|
||||
$default = $today->copy();
|
||||
if ($dow !== 0) {
|
||||
$default->modify('next sunday');
|
||||
$default = $default->copy()->modify('next sunday');
|
||||
}
|
||||
|
||||
$schoolYear = (string) ($this->configService->context()['school_year'] ?? '');
|
||||
@@ -33,13 +33,13 @@ class ParentAttendanceReportCalendarService
|
||||
|
||||
$dates = [];
|
||||
for ($i = $weeksBefore; $i >= 1; $i--) {
|
||||
$dates[] = (clone $thisSunday)->modify('-' . $i . ' week');
|
||||
$dates[] = $thisSunday->copy()->modify('-' . $i . ' week');
|
||||
}
|
||||
|
||||
$cursor = clone $thisSunday;
|
||||
$cursor = $thisSunday->copy();
|
||||
while ($cursor <= $cap) {
|
||||
$dates[] = clone $cursor;
|
||||
$cursor->modify('+1 week');
|
||||
$dates[] = $cursor->copy();
|
||||
$cursor = $cursor->copy()->modify('+1 week');
|
||||
}
|
||||
|
||||
$rangeStart = reset($dates);
|
||||
@@ -108,7 +108,7 @@ class ParentAttendanceReportCalendarService
|
||||
|
||||
public function firstSundayOfJune(string $schoolYear): \DateTime
|
||||
{
|
||||
$today = new \DateTime('today');
|
||||
$today = now()->startOfDay();
|
||||
$endYear = null;
|
||||
if (preg_match('/^(\\d{4})\\D(\\d{4})$/', $schoolYear, $m)) {
|
||||
$endYear = (int) $m[2];
|
||||
@@ -118,10 +118,11 @@ class ParentAttendanceReportCalendarService
|
||||
$endYear = ((int) $today->format('n') > 6) ? ($y + 1) : $y;
|
||||
}
|
||||
|
||||
$juneFirst = new \DateTime(sprintf('%04d-06-01', $endYear));
|
||||
$juneFirst = now()->parse(sprintf('%04d-06-01', $endYear))->startOfDay();
|
||||
if ((int) $juneFirst->format('w') !== 0) {
|
||||
$juneFirst->modify('next sunday');
|
||||
$juneFirst = $juneFirst->copy()->modify('next sunday');
|
||||
}
|
||||
|
||||
return $juneFirst;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -82,6 +82,7 @@ class ParentRegistrationService
|
||||
$emergencyContacts,
|
||||
$schoolYear,
|
||||
$semester,
|
||||
$ageReference,
|
||||
&$createdStudentIds,
|
||||
&$skippedStudents
|
||||
) {
|
||||
|
||||
@@ -16,7 +16,7 @@ class PaymentNotificationDispatchService
|
||||
$notification = Notification::query()->create([
|
||||
'title' => $title,
|
||||
'message' => $message,
|
||||
'target_group' => 'user',
|
||||
'target_group' => 'everyone',
|
||||
'delivery_channels' => $channels,
|
||||
'priority' => 'normal',
|
||||
'status' => 'sent',
|
||||
|
||||
@@ -67,7 +67,9 @@ class SemesterRangeService
|
||||
$lastDay = $this->configService->getLastSchoolDay();
|
||||
|
||||
try {
|
||||
$target = new DateTimeImmutable($date ?: 'now');
|
||||
$target = ($date !== null && $date !== '')
|
||||
? new DateTimeImmutable($date)
|
||||
: DateTimeImmutable::createFromInterface(now());
|
||||
} catch (\Throwable) {
|
||||
return '';
|
||||
}
|
||||
|
||||
@@ -96,7 +96,7 @@ class TeacherDashboardService
|
||||
->whereIn('tc.class_section_id', $classSectionIds)
|
||||
->where('tc.school_year', $schoolYear)
|
||||
->orderBy('tc.class_section_id')
|
||||
->orderByRaw("FIELD(tc.position,'main','ta')")
|
||||
->orderByRaw("CASE tc.position WHEN 'main' THEN 0 WHEN 'ta' THEN 1 ELSE 2 END")
|
||||
->orderBy('u.firstname')
|
||||
->get();
|
||||
|
||||
|
||||
Reference in New Issue
Block a user