update the new school year model
This commit is contained in:
@@ -4,6 +4,7 @@ namespace App\Http\Controllers\Api\BadgeScan;
|
|||||||
|
|
||||||
use App\Http\Controllers\Api\Core\BaseApiController;
|
use App\Http\Controllers\Api\Core\BaseApiController;
|
||||||
use App\Services\BadgeScan\BadgeScanService;
|
use App\Services\BadgeScan\BadgeScanService;
|
||||||
|
use App\Services\SchoolYears\SchoolYearContextService;
|
||||||
use Illuminate\Http\JsonResponse;
|
use Illuminate\Http\JsonResponse;
|
||||||
use Illuminate\Http\Request;
|
use Illuminate\Http\Request;
|
||||||
use Illuminate\Support\Facades\Validator;
|
use Illuminate\Support\Facades\Validator;
|
||||||
@@ -13,6 +14,7 @@ class BadgeScanController extends BaseApiController
|
|||||||
{
|
{
|
||||||
public function __construct(
|
public function __construct(
|
||||||
private BadgeScanService $badgeScan,
|
private BadgeScanService $badgeScan,
|
||||||
|
private SchoolYearContextService $schoolYears,
|
||||||
) {
|
) {
|
||||||
parent::__construct();
|
parent::__construct();
|
||||||
}
|
}
|
||||||
@@ -24,13 +26,21 @@ class BadgeScanController extends BaseApiController
|
|||||||
{
|
{
|
||||||
$validator = Validator::make($request->all(), [
|
$validator = Validator::make($request->all(), [
|
||||||
'badge_scan' => ['required', 'string', 'max:255'],
|
'badge_scan' => ['required', 'string', 'max:255'],
|
||||||
|
'school_year' => ['nullable', 'string', 'max:50'],
|
||||||
|
'semester' => ['nullable', 'string', 'max:50'],
|
||||||
]);
|
]);
|
||||||
|
|
||||||
if ($validator->fails()) {
|
if ($validator->fails()) {
|
||||||
return $this->respondValidationError($validator->errors()->toArray());
|
return $this->respondValidationError($validator->errors()->toArray());
|
||||||
}
|
}
|
||||||
|
|
||||||
$result = $this->badgeScan->processScan($validator->validated()['badge_scan']);
|
$payload = $validator->validated();
|
||||||
|
$context = $this->schoolYears->options($payload['school_year'] ?? null, $payload['semester'] ?? null);
|
||||||
|
$result = $this->badgeScan->processScan(
|
||||||
|
$payload['badge_scan'],
|
||||||
|
$context['school_year'] ?? null,
|
||||||
|
$context['semester'] ?? null
|
||||||
|
);
|
||||||
|
|
||||||
if (! $result['recognized']) {
|
if (! $result['recognized']) {
|
||||||
return $this->error($result['message'], Response::HTTP_NOT_FOUND);
|
return $this->error($result['message'], Response::HTTP_NOT_FOUND);
|
||||||
@@ -46,10 +56,19 @@ class BadgeScanController extends BaseApiController
|
|||||||
/**
|
/**
|
||||||
* Authenticated scan log listing (legacy RFIDController::log).
|
* Authenticated scan log listing (legacy RFIDController::log).
|
||||||
*/
|
*/
|
||||||
public function logs(): JsonResponse
|
public function logs(Request $request): JsonResponse
|
||||||
{
|
{
|
||||||
|
$context = $this->schoolYears->options(
|
||||||
|
$request->query('school_year'),
|
||||||
|
$request->query('semester')
|
||||||
|
);
|
||||||
|
|
||||||
return $this->success([
|
return $this->success([
|
||||||
'logs' => $this->badgeScan->scanLogRows(),
|
'logs' => $this->badgeScan->scanLogRows(
|
||||||
|
$context['school_year'] ?? null,
|
||||||
|
$context['semester'] ?? null
|
||||||
|
),
|
||||||
|
'meta' => $context,
|
||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -40,7 +40,9 @@ class ClassProgressController extends BaseApiController
|
|||||||
return $this->respondSuccess(
|
return $this->respondSuccess(
|
||||||
$this->buildMetaPayload(
|
$this->buildMetaPayload(
|
||||||
$request->validated('class_id'),
|
$request->validated('class_id'),
|
||||||
(int) ($request->validated('sunday_count') ?? 12)
|
(int) ($request->validated('sunday_count') ?? 12),
|
||||||
|
$request->query('school_year'),
|
||||||
|
$request->query('semester')
|
||||||
),
|
),
|
||||||
'Progress metadata loaded.'
|
'Progress metadata loaded.'
|
||||||
);
|
);
|
||||||
@@ -66,7 +68,7 @@ class ClassProgressController extends BaseApiController
|
|||||||
fn (array $assignment) => (int) ($assignment['class_id'] ?? 0) === $activeClassId
|
fn (array $assignment) => (int) ($assignment['class_id'] ?? 0) === $activeClassId
|
||||||
) ?? ($assignments[0] ?? []);
|
) ?? ($assignments[0] ?? []);
|
||||||
$sundayCount = (int) ($request->validated('sunday_count') ?? 12);
|
$sundayCount = (int) ($request->validated('sunday_count') ?? 12);
|
||||||
$payload = $this->buildMetaPayload($activeClassId > 0 ? $activeClassId : null, $sundayCount);
|
$payload = $this->buildMetaPayload($activeClassId > 0 ? $activeClassId : null, $sundayCount, $schoolYear, $semester);
|
||||||
$payload['classes'] = $assignments;
|
$payload['classes'] = $assignments;
|
||||||
$payload['defaults'] = [
|
$payload['defaults'] = [
|
||||||
'class_id' => $activeClassId > 0 ? $activeClassId : null,
|
'class_id' => $activeClassId > 0 ? $activeClassId : null,
|
||||||
@@ -87,6 +89,9 @@ class ClassProgressController extends BaseApiController
|
|||||||
}
|
}
|
||||||
|
|
||||||
$filters = $request->validated();
|
$filters = $request->validated();
|
||||||
|
$meta = $this->queryService->meta($filters['school_year'] ?? null, $filters['semester'] ?? null);
|
||||||
|
$filters['school_year'] = $filters['school_year'] ?? ($meta['school_year'] ?? null);
|
||||||
|
$filters['semester'] = $filters['semester'] ?? ($meta['semester'] ?? null);
|
||||||
$paginator = $this->queryService->listReports($auth, $filters);
|
$paginator = $this->queryService->listReports($auth, $filters);
|
||||||
|
|
||||||
if (!empty($filters['group_by_week'])) {
|
if (!empty($filters['group_by_week'])) {
|
||||||
@@ -96,6 +101,7 @@ class ClassProgressController extends BaseApiController
|
|||||||
|
|
||||||
return $this->respondSuccess([
|
return $this->respondSuccess([
|
||||||
'items' => ClassProgressReportResource::collection($paginator->items()),
|
'items' => ClassProgressReportResource::collection($paginator->items()),
|
||||||
|
'meta' => $meta,
|
||||||
'pagination' => [
|
'pagination' => [
|
||||||
'current_page' => $paginator->currentPage(),
|
'current_page' => $paginator->currentPage(),
|
||||||
'per_page' => $paginator->perPage(),
|
'per_page' => $paginator->perPage(),
|
||||||
@@ -241,13 +247,16 @@ class ClassProgressController extends BaseApiController
|
|||||||
return $user;
|
return $user;
|
||||||
}
|
}
|
||||||
|
|
||||||
private function buildMetaPayload(?int $classId, int $sundayCount): array
|
private function buildMetaPayload(?int $classId, int $sundayCount, ?string $schoolYear = null, ?string $semester = null): array
|
||||||
{
|
{
|
||||||
|
$meta = $this->queryService->meta($schoolYear, $semester);
|
||||||
|
|
||||||
return [
|
return [
|
||||||
'subject_sections' => $this->metaService->subjectSections(),
|
'subject_sections' => $this->metaService->subjectSections(),
|
||||||
'status_options' => $this->metaService->statusOptions(),
|
'status_options' => $this->metaService->statusOptions(),
|
||||||
'sunday_options' => $this->metaService->sundayOptionsAlignedWithCi($sundayCount),
|
'sunday_options' => $this->metaService->sundayOptionsAlignedWithCi($sundayCount),
|
||||||
'curriculum' => $this->metaService->curriculumOptions($classId),
|
'curriculum' => $this->metaService->curriculumOptions($classId, $meta['school_year'] ?? null),
|
||||||
|
'meta' => $meta,
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -7,6 +7,7 @@ use App\Http\Requests\SchoolYears\CloseSchoolYearRequest;
|
|||||||
use App\Http\Requests\SchoolYears\PreviewCloseSchoolYearRequest;
|
use App\Http\Requests\SchoolYears\PreviewCloseSchoolYearRequest;
|
||||||
use App\Http\Requests\SchoolYears\SaveSchoolYearRequest;
|
use App\Http\Requests\SchoolYears\SaveSchoolYearRequest;
|
||||||
use App\Services\SchoolYears\SchoolYearClosureService;
|
use App\Services\SchoolYears\SchoolYearClosureService;
|
||||||
|
use App\Services\SchoolYears\SchoolYearContextService;
|
||||||
use Illuminate\Http\JsonResponse;
|
use Illuminate\Http\JsonResponse;
|
||||||
use Illuminate\Http\Request;
|
use Illuminate\Http\Request;
|
||||||
use Illuminate\Validation\ValidationException;
|
use Illuminate\Validation\ValidationException;
|
||||||
@@ -14,14 +15,44 @@ use Symfony\Component\HttpFoundation\Response;
|
|||||||
|
|
||||||
class SchoolYearController extends BaseApiController
|
class SchoolYearController extends BaseApiController
|
||||||
{
|
{
|
||||||
public function __construct(private SchoolYearClosureService $service)
|
public function __construct(
|
||||||
{
|
private SchoolYearClosureService $service,
|
||||||
|
private SchoolYearContextService $context
|
||||||
|
) {
|
||||||
parent::__construct();
|
parent::__construct();
|
||||||
}
|
}
|
||||||
|
|
||||||
public function index(): JsonResponse
|
public function index(Request $request): JsonResponse
|
||||||
{
|
{
|
||||||
return response()->json(['ok' => true, 'data' => $this->service->list()]);
|
$context = $this->context->options(
|
||||||
|
$request->query('school_year'),
|
||||||
|
$request->query('semester')
|
||||||
|
);
|
||||||
|
|
||||||
|
return response()->json([
|
||||||
|
'ok' => true,
|
||||||
|
'data' => $this->service->list(),
|
||||||
|
'meta' => $context,
|
||||||
|
'school_years' => $context['school_years'],
|
||||||
|
'current_school_year' => $context['current_school_year'],
|
||||||
|
'current_semester' => $context['current_semester'],
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function current(Request $request): JsonResponse
|
||||||
|
{
|
||||||
|
return response()->json([
|
||||||
|
'ok' => true,
|
||||||
|
'data' => $this->context->options(
|
||||||
|
$request->query('school_year'),
|
||||||
|
$request->query('semester')
|
||||||
|
),
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function options(Request $request): JsonResponse
|
||||||
|
{
|
||||||
|
return $this->current($request);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function store(SaveSchoolYearRequest $request): JsonResponse
|
public function store(SaveSchoolYearRequest $request): JsonResponse
|
||||||
|
|||||||
@@ -10,6 +10,7 @@ use App\Http\Resources\Subjects\SubjectCurriculumResource;
|
|||||||
use App\Models\SubjectCurriculum;
|
use App\Models\SubjectCurriculum;
|
||||||
use App\Services\Subjects\SubjectCurriculumService;
|
use App\Services\Subjects\SubjectCurriculumService;
|
||||||
use Illuminate\Http\JsonResponse;
|
use Illuminate\Http\JsonResponse;
|
||||||
|
use Illuminate\Http\Request;
|
||||||
|
|
||||||
class SubjectCurriculumController extends BaseApiController
|
class SubjectCurriculumController extends BaseApiController
|
||||||
{
|
{
|
||||||
@@ -18,15 +19,16 @@ class SubjectCurriculumController extends BaseApiController
|
|||||||
parent::__construct();
|
parent::__construct();
|
||||||
}
|
}
|
||||||
|
|
||||||
public function index(): JsonResponse
|
public function index(Request $request): JsonResponse
|
||||||
{
|
{
|
||||||
$data = $this->service->list();
|
$data = $this->service->list($request->query('school_year'));
|
||||||
|
|
||||||
return response()->json([
|
return response()->json([
|
||||||
'ok' => true,
|
'ok' => true,
|
||||||
'classes' => SubjectClassResource::collection($data['classes']),
|
'classes' => SubjectClassResource::collection($data['classes']),
|
||||||
'entries' => SubjectCurriculumResource::collection($data['entries']),
|
'entries' => SubjectCurriculumResource::collection($data['entries']),
|
||||||
'subject_labels' => $data['subject_labels'],
|
'subject_labels' => $data['subject_labels'],
|
||||||
|
'meta' => $data['meta'],
|
||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -18,6 +18,8 @@ class ClassProgressIndexRequest extends ClassProgressFormRequest
|
|||||||
return [
|
return [
|
||||||
'class_section_id' => ['nullable', 'integer', 'min:1'],
|
'class_section_id' => ['nullable', 'integer', 'min:1'],
|
||||||
'teacher_id' => ['nullable', 'integer', 'min:1'],
|
'teacher_id' => ['nullable', 'integer', 'min:1'],
|
||||||
|
'school_year' => ['nullable', 'string', 'max:20'],
|
||||||
|
'semester' => ['nullable', 'string', 'max:20'],
|
||||||
'week_start' => ['nullable', 'date_format:Y-m-d'],
|
'week_start' => ['nullable', 'date_format:Y-m-d'],
|
||||||
'week_end' => ['nullable', 'date_format:Y-m-d'],
|
'week_end' => ['nullable', 'date_format:Y-m-d'],
|
||||||
'subject' => ['nullable', 'string', 'max:120'],
|
'subject' => ['nullable', 'string', 'max:120'],
|
||||||
|
|||||||
@@ -15,6 +15,8 @@ class ClassProgressStoreRequest extends ClassProgressFormRequest
|
|||||||
{
|
{
|
||||||
$rules = [
|
$rules = [
|
||||||
'class_section_id' => ['required', 'integer', 'exists:classSection,class_section_id'],
|
'class_section_id' => ['required', 'integer', 'exists:classSection,class_section_id'],
|
||||||
|
'school_year' => ['nullable', 'string', 'max:20'],
|
||||||
|
'semester' => ['nullable', 'string', 'max:20'],
|
||||||
'week_start' => ['required', 'date_format:Y-m-d'],
|
'week_start' => ['required', 'date_format:Y-m-d'],
|
||||||
'week_end' => ['nullable', 'date_format:Y-m-d'],
|
'week_end' => ['nullable', 'date_format:Y-m-d'],
|
||||||
'support_needed' => ['nullable', 'string'],
|
'support_needed' => ['nullable', 'string'],
|
||||||
|
|||||||
@@ -10,6 +10,7 @@ class SubjectCurriculumStoreRequest extends ApiFormRequest
|
|||||||
{
|
{
|
||||||
return [
|
return [
|
||||||
'class_id' => ['required', 'integer', 'min:1'],
|
'class_id' => ['required', 'integer', 'min:1'],
|
||||||
|
'school_year' => ['nullable', 'string', 'max:20'],
|
||||||
'subject' => ['required', 'in:islamic,quran'],
|
'subject' => ['required', 'in:islamic,quran'],
|
||||||
'chapter_name' => ['required', 'string', 'max:255'],
|
'chapter_name' => ['required', 'string', 'max:255'],
|
||||||
'unit_number' => ['nullable', 'integer'],
|
'unit_number' => ['nullable', 'integer'],
|
||||||
|
|||||||
@@ -10,6 +10,7 @@ class SubjectCurriculumUpdateRequest extends ApiFormRequest
|
|||||||
{
|
{
|
||||||
return [
|
return [
|
||||||
'class_id' => ['required', 'integer', 'min:1'],
|
'class_id' => ['required', 'integer', 'min:1'],
|
||||||
|
'school_year' => ['nullable', 'string', 'max:20'],
|
||||||
'subject' => ['required', 'in:islamic,quran'],
|
'subject' => ['required', 'in:islamic,quran'],
|
||||||
'chapter_name' => ['required', 'string', 'max:255'],
|
'chapter_name' => ['required', 'string', 'max:255'],
|
||||||
'unit_number' => ['nullable', 'integer'],
|
'unit_number' => ['nullable', 'integer'],
|
||||||
|
|||||||
@@ -16,6 +16,8 @@ class ClassProgressReport extends BaseModel
|
|||||||
protected $fillable = [
|
protected $fillable = [
|
||||||
'class_section_id',
|
'class_section_id',
|
||||||
'teacher_id',
|
'teacher_id',
|
||||||
|
'school_year',
|
||||||
|
'semester',
|
||||||
'week_start',
|
'week_start',
|
||||||
'week_end',
|
'week_end',
|
||||||
'subject',
|
'subject',
|
||||||
|
|||||||
@@ -5,6 +5,7 @@ namespace App\Models;
|
|||||||
use Illuminate\Database\Eloquent\Builder;
|
use Illuminate\Database\Eloquent\Builder;
|
||||||
use App\Models\BaseModel;
|
use App\Models\BaseModel;
|
||||||
use Illuminate\Database\Eloquent\Relations\BelongsTo;
|
use Illuminate\Database\Eloquent\Relations\BelongsTo;
|
||||||
|
use Illuminate\Support\Facades\Schema;
|
||||||
|
|
||||||
class SubjectCurriculum extends BaseModel
|
class SubjectCurriculum extends BaseModel
|
||||||
{
|
{
|
||||||
@@ -12,6 +13,7 @@ class SubjectCurriculum extends BaseModel
|
|||||||
|
|
||||||
protected $fillable = [
|
protected $fillable = [
|
||||||
'class_id',
|
'class_id',
|
||||||
|
'school_year',
|
||||||
'subject',
|
'subject',
|
||||||
'unit_number',
|
'unit_number',
|
||||||
'unit_title',
|
'unit_title',
|
||||||
@@ -54,6 +56,16 @@ class SubjectCurriculum extends BaseModel
|
|||||||
return $q->where('subject', $subject);
|
return $q->where('subject', $subject);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function scopeForSchoolYear(Builder $q, ?string $schoolYear): Builder
|
||||||
|
{
|
||||||
|
$schoolYear = trim((string) $schoolYear);
|
||||||
|
if ($schoolYear === '' || !Schema::hasColumn($this->getTable(), 'school_year')) {
|
||||||
|
return $q;
|
||||||
|
}
|
||||||
|
|
||||||
|
return $q->where('school_year', $schoolYear);
|
||||||
|
}
|
||||||
|
|
||||||
public function scopeOrdered(Builder $q): Builder
|
public function scopeOrdered(Builder $q): Builder
|
||||||
{
|
{
|
||||||
return $q->orderBy('unit_number', 'asc')
|
return $q->orderBy('unit_number', 'asc')
|
||||||
@@ -68,11 +80,12 @@ class SubjectCurriculum extends BaseModel
|
|||||||
/**
|
/**
|
||||||
* legacy: getOptionsForClass($classId, $subject)
|
* legacy: getOptionsForClass($classId, $subject)
|
||||||
*/
|
*/
|
||||||
public static function getOptionsForClass(int $classId, string $subject): array
|
public static function getOptionsForClass(int $classId, string $subject, ?string $schoolYear = null): array
|
||||||
{
|
{
|
||||||
return static::query()
|
return static::query()
|
||||||
->forClass($classId)
|
->forClass($classId)
|
||||||
->forSubject($subject)
|
->forSubject($subject)
|
||||||
|
->forSchoolYear($schoolYear)
|
||||||
->ordered()
|
->ordered()
|
||||||
->get()
|
->get()
|
||||||
->all();
|
->all();
|
||||||
|
|||||||
@@ -17,7 +17,7 @@ class BadgeScanService
|
|||||||
/**
|
/**
|
||||||
* @return array{recognized: bool, message: string, user_id?: int, display_name?: string}
|
* @return array{recognized: bool, message: string, user_id?: int, display_name?: string}
|
||||||
*/
|
*/
|
||||||
public function processScan(string $badgeScan): array
|
public function processScan(string $badgeScan, ?string $schoolYear = null, ?string $semester = null): array
|
||||||
{
|
{
|
||||||
$tag = trim($badgeScan);
|
$tag = trim($badgeScan);
|
||||||
if ($tag === '') {
|
if ($tag === '') {
|
||||||
@@ -51,8 +51,8 @@ class BadgeScanService
|
|||||||
'user_id' => $user->id,
|
'user_id' => $user->id,
|
||||||
'card_id' => $tag,
|
'card_id' => $tag,
|
||||||
'scan_time' => now(),
|
'scan_time' => now(),
|
||||||
'school_year' => Configuration::getConfigValueByKey('school_year'),
|
'school_year' => $schoolYear ?: Configuration::getConfigValueByKey('school_year'),
|
||||||
'semester' => Configuration::getConfigValueByKey('semester'),
|
'semester' => $semester ?: Configuration::getConfigValueByKey('semester'),
|
||||||
]);
|
]);
|
||||||
|
|
||||||
return [
|
return [
|
||||||
@@ -68,13 +68,13 @@ class BadgeScanService
|
|||||||
*
|
*
|
||||||
* @return list<array<string, mixed>>
|
* @return list<array<string, mixed>>
|
||||||
*/
|
*/
|
||||||
public function scanLogRows(): array
|
public function scanLogRows(?string $schoolYear = null, ?string $semester = null): array
|
||||||
{
|
{
|
||||||
if (! Schema::hasTable('scan_log')) {
|
if (! Schema::hasTable('scan_log')) {
|
||||||
return [];
|
return [];
|
||||||
}
|
}
|
||||||
|
|
||||||
return DB::table('scan_log')
|
$query = DB::table('scan_log')
|
||||||
->select([
|
->select([
|
||||||
'scan_log.id',
|
'scan_log.id',
|
||||||
'scan_log.user_id',
|
'scan_log.user_id',
|
||||||
@@ -88,7 +88,17 @@ class BadgeScanService
|
|||||||
'students.lastname as student_lastname',
|
'students.lastname as student_lastname',
|
||||||
])
|
])
|
||||||
->leftJoin('users', 'users.id', '=', 'scan_log.user_id')
|
->leftJoin('users', 'users.id', '=', 'scan_log.user_id')
|
||||||
->leftJoin('students', 'students.rfid_tag', '=', 'scan_log.card_id')
|
->leftJoin('students', 'students.rfid_tag', '=', 'scan_log.card_id');
|
||||||
|
|
||||||
|
if ($schoolYear !== null && trim($schoolYear) !== '') {
|
||||||
|
$query->where('scan_log.school_year', trim($schoolYear));
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($semester !== null && trim($semester) !== '') {
|
||||||
|
$query->where('scan_log.semester', trim($semester));
|
||||||
|
}
|
||||||
|
|
||||||
|
return $query
|
||||||
->orderByDesc('scan_log.scan_time')
|
->orderByDesc('scan_log.scan_time')
|
||||||
->limit(self::LOG_LIMIT)
|
->limit(self::LOG_LIMIT)
|
||||||
->get()
|
->get()
|
||||||
|
|||||||
@@ -138,7 +138,7 @@ class ClassProgressMetaService
|
|||||||
return $svc->getSchoolYearRange($schoolYear);
|
return $svc->getSchoolYearRange($schoolYear);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function curriculumOptions(?int $classId): array
|
public function curriculumOptions(?int $classId, ?string $schoolYear = null): array
|
||||||
{
|
{
|
||||||
if (!$classId) {
|
if (!$classId) {
|
||||||
return [];
|
return [];
|
||||||
@@ -146,7 +146,7 @@ class ClassProgressMetaService
|
|||||||
|
|
||||||
$options = [];
|
$options = [];
|
||||||
foreach ($this->subjectSections() as $slug => $section) {
|
foreach ($this->subjectSections() as $slug => $section) {
|
||||||
$options[$slug] = SubjectCurriculum::getOptionsForClass((int) $classId, (string) $slug);
|
$options[$slug] = SubjectCurriculum::getOptionsForClass((int) $classId, (string) $slug, $schoolYear);
|
||||||
}
|
}
|
||||||
|
|
||||||
return $options;
|
return $options;
|
||||||
|
|||||||
@@ -6,6 +6,8 @@ use App\Models\ClassProgressAttachment;
|
|||||||
use App\Models\ClassProgressReport;
|
use App\Models\ClassProgressReport;
|
||||||
use App\Models\TeacherClass;
|
use App\Models\TeacherClass;
|
||||||
use App\Models\User;
|
use App\Models\User;
|
||||||
|
use App\Services\SchoolYears\SchoolYearContextService;
|
||||||
|
use Illuminate\Support\Facades\Schema;
|
||||||
use Illuminate\Support\Collection;
|
use Illuminate\Support\Collection;
|
||||||
use Illuminate\Support\Facades\DB;
|
use Illuminate\Support\Facades\DB;
|
||||||
use Illuminate\Support\Facades\Log;
|
use Illuminate\Support\Facades\Log;
|
||||||
@@ -16,6 +18,7 @@ class ClassProgressMutationService
|
|||||||
private ClassProgressRuleService $rules,
|
private ClassProgressRuleService $rules,
|
||||||
private ClassProgressAttachmentService $attachments,
|
private ClassProgressAttachmentService $attachments,
|
||||||
private ClassProgressQueryService $queries,
|
private ClassProgressQueryService $queries,
|
||||||
|
private SchoolYearContextService $schoolYears,
|
||||||
) {}
|
) {}
|
||||||
|
|
||||||
public function createReports(User $user, array $payload, array $filesBySubject): Collection
|
public function createReports(User $user, array $payload, array $filesBySubject): Collection
|
||||||
@@ -25,6 +28,9 @@ class ClassProgressMutationService
|
|||||||
|
|
||||||
$this->assertTeacherAssignment($user, $classSectionId);
|
$this->assertTeacherAssignment($user, $classSectionId);
|
||||||
|
|
||||||
|
$schoolYear = $this->schoolYears->currentSchoolYear($payload['school_year'] ?? null);
|
||||||
|
$semester = $this->schoolYears->currentSemester($payload['semester'] ?? null);
|
||||||
|
|
||||||
$weekStart = (string) ($payload['week_start'] ?? '');
|
$weekStart = (string) ($payload['week_start'] ?? '');
|
||||||
$weekEnd = $this->rules->ensureWeekEnd($weekStart, $payload['week_end'] ?? null);
|
$weekEnd = $this->rules->ensureWeekEnd($weekStart, $payload['week_end'] ?? null);
|
||||||
|
|
||||||
@@ -51,7 +57,7 @@ class ClassProgressMutationService
|
|||||||
throw new \InvalidArgumentException('CONFIRM_OVERWRITE');
|
throw new \InvalidArgumentException('CONFIRM_OVERWRITE');
|
||||||
}
|
}
|
||||||
|
|
||||||
$reports = DB::transaction(function () use ($user, $payload, $subjectSections, $classSectionId, $weekStart, $weekEnd, $filesBySubject, $confirmOverwrite, $existingIds) {
|
$reports = DB::transaction(function () use ($user, $payload, $subjectSections, $classSectionId, $schoolYear, $semester, $weekStart, $weekEnd, $filesBySubject, $confirmOverwrite, $existingIds) {
|
||||||
if ($existingIds !== [] && $confirmOverwrite) {
|
if ($existingIds !== [] && $confirmOverwrite) {
|
||||||
ClassProgressAttachment::query()->whereIn('report_id', $existingIds)->delete();
|
ClassProgressAttachment::query()->whereIn('report_id', $existingIds)->delete();
|
||||||
ClassProgressReport::query()->whereIn('id', $existingIds)->delete();
|
ClassProgressReport::query()->whereIn('id', $existingIds)->delete();
|
||||||
@@ -70,7 +76,7 @@ class ClassProgressMutationService
|
|||||||
$chapterValues = (array) ($payload["chapter_{$slug}"] ?? []);
|
$chapterValues = (array) ($payload["chapter_{$slug}"] ?? []);
|
||||||
$unitTitle = $this->rules->buildUnitChapterSummary($unitValues, $chapterValues);
|
$unitTitle = $this->rules->buildUnitChapterSummary($unitValues, $chapterValues);
|
||||||
|
|
||||||
$report = ClassProgressReport::query()->create([
|
$data = [
|
||||||
'teacher_id' => $user->id,
|
'teacher_id' => $user->id,
|
||||||
'class_section_id' => $classSectionId,
|
'class_section_id' => $classSectionId,
|
||||||
'week_start' => $weekStart,
|
'week_start' => $weekStart,
|
||||||
@@ -82,7 +88,16 @@ class ClassProgressMutationService
|
|||||||
'status' => $this->rules->defaultStatus(),
|
'status' => $this->rules->defaultStatus(),
|
||||||
'support_needed' => $payload['support_needed'] ?? null,
|
'support_needed' => $payload['support_needed'] ?? null,
|
||||||
'flags_json' => $this->rules->normalizeFlags($payload['flags'] ?? null),
|
'flags_json' => $this->rules->normalizeFlags($payload['flags'] ?? null),
|
||||||
]);
|
];
|
||||||
|
|
||||||
|
if (Schema::hasColumn('class_progress_reports', 'school_year')) {
|
||||||
|
$data['school_year'] = $schoolYear !== '' ? $schoolYear : null;
|
||||||
|
}
|
||||||
|
if (Schema::hasColumn('class_progress_reports', 'semester')) {
|
||||||
|
$data['semester'] = $semester !== '' ? $semester : null;
|
||||||
|
}
|
||||||
|
|
||||||
|
$report = ClassProgressReport::query()->create($data);
|
||||||
|
|
||||||
$attachments = $filesBySubject[$slug] ?? [];
|
$attachments = $filesBySubject[$slug] ?? [];
|
||||||
$stored = $this->attachments->storeAttachments($report->id, $attachments);
|
$stored = $this->attachments->storeAttachments($report->id, $attachments);
|
||||||
@@ -121,6 +136,9 @@ class ClassProgressMutationService
|
|||||||
|
|
||||||
$this->assertTeacherAssignment($user, $classSectionId);
|
$this->assertTeacherAssignment($user, $classSectionId);
|
||||||
|
|
||||||
|
$schoolYear = $this->schoolYears->currentSchoolYear($payload['school_year'] ?? null);
|
||||||
|
$semester = $this->schoolYears->currentSemester($payload['semester'] ?? null);
|
||||||
|
|
||||||
$weekStart = (string) ($payload['week_start'] ?? '');
|
$weekStart = (string) ($payload['week_start'] ?? '');
|
||||||
$weekEnd = $this->rules->ensureWeekEnd($weekStart, $payload['week_end'] ?? null);
|
$weekEnd = $this->rules->ensureWeekEnd($weekStart, $payload['week_end'] ?? null);
|
||||||
|
|
||||||
@@ -172,6 +190,8 @@ class ClassProgressMutationService
|
|||||||
$classSectionId,
|
$classSectionId,
|
||||||
$weekStart,
|
$weekStart,
|
||||||
$weekEnd,
|
$weekEnd,
|
||||||
|
$schoolYear,
|
||||||
|
$semester,
|
||||||
$filesBySubject,
|
$filesBySubject,
|
||||||
$allowedIds,
|
$allowedIds,
|
||||||
$originalWeekStart,
|
$originalWeekStart,
|
||||||
@@ -226,6 +246,13 @@ class ClassProgressMutationService
|
|||||||
$data['flags_json'] = $this->rules->normalizeFlags($payload['flags'] ?? null);
|
$data['flags_json'] = $this->rules->normalizeFlags($payload['flags'] ?? null);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (Schema::hasColumn('class_progress_reports', 'school_year')) {
|
||||||
|
$data['school_year'] = $schoolYear !== '' ? $schoolYear : null;
|
||||||
|
}
|
||||||
|
if (Schema::hasColumn('class_progress_reports', 'semester')) {
|
||||||
|
$data['semester'] = $semester !== '' ? $semester : null;
|
||||||
|
}
|
||||||
|
|
||||||
if ($existing) {
|
if ($existing) {
|
||||||
$existing->fill($data);
|
$existing->fill($data);
|
||||||
$existing->save();
|
$existing->save();
|
||||||
|
|||||||
@@ -7,10 +7,21 @@ use App\Models\ClassProgressReport;
|
|||||||
use App\Models\Configuration;
|
use App\Models\Configuration;
|
||||||
use App\Models\TeacherClass;
|
use App\Models\TeacherClass;
|
||||||
use App\Models\User;
|
use App\Models\User;
|
||||||
|
use App\Services\SchoolYears\SchoolYearContextService;
|
||||||
|
use Illuminate\Support\Facades\Schema;
|
||||||
use Illuminate\Contracts\Pagination\LengthAwarePaginator;
|
use Illuminate\Contracts\Pagination\LengthAwarePaginator;
|
||||||
|
|
||||||
class ClassProgressQueryService
|
class ClassProgressQueryService
|
||||||
{
|
{
|
||||||
|
private SchoolYearContextService $schoolYears;
|
||||||
|
|
||||||
|
public function __construct($schoolYears = null)
|
||||||
|
{
|
||||||
|
$this->schoolYears = $schoolYears instanceof SchoolYearContextService
|
||||||
|
? $schoolYears
|
||||||
|
: app(SchoolYearContextService::class);
|
||||||
|
}
|
||||||
|
|
||||||
public function listReports(User $user, array $filters): LengthAwarePaginator
|
public function listReports(User $user, array $filters): LengthAwarePaginator
|
||||||
{
|
{
|
||||||
$query = ClassProgressReport::query()
|
$query = ClassProgressReport::query()
|
||||||
@@ -51,6 +62,14 @@ class ClassProgressQueryService
|
|||||||
$query->where('status', (string) $filters['status']);
|
$query->where('status', (string) $filters['status']);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (!empty($filters['school_year']) && Schema::hasColumn('class_progress_reports', 'school_year')) {
|
||||||
|
$query->where('school_year', (string) $filters['school_year']);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!empty($filters['semester']) && Schema::hasColumn('class_progress_reports', 'semester')) {
|
||||||
|
$query->where('semester', (string) $filters['semester']);
|
||||||
|
}
|
||||||
|
|
||||||
if (!empty($filters['week_start'])) {
|
if (!empty($filters['week_start'])) {
|
||||||
$query->whereDate('week_start', '>=', $filters['week_start']);
|
$query->whereDate('week_start', '>=', $filters['week_start']);
|
||||||
}
|
}
|
||||||
@@ -132,6 +151,11 @@ class ClassProgressQueryService
|
|||||||
return TeacherClass::getClassAssignmentsByUserId($user->id, $schoolYear, $semester);
|
return TeacherClass::getClassAssignmentsByUserId($user->id, $schoolYear, $semester);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function meta(?string $schoolYear = null, ?string $semester = null): array
|
||||||
|
{
|
||||||
|
return $this->schoolYears->options($schoolYear, $semester);
|
||||||
|
}
|
||||||
|
|
||||||
private function statusLabel(?string $status): string
|
private function statusLabel(?string $status): string
|
||||||
{
|
{
|
||||||
$options = (array) config('progress.status_options', []);
|
$options = (array) config('progress.status_options', []);
|
||||||
|
|||||||
@@ -0,0 +1,191 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Services\SchoolYears;
|
||||||
|
|
||||||
|
use App\Models\Configuration;
|
||||||
|
use App\Models\SchoolYear;
|
||||||
|
use Illuminate\Support\Facades\DB;
|
||||||
|
use Illuminate\Support\Facades\Schema;
|
||||||
|
|
||||||
|
class SchoolYearContextService
|
||||||
|
{
|
||||||
|
public function __construct(private SchoolYearResolver $resolver)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
public function currentSchoolYear(?string $requested = null): string
|
||||||
|
{
|
||||||
|
$requested = trim((string) $requested);
|
||||||
|
if ($requested !== '') {
|
||||||
|
return $requested;
|
||||||
|
}
|
||||||
|
|
||||||
|
$current = trim((string) (Configuration::getConfig('school_year') ?? ''));
|
||||||
|
if ($current !== '') {
|
||||||
|
return $current;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (Schema::hasTable('school_years')) {
|
||||||
|
$year = SchoolYear::query()->where('is_current', true)->orderByDesc('id')->first();
|
||||||
|
if ($year && trim((string) $year->name) !== '') {
|
||||||
|
return (string) $year->name;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return '';
|
||||||
|
}
|
||||||
|
|
||||||
|
public function currentSemester(?string $requested = null): string
|
||||||
|
{
|
||||||
|
$requested = trim((string) $requested);
|
||||||
|
if ($requested !== '') {
|
||||||
|
return $requested;
|
||||||
|
}
|
||||||
|
|
||||||
|
return trim((string) (Configuration::getConfig('semester') ?? ''));
|
||||||
|
}
|
||||||
|
|
||||||
|
public function options(?string $selectedSchoolYear = null, ?string $selectedSemester = null): array
|
||||||
|
{
|
||||||
|
if (Schema::hasTable('school_years')) {
|
||||||
|
$this->resolver->ensureCurrentTracked();
|
||||||
|
}
|
||||||
|
|
||||||
|
$currentSchoolYear = $this->currentSchoolYear($selectedSchoolYear);
|
||||||
|
$currentSemester = $this->currentSemester($selectedSemester);
|
||||||
|
|
||||||
|
$schoolYears = $this->schoolYears($currentSchoolYear);
|
||||||
|
$semesters = $this->semesters($currentSemester);
|
||||||
|
|
||||||
|
return [
|
||||||
|
'school_year' => $currentSchoolYear !== '' ? $currentSchoolYear : null,
|
||||||
|
'current_school_year' => $currentSchoolYear !== '' ? $currentSchoolYear : null,
|
||||||
|
'semester' => $currentSemester !== '' ? $currentSemester : null,
|
||||||
|
'current_semester' => $currentSemester !== '' ? $currentSemester : null,
|
||||||
|
'school_years' => $schoolYears,
|
||||||
|
'semesters' => $semesters,
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
public function schoolYears(?string $include = null): array
|
||||||
|
{
|
||||||
|
$years = [];
|
||||||
|
|
||||||
|
if (Schema::hasTable('school_years')) {
|
||||||
|
$years = SchoolYear::query()
|
||||||
|
->orderByDesc('start_date')
|
||||||
|
->orderByDesc('id')
|
||||||
|
->get()
|
||||||
|
->map(fn (SchoolYear $year) => [
|
||||||
|
'id' => (int) $year->id,
|
||||||
|
'name' => (string) $year->name,
|
||||||
|
'label' => (string) $year->name,
|
||||||
|
'start_date' => $year->start_date ? $year->start_date->format('Y-m-d') : null,
|
||||||
|
'end_date' => $year->end_date ? $year->end_date->format('Y-m-d') : null,
|
||||||
|
'status' => (string) $year->status,
|
||||||
|
'is_current' => (bool) $year->is_current,
|
||||||
|
'is_editable' => $year->status === SchoolYear::STATUS_ACTIVE,
|
||||||
|
])
|
||||||
|
->all();
|
||||||
|
}
|
||||||
|
|
||||||
|
$seen = [];
|
||||||
|
foreach ($years as $row) {
|
||||||
|
$seen[$row['name']] = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach ($this->legacySchoolYearNames() as $name) {
|
||||||
|
if (!isset($seen[$name])) {
|
||||||
|
$years[] = [
|
||||||
|
'id' => null,
|
||||||
|
'name' => $name,
|
||||||
|
'label' => $name,
|
||||||
|
'start_date' => null,
|
||||||
|
'end_date' => null,
|
||||||
|
'status' => 'legacy',
|
||||||
|
'is_current' => false,
|
||||||
|
'is_editable' => true,
|
||||||
|
];
|
||||||
|
$seen[$name] = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
$include = trim((string) $include);
|
||||||
|
if ($include !== '' && !isset($seen[$include])) {
|
||||||
|
array_unshift($years, [
|
||||||
|
'id' => null,
|
||||||
|
'name' => $include,
|
||||||
|
'label' => $include,
|
||||||
|
'start_date' => null,
|
||||||
|
'end_date' => null,
|
||||||
|
'status' => 'selected',
|
||||||
|
'is_current' => true,
|
||||||
|
'is_editable' => true,
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
|
return $years;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function semesters(?string $include = null): array
|
||||||
|
{
|
||||||
|
$names = ['Fall', 'Spring'];
|
||||||
|
$current = trim((string) $include);
|
||||||
|
if ($current !== '' && !in_array($current, $names, true)) {
|
||||||
|
array_unshift($names, $current);
|
||||||
|
}
|
||||||
|
|
||||||
|
return array_map(fn (string $name) => [
|
||||||
|
'name' => $name,
|
||||||
|
'label' => $name,
|
||||||
|
'is_current' => $current !== '' && strcasecmp($name, $current) === 0,
|
||||||
|
], $names);
|
||||||
|
}
|
||||||
|
|
||||||
|
private function legacySchoolYearNames(): array
|
||||||
|
{
|
||||||
|
$tables = [
|
||||||
|
'students', 'parents', 'users', 'staff', 'student_class', 'teacher_class',
|
||||||
|
'events', 'calendar_events', 'scan_log', 'exam_drafts', 'inventory_items',
|
||||||
|
'inventory_movements', 'certificate_records', 'semester_scores', 'final_scores',
|
||||||
|
'student_decisions', 'below_sixty_decisions', 'whatsapp_group_links',
|
||||||
|
];
|
||||||
|
|
||||||
|
$names = [];
|
||||||
|
foreach ($tables as $table) {
|
||||||
|
if (!Schema::hasTable($table) || !Schema::hasColumn($table, 'school_year')) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
$rows = DB::table($table)
|
||||||
|
->whereNotNull('school_year')
|
||||||
|
->where('school_year', '!=', '')
|
||||||
|
->distinct()
|
||||||
|
->orderByDesc('school_year')
|
||||||
|
->limit(20)
|
||||||
|
->pluck('school_year')
|
||||||
|
->all();
|
||||||
|
} catch (\Throwable $e) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach ($rows as $row) {
|
||||||
|
$name = trim((string) $row);
|
||||||
|
if ($name !== '') {
|
||||||
|
$names[$name] = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
$configured = trim((string) (Configuration::getConfig('school_year') ?? ''));
|
||||||
|
if ($configured !== '') {
|
||||||
|
$names[$configured] = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
$out = array_keys($names);
|
||||||
|
rsort($out, SORT_NATURAL);
|
||||||
|
|
||||||
|
return $out;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -4,25 +4,46 @@ namespace App\Services\Subjects;
|
|||||||
|
|
||||||
use App\Models\SchoolClass;
|
use App\Models\SchoolClass;
|
||||||
use App\Models\SubjectCurriculum;
|
use App\Models\SubjectCurriculum;
|
||||||
|
use App\Services\SchoolYears\SchoolYearContextService;
|
||||||
use Illuminate\Support\Facades\DB;
|
use Illuminate\Support\Facades\DB;
|
||||||
|
use Illuminate\Support\Facades\Schema;
|
||||||
|
|
||||||
class SubjectCurriculumService
|
class SubjectCurriculumService
|
||||||
{
|
{
|
||||||
|
private SchoolYearContextService $schoolYears;
|
||||||
|
|
||||||
public const SUBJECT_OPTIONS = [
|
public const SUBJECT_OPTIONS = [
|
||||||
'islamic' => 'Islamic Studies',
|
'islamic' => 'Islamic Studies',
|
||||||
'quran' => 'Quran/Arabic',
|
'quran' => 'Quran/Arabic',
|
||||||
];
|
];
|
||||||
|
|
||||||
public function list(): array
|
public function __construct(?SchoolYearContextService $schoolYears = null)
|
||||||
|
{
|
||||||
|
$this->schoolYears = $schoolYears ?: app(SchoolYearContextService::class);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function list(?string $schoolYear = null): array
|
||||||
{
|
{
|
||||||
$classes = SchoolClass::query()
|
$classes = SchoolClass::query()
|
||||||
->orderBy('class_name')
|
->orderBy('class_name')
|
||||||
->get()
|
->get()
|
||||||
->toArray();
|
->toArray();
|
||||||
|
|
||||||
$entries = DB::table('subject_curriculum_items as sci')
|
$resolvedSchoolYear = $this->schoolYears->currentSchoolYear($schoolYear);
|
||||||
|
|
||||||
|
$entriesQuery = DB::table('subject_curriculum_items as sci')
|
||||||
->leftJoin('classes', 'classes.id', '=', 'sci.class_id')
|
->leftJoin('classes', 'classes.id', '=', 'sci.class_id')
|
||||||
->select('sci.*', 'classes.class_name')
|
->select('sci.*', 'classes.class_name');
|
||||||
|
|
||||||
|
if ($resolvedSchoolYear !== '' && Schema::hasColumn('subject_curriculum_items', 'school_year')) {
|
||||||
|
$entriesQuery->where(function ($query) use ($resolvedSchoolYear) {
|
||||||
|
$query->where('sci.school_year', $resolvedSchoolYear)
|
||||||
|
->orWhereNull('sci.school_year')
|
||||||
|
->orWhere('sci.school_year', '');
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
$entries = $entriesQuery
|
||||||
->orderBy('classes.class_name')
|
->orderBy('classes.class_name')
|
||||||
->orderBy('sci.subject')
|
->orderBy('sci.subject')
|
||||||
->orderBy('sci.unit_number')
|
->orderBy('sci.unit_number')
|
||||||
@@ -35,18 +56,25 @@ class SubjectCurriculumService
|
|||||||
'classes' => $classes,
|
'classes' => $classes,
|
||||||
'entries' => $entries,
|
'entries' => $entries,
|
||||||
'subject_labels' => self::SUBJECT_OPTIONS,
|
'subject_labels' => self::SUBJECT_OPTIONS,
|
||||||
|
'meta' => $this->schoolYears->options($resolvedSchoolYear),
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
public function store(array $payload): SubjectCurriculum
|
public function store(array $payload): SubjectCurriculum
|
||||||
{
|
{
|
||||||
return SubjectCurriculum::query()->create([
|
$data = [
|
||||||
'class_id' => (int) $payload['class_id'],
|
'class_id' => (int) $payload['class_id'],
|
||||||
'subject' => (string) $payload['subject'],
|
'subject' => (string) $payload['subject'],
|
||||||
'unit_number' => $payload['unit_number'] !== null ? (int) $payload['unit_number'] : null,
|
'unit_number' => $payload['unit_number'] !== null ? (int) $payload['unit_number'] : null,
|
||||||
'unit_title' => $payload['unit_title'] !== '' ? $payload['unit_title'] : null,
|
'unit_title' => $payload['unit_title'] !== '' ? $payload['unit_title'] : null,
|
||||||
'chapter_name' => (string) $payload['chapter_name'],
|
'chapter_name' => (string) $payload['chapter_name'],
|
||||||
]);
|
];
|
||||||
|
|
||||||
|
if (Schema::hasColumn('subject_curriculum_items', 'school_year')) {
|
||||||
|
$data['school_year'] = $this->schoolYears->currentSchoolYear($payload['school_year'] ?? null) ?: null;
|
||||||
|
}
|
||||||
|
|
||||||
|
return SubjectCurriculum::query()->create($data);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function update(int $id, array $payload): ?SubjectCurriculum
|
public function update(int $id, array $payload): ?SubjectCurriculum
|
||||||
@@ -56,13 +84,19 @@ class SubjectCurriculumService
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
$entry->update([
|
$data = [
|
||||||
'class_id' => (int) $payload['class_id'],
|
'class_id' => (int) $payload['class_id'],
|
||||||
'subject' => (string) $payload['subject'],
|
'subject' => (string) $payload['subject'],
|
||||||
'unit_number' => $payload['unit_number'] !== null ? (int) $payload['unit_number'] : null,
|
'unit_number' => $payload['unit_number'] !== null ? (int) $payload['unit_number'] : null,
|
||||||
'unit_title' => $payload['unit_title'] !== '' ? $payload['unit_title'] : null,
|
'unit_title' => $payload['unit_title'] !== '' ? $payload['unit_title'] : null,
|
||||||
'chapter_name' => (string) $payload['chapter_name'],
|
'chapter_name' => (string) $payload['chapter_name'],
|
||||||
]);
|
];
|
||||||
|
|
||||||
|
if (Schema::hasColumn('subject_curriculum_items', 'school_year')) {
|
||||||
|
$data['school_year'] = $this->schoolYears->currentSchoolYear($payload['school_year'] ?? null) ?: null;
|
||||||
|
}
|
||||||
|
|
||||||
|
$entry->update($data);
|
||||||
|
|
||||||
return $entry;
|
return $entry;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,30 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
use Illuminate\Database\Migrations\Migration;
|
||||||
|
use Illuminate\Database\Schema\Blueprint;
|
||||||
|
use Illuminate\Support\Facades\Schema;
|
||||||
|
|
||||||
|
return new class extends Migration
|
||||||
|
{
|
||||||
|
public function up(): void
|
||||||
|
{
|
||||||
|
if (! Schema::hasTable('subject_curriculum_items') || Schema::hasColumn('subject_curriculum_items', 'school_year')) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
Schema::table('subject_curriculum_items', function (Blueprint $table) {
|
||||||
|
$table->string('school_year', 20)->nullable()->after('class_id')->index();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
public function down(): void
|
||||||
|
{
|
||||||
|
if (! Schema::hasTable('subject_curriculum_items') || ! Schema::hasColumn('subject_curriculum_items', 'school_year')) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
Schema::table('subject_curriculum_items', function (Blueprint $table) {
|
||||||
|
$table->dropColumn('school_year');
|
||||||
|
});
|
||||||
|
}
|
||||||
|
};
|
||||||
+40
@@ -0,0 +1,40 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
use Illuminate\Database\Migrations\Migration;
|
||||||
|
use Illuminate\Database\Schema\Blueprint;
|
||||||
|
use Illuminate\Support\Facades\Schema;
|
||||||
|
|
||||||
|
return new class extends Migration
|
||||||
|
{
|
||||||
|
public function up(): void
|
||||||
|
{
|
||||||
|
if (! Schema::hasTable('class_progress_reports')) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
Schema::table('class_progress_reports', function (Blueprint $table) {
|
||||||
|
if (! Schema::hasColumn('class_progress_reports', 'school_year')) {
|
||||||
|
$table->string('school_year', 20)->nullable()->after('teacher_id')->index();
|
||||||
|
}
|
||||||
|
if (! Schema::hasColumn('class_progress_reports', 'semester')) {
|
||||||
|
$table->string('semester', 20)->nullable()->after('school_year')->index();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
public function down(): void
|
||||||
|
{
|
||||||
|
if (! Schema::hasTable('class_progress_reports')) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
Schema::table('class_progress_reports', function (Blueprint $table) {
|
||||||
|
if (Schema::hasColumn('class_progress_reports', 'semester')) {
|
||||||
|
$table->dropColumn('semester');
|
||||||
|
}
|
||||||
|
if (Schema::hasColumn('class_progress_reports', 'school_year')) {
|
||||||
|
$table->dropColumn('school_year');
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
};
|
||||||
@@ -478,6 +478,8 @@ Route::prefix('v1')->group(function () {
|
|||||||
|
|
||||||
Route::middleware(['auth:api', 'admin.access'])->prefix('school-years')->group(function () {
|
Route::middleware(['auth:api', 'admin.access'])->prefix('school-years')->group(function () {
|
||||||
Route::get('/', [SchoolYearController::class, 'index']);
|
Route::get('/', [SchoolYearController::class, 'index']);
|
||||||
|
Route::get('current', [SchoolYearController::class, 'current']);
|
||||||
|
Route::get('options', [SchoolYearController::class, 'options']);
|
||||||
Route::post('/', [SchoolYearController::class, 'store']);
|
Route::post('/', [SchoolYearController::class, 'store']);
|
||||||
Route::patch('{schoolYear}', [SchoolYearController::class, 'update'])->whereNumber('schoolYear');
|
Route::patch('{schoolYear}', [SchoolYearController::class, 'update'])->whereNumber('schoolYear');
|
||||||
Route::get('{schoolYear}/summary', [SchoolYearController::class, 'summary'])->whereNumber('schoolYear');
|
Route::get('{schoolYear}/summary', [SchoolYearController::class, 'summary'])->whereNumber('schoolYear');
|
||||||
|
|||||||
Reference in New Issue
Block a user