record_assenze = []; $end_day = now()->yesterday(); $limit = $end_day->endOfDay(); $this->year = ($end_day->month >= $this->fiscalStartMonth) ? $end_day->year : $end_day->year - 1; $dayMap = [ 'lun' => 1, 'mar' => 2, 'mer' => 3, 'gio' => 4, 'ven' => 5, 'sab' => 6, 'dom' => 0, ]; try { $courses = \App\Models\Course::whereDate('date_from', '<=', $limit) ->whereDate('date_to', '>=', $limit) ->where('active', true) ->where('enabled', true) ->get(); foreach ($courses as $course) { $course_members = \App\Models\MemberCourse::with(['member' => function ($q) { $q->where(function ($q) { $q->where('is_archived', false)->orWhereNull('is_archived'); })->where(function ($q) { $q->where('is_deleted', false)->orWhereNull('is_deleted'); }); }]) ->where('course_id', $course->id) ->whereHas('member', function ($q) { $q->where(function ($q) { $q->where('is_archived', false)->orWhereNull('is_archived'); })->where(function ($q) { $q->where('is_deleted', false)->orWhereNull('is_deleted'); }); }) ->get(); $courseCalendars = \App\Models\Calendar::where('course_id', $course->id)->where('from', '<=', $limit)->where('status', '<>', 99)->get(); $calendarIndex = []; foreach ($courseCalendars as $cal) { $fromKey = Carbon::parse($cal->from)->toDateTimeString(); $toKey = Carbon::parse($cal->to)->toDateTimeString(); $key = $fromKey . '|' . $toKey; $calendarIndex[$key] = $cal; } $memberIds = $course_members->pluck('member_id')->unique()->values(); $makeups = \App\Models\Presence::query() ->join('calendars', 'presences.calendar_id', '=', 'calendars.id') ->where('presences.motivation_course_id', $course->id) ->whereIn('presences.member_id', $memberIds) ->where('presences.status', '<>', 99) ->where('calendars.from', '<=', $limit) ->where('calendars.status', '<>', 99) ->selectRaw('presences.member_id, MAX(calendars.from) as last_makeup_from') ->groupBy('presences.member_id') ->get(); $lastMakeupByMember = []; foreach ($makeups as $m) { $lastMakeupByMember[$m->member_id] = Carbon::parse($m->last_makeup_from); } $this->record_assenze[$course->id] = [ 'course' => [ 'id' => $course->id, 'name' => $course->getDetailsName(), ], 'members' => [], ]; foreach ($course_members as $course_member) { $this->record_assenze[$course->id]['members'][$course_member->id] = [ 'member' => [ 'id' => $course_member->member->id, 'first_name' => $course_member->member->first_name, 'last_name' => $course_member->member->last_name, ], 'count' => 0, 'dates' => [], ]; $months = array_column( array_filter(json_decode($course_member->months, true), fn($m) => $m['status'] != 2), 'm' ); sort($months); $when = json_decode($course_member->when, true); $memberCalendars = []; foreach ($when as $period) { $days = $period['day']; if (!$days) { continue; } $fromTime = $period['from']; $toTime = $period['to']; $ranges = $this->generateFiscalDateRanges($months, $days, $dayMap, $fromTime, $toTime); foreach ($ranges as $range) { $key = $range['from']->toDateTimeString() . '|' . $range['to']->toDateTimeString(); if (isset($calendarIndex[$key])) { $memberCalendars[] = $calendarIndex[$key]; } } } usort($memberCalendars, fn($a, $b) => $b->to <=> $a->to); $mid = $course_member->member_id; $lastAttendance = $lastMakeupByMember[$mid] ?? null; foreach ($memberCalendars as $calendar) { $lessonFrom = Carbon::parse($calendar->from); if ($lastAttendance && $lessonFrom->lte($lastAttendance)) { break; } $pKey = $mid . '|' . $calendar->id; $hasPresence = isset($presenceIndex[$pKey]); if ($hasPresence) { break; } // assenza vera $this->record_assenze[$course->id]['members'][$course_member->id]['count']++; $this->record_assenze[$course->id]['members'][$course_member->id]['dates'][] = [ 'calendar_id' => $calendar->id, 'date' => $lessonFrom->translatedFormat('d/m'), ]; } if ($this->record_assenze[$course->id]['members'][$course_member->id]['count'] < 2) { unset($this->record_assenze[$course->id]['members'][$course_member->id]); } } if (empty($this->record_assenze[$course->id]['members'])) { unset($this->record_assenze[$course->id]); } else { $members = $this->record_assenze[$course->id]['members']; usort($members, function ($a, $b) { if ($a['count'] !== $b['count']) { return $b['count'] <=> $a['count']; } $last = strcmp($a['member']['last_name'], $b['member']['last_name']); if ($last !== 0) { return $last; } return strcmp($a['member']['first_name'], $b['member']['first_name']); }); $this->record_assenze[$course->id]['members'] = $members; } } usort($this->record_assenze, fn($a, $b) => $a['course']['name'] <=> $b['course']['name']); } catch (\Throwable $e) { dd($e->getMessage()); } $this->original_record_assenze = $this->record_assenze; } public function render() { setlocale(LC_ALL, 'it_IT'); // $this->records = []; // $to = date("Y-m-d 23:59:59"); // $calendars = \App\Models\Calendar::where('from', '<=', $to)->orderBy('from')->get(); // $month = date("n"); // $this->year = ($month >= 9) ? date("Y") : date("Y") - 1; // $start = date("Y-m-d H:i:s", mktime(0, 0, 0, 9, 1, $this->year)); // $end = date("Y-m-d 23:59:59"); // $calendars = \App\Models\Calendar::whereBetween('from', [$start, $end])->orderBy('from')->get(); // foreach ($calendars as $calendar) { // $presences = \App\Models\Presence::where('calendar_id', $calendar->id)->where('status', '<>', 99); // $presences = $presences->pluck('member_id')->toArray(); // $presences_annullate = \App\Models\Presence::where('calendar_id', $calendar->id)->where('status', 99)->pluck('member_id')->toArray(); // $days = ['dom', 'lun', 'mar', 'mer', 'gio', 'ven', 'sab']; // $dow = date('w', strtotime($calendar->from)); // $d = $days[$dow]; // $h = date('H:i', strtotime($calendar->from)); // // Elenco corsi per tipologia in base al calendario // $courses = \App\Models\Course::where('name', $calendar->name)->where('date_from', '<=', $calendar->from)->where('date_to', '>=', $calendar->to); // $courses = $courses->pluck('id')->toArray(); // $months = date("n", strtotime($calendar->from)); // // Elenco utenti iscritti al corso "padre" // $members = \App\Models\MemberCourse::where('when', 'like', "%" . $d . "%") // ->where('when', 'like', '%"from":"' . $h . '"%') // ->whereNot('months', 'like', '%"m":' . $months . ',"status":2%') // ->whereDate('date_from', '<=', $calendar->from) // ->whereDate('date_to', '>=', $calendar->from) // ->whereIn('course_id', $courses) // ->orderBy('member_id') // ->get(); // //$members = \App\Models\MemberCourse::where('when', 'like', "%" . $d . "%")->where('when', 'like', '%"from":"' . $h . '"%')->whereIn('member_id', $presences)->whereIn('course_id', $courses)->get(); // foreach ($members as $member) { // $presence = \App\Models\Presence::where('member_id', $member->member->id)->where('calendar_id', $calendar->id)->first(); // if (!in_array($member->member->id, $presences)) { // if (!in_array($member->member->id, $presences_annullate)) { // if (array_key_exists($member->member->id, $this->records)) { // $this->records[$member->member->id]['total'] += 1; // $this->records[$member->member->id]['date'] .= " - " . date("d/m", strtotime($calendar->from)); // } else // $this->records[$member->member->id] = array("last_name" => $member->member->last_name, "first_name" => $member->member->first_name, "course" => $calendar->name, "total" => 1, "date" => date("d/m", strtotime($calendar->from))); // } // } else { // if (array_key_exists($member->member->id, $this->records)) // unset($this->records[$member->member->id]); // } // } // } // array_multisort(array_column($this->records, 'total'), SORT_DESC, $this->records); return view('livewire.absence_report'); } public function applySearch() { $this->search = trim($this->search); $this->filter(); } public function resetSearch() { $this->search = ''; $this->record_assenze = $this->original_record_assenze; } protected function filter() { $this->record_assenze = $this->original_record_assenze; if ($this->search === '') return; $needle = mb_strtolower($this->search); foreach ($this->record_assenze as $courseId => $courseData) { $courseData['members'] = array_values(array_filter($courseData['members'], function ($m) use ($needle) { $full = mb_strtolower((trim($m['member']['last_name']) ?? '') . ' ' . (trim($m['member']['first_name']) ?? '')); $full2 = mb_strtolower((trim($m['member']['first_name']) ?? '') . ' ' . (trim($m['member']['last_name']) ?? '')); return str_contains($full, $needle) || str_contains($full2, $needle); })); if (empty($courseData['members'])) { unset($this->record_assenze[$courseId]); } else { $this->record_assenze[$courseId] = $courseData; } } } protected function generateFiscalDateRanges($months, $days, $dayMap, $fromTime, $toTime) { $limit = now()->yesterday()->endOfDay(); $end_day = $limit; $startYear = ($end_day->month >= $this->fiscalStartMonth) ? $end_day->year : $end_day->year - 1; $allowedDow = collect($days) ->map(fn($d) => $dayMap[$d] ?? null) ->filter(fn($v) => $v !== null) ->unique() ->values(); $fromCarbon = Carbon::parse($fromTime); $toCarbon = Carbon::parse($toTime); $fromHour = $fromCarbon->hour; $fromMinute = $fromCarbon->minute; $fromSecond = $fromCarbon->second; $toHour = $toCarbon->hour; $toMinute = $toCarbon->minute; $toSecond = $toCarbon->second; $ranges = []; foreach ($months as $month) { $yearForMonth = ($month >= $this->fiscalStartMonth) ? $startYear : $startYear + 1; if ( $yearForMonth > $limit->year || ($yearForMonth === $limit->year && $month > $limit->month) ) { continue; } $firstOfMonth = Carbon::create($yearForMonth, $month, 1)->startOfDay(); foreach ($allowedDow as $dow) { $offset = ($dow - $firstOfMonth->dayOfWeek + 7) % 7; $current = $firstOfMonth->copy()->addDays($offset); while ($current->month === $month) { $fromDateTime = $current->copy()->setTime($fromHour, $fromMinute, $fromSecond); $toDateTime = $current->copy()->setTime($toHour, $toMinute, $toSecond); if ($fromDateTime->gt($limit)) { break; } $ranges[] = [ 'from' => $fromDateTime, 'to' => $toDateTime, ]; $current->addWeek(); } } } usort($ranges, fn($a, $b) => $a['from'] <=> $b['from']); return $ranges; } }