@php $user = auth()->user(); $household = $user->employers()->whereNull('archived_at')->first() ?? \App\Models\Employer::query() ->where('linked_parent_user_id', $user->id) ->whereNull('archived_at') ->first(); $completion = app(\App\Support\HouseholdProfileCompletion::class)->summary($household); $profileDone = $completion['is_complete'] && $completion['has_children']; $agendaNounou = $household?->user?->isNounou() ? $household->user : $household?->nounouLinks()->whereNull('unlinked_at')->latest('linked_at')->first()?->user; $agendaHasActiveContract = $household ? $household->children()->whereNull('archived_at')->whereHas('contracts', fn ($query) => $query->where('status', 'active'))->exists() : false; $agendaIcsUrl = ($agendaNounou && $agendaHasActiveContract) ? url('/agenda/'.\Illuminate\Support\Str::slug($agendaNounou->name ?: 'assmat').'-'.$agendaNounou->id .'/family-'.\Illuminate\Support\Str::slug($household->family_label).'-'.$household->id.'.ics') : null; $firstName = trim($user->first_name ?: (explode(' ', trim($user->name))[0] ?? '')); $todayLabel = ucfirst(now()->locale('fr')->isoFormat('dddd D MMMM')); $dayKeys = ['monday', 'tuesday', 'wednesday', 'thursday', 'friday', 'saturday', 'sunday']; $dayShortLabels = ['monday' => 'Lun', 'tuesday' => 'Mar', 'wednesday' => 'Mer', 'thursday' => 'Jeu', 'friday' => 'Ven', 'saturday' => 'Sam', 'sunday' => 'Dim']; $planningMode = in_array((string) request('planning_days'), ['7', 'weekend'], true) ? (string) request('planning_days') : '5'; $planningDaysCount = $planningMode === 'weekend' ? 7 : (int) $planningMode; $childrenQuery = \App\Models\ChildRecord::query() ->whereNull('archived_at') ->whereHas('employer', function ($query) use ($user): void { $query ->whereNull('archived_at') ->where(function ($query) use ($user): void { $query ->where('user_id', $user->id) ->orWhere('linked_parent_user_id', $user->id); }); }); $requestedPlanningWeek = request('planning_week'); try { $parsedPlanningWeek = $requestedPlanningWeek ? \Carbon\Carbon::createFromFormat('!Y-m-d', $requestedPlanningWeek) : null; $planningWeekStart = $parsedPlanningWeek ? $parsedPlanningWeek->copy() : now()->startOfWeek(); } catch (\Throwable) { $planningWeekStart = now()->startOfWeek(); } $planningWeekEnd = $planningWeekStart->copy()->addDays($planningDaysCount - 1)->endOfDay(); $planningWeekLabel = $planningWeekStart->translatedFormat('j').' – '.$planningWeekEnd->translatedFormat('j F Y'); $requestedPlanningDay = request('planning_day'); try { $parsedPlanningDay = $requestedPlanningDay ? \Carbon\Carbon::createFromFormat('!Y-m-d', $requestedPlanningDay) : null; } catch (\Throwable) { $parsedPlanningDay = null; } $previousPlanningWeekUrl = route('parent.dashboard', array_filter([ 'planning_week' => $planningWeekStart->copy()->subWeek()->toDateString(), 'planning_days' => $planningMode, 'payment_month' => request('payment_month'), ])); $nextPlanningWeekUrl = route('parent.dashboard', array_filter([ 'planning_week' => $planningWeekStart->copy()->addWeek()->toDateString(), 'planning_days' => $planningMode, 'payment_month' => request('payment_month'), ])); $weekJumpBaseUrl = route('parent.dashboard', array_filter([ 'planning_days' => $planningMode, 'payment_month' => request('payment_month'), ])); $weekJumpUrlPrefix = $weekJumpBaseUrl.(str_contains($weekJumpBaseUrl, '?') ? '&' : '?').'planning_week='; $fiveDaysPlanningUrl = route('parent.dashboard', array_filter([ 'planning_week' => $planningWeekStart->toDateString(), 'planning_day' => request('planning_day'), 'planning_days' => 5, 'payment_month' => request('payment_month'), ])); $sevenDaysPlanningUrl = route('parent.dashboard', array_filter([ 'planning_week' => $planningWeekStart->toDateString(), 'planning_day' => request('planning_day'), 'planning_days' => 7, 'payment_month' => request('payment_month'), ])); $weekendPlanningUrl = route('parent.dashboard', array_filter([ 'planning_week' => $planningWeekStart->toDateString(), 'planning_day' => request('planning_day'), 'planning_days' => 'weekend', 'payment_month' => request('payment_month'), ])); $planningContractStatuses = ['draft', 'cancelled']; $planningChildren = (clone $childrenQuery) ->whereHas('contracts', fn ($query) => $query->whereNotIn('status', $planningContractStatuses)->whereNotNull('weekly_schedule')) ->with(['employer.nounouLinks.user', 'contracts' => fn ($query) => $query->whereNotIn('status', $planningContractStatuses)->whereNotNull('weekly_schedule')->orderByDesc('started_on')]) ->orderBy('first_name') ->get(); $planningOverrides = \App\Models\PlanningOverride::query() ->whereIn('child_record_id', $planningChildren->pluck('id')) ->whereBetween('date', [$planningWeekStart->toDateString(), $planningWeekEnd->toDateString()]) ->get() ->keyBy(fn ($override): string => $override->date->toDateString().'|'.$override->child_record_id); $planningNounouIds = $planningChildren ->pluck('employer.nounouLinks') ->flatten() ->whereNull('unlinked_at') ->pluck('user_id') ->unique() ->values(); $planningLeaves = \App\Models\LeavePeriod::query() ->whereIn('user_id', $planningNounouIds) ->whereDate('started_on', '<=', $planningWeekEnd->toDateString()) ->whereDate('ended_on', '>=', $planningWeekStart->toDateString()) ->with('contracts') ->get(); $planningActivitiesByDate = \App\Models\PlanningActivity::query() ->whereIn('user_id', $planningNounouIds) ->whereBetween('date', [$planningWeekStart->toDateString(), $planningWeekEnd->toDateString()]) ->orderBy('time') ->get() ->groupBy(fn ($activity): string => $activity->date->toDateString()); $planningHolidaysByDate = collect(\App\Support\FrenchPublicHolidays::between($planningWeekStart, $planningWeekEnd)) ->keyBy('date'); $planningSlotMinutes = function (?string $start, ?string $end): int { if (! $start || ! $end) { return 0; } return abs(\Carbon\Carbon::parse($end)->diffInMinutes(\Carbon\Carbon::parse($start))); }; $planningDayEntries = function (\Carbon\Carbon $date) use ($planningChildren, $planningOverrides, $dayKeys, $planningSlotMinutes) { return $planningChildren ->map(function ($child) use ($date, $planningOverrides, $dayKeys, $planningSlotMinutes) { $contract = $child->contracts->first(function ($contract) use ($date): bool { $effectiveEnd = collect([ $contract->ended_at?->toDateString(), $contract->ends_on?->toDateString(), ])->filter()->min(); return (! $contract->started_on || $contract->started_on->lte($date)) && ($effectiveEnd === null || $date->toDateString() <= $effectiveEnd); }); if (! $contract) { return null; } $override = $planningOverrides->get($date->toDateString().'|'.$child->id); $isAbsent = $override?->status === 'absent'; $schedule = $contract->effectiveWeeklySchedule(); $dayKey = $dayKeys[$date->dayOfWeekIso - 1]; $plannedSlot = $schedule[$dayKey] ?? null; if (! $override && ! ($plannedSlot['enabled'] ?? false)) { return null; } $start = $override?->actual_start_time ? substr($override->actual_start_time, 0, 5) : ($override?->start_time ? substr($override->start_time, 0, 5) : ($plannedSlot['start'] ?? null)); $end = $override?->actual_end_time ? substr($override->actual_end_time, 0, 5) : ($override?->end_time ? substr($override->end_time, 0, 5) : ($plannedSlot['end'] ?? null)); $minutes = $planningSlotMinutes($start, $end); if (! $isAbsent && $minutes <= 0) { return null; } $nounou = $child->employer->nounouLinks->firstWhere('unlinked_at', null)?->user; $isPastDay = $date->copy()->startOfDay()->lt(now()->startOfDay()); $minimumParentChangeDate = now()->startOfDay()->addWeeks(max(0, (int) $contract->schedule_notice_weeks)); $canAdjustSchedule = ! $isPastDay && $date->copy()->startOfDay()->gte($minimumParentChangeDate); return [ 'initials' => mb_strtoupper(mb_substr($child->first_name, 0, 1).mb_substr((string) $child->last_name, 0, 1)), 'color' => \App\Support\ChildColorPalette::key($child), 'child' => $child->first_name ?: 'Mon enfant', 'child_last_name' => $child->last_name, 'family' => $nounou ? trim($nounou->first_name.' '.$nounou->last_name) : 'Mon assistante maternelle', 'date' => $date->toDateString(), 'modal_id' => 'slot-'.$date->format('Ymd').'-'.$child->id, 'start' => $start, 'end' => $end, 'planned_start' => $plannedSlot['start'] ?? $start, 'planned_end' => $plannedSlot['end'] ?? $end, 'hours' => $minutes > 0 ? intdiv($minutes, 60).'h'.str_pad((string) ($minutes % 60), 2, '0', STR_PAD_LEFT) : null, 'minutes' => $isAbsent ? 0 : $minutes, 'is_absent' => $isAbsent, 'status_label' => $isAbsent ? ('Absence '.($override->reason === 'injust' ? 'injustifiée' : 'justifiée')) : ($contract->status === 'to_sign' ? 'En attente de signature' : 'Confirmé'), 'status_class' => $isAbsent ? 'dashboard-week-status-locked' : ($contract->status === 'to_sign' ? 'dashboard-week-status-pending' : ''), 'status_icon' => $isAbsent ? 'ph-eye-slash' : ($contract->status === 'to_sign' ? 'ph-hourglass' : 'ph-check-circle'), 'can_open' => ! $isPastDay, 'can_adjust_schedule' => $canAdjustSchedule, 'locked_label' => 'Garde passée', 'update_url' => route('parent.planning.overrides.update', [$date->toDateString(), $child]), ]; }) ->filter() ->sortBy('start') ->values(); }; $planningWeekDays = collect(\Carbon\CarbonPeriod::create($planningWeekStart, $planningWeekEnd)) ->map(function ($date) use ($dayShortLabels, $dayKeys, $planningDayEntries, $planningMode, $planningActivitiesByDate, $planningHolidaysByDate, $planningLeaves) { $dayLeave = \App\Models\LeavePeriod::coveringDate($planningLeaves, $date); $entries = $dayLeave ? collect() : $planningDayEntries($date); $minutes = $entries->sum('minutes'); return [ 'date' => $date->copy(), 'label' => mb_strtoupper($dayShortLabels[$dayKeys[$date->dayOfWeekIso - 1]]).'.', 'day_number' => $date->format('d'), 'is_selected' => false, 'is_weekend' => $date->isWeekend(), 'entries' => $entries, 'activities' => $dayLeave ? collect() : $planningActivitiesByDate->get($date->toDateString(), collect()), 'holiday' => $planningHolidaysByDate->get($date->toDateString()), 'leave' => $dayLeave ? [ 'label' => \App\Models\LeavePeriod::TYPE_LABELS[$dayLeave->leave_type] ?? 'Absence', 'class' => \App\Models\LeavePeriod::TYPE_CSS_CLASSES[$dayLeave->leave_type] ?? 'planning-allday-pill--leave-other', 'icon' => \App\Models\LeavePeriod::TYPE_ICONS[$dayLeave->leave_type] ?? 'ph ph-island', ] : null, 'minutes' => $minutes, 'hours' => intdiv($minutes, 60).'h'.($minutes % 60 ? str_pad((string) ($minutes % 60), 2, '0', STR_PAD_LEFT) : ''), 'url' => route('parent.dashboard', array_filter([ 'planning_week' => $date->copy()->startOfWeek()->toDateString(), 'planning_day' => $date->toDateString(), 'planning_days' => $planningMode, 'payment_month' => request('payment_month'), ])), ]; }) ->when($planningMode === 'weekend', fn ($days) => $days->filter(fn ($day) => $day['date']->isWeekend())->values()); $planningWeekTotalMinutes = (int) $planningWeekDays->sum('minutes'); $planningWeekTotalLabel = intdiv($planningWeekTotalMinutes, 60).'h'.($planningWeekTotalMinutes % 60 ? str_pad((string) ($planningWeekTotalMinutes % 60), 2, '0', STR_PAD_LEFT) : '').' prévues'; $selectedPlanningDate = $parsedPlanningDay && $parsedPlanningDay->betweenIncluded($planningWeekStart, $planningWeekEnd) && ($planningMode !== 'weekend' || $parsedPlanningDay->isWeekend()) ? $parsedPlanningDay : ($planningWeekDays->first(fn ($day) => $day['entries']->isNotEmpty())['date'] ?? ($planningMode === 'weekend' ? $planningWeekStart->copy()->addDays(5) : (now()->betweenIncluded($planningWeekStart, $planningWeekEnd) ? now()->startOfDay() : $planningWeekStart->copy()))); $planningWeekDays = $planningWeekDays ->map(function ($day) use ($selectedPlanningDate) { $day['is_selected'] = $day['date']->isSameDay($selectedPlanningDate); return $day; }) ->values(); $selectedPlanningDay = $planningWeekDays->firstWhere('is_selected', true) ?? $planningWeekDays->first(); $requestedPaymentMonth = request('payment_month'); try { $parsedPaymentMonth = $requestedPaymentMonth ? \Carbon\Carbon::createFromFormat('!Y-m', $requestedPaymentMonth) : null; $paymentMonth = $parsedPaymentMonth ? $parsedPaymentMonth->startOfMonth() : now()->startOfMonth(); } catch (\Throwable) { $paymentMonth = now()->startOfMonth(); } $paymentMonthEnd = $paymentMonth->copy()->endOfMonth(); $paymentMonthLabel = ucfirst($paymentMonth->copy()->locale('fr')->isoFormat('MMMM YYYY')); $isPaymentProjection = $paymentMonth->greaterThan(now()->startOfMonth()); $previousPaymentMonthUrl = route('parent.dashboard', ['payment_month' => $paymentMonth->copy()->subMonth()->format('Y-m')]); $nextPaymentMonthUrl = route('parent.dashboard', ['payment_month' => $paymentMonth->copy()->addMonth()->format('Y-m')]); $timeMinutes = function (?string $time): ?int { if (! $time) { return null; } [$hours, $minutes] = array_map('intval', array_slice(explode(':', $time), 0, 2)); return ($hours * 60) + $minutes; }; $slotMinutes = function (?string $start, ?string $end) use ($timeMinutes): int { $startMinutes = $timeMinutes($start); $endMinutes = $timeMinutes($end); return $startMinutes !== null && $endMinutes !== null && $endMinutes > $startMinutes ? $endMinutes - $startMinutes : 0; }; $formatHours = fn (int $minutes): string => intdiv($minutes, 60).'h'.str_pad((string) ($minutes % 60), 2, '0', STR_PAD_LEFT); $formatMoney = fn (float $amount): string => number_format($amount, 2, ',', ' ').' €'; $paymentMissing = []; $contractStatusesForPayment = ['draft', 'cancelled']; $paymentChildren = (clone $childrenQuery) ->whereHas('contracts', function ($query) use ($contractStatusesForPayment, $paymentMonth, $paymentMonthEnd): void { $query ->where(function ($query) use ($contractStatusesForPayment, $paymentMonth): void { $query->whereNotIn('status', $contractStatusesForPayment) ->orWhere(function ($query) use ($paymentMonth): void { $query->where('status', 'draft')->whereExists(function ($declarations) use ($paymentMonth): void { $declarations->selectRaw('1')->from('imported_monthly_declarations') ->whereColumn('imported_monthly_declarations.child_record_id', 'contracts.child_record_id') ->whereDate('month', $paymentMonth->toDateString()); }); }); }) ->whereNotNull('started_on') ->whereDate('started_on', '<=', $paymentMonthEnd) ->where(function ($query) use ($paymentMonth): void { $query->whereNull('ended_at')->orWhereDate('ended_at', '>=', $paymentMonth); }) ->where(function ($query) use ($paymentMonth): void { $query->whereNull('ends_on')->orWhereDate('ends_on', '>=', $paymentMonth); }); }) ->with(['employer.children', 'employer.nounouLinks.user', 'contracts' => function ($query) use ($contractStatusesForPayment, $paymentMonth, $paymentMonthEnd): void { $query ->where(function ($query) use ($contractStatusesForPayment, $paymentMonth): void { $query->whereNotIn('status', $contractStatusesForPayment) ->orWhere(function ($query) use ($paymentMonth): void { $query->where('status', 'draft')->whereExists(function ($declarations) use ($paymentMonth): void { $declarations->selectRaw('1')->from('imported_monthly_declarations') ->whereColumn('imported_monthly_declarations.child_record_id', 'contracts.child_record_id') ->whereDate('month', $paymentMonth->toDateString()); }); }); }) ->whereNotNull('started_on') ->whereDate('started_on', '<=', $paymentMonthEnd) ->where(function ($query) use ($paymentMonth): void { $query->whereNull('ended_at')->orWhereDate('ended_at', '>=', $paymentMonth); }) ->where(function ($query) use ($paymentMonth): void { $query->whereNull('ends_on')->orWhereDate('ends_on', '>=', $paymentMonth); }) ->orderByDesc('started_on'); }]) ->orderBy('first_name') ->get(); $paymentOverrides = \App\Models\PlanningOverride::query() ->whereIn('child_record_id', $paymentChildren->pluck('id')) ->whereBetween('date', [$paymentMonth->toDateString(), $paymentMonthEnd->toDateString()]) ->get() ->keyBy(fn ($override): string => $override->date->toDateString().'|'.$override->child_record_id); $paymentNounouIds = $paymentChildren ->pluck('employer.nounouLinks') ->flatten() ->whereNull('unlinked_at') ->pluck('user_id') ->unique() ->values(); $paymentLeaves = \App\Models\LeavePeriod::query() ->whereIn('user_id', $paymentNounouIds) ->whereDate('started_on', '<=', $paymentMonthEnd->toDateString()) ->whereDate('ended_on', '>=', $paymentMonth->toDateString()) ->with('contracts') ->get(); $paymentMonthKey = $paymentMonth->format('Y-m'); $paymentRows = $paymentChildren ->map(function ($child) use ($dayKeys, $paymentMonth, $paymentMonthEnd, $paymentMonthKey, $paymentMonthLabel, $paymentOverrides, $paymentLeaves, $slotMinutes, $formatHours, $formatMoney) { $minutes = 0; $days = 0; $maintenanceTotal = 0.0; $maintenanceDays = 0; $mealTotal = 0.0; $mealDays = 0; $snackTotal = 0.0; $snackDays = 0; $travelTotal = 0.0; $travelKmSum = 0.0; $dayRecords = []; $currentContract = null; foreach (\Carbon\CarbonPeriod::create($paymentMonth, $paymentMonthEnd) as $date) { $contract = $child->contracts->first(function ($contract) use ($date): bool { $effectiveEnd = collect([ $contract->ended_at?->toDateString(), $contract->ends_on?->toDateString(), ])->filter()->min(); return $contract->started_on && $contract->started_on->lte($date) && ($effectiveEnd === null || $date->toDateString() <= $effectiveEnd); }); if (! $contract) { continue; } $currentContract = $contract; $dayKey = $dayKeys[$date->dayOfWeekIso - 1]; $override = $paymentOverrides->get($date->toDateString().'|'.$child->id); $leave = \App\Models\LeavePeriod::coveringDate($paymentLeaves, $date); if ($override?->status === 'absent' || $leave !== null) { if ($leave !== null) { $missedSlot = $contract->effectiveWeeklySchedule()[$dayKey] ?? null; $missedMinutes = ($missedSlot['enabled'] ?? false) ? $slotMinutes($missedSlot['start'] ?? null, $missedSlot['end'] ?? null) : 0; $dayRecords[] = ['date' => \Carbon\CarbonImmutable::instance($date), 'minutes' => 0, 'amount' => 0.0, 'leaveType' => $leave->leave_type, 'missedMinutes' => $missedMinutes]; } continue; } $schedule = $contract->effectiveWeeklySchedule(); $plannedSlot = $schedule[$dayKey] ?? null; $plannedStart = $override?->start_time ? substr($override->start_time, 0, 5) : ($plannedSlot['start'] ?? null); $plannedEnd = $override?->end_time ? substr($override->end_time, 0, 5) : ($plannedSlot['end'] ?? null); $actualStart = $override?->actual_start_time ? substr($override->actual_start_time, 0, 5) : null; $actualEnd = $override?->actual_end_time ? substr($override->actual_end_time, 0, 5) : null; $dayMinutes = $slotMinutes($actualStart ?: $plannedStart, $actualEnd ?: $plannedEnd); if ($dayMinutes <= 0 || (! $override && ! ($plannedSlot['enabled'] ?? false))) { continue; } $dayAmount = ($dayMinutes / 60) * (float) $contract->hourly_rate_net; $days++; $minutes += $dayMinutes; if ((float) $contract->maintenance_indemnity > 0) { $maintenanceTotal += (float) $contract->maintenance_indemnity; $maintenanceDays++; } if ($contract->meal_provider === 'assistant') { $mealTotal += (float) $contract->meal_indemnity; $mealDays++; } if ($contract->snack_provider === 'assistant') { $snackTotal += (float) $contract->snack_indemnity; $snackDays++; } $dayTravelKm = (float) ($override?->travel_km ?? 0); if ($dayTravelKm > 0 && (float) $contract->travel_expense_per_km > 0) { $travelKmSum += $dayTravelKm; $travelTotal += $dayTravelKm * (float) $contract->travel_expense_per_km; } $dayRecords[] = ['date' => \Carbon\CarbonImmutable::instance($date), 'minutes' => $dayMinutes, 'amount' => $dayAmount, 'leaveType' => null]; } $indemnitiesRaw = [ 'maintenance' => $maintenanceTotal > 0 ? ['days' => $maintenanceDays, 'total' => $maintenanceTotal] : null, 'meal' => $mealTotal > 0 ? ['days' => $mealDays, 'total' => $mealTotal] : null, 'snack' => $snackTotal > 0 ? ['days' => $snackDays, 'total' => $snackTotal] : null, 'travel' => $travelTotal > 0 ? ['km' => $travelKmSum, 'total' => $travelTotal] : null, ]; $maintenance = $maintenanceTotal; $meal = $mealTotal + $snackTotal; if ($days === 0 && $minutes === 0 && ! ($currentContract && \App\Support\MonthlyCareCostCalculator::hasImportedMonth($currentContract, $paymentMonth))) { return null; } $cpCompensation = 0.0; $cpDaysPaid = 0.0; $absenceDeduction = 0.0; $leaveSummaries = []; if ($currentContract) { $cpService = new \App\Domain\Leave\PaidLeavePaymentService; foreach (\App\Models\PaidLeavePeriod::query()->where('contract_id', $currentContract->id)->get() as $plp) { $cpCompensation += $cpService->compensationDueForMonth( $currentContract, $plp, \Carbon\CarbonImmutable::instance($paymentMonth), ); } $contractLeaveRows = \Illuminate\Support\Facades\DB::table('leave_period_contract') ->join('leave_periods', 'leave_periods.id', '=', 'leave_period_contract.leave_period_id') ->where('leave_period_contract.contract_id', $currentContract->id) ->whereDate('leave_periods.started_on', '<=', $paymentMonthEnd->toDateString()) ->whereDate('leave_periods.ended_on', '>=', $paymentMonth->toDateString()) ->select([ 'leave_periods.started_on', 'leave_periods.ended_on', 'leave_periods.leave_type', 'leave_period_contract.paid_leave_days_used', 'leave_period_contract.unpaid_annual_complementary_days', 'leave_period_contract.selected_valuation_method', 'leave_period_contract.paid_leave_compensation', 'leave_period_contract.salary_deduction_by_month', ]) ->get(); foreach ($contractLeaveRows as $row) { $deductionMap = $row->salary_deduction_by_month ? json_decode($row->salary_deduction_by_month, true) : []; $deductionThisMonth = (float) ($deductionMap[$paymentMonthKey] ?? 0); $absenceDeduction += $deductionThisMonth; $cpDaysPaid += (float) $row->paid_leave_days_used; $leaveSummaries[] = [ 'type' => \App\Models\LeavePeriod::TYPE_LABELS[$row->leave_type] ?? $row->leave_type, 'period' => \Carbon\Carbon::parse($row->started_on)->translatedFormat('d/m').' → '.\Carbon\Carbon::parse($row->ended_on)->translatedFormat('d/m'), 'paidDays' => (float) $row->paid_leave_days_used, 'unpaidDays' => (float) $row->unpaid_annual_complementary_days, 'compensation' => (float) $row->paid_leave_compensation > 0 ? $formatMoney((float) $row->paid_leave_compensation) : null, 'deductionThisMonth' => $deductionThisMonth > 0 ? $formatMoney($deductionThisMonth) : null, ]; } } $employer = $child->employer; $cpDaysRemaining = $cpDaysPaid; foreach ($dayRecords as $i => $record) { if (($record['leaveType'] ?? null) !== 'annual') { continue; } if ($cpDaysRemaining >= 1) { $cpDaysRemaining -= 1; } else { $dayRecords[$i]['leaveType'] = 'unpaid'; } } $monthlyDetail = \App\Support\MonthlyCareCostCalculator::build( $dayRecords, $paymentMonth, $paymentMonthEnd, $currentContract, $indemnitiesRaw, $cpCompensation, $absenceDeduction, $child->contracts, ); $cpCompensation = $monthlyDetail['cpCompensationRaw']; $absenceDeduction = $monthlyDetail['absenceDeductionRaw']; if ($monthlyDetail['importedDays'] !== null) { $cpDaysPaid = 0; $leaveSummaries = []; } $minutes = $monthlyDetail['totalMinutes']; $days = $monthlyDetail['importedDays'] ?? $days; $indemnitiesRaw = $monthlyDetail['indemnitiesRaw']; $maintenance = $indemnitiesRaw['maintenance']['total'] ?? 0; $meal = ($indemnitiesRaw['meal']['total'] ?? 0) + ($indemnitiesRaw['snack']['total'] ?? 0); $salary = $monthlyDetail['cost']['totalHoursAmountRaw'] + $cpCompensation - $absenceDeduction; $total = $monthlyDetail['cost']['netToPayRaw']; $aids = \App\Support\MonthlyCmgAidsCalculator::build( $employer, $child, $paymentMonth, $minutes, $salary, $maintenance, $meal, ); $cmgAvailable = $aids['available']; $cmg = (float) $aids['estimatedCmg']; $resteACharge = max(0.0, $total - ($cmgAvailable ? $cmg : 0.0)); $aids['costOfCare'] = $total; $aids['payTotal'] = $formatMoney($total); $aids['cmgFormatted'] = $cmgAvailable ? $formatMoney($cmg) : null; $aids['resteACharge'] = $formatMoney($resteACharge); $nounou = $employer->nounouLinks->firstWhere('unlinked_at', null)?->user; return [ 'initials' => mb_strtoupper(mb_substr($child->first_name, 0, 1).mb_substr((string) $child->last_name, 0, 1)), 'color' => \App\Support\ChildColorPalette::key($child), 'child' => $child->first_name ?: 'Mon enfant', 'family' => $nounou ? trim($nounou->first_name.' '.$nounou->last_name) : 'Mon assistante maternelle', 'hours' => $formatHours($minutes), 'days' => $days, 'total' => $formatMoney($total), 'cmg' => $cmgAvailable ? $formatMoney($cmg) : 'À compléter', 'out_of_pocket' => $cmgAvailable ? $formatMoney(max(0, $total - $cmg)) : $formatMoney($total), 'cmg_missing' => $aids['missing'], 'minutes' => $minutes, 'days_raw' => $days, 'total_raw' => $total, 'cmg_raw' => $cmgAvailable ? $cmg : 0.0, 'out_of_pocket_raw' => $cmgAvailable ? max(0, $total - $cmg) : $total, 'cmg_available' => $cmgAvailable, 'details' => array_merge($monthlyDetail, [ 'initials' => mb_strtoupper(mb_substr($child->first_name, 0, 1).mb_substr((string) $child->last_name, 0, 1)), 'color' => \App\Support\ChildColorPalette::key($child), 'child' => $child->first_name ?: 'Mon enfant', 'family' => $nounou ? trim($nounou->first_name.' '.$nounou->last_name) : 'Mon assistante maternelle', 'month' => mb_strtolower($paymentMonthLabel), 'hourlyRate' => $currentContract ? $formatMoney((float) $currentContract->hourly_rate_net).'/h' : null, 'contractUrl' => $currentContract ? route('contracts.show', $currentContract) : null, 'cpDaysPaid' => $cpDaysPaid > 0 ? $cpDaysPaid : null, 'cpCompensation' => $cpCompensation > 0 ? $formatMoney($cpCompensation) : null, 'absenceDeduction' => $absenceDeduction > 0 ? $formatMoney($absenceDeduction) : null, 'leaves' => $leaveSummaries, 'total' => $formatMoney($total), 'aids' => $aids, 'cmgMissing' => $aids['missing'], ]), ]; }) ->filter() ->values(); $paymentTotalMinutes = (int) $paymentRows->sum('minutes'); $paymentTotalDays = (int) $paymentRows->sum('days_raw'); $paymentTotal = (float) $paymentRows->sum('total_raw'); $paymentTotalCmg = (float) $paymentRows->sum('cmg_raw'); $paymentTotalOutOfPocket = (float) $paymentRows->sum('out_of_pocket_raw'); $paymentMissing = $paymentRows->flatMap(fn ($row) => $row['cmg_missing'])->unique()->values(); @endphp
@unless ($profileDone)

Mon espace personnel

Bienvenue dans naneasy{{ $firstName !== '' ? ', '.$firstName : '' }}

Je complète les informations de mon foyer pour préparer mes déclarations et mes aides. Je peux importer mon avis d'imposition pour aller plus vite.

@endunless @if ($profileDone) @if ($agendaHasActiveContract)

Salut {{ $firstName !== '' ? $firstName : $user->name }}

{{ $todayLabel }}

@else

Mon espace personnel

{{ $firstName !== '' ? $firstName.', ' : '' }} le premier mois est offert

Aucun moyen de paiement à renseigner : je profite d'un mois complet, gratuit et automatique, pour tester tous les avantages de naneasy avant de préparer mon premier contrat.

@endif
@if ($agendaHasActiveContract)

La semaine de mes enfants{{ $planningWeekTotalLabel }}

$planningMode === '5', 'dashboard-week-grid-weekend' => $planningMode === 'weekend'])> @foreach ($planningWeekDays as $day) $day['is_selected'], 'dashboard-week-day-muted' => $day['entries']->isEmpty()])>
{{ $day['label'] }} {{ $day['day_number'] }}
@if ($day['leave'])
Absence · {{ $day['leave']['label'] }}
@else @forelse ($day['entries']->take(3) as $entry)
$entry['is_absent']])> {{ $entry['child'] }} {{ $entry['is_absent'] ? 'Absent(e)' : $entry['start'].' – '.$entry['end'] }}
@empty {{ $day['is_weekend'] ? 'Week-end' : 'Aucune garde' }} @endforelse @endif
@if ($day['holiday'] || $day['activities']->isNotEmpty())
@if ($day['holiday'])
{{ $day['holiday']['label'] }} Jour férié
@endif @foreach ($day['activities']->take(2) as $activity)
{{ $activity->label }} {{ $activity->place ?: ($activity->time ? substr($activity->time, 0, 5) : 'Activité prévue') }}
@endforeach
@endif
@endforeach
@unless ($selectedPlanningDay['leave'])

{{ ucfirst($selectedPlanningDay['date']->locale('fr')->isoFormat('dddd D MMMM')) }}

{{ $selectedPlanningDay['entries']->count() }} {{ $selectedPlanningDay['entries']->count() > 1 ? 'gardes prévues' : 'garde prévue' }}
@forelse ($selectedPlanningDay['entries'] as $entry)
$entry['can_open'], 'dashboard-week-detail-row-locked' => ! $entry['can_open'] || $entry['is_absent']]) @if ($entry['can_open']) role="button" tabindex="0" x-on:click="openSlot = @js($entry['modal_id'])" x-on:keydown.enter.prevent="openSlot = @js($entry['modal_id'])" x-on:keydown.space.prevent="openSlot = @js($entry['modal_id'])" @endif >
{{ $entry['child'] }} {{ $entry['family'] }}
Horaires {{ $entry['is_absent'] ? '—' : $entry['start'].' – '.$entry['end'] }}
Durée {{ $entry['is_absent'] ? '—' : $entry['hours'] }}
@if ($entry['can_open']) {{ $entry['status_label'] }} @else {{ $entry['locked_label'] }} @endif
@if ($entry['can_open'])
@include('planning.partials.slot-modal', [ 'entry' => $entry, 'dateLabel' => ucfirst($selectedPlanningDay['date']->locale('fr')->isoFormat('dddd D MMMM')), 'closeAction' => 'openSlot = null', 'isParent' => true, 'canAdjustSchedule' => $entry['can_adjust_schedule'], ])
@endif @empty

Aucune garde prévue ce jour-là.

@endforelse
@endunless

Ce que je vais payer

{{ $paymentMonthLabel }} · {{ $isPaymentProjection ? 'estimation' : 'réalisé et prévisionnel' }}
{{ $paymentMonthLabel }}
Heures {{ $formatHours($paymentTotalMinutes) }}
À payer {{ $formatMoney($paymentTotal) }}
CMG estimée {{ $formatMoney($paymentTotalCmg) }}
Reste à charge estimé {{ $formatMoney($paymentTotalOutOfPocket) }}
@if ($paymentMissing->isNotEmpty())

Je complète {{ $paymentMissing->implode(', ') }} pour affiner mon estimation CMG.

@endif
@if ($paymentRows->isNotEmpty())
Enfant Heures Jours À payer CMG est. Reste est.
@endif @forelse ($paymentRows as $row)
{{ $row['child'] }} {{ $row['family'] }}
{{ $row['hours'] }} {{ $row['days'] }} j {{ $row['total'] }} {{ $row['cmg'] }} {{ $row['out_of_pocket'] }}
@empty

Aucun montant calculable pour ce mois.

@endforelse
@else

Votre calendrier de garde arrive bientôt

Le calendrier de garde et la projection de salaire s'afficheront ici dès qu'un contrat aura été validé.

Préparer mon premier contrat
@endif
@endif