|
@@ -3,48 +3,147 @@
|
|
|
namespace App\Services;
|
|
namespace App\Services;
|
|
|
|
|
|
|
|
use App\Models\SchoolClass;
|
|
use App\Models\SchoolClass;
|
|
|
|
|
+use App\Models\ClassAttendance;
|
|
|
|
|
+use App\Models\Student;
|
|
|
use Illuminate\Database\Eloquent\Collection;
|
|
use Illuminate\Database\Eloquent\Collection;
|
|
|
|
|
+use Illuminate\Support\Facades\DB;
|
|
|
|
|
|
|
|
class ClassService
|
|
class ClassService
|
|
|
{
|
|
{
|
|
|
- public function getAll(): Collection
|
|
|
|
|
|
|
+ /**
|
|
|
|
|
+ * Aulas da unidade ativa, com nome do pacote e contadores
|
|
|
|
|
+ * (total de alunos do pacote e quantos foram marcados presentes).
|
|
|
|
|
+ */
|
|
|
|
|
+ public function getAllByUnit(int $unitId): Collection
|
|
|
{
|
|
{
|
|
|
- return SchoolClass::orderBy('created_at', 'desc')
|
|
|
|
|
- ->get();
|
|
|
|
|
|
|
+ return SchoolClass::with('packageUnit')
|
|
|
|
|
+ ->withCount([
|
|
|
|
|
+ 'attendances as present_count' => fn ($q) => $q->where('in_class', true),
|
|
|
|
|
+ ])
|
|
|
|
|
+ ->where('unit_id', $unitId)
|
|
|
|
|
+ ->orderBy('date_time_start')
|
|
|
|
|
+ ->get()
|
|
|
|
|
+ ->each(function (SchoolClass $class) {
|
|
|
|
|
+ $class->students_count = $this->activeStudentsQuery($class->class_package_unit_id, $class->unit_id)->count();
|
|
|
|
|
+ });
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
public function findById(int $id): ?SchoolClass
|
|
public function findById(int $id): ?SchoolClass
|
|
|
{
|
|
{
|
|
|
- return SchoolClass::find($id);
|
|
|
|
|
|
|
+ return SchoolClass::with('packageUnit')->find($id);
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
public function create(array $data): SchoolClass
|
|
public function create(array $data): SchoolClass
|
|
|
{
|
|
{
|
|
|
- return SchoolClass::create($data);
|
|
|
|
|
|
|
+ return SchoolClass::create($data)->load('packageUnit');
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
public function update(int $id, array $data): ?SchoolClass
|
|
public function update(int $id, array $data): ?SchoolClass
|
|
|
{
|
|
{
|
|
|
- $model = $this->findById($id);
|
|
|
|
|
|
|
+ $model = SchoolClass::find($id);
|
|
|
|
|
|
|
|
if (!$model) {
|
|
if (!$model) {
|
|
|
return null;
|
|
return null;
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
$model->update($data);
|
|
$model->update($data);
|
|
|
- return $model->fresh();
|
|
|
|
|
|
|
+ return $model->fresh('packageUnit');
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
public function delete(int $id): bool
|
|
public function delete(int $id): bool
|
|
|
{
|
|
{
|
|
|
- $model = $this->findById($id);
|
|
|
|
|
|
|
+ $model = SchoolClass::find($id);
|
|
|
|
|
|
|
|
if (!$model) {
|
|
if (!$model) {
|
|
|
return false;
|
|
return false;
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
- return $model->delete();
|
|
|
|
|
|
|
+ return (bool) $model->delete();
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ /**
|
|
|
|
|
+ * Lista os alunos com contrato ATIVO no pacote vinculado à aula,
|
|
|
|
|
+ * já mesclando a presença registrada (se houver) para aquela aula.
|
|
|
|
|
+ */
|
|
|
|
|
+ public function getStudentsForClass(int $classId): array
|
|
|
|
|
+ {
|
|
|
|
|
+ $class = SchoolClass::find($classId);
|
|
|
|
|
+ if (!$class) {
|
|
|
|
|
+ return [];
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ $students = $this->activeStudentsQuery($class->class_package_unit_id, $class->unit_id)
|
|
|
|
|
+ ->orderBy('students.name')
|
|
|
|
|
+ ->get();
|
|
|
|
|
+
|
|
|
|
|
+ // Presenças já registradas nesta aula, indexadas por aluno
|
|
|
|
|
+ $attendances = ClassAttendance::where('class_id', $classId)
|
|
|
|
|
+ ->get()
|
|
|
|
|
+ ->keyBy('student_id');
|
|
|
|
|
+
|
|
|
|
|
+ // Total de aulas presenciadas por aluno (em qualquer aula)
|
|
|
|
|
+ $attendedCounts = ClassAttendance::select('student_id', DB::raw('COUNT(*) as total'))
|
|
|
|
|
+ ->whereIn('student_id', $students->pluck('id'))
|
|
|
|
|
+ ->where('in_class', true)
|
|
|
|
|
+ ->groupBy('student_id')
|
|
|
|
|
+ ->pluck('total', 'student_id');
|
|
|
|
|
+
|
|
|
|
|
+ return $students->map(function (Student $student) use ($attendances, $attendedCounts) {
|
|
|
|
|
+ $attendance = $attendances->get($student->id);
|
|
|
|
|
+
|
|
|
|
|
+ return [
|
|
|
|
|
+ 'student_id' => $student->id,
|
|
|
|
|
+ 'name' => $student->name,
|
|
|
|
|
+ 'photo_url' => $student->photo_url,
|
|
|
|
|
+ 'classes_count' => (int) ($attendedCounts[$student->id] ?? 0),
|
|
|
|
|
+ 'in_class' => (bool) ($attendance?->in_class ?? false),
|
|
|
|
|
+ 'justified' => (bool) ($attendance?->justified ?? false),
|
|
|
|
|
+ 'notes' => $attendance?->notes,
|
|
|
|
|
+ ];
|
|
|
|
|
+ })->all();
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
- // Add custom business logic methods here
|
|
|
|
|
|
|
+ /**
|
|
|
|
|
+ * Salva a presença da aula em lote (upsert por aluno).
|
|
|
|
|
+ *
|
|
|
|
|
+ * @param array<int, array{student_id:int, in_class:bool, justified?:bool, notes?:string|null}> $items
|
|
|
|
|
+ */
|
|
|
|
|
+ public function saveAttendance(int $classId, array $items): void
|
|
|
|
|
+ {
|
|
|
|
|
+ DB::transaction(function () use ($classId, $items) {
|
|
|
|
|
+ foreach ($items as $item) {
|
|
|
|
|
+ $inClass = (bool) ($item['in_class'] ?? false);
|
|
|
|
|
+ // Justificativa só vale para faltas; presença nunca fica justificada.
|
|
|
|
|
+ $justified = $inClass ? false : (bool) ($item['justified'] ?? false);
|
|
|
|
|
+
|
|
|
|
|
+ ClassAttendance::updateOrCreate(
|
|
|
|
|
+ [
|
|
|
|
|
+ 'class_id' => $classId,
|
|
|
|
|
+ 'student_id' => $item['student_id'],
|
|
|
|
|
+ ],
|
|
|
|
|
+ [
|
|
|
|
|
+ 'in_class' => $inClass,
|
|
|
|
|
+ 'justified' => $justified,
|
|
|
|
|
+ 'notes' => $justified ? ($item['notes'] ?? null) : null,
|
|
|
|
|
+ ],
|
|
|
|
|
+ );
|
|
|
|
|
+ }
|
|
|
|
|
+ });
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ /**
|
|
|
|
|
+ * Query base: alunos com contrato ativo no pacote informado (e na unidade).
|
|
|
|
|
+ */
|
|
|
|
|
+ private function activeStudentsQuery(?int $classPackageUnitId, int $unitId)
|
|
|
|
|
+ {
|
|
|
|
|
+ return Student::query()
|
|
|
|
|
+ ->whereHas('contracts', function ($q) use ($classPackageUnitId, $unitId) {
|
|
|
|
|
+ $q->where('status', 'active')
|
|
|
|
|
+ ->where('unit_id', $unitId);
|
|
|
|
|
+
|
|
|
|
|
+ if ($classPackageUnitId) {
|
|
|
|
|
+ $q->where('class_package_unit_id', $classPackageUnitId);
|
|
|
|
|
+ }
|
|
|
|
|
+ });
|
|
|
|
|
+ }
|
|
|
}
|
|
}
|