Compare commits
2 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
f0ee489c26 | ||
| 01f8058f56 |
@@ -1,2 +1,2 @@
|
|||||||
parameters:
|
parameters:
|
||||||
app.version: '0.1.39'
|
app.version: '0.1.40'
|
||||||
|
|||||||
@@ -124,18 +124,20 @@ Documents complementaires:
|
|||||||
- Colonnes spécifiques (vue jour):
|
- Colonnes spécifiques (vue jour):
|
||||||
- Heure de jour (durée HH:MM via TimeSelect)
|
- Heure de jour (durée HH:MM via TimeSelect)
|
||||||
- Heure de nuit (durée HH:MM via TimeSelect)
|
- Heure de nuit (durée HH:MM via TimeSelect)
|
||||||
- Total (somme jour + nuit, calculé)
|
- Heure atelier (durée HH:MM via TimeSelect)
|
||||||
|
- Total (somme jour + nuit + atelier, calculé)
|
||||||
- Petit déjeuner (checkbox)
|
- Petit déjeuner (checkbox)
|
||||||
- Déjeuner (checkbox)
|
- Déjeuner (checkbox)
|
||||||
|
- Dîner (checkbox)
|
||||||
- Nuitée (checkbox)
|
- Nuitée (checkbox)
|
||||||
- Stockage backend:
|
- Stockage backend:
|
||||||
- `dayHoursMinutes` et `nightHoursMinutes` (entiers, minutes) sur `WorkHour`
|
- `dayHoursMinutes`, `nightHoursMinutes` et `workshopHoursMinutes` (entiers, minutes) sur `WorkHour`
|
||||||
- `hasBreakfast`, `hasLunch`, `hasOvernight` (booleans) sur `WorkHour`
|
- `hasBreakfast`, `hasLunch`, `hasDinner`, `hasOvernight` (booleans) sur `WorkHour`
|
||||||
- les champs time classiques (morning/afternoon/evening) sont mis à null pour les chauffeurs
|
- les champs time classiques (morning/afternoon/evening) sont mis à null pour les chauffeurs
|
||||||
- Validation: même logique que les heures classiques (`isValid`, `isSiteValid`, bulk)
|
- Validation: même logique que les heures classiques (`isValid`, `isSiteValid`, bulk)
|
||||||
- Vue semaine:
|
- Vue semaine:
|
||||||
- jour/nuit par jour + indicateurs repas/nuitée
|
- jour/nuit/atelier par jour + indicateurs repas/dîner/nuitée
|
||||||
- totaux hebdo: jour, nuit, total, compteurs petit déj/déjeuner/nuitée
|
- totaux hebdo: jour, nuit, atelier, total, compteurs petit déj/déjeuner/dîner/nuitée
|
||||||
- pas de calcul d'heures supplémentaires pour les conducteurs
|
- pas de calcul d'heures supplémentaires pour les conducteurs
|
||||||
- Le flag `isDriver` est sur `EmployeeContractPeriod` (un employé peut changer de statut chauffeur selon la période)
|
- Le flag `isDriver` est sur `EmployeeContractPeriod` (un employé peut changer de statut chauffeur selon la période)
|
||||||
- Exposé en API via un getter virtuel sur `Employee` (`employee:read`) qui résout depuis la période active
|
- Exposé en API via un getter virtuel sur `Employee` (`employee:read`) qui résout depuis la période active
|
||||||
|
|||||||
@@ -9,9 +9,11 @@
|
|||||||
<span class="pl-2">Absence</span>
|
<span class="pl-2">Absence</span>
|
||||||
<span class="pl-4">Heure de jour</span>
|
<span class="pl-4">Heure de jour</span>
|
||||||
<span class="pl-2">Heure de nuit</span>
|
<span class="pl-2">Heure de nuit</span>
|
||||||
|
<span class="pl-2">Heure atelier</span>
|
||||||
<span class="pl-2">Total</span>
|
<span class="pl-2">Total</span>
|
||||||
<span>Petit déj.</span>
|
<span>Petit déj.</span>
|
||||||
<span>Déjeuner</span>
|
<span>Déjeuner</span>
|
||||||
|
<span>Dîner</span>
|
||||||
<span>Nuitée</span>
|
<span>Nuitée</span>
|
||||||
<span v-if="isAdmin" class="flex justify-between items-center">
|
<span v-if="isAdmin" class="flex justify-between items-center">
|
||||||
<span>Valider</span>
|
<span>Valider</span>
|
||||||
@@ -96,6 +98,12 @@
|
|||||||
:disabled="!hasContractAtSelectedDate(employee.id) || isRowLocked(employee.id)"
|
:disabled="!hasContractAtSelectedDate(employee.id) || isRowLocked(employee.id)"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
<div class="pl-2">
|
||||||
|
<TimeSelect
|
||||||
|
v-model="rows[employee.id].workshopHours"
|
||||||
|
:disabled="!hasContractAtSelectedDate(employee.id) || isRowLocked(employee.id)"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
<div class="pl-2 text-sm font-semibold">
|
<div class="pl-2 text-sm font-semibold">
|
||||||
{{ formatMinutes(getRowMetrics(employee.id).totalMinutes) }}
|
{{ formatMinutes(getRowMetrics(employee.id).totalMinutes) }}
|
||||||
</div>
|
</div>
|
||||||
@@ -115,6 +123,14 @@
|
|||||||
:disabled="!hasContractAtSelectedDate(employee.id) || isRowLocked(employee.id)"
|
:disabled="!hasContractAtSelectedDate(employee.id) || isRowLocked(employee.id)"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
<div class="flex">
|
||||||
|
<input
|
||||||
|
v-model="rows[employee.id].hasDinner"
|
||||||
|
type="checkbox"
|
||||||
|
class="cursor-pointer h-4 w-4"
|
||||||
|
:disabled="!hasContractAtSelectedDate(employee.id) || isRowLocked(employee.id)"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
<div class="flex">
|
<div class="flex">
|
||||||
<input
|
<input
|
||||||
v-model="rows[employee.id].hasOvernight"
|
v-model="rows[employee.id].hasOvernight"
|
||||||
|
|||||||
@@ -7,8 +7,9 @@
|
|||||||
:style="{ gridTemplateColumns: weekGridCols }"
|
:style="{ gridTemplateColumns: weekGridCols }"
|
||||||
>
|
>
|
||||||
<span>Nom</span>
|
<span>Nom</span>
|
||||||
<span v-for="day in weekDayHeaders" :key="day.date" class="text-left">{{ day.label }}</span>
|
<span v-for="day in weekDayHeaders" :key="day.date" class="text-left">{{ day.weekday }}<br>{{ day.dayDate }}</span>
|
||||||
<span>Jour/Nuit <br>sem.</span>
|
<span>Jour/Nuit <br>sem.</span>
|
||||||
|
<span>Atelier <br>sem.</span>
|
||||||
<span>Total <br>sem.</span>
|
<span>Total <br>sem.</span>
|
||||||
<span>Total <br>h. supp.</span>
|
<span>Total <br>h. supp.</span>
|
||||||
<span>+25%</span>
|
<span>+25%</span>
|
||||||
@@ -16,7 +17,8 @@
|
|||||||
<span>Total <br>récup.</span>
|
<span>Total <br>récup.</span>
|
||||||
<span>Petit <br>déj.</span>
|
<span>Petit <br>déj.</span>
|
||||||
<span>Déj.</span>
|
<span>Déj.</span>
|
||||||
<span>Nuitée</span>
|
<span>Dîner</span>
|
||||||
|
<span>Nuit.</span>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="border-x border-b border-primary-500 rounded-b-md">
|
<div class="border-x border-b border-primary-500 rounded-b-md">
|
||||||
@@ -44,9 +46,11 @@
|
|||||||
>
|
>
|
||||||
<div>J {{ formatMinutes(daily.dayMinutes) }}</div>
|
<div>J {{ formatMinutes(daily.dayMinutes) }}</div>
|
||||||
<div>N {{ formatMinutes(daily.nightMinutes) }}</div>
|
<div>N {{ formatMinutes(daily.nightMinutes) }}</div>
|
||||||
<div v-if="daily.hasBreakfast || daily.hasLunch || daily.hasOvernight" class="text-[10px] flex gap-1 mt-0.5">
|
<div v-if="daily.workshopMinutes">A {{ formatMinutes(daily.workshopMinutes) }}</div>
|
||||||
|
<div v-if="daily.hasBreakfast || daily.hasLunch || daily.hasDinner || daily.hasOvernight" class="text-[10px] flex gap-1 mt-0.5">
|
||||||
<span v-if="daily.hasBreakfast" title="Petit déjeuner">PD</span>
|
<span v-if="daily.hasBreakfast" title="Petit déjeuner">PD</span>
|
||||||
<span v-if="daily.hasLunch" title="Déjeuner">DJ</span>
|
<span v-if="daily.hasLunch" title="Déjeuner">DJ</span>
|
||||||
|
<span v-if="daily.hasDinner" title="Dîner">DI</span>
|
||||||
<span v-if="daily.hasOvernight" title="Nuitée">NU</span>
|
<span v-if="daily.hasOvernight" title="Nuitée">NU</span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -55,6 +59,9 @@
|
|||||||
<div>J {{ formatMinutes(row.weeklyDayMinutes) }}</div>
|
<div>J {{ formatMinutes(row.weeklyDayMinutes) }}</div>
|
||||||
<div>N {{ formatMinutes(row.weeklyNightMinutes) }}</div>
|
<div>N {{ formatMinutes(row.weeklyNightMinutes) }}</div>
|
||||||
</div>
|
</div>
|
||||||
|
<div class="font-semibold">
|
||||||
|
{{ formatMinutes(row.weeklyWorkshopMinutes ?? 0) }}
|
||||||
|
</div>
|
||||||
<div class="font-semibold">
|
<div class="font-semibold">
|
||||||
{{ formatMinutes(row.weeklyTotalMinutes) }}
|
{{ formatMinutes(row.weeklyTotalMinutes) }}
|
||||||
</div>
|
</div>
|
||||||
@@ -72,6 +79,7 @@
|
|||||||
</div>
|
</div>
|
||||||
<div class="font-semibold">{{ row.weeklyBreakfastCount ?? 0 }}</div>
|
<div class="font-semibold">{{ row.weeklyBreakfastCount ?? 0 }}</div>
|
||||||
<div class="font-semibold">{{ row.weeklyLunchCount ?? 0 }}</div>
|
<div class="font-semibold">{{ row.weeklyLunchCount ?? 0 }}</div>
|
||||||
|
<div class="font-semibold">{{ row.weeklyDinnerCount ?? 0 }}</div>
|
||||||
<div class="font-semibold">{{ row.weeklyOvernightCount ?? 0 }}</div>
|
<div class="font-semibold">{{ row.weeklyOvernightCount ?? 0 }}</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -94,7 +102,7 @@ defineProps<{
|
|||||||
isWeekLoading: boolean
|
isWeekLoading: boolean
|
||||||
weekGridCols: string
|
weekGridCols: string
|
||||||
weeklySummary: WeeklyWorkHourSummary | null
|
weeklySummary: WeeklyWorkHourSummary | null
|
||||||
weekDayHeaders: Array<{ date: string; label: string }>
|
weekDayHeaders: Array<{ date: string; weekday: string; dayDate: string }>
|
||||||
formatMinutes: (minutes: number) => string
|
formatMinutes: (minutes: number) => string
|
||||||
}>()
|
}>()
|
||||||
</script>
|
</script>
|
||||||
|
|||||||
@@ -22,7 +22,6 @@ import {
|
|||||||
} from '~/services/work-hours'
|
} from '~/services/work-hours'
|
||||||
import {
|
import {
|
||||||
formatDateLongFr,
|
formatDateLongFr,
|
||||||
formatWeekDayHeaderFr,
|
|
||||||
formatWeekRangeFr,
|
formatWeekRangeFr,
|
||||||
getIsoWeekNumber,
|
getIsoWeekNumber,
|
||||||
getOffsetFromTodayYmd,
|
getOffsetFromTodayYmd,
|
||||||
@@ -73,10 +72,10 @@ export const useDriverHoursPage = () => {
|
|||||||
const dayGridCols = computed(() => {
|
const dayGridCols = computed(() => {
|
||||||
const metricCol = '0.4fr'
|
const metricCol = '0.4fr'
|
||||||
const validationCols = isAdmin.value ? `${metricCol}` : `${metricCol} ${metricCol}`
|
const validationCols = isAdmin.value ? `${metricCol}` : `${metricCol} ${metricCol}`
|
||||||
return `1.2fr 0.6fr 0.8fr 0.8fr ${metricCol} ${metricCol} ${metricCol} ${metricCol} ${validationCols}`
|
return `1.2fr 0.6fr 0.8fr 0.8fr 0.8fr ${metricCol} ${metricCol} ${metricCol} ${metricCol} ${metricCol} ${validationCols}`
|
||||||
})
|
})
|
||||||
|
|
||||||
const weekGridCols = '1.6fr repeat(7, 1fr) repeat(6, 0.6fr) repeat(3, 0.4fr)'
|
const weekGridCols = '1.6fr repeat(7, 0.6fr) repeat(7, 0.6fr) repeat(4, 0.4fr)'
|
||||||
|
|
||||||
const sites = computed<Site[]>(() => {
|
const sites = computed<Site[]>(() => {
|
||||||
const siteMap = new Map<number, Site>()
|
const siteMap = new Map<number, Site>()
|
||||||
@@ -265,7 +264,13 @@ export const useDriverHoursPage = () => {
|
|||||||
|
|
||||||
const weekDayHeaders = computed(() => {
|
const weekDayHeaders = computed(() => {
|
||||||
const days = weeklySummary.value?.days ?? []
|
const days = weeklySummary.value?.days ?? []
|
||||||
return days.map((date) => ({ date, label: formatWeekDayHeaderFr(date) }))
|
return days.map((date) => {
|
||||||
|
const parsed = parseYmd(date)
|
||||||
|
if (!parsed) return { date, weekday: '', dayDate: '' }
|
||||||
|
const weekday = new Intl.DateTimeFormat('fr-FR', { weekday: 'short' }).format(parsed)
|
||||||
|
const dayDate = new Intl.DateTimeFormat('fr-FR', { day: '2-digit', month: '2-digit' }).format(parsed)
|
||||||
|
return { date, weekday, dayDate }
|
||||||
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
const shiftDate = (steps: number) => {
|
const shiftDate = (steps: number) => {
|
||||||
@@ -331,8 +336,10 @@ export const useDriverHoursPage = () => {
|
|||||||
workHourId: null,
|
workHourId: null,
|
||||||
dayHours: '',
|
dayHours: '',
|
||||||
nightHours: '',
|
nightHours: '',
|
||||||
|
workshopHours: '',
|
||||||
hasBreakfast: false,
|
hasBreakfast: false,
|
||||||
hasLunch: false,
|
hasLunch: false,
|
||||||
|
hasDinner: false,
|
||||||
hasOvernight: false,
|
hasOvernight: false,
|
||||||
isSiteValid: false,
|
isSiteValid: false,
|
||||||
isValid: false,
|
isValid: false,
|
||||||
@@ -357,8 +364,9 @@ export const useDriverHoursPage = () => {
|
|||||||
const row = rows.value[employeeId] ?? emptyRow()
|
const row = rows.value[employeeId] ?? emptyRow()
|
||||||
const dayMinutes = toMinutes(row.dayHours)
|
const dayMinutes = toMinutes(row.dayHours)
|
||||||
const nightMinutes = toMinutes(row.nightHours)
|
const nightMinutes = toMinutes(row.nightHours)
|
||||||
const totalMinutes = dayMinutes + nightMinutes
|
const workshopMinutes = toMinutes(row.workshopHours)
|
||||||
return { dayMinutes, nightMinutes, totalMinutes }
|
const totalMinutes = dayMinutes + nightMinutes + workshopMinutes
|
||||||
|
return { dayMinutes, nightMinutes, workshopMinutes, totalMinutes }
|
||||||
}
|
}
|
||||||
|
|
||||||
const getRowAbsenceLabel = (employeeId: number) => {
|
const getRowAbsenceLabel = (employeeId: number) => {
|
||||||
@@ -412,8 +420,10 @@ export const useDriverHoursPage = () => {
|
|||||||
workHourId: workHour?.id ?? null,
|
workHourId: workHour?.id ?? null,
|
||||||
dayHours: minutesToTimeString(workHour?.dayHoursMinutes),
|
dayHours: minutesToTimeString(workHour?.dayHoursMinutes),
|
||||||
nightHours: minutesToTimeString(workHour?.nightHoursMinutes),
|
nightHours: minutesToTimeString(workHour?.nightHoursMinutes),
|
||||||
|
workshopHours: minutesToTimeString(workHour?.workshopHoursMinutes),
|
||||||
hasBreakfast: workHour?.hasBreakfast ?? false,
|
hasBreakfast: workHour?.hasBreakfast ?? false,
|
||||||
hasLunch: workHour?.hasLunch ?? false,
|
hasLunch: workHour?.hasLunch ?? false,
|
||||||
|
hasDinner: workHour?.hasDinner ?? false,
|
||||||
hasOvernight: workHour?.hasOvernight ?? false,
|
hasOvernight: workHour?.hasOvernight ?? false,
|
||||||
isSiteValid: workHour?.isSiteValid ?? false,
|
isSiteValid: workHour?.isSiteValid ?? false,
|
||||||
isValid: workHour?.isValid ?? false,
|
isValid: workHour?.isValid ?? false,
|
||||||
@@ -556,8 +566,10 @@ export const useDriverHoursPage = () => {
|
|||||||
isPresentAfternoon: false,
|
isPresentAfternoon: false,
|
||||||
dayHoursMinutes: null,
|
dayHoursMinutes: null,
|
||||||
nightHoursMinutes: null,
|
nightHoursMinutes: null,
|
||||||
|
workshopHoursMinutes: null,
|
||||||
hasBreakfast: false,
|
hasBreakfast: false,
|
||||||
hasLunch: false,
|
hasLunch: false,
|
||||||
|
hasDinner: false,
|
||||||
hasOvernight: false
|
hasOvernight: false
|
||||||
})
|
})
|
||||||
|
|
||||||
@@ -859,6 +871,7 @@ export const useDriverHoursPage = () => {
|
|||||||
const row = rows.value[employeeId] ?? emptyRow()
|
const row = rows.value[employeeId] ?? emptyRow()
|
||||||
const dayMin = toMinutes(row.dayHours)
|
const dayMin = toMinutes(row.dayHours)
|
||||||
const nightMin = toMinutes(row.nightHours)
|
const nightMin = toMinutes(row.nightHours)
|
||||||
|
const workshopMin = toMinutes(row.workshopHours)
|
||||||
|
|
||||||
return {
|
return {
|
||||||
employeeId,
|
employeeId,
|
||||||
@@ -872,8 +885,10 @@ export const useDriverHoursPage = () => {
|
|||||||
isPresentAfternoon: false,
|
isPresentAfternoon: false,
|
||||||
dayHoursMinutes: dayMin || null,
|
dayHoursMinutes: dayMin || null,
|
||||||
nightHoursMinutes: nightMin || null,
|
nightHoursMinutes: nightMin || null,
|
||||||
|
workshopHoursMinutes: workshopMin || null,
|
||||||
hasBreakfast: row.hasBreakfast,
|
hasBreakfast: row.hasBreakfast,
|
||||||
hasLunch: row.hasLunch,
|
hasLunch: row.hasLunch,
|
||||||
|
hasDinner: row.hasDinner,
|
||||||
hasOvernight: row.hasOvernight
|
hasOvernight: row.hasOvernight
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -21,14 +21,16 @@
|
|||||||
<NuxtLink
|
<NuxtLink
|
||||||
to="/hours"
|
to="/hours"
|
||||||
class="flex items-center gap-2 py-2 text-md text-black hover:bg-tertiary-500 hover:text-primary-500"
|
class="flex items-center gap-2 py-2 text-md text-black hover:bg-tertiary-500 hover:text-primary-500"
|
||||||
:class="route.path.startsWith('/hours')
|
:class="[
|
||||||
? 'bg-tertiary-500 text-primary-500 font-bold'
|
route.path.startsWith('/hours') ? 'bg-tertiary-500 text-primary-500 font-bold' : '',
|
||||||
: ''"
|
!isAdmin ? 'border-t border-secondary-500 pt-3' : ''
|
||||||
|
]"
|
||||||
>
|
>
|
||||||
<Icon name="mdi:clock-time-four-outline" size="24"/>
|
<Icon name="mdi:clock-time-four-outline" size="24"/>
|
||||||
<p>Heures</p>
|
<p>Heures</p>
|
||||||
</NuxtLink>
|
</NuxtLink>
|
||||||
<NuxtLink
|
<NuxtLink
|
||||||
|
v-if="isAdmin"
|
||||||
to="/driver-hours"
|
to="/driver-hours"
|
||||||
class="flex items-center gap-2 py-2 text-md text-black hover:bg-tertiary-500 hover:text-primary-500"
|
class="flex items-center gap-2 py-2 text-md text-black hover:bg-tertiary-500 hover:text-primary-500"
|
||||||
:class="route.path.startsWith('/driver-hours')
|
:class="route.path.startsWith('/driver-hours')
|
||||||
|
|||||||
@@ -68,7 +68,8 @@ const handleSubmit = async () => {
|
|||||||
try {
|
try {
|
||||||
await auth.login(username.value, password.value)
|
await auth.login(username.value, password.value)
|
||||||
|
|
||||||
await router.push('/calendar')
|
const isAdmin = auth.user?.roles?.includes('ROLE_ADMIN')
|
||||||
|
await router.push(isAdmin ? '/calendar' : '/hours')
|
||||||
} finally {
|
} finally {
|
||||||
isSubmitting.value = false
|
isSubmitting.value = false
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -15,8 +15,10 @@ export type WorkHour = {
|
|||||||
isPresentAfternoon?: boolean
|
isPresentAfternoon?: boolean
|
||||||
dayHoursMinutes?: number | null
|
dayHoursMinutes?: number | null
|
||||||
nightHoursMinutes?: number | null
|
nightHoursMinutes?: number | null
|
||||||
|
workshopHoursMinutes?: number | null
|
||||||
hasBreakfast?: boolean
|
hasBreakfast?: boolean
|
||||||
hasLunch?: boolean
|
hasLunch?: boolean
|
||||||
|
hasDinner?: boolean
|
||||||
hasOvernight?: boolean
|
hasOvernight?: boolean
|
||||||
isSiteValid?: boolean
|
isSiteValid?: boolean
|
||||||
isValid?: boolean
|
isValid?: boolean
|
||||||
@@ -35,8 +37,10 @@ export type WorkHourEntryPayload = {
|
|||||||
isPresentAfternoon?: boolean
|
isPresentAfternoon?: boolean
|
||||||
dayHoursMinutes?: number | null
|
dayHoursMinutes?: number | null
|
||||||
nightHoursMinutes?: number | null
|
nightHoursMinutes?: number | null
|
||||||
|
workshopHoursMinutes?: number | null
|
||||||
hasBreakfast?: boolean
|
hasBreakfast?: boolean
|
||||||
hasLunch?: boolean
|
hasLunch?: boolean
|
||||||
|
hasDinner?: boolean
|
||||||
hasOvernight?: boolean
|
hasOvernight?: boolean
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -44,6 +48,7 @@ export type WeeklyWorkHourDailySummary = {
|
|||||||
date: string
|
date: string
|
||||||
dayMinutes: number
|
dayMinutes: number
|
||||||
nightMinutes: number
|
nightMinutes: number
|
||||||
|
workshopMinutes?: number
|
||||||
totalMinutes: number
|
totalMinutes: number
|
||||||
present?: number | null
|
present?: number | null
|
||||||
hasAbsence?: boolean
|
hasAbsence?: boolean
|
||||||
@@ -51,6 +56,7 @@ export type WeeklyWorkHourDailySummary = {
|
|||||||
absenceColor?: string | null
|
absenceColor?: string | null
|
||||||
hasBreakfast?: boolean
|
hasBreakfast?: boolean
|
||||||
hasLunch?: boolean
|
hasLunch?: boolean
|
||||||
|
hasDinner?: boolean
|
||||||
hasOvernight?: boolean
|
hasOvernight?: boolean
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -65,6 +71,7 @@ export type WeeklyWorkHourRowSummary = {
|
|||||||
daily: WeeklyWorkHourDailySummary[]
|
daily: WeeklyWorkHourDailySummary[]
|
||||||
weeklyDayMinutes: number
|
weeklyDayMinutes: number
|
||||||
weeklyNightMinutes: number
|
weeklyNightMinutes: number
|
||||||
|
weeklyWorkshopMinutes?: number
|
||||||
weeklyTotalMinutes: number
|
weeklyTotalMinutes: number
|
||||||
weeklyPresenceCount?: number
|
weeklyPresenceCount?: number
|
||||||
weeklyOvertimeTotalMinutes?: number
|
weeklyOvertimeTotalMinutes?: number
|
||||||
@@ -74,6 +81,7 @@ export type WeeklyWorkHourRowSummary = {
|
|||||||
isDriver?: boolean
|
isDriver?: boolean
|
||||||
weeklyBreakfastCount?: number
|
weeklyBreakfastCount?: number
|
||||||
weeklyLunchCount?: number
|
weeklyLunchCount?: number
|
||||||
|
weeklyDinnerCount?: number
|
||||||
weeklyOvernightCount?: number
|
weeklyOvernightCount?: number
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -106,8 +114,10 @@ export type DriverHourRow = {
|
|||||||
workHourId: number | null
|
workHourId: number | null
|
||||||
dayHours: string
|
dayHours: string
|
||||||
nightHours: string
|
nightHours: string
|
||||||
|
workshopHours: string
|
||||||
hasBreakfast: boolean
|
hasBreakfast: boolean
|
||||||
hasLunch: boolean
|
hasLunch: boolean
|
||||||
|
hasDinner: boolean
|
||||||
hasOvernight: boolean
|
hasOvernight: boolean
|
||||||
isSiteValid: boolean
|
isSiteValid: boolean
|
||||||
isValid: boolean
|
isValid: boolean
|
||||||
|
|||||||
26
migrations/Version20260316100000.php
Normal file
26
migrations/Version20260316100000.php
Normal file
@@ -0,0 +1,26 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
|
namespace DoctrineMigrations;
|
||||||
|
|
||||||
|
use Doctrine\DBAL\Schema\Schema;
|
||||||
|
use Doctrine\Migrations\AbstractMigration;
|
||||||
|
|
||||||
|
final class Version20260316100000 extends AbstractMigration
|
||||||
|
{
|
||||||
|
public function getDescription(): string
|
||||||
|
{
|
||||||
|
return 'Add has_dinner column to work_hours';
|
||||||
|
}
|
||||||
|
|
||||||
|
public function up(Schema $schema): void
|
||||||
|
{
|
||||||
|
$this->addSql('ALTER TABLE work_hours ADD has_dinner BOOLEAN DEFAULT FALSE NOT NULL');
|
||||||
|
}
|
||||||
|
|
||||||
|
public function down(Schema $schema): void
|
||||||
|
{
|
||||||
|
$this->addSql('ALTER TABLE work_hours DROP COLUMN has_dinner');
|
||||||
|
}
|
||||||
|
}
|
||||||
26
migrations/Version20260316100100.php
Normal file
26
migrations/Version20260316100100.php
Normal file
@@ -0,0 +1,26 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
|
namespace DoctrineMigrations;
|
||||||
|
|
||||||
|
use Doctrine\DBAL\Schema\Schema;
|
||||||
|
use Doctrine\Migrations\AbstractMigration;
|
||||||
|
|
||||||
|
final class Version20260316100100 extends AbstractMigration
|
||||||
|
{
|
||||||
|
public function getDescription(): string
|
||||||
|
{
|
||||||
|
return 'Add workshop_hours_minutes column to work_hours';
|
||||||
|
}
|
||||||
|
|
||||||
|
public function up(Schema $schema): void
|
||||||
|
{
|
||||||
|
$this->addSql('ALTER TABLE work_hours ADD workshop_hours_minutes INTEGER DEFAULT NULL');
|
||||||
|
}
|
||||||
|
|
||||||
|
public function down(Schema $schema): void
|
||||||
|
{
|
||||||
|
$this->addSql('ALTER TABLE work_hours DROP COLUMN workshop_hours_minutes');
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -10,6 +10,7 @@ final class WeeklyDaySummary
|
|||||||
public string $date,
|
public string $date,
|
||||||
public int $dayMinutes,
|
public int $dayMinutes,
|
||||||
public int $nightMinutes,
|
public int $nightMinutes,
|
||||||
|
public int $workshopMinutes,
|
||||||
public int $totalMinutes,
|
public int $totalMinutes,
|
||||||
public ?float $present = null,
|
public ?float $present = null,
|
||||||
public bool $hasAbsence = false,
|
public bool $hasAbsence = false,
|
||||||
@@ -17,6 +18,7 @@ final class WeeklyDaySummary
|
|||||||
public ?string $absenceColor = null,
|
public ?string $absenceColor = null,
|
||||||
public bool $hasBreakfast = false,
|
public bool $hasBreakfast = false,
|
||||||
public bool $hasLunch = false,
|
public bool $hasLunch = false,
|
||||||
|
public bool $hasDinner = false,
|
||||||
public bool $hasOvernight = false,
|
public bool $hasOvernight = false,
|
||||||
) {}
|
) {}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -20,6 +20,7 @@ final class WeeklySummaryRow
|
|||||||
public array $daily,
|
public array $daily,
|
||||||
public int $weeklyDayMinutes,
|
public int $weeklyDayMinutes,
|
||||||
public int $weeklyNightMinutes,
|
public int $weeklyNightMinutes,
|
||||||
|
public int $weeklyWorkshopMinutes,
|
||||||
public int $weeklyTotalMinutes,
|
public int $weeklyTotalMinutes,
|
||||||
public float $weeklyPresenceCount,
|
public float $weeklyPresenceCount,
|
||||||
public int $weeklyOvertimeTotalMinutes,
|
public int $weeklyOvertimeTotalMinutes,
|
||||||
@@ -29,6 +30,7 @@ final class WeeklySummaryRow
|
|||||||
public bool $isDriver = false,
|
public bool $isDriver = false,
|
||||||
public int $weeklyBreakfastCount = 0,
|
public int $weeklyBreakfastCount = 0,
|
||||||
public int $weeklyLunchCount = 0,
|
public int $weeklyLunchCount = 0,
|
||||||
|
public int $weeklyDinnerCount = 0,
|
||||||
public int $weeklyOvernightCount = 0,
|
public int $weeklyOvernightCount = 0,
|
||||||
) {}
|
) {}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -107,6 +107,10 @@ class WorkHour
|
|||||||
#[Groups(['work_hour:read'])]
|
#[Groups(['work_hour:read'])]
|
||||||
private ?int $nightHoursMinutes = null;
|
private ?int $nightHoursMinutes = null;
|
||||||
|
|
||||||
|
#[ORM\Column(type: 'integer', nullable: true)]
|
||||||
|
#[Groups(['work_hour:read'])]
|
||||||
|
private ?int $workshopHoursMinutes = null;
|
||||||
|
|
||||||
#[ORM\Column(type: 'boolean', options: ['default' => false])]
|
#[ORM\Column(type: 'boolean', options: ['default' => false])]
|
||||||
#[Groups(['work_hour:read'])]
|
#[Groups(['work_hour:read'])]
|
||||||
private bool $hasBreakfast = false;
|
private bool $hasBreakfast = false;
|
||||||
@@ -115,6 +119,10 @@ class WorkHour
|
|||||||
#[Groups(['work_hour:read'])]
|
#[Groups(['work_hour:read'])]
|
||||||
private bool $hasLunch = false;
|
private bool $hasLunch = false;
|
||||||
|
|
||||||
|
#[ORM\Column(type: 'boolean', options: ['default' => false])]
|
||||||
|
#[Groups(['work_hour:read'])]
|
||||||
|
private bool $hasDinner = false;
|
||||||
|
|
||||||
#[ORM\Column(type: 'boolean', options: ['default' => false])]
|
#[ORM\Column(type: 'boolean', options: ['default' => false])]
|
||||||
#[Groups(['work_hour:read'])]
|
#[Groups(['work_hour:read'])]
|
||||||
private bool $hasOvernight = false;
|
private bool $hasOvernight = false;
|
||||||
@@ -256,6 +264,18 @@ class WorkHour
|
|||||||
return $this;
|
return $this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function getWorkshopHoursMinutes(): ?int
|
||||||
|
{
|
||||||
|
return $this->workshopHoursMinutes;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function setWorkshopHoursMinutes(?int $workshopHoursMinutes): self
|
||||||
|
{
|
||||||
|
$this->workshopHoursMinutes = $workshopHoursMinutes;
|
||||||
|
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
public function getHasBreakfast(): bool
|
public function getHasBreakfast(): bool
|
||||||
{
|
{
|
||||||
return $this->hasBreakfast;
|
return $this->hasBreakfast;
|
||||||
@@ -280,6 +300,18 @@ class WorkHour
|
|||||||
return $this;
|
return $this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function getHasDinner(): bool
|
||||||
|
{
|
||||||
|
return $this->hasDinner;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function setHasDinner(bool $hasDinner): self
|
||||||
|
{
|
||||||
|
$this->hasDinner = $hasDinner;
|
||||||
|
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
public function getHasOvernight(): bool
|
public function getHasOvernight(): bool
|
||||||
{
|
{
|
||||||
return $this->hasOvernight;
|
return $this->hasOvernight;
|
||||||
|
|||||||
@@ -229,8 +229,10 @@ final readonly class WorkHourBulkUpsertProcessor implements ProcessorInterface
|
|||||||
* isPresentAfternoon:bool,
|
* isPresentAfternoon:bool,
|
||||||
* dayHoursMinutes:?int,
|
* dayHoursMinutes:?int,
|
||||||
* nightHoursMinutes:?int,
|
* nightHoursMinutes:?int,
|
||||||
|
* workshopHoursMinutes:?int,
|
||||||
* hasBreakfast:bool,
|
* hasBreakfast:bool,
|
||||||
* hasLunch:bool,
|
* hasLunch:bool,
|
||||||
|
* hasDinner:bool,
|
||||||
* hasOvernight:bool
|
* hasOvernight:bool
|
||||||
* }
|
* }
|
||||||
*/
|
*/
|
||||||
@@ -238,37 +240,41 @@ final readonly class WorkHourBulkUpsertProcessor implements ProcessorInterface
|
|||||||
{
|
{
|
||||||
if ($isDriver) {
|
if ($isDriver) {
|
||||||
return [
|
return [
|
||||||
'morningFrom' => null,
|
'morningFrom' => null,
|
||||||
'morningTo' => null,
|
'morningTo' => null,
|
||||||
'afternoonFrom' => null,
|
'afternoonFrom' => null,
|
||||||
'afternoonTo' => null,
|
'afternoonTo' => null,
|
||||||
'eveningFrom' => null,
|
'eveningFrom' => null,
|
||||||
'eveningTo' => null,
|
'eveningTo' => null,
|
||||||
'isPresentMorning' => false,
|
'isPresentMorning' => false,
|
||||||
'isPresentAfternoon' => false,
|
'isPresentAfternoon' => false,
|
||||||
'dayHoursMinutes' => $this->normalizeMinutes($entry['dayHoursMinutes'] ?? null, $employeeId, 'dayHoursMinutes'),
|
'dayHoursMinutes' => $this->normalizeMinutes($entry['dayHoursMinutes'] ?? null, $employeeId, 'dayHoursMinutes'),
|
||||||
'nightHoursMinutes' => $this->normalizeMinutes($entry['nightHoursMinutes'] ?? null, $employeeId, 'nightHoursMinutes'),
|
'nightHoursMinutes' => $this->normalizeMinutes($entry['nightHoursMinutes'] ?? null, $employeeId, 'nightHoursMinutes'),
|
||||||
'hasBreakfast' => $this->normalizePresence($entry['hasBreakfast'] ?? false, $employeeId, 'hasBreakfast'),
|
'workshopHoursMinutes' => $this->normalizeMinutes($entry['workshopHoursMinutes'] ?? null, $employeeId, 'workshopHoursMinutes'),
|
||||||
'hasLunch' => $this->normalizePresence($entry['hasLunch'] ?? false, $employeeId, 'hasLunch'),
|
'hasBreakfast' => $this->normalizePresence($entry['hasBreakfast'] ?? false, $employeeId, 'hasBreakfast'),
|
||||||
'hasOvernight' => $this->normalizePresence($entry['hasOvernight'] ?? false, $employeeId, 'hasOvernight'),
|
'hasLunch' => $this->normalizePresence($entry['hasLunch'] ?? false, $employeeId, 'hasLunch'),
|
||||||
|
'hasDinner' => $this->normalizePresence($entry['hasDinner'] ?? false, $employeeId, 'hasDinner'),
|
||||||
|
'hasOvernight' => $this->normalizePresence($entry['hasOvernight'] ?? false, $employeeId, 'hasOvernight'),
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
if ($isPresenceTracking) {
|
if ($isPresenceTracking) {
|
||||||
return [
|
return [
|
||||||
'morningFrom' => null,
|
'morningFrom' => null,
|
||||||
'morningTo' => null,
|
'morningTo' => null,
|
||||||
'afternoonFrom' => null,
|
'afternoonFrom' => null,
|
||||||
'afternoonTo' => null,
|
'afternoonTo' => null,
|
||||||
'eveningFrom' => null,
|
'eveningFrom' => null,
|
||||||
'eveningTo' => null,
|
'eveningTo' => null,
|
||||||
'isPresentMorning' => $this->normalizePresence($entry['isPresentMorning'] ?? false, $employeeId, 'isPresentMorning'),
|
'isPresentMorning' => $this->normalizePresence($entry['isPresentMorning'] ?? false, $employeeId, 'isPresentMorning'),
|
||||||
'isPresentAfternoon' => $this->normalizePresence($entry['isPresentAfternoon'] ?? false, $employeeId, 'isPresentAfternoon'),
|
'isPresentAfternoon' => $this->normalizePresence($entry['isPresentAfternoon'] ?? false, $employeeId, 'isPresentAfternoon'),
|
||||||
'dayHoursMinutes' => null,
|
'dayHoursMinutes' => null,
|
||||||
'nightHoursMinutes' => null,
|
'nightHoursMinutes' => null,
|
||||||
'hasBreakfast' => false,
|
'workshopHoursMinutes' => null,
|
||||||
'hasLunch' => false,
|
'hasBreakfast' => false,
|
||||||
'hasOvernight' => false,
|
'hasLunch' => false,
|
||||||
|
'hasDinner' => false,
|
||||||
|
'hasOvernight' => false,
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -281,13 +287,15 @@ final readonly class WorkHourBulkUpsertProcessor implements ProcessorInterface
|
|||||||
'eveningTo' => $this->normalizeTime($entry['eveningTo'] ?? null, $employeeId, 'eveningTo'),
|
'eveningTo' => $this->normalizeTime($entry['eveningTo'] ?? null, $employeeId, 'eveningTo'),
|
||||||
// On conserve aussi la présence si envoyée (cas forfait affiché côté UI),
|
// On conserve aussi la présence si envoyée (cas forfait affiché côté UI),
|
||||||
// même si le contrat résolu ce jour est en suivi horaire.
|
// même si le contrat résolu ce jour est en suivi horaire.
|
||||||
'isPresentMorning' => $this->normalizePresence($entry['isPresentMorning'] ?? false, $employeeId, 'isPresentMorning'),
|
'isPresentMorning' => $this->normalizePresence($entry['isPresentMorning'] ?? false, $employeeId, 'isPresentMorning'),
|
||||||
'isPresentAfternoon' => $this->normalizePresence($entry['isPresentAfternoon'] ?? false, $employeeId, 'isPresentAfternoon'),
|
'isPresentAfternoon' => $this->normalizePresence($entry['isPresentAfternoon'] ?? false, $employeeId, 'isPresentAfternoon'),
|
||||||
'dayHoursMinutes' => null,
|
'dayHoursMinutes' => null,
|
||||||
'nightHoursMinutes' => null,
|
'nightHoursMinutes' => null,
|
||||||
'hasBreakfast' => false,
|
'workshopHoursMinutes' => null,
|
||||||
'hasLunch' => false,
|
'hasBreakfast' => false,
|
||||||
'hasOvernight' => false,
|
'hasLunch' => false,
|
||||||
|
'hasDinner' => false,
|
||||||
|
'hasOvernight' => false,
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -368,8 +376,10 @@ final readonly class WorkHourBulkUpsertProcessor implements ProcessorInterface
|
|||||||
* isPresentAfternoon:bool,
|
* isPresentAfternoon:bool,
|
||||||
* dayHoursMinutes:?int,
|
* dayHoursMinutes:?int,
|
||||||
* nightHoursMinutes:?int,
|
* nightHoursMinutes:?int,
|
||||||
|
* workshopHoursMinutes:?int,
|
||||||
* hasBreakfast:bool,
|
* hasBreakfast:bool,
|
||||||
* hasLunch:bool,
|
* hasLunch:bool,
|
||||||
|
* hasDinner:bool,
|
||||||
* hasOvernight:bool
|
* hasOvernight:bool
|
||||||
* } $entry
|
* } $entry
|
||||||
*/
|
*/
|
||||||
@@ -385,8 +395,10 @@ final readonly class WorkHourBulkUpsertProcessor implements ProcessorInterface
|
|||||||
&& false === $entry['isPresentAfternoon']
|
&& false === $entry['isPresentAfternoon']
|
||||||
&& (null === $entry['dayHoursMinutes'] || 0 === $entry['dayHoursMinutes'])
|
&& (null === $entry['dayHoursMinutes'] || 0 === $entry['dayHoursMinutes'])
|
||||||
&& (null === $entry['nightHoursMinutes'] || 0 === $entry['nightHoursMinutes'])
|
&& (null === $entry['nightHoursMinutes'] || 0 === $entry['nightHoursMinutes'])
|
||||||
|
&& (null === $entry['workshopHoursMinutes'] || 0 === $entry['workshopHoursMinutes'])
|
||||||
&& false === $entry['hasBreakfast']
|
&& false === $entry['hasBreakfast']
|
||||||
&& false === $entry['hasLunch']
|
&& false === $entry['hasLunch']
|
||||||
|
&& false === $entry['hasDinner']
|
||||||
&& false === $entry['hasOvernight'];
|
&& false === $entry['hasOvernight'];
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -402,8 +414,10 @@ final readonly class WorkHourBulkUpsertProcessor implements ProcessorInterface
|
|||||||
* isPresentAfternoon:bool,
|
* isPresentAfternoon:bool,
|
||||||
* dayHoursMinutes:?int,
|
* dayHoursMinutes:?int,
|
||||||
* nightHoursMinutes:?int,
|
* nightHoursMinutes:?int,
|
||||||
|
* workshopHoursMinutes:?int,
|
||||||
* hasBreakfast:bool,
|
* hasBreakfast:bool,
|
||||||
* hasLunch:bool,
|
* hasLunch:bool,
|
||||||
|
* hasDinner:bool,
|
||||||
* hasOvernight:bool
|
* hasOvernight:bool
|
||||||
* } $entry
|
* } $entry
|
||||||
*/
|
*/
|
||||||
@@ -420,8 +434,10 @@ final readonly class WorkHourBulkUpsertProcessor implements ProcessorInterface
|
|||||||
->setIsPresentAfternoon($entry['isPresentAfternoon'])
|
->setIsPresentAfternoon($entry['isPresentAfternoon'])
|
||||||
->setDayHoursMinutes($entry['dayHoursMinutes'])
|
->setDayHoursMinutes($entry['dayHoursMinutes'])
|
||||||
->setNightHoursMinutes($entry['nightHoursMinutes'])
|
->setNightHoursMinutes($entry['nightHoursMinutes'])
|
||||||
|
->setWorkshopHoursMinutes($entry['workshopHoursMinutes'])
|
||||||
->setHasBreakfast($entry['hasBreakfast'])
|
->setHasBreakfast($entry['hasBreakfast'])
|
||||||
->setHasLunch($entry['hasLunch'])
|
->setHasLunch($entry['hasLunch'])
|
||||||
|
->setHasDinner($entry['hasDinner'])
|
||||||
->setHasOvernight($entry['hasOvernight'])
|
->setHasOvernight($entry['hasOvernight'])
|
||||||
// Toute modification invalide la validation chef de site.
|
// Toute modification invalide la validation chef de site.
|
||||||
->setIsSiteValid(false)
|
->setIsSiteValid(false)
|
||||||
@@ -442,8 +458,10 @@ final readonly class WorkHourBulkUpsertProcessor implements ProcessorInterface
|
|||||||
* isPresentAfternoon:bool,
|
* isPresentAfternoon:bool,
|
||||||
* dayHoursMinutes:?int,
|
* dayHoursMinutes:?int,
|
||||||
* nightHoursMinutes:?int,
|
* nightHoursMinutes:?int,
|
||||||
|
* workshopHoursMinutes:?int,
|
||||||
* hasBreakfast:bool,
|
* hasBreakfast:bool,
|
||||||
* hasLunch:bool,
|
* hasLunch:bool,
|
||||||
|
* hasDinner:bool,
|
||||||
* hasOvernight:bool
|
* hasOvernight:bool
|
||||||
* } $entry
|
* } $entry
|
||||||
*/
|
*/
|
||||||
@@ -459,8 +477,10 @@ final readonly class WorkHourBulkUpsertProcessor implements ProcessorInterface
|
|||||||
&& $workHour->getIsPresentAfternoon() === $entry['isPresentAfternoon']
|
&& $workHour->getIsPresentAfternoon() === $entry['isPresentAfternoon']
|
||||||
&& $workHour->getDayHoursMinutes() === $entry['dayHoursMinutes']
|
&& $workHour->getDayHoursMinutes() === $entry['dayHoursMinutes']
|
||||||
&& $workHour->getNightHoursMinutes() === $entry['nightHoursMinutes']
|
&& $workHour->getNightHoursMinutes() === $entry['nightHoursMinutes']
|
||||||
|
&& $workHour->getWorkshopHoursMinutes() === $entry['workshopHoursMinutes']
|
||||||
&& $workHour->getHasBreakfast() === $entry['hasBreakfast']
|
&& $workHour->getHasBreakfast() === $entry['hasBreakfast']
|
||||||
&& $workHour->getHasLunch() === $entry['hasLunch']
|
&& $workHour->getHasLunch() === $entry['hasLunch']
|
||||||
|
&& $workHour->getHasDinner() === $entry['hasDinner']
|
||||||
&& $workHour->getHasOvernight() === $entry['hasOvernight'];
|
&& $workHour->getHasOvernight() === $entry['hasOvernight'];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -127,14 +127,16 @@ final readonly class WorkHourWeeklySummaryProvider implements ProviderInterface
|
|||||||
// Pré-calcul des métriques par salarié/date pour simplifier l'agrégation finale.
|
// Pré-calcul des métriques par salarié/date pour simplifier l'agrégation finale.
|
||||||
$dateKey = $workHour->getWorkDate()->format('Y-m-d');
|
$dateKey = $workHour->getWorkDate()->format('Y-m-d');
|
||||||
$metricsByEmployeeDate[$employeeId][$dateKey] = [
|
$metricsByEmployeeDate[$employeeId][$dateKey] = [
|
||||||
'metrics' => $this->computeMetrics($workHour),
|
'metrics' => $this->computeMetrics($workHour),
|
||||||
'isPresentMorning' => $workHour->getIsPresentMorning(),
|
'isPresentMorning' => $workHour->getIsPresentMorning(),
|
||||||
'isPresentAfternoon' => $workHour->getIsPresentAfternoon(),
|
'isPresentAfternoon' => $workHour->getIsPresentAfternoon(),
|
||||||
'dayHoursMinutes' => $workHour->getDayHoursMinutes(),
|
'dayHoursMinutes' => $workHour->getDayHoursMinutes(),
|
||||||
'nightHoursMinutes' => $workHour->getNightHoursMinutes(),
|
'nightHoursMinutes' => $workHour->getNightHoursMinutes(),
|
||||||
'hasBreakfast' => $workHour->getHasBreakfast(),
|
'workshopHoursMinutes' => $workHour->getWorkshopHoursMinutes(),
|
||||||
'hasLunch' => $workHour->getHasLunch(),
|
'hasBreakfast' => $workHour->getHasBreakfast(),
|
||||||
'hasOvernight' => $workHour->getHasOvernight(),
|
'hasLunch' => $workHour->getHasLunch(),
|
||||||
|
'hasDinner' => $workHour->getHasDinner(),
|
||||||
|
'hasOvernight' => $workHour->getHasOvernight(),
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -185,14 +187,16 @@ final readonly class WorkHourWeeklySummaryProvider implements ProviderInterface
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
$weeklyDayMinutes = 0;
|
$weeklyDayMinutes = 0;
|
||||||
$weeklyNightMinutes = 0;
|
$weeklyNightMinutes = 0;
|
||||||
$weeklyTotalMinutes = 0;
|
$weeklyWorkshopMinutes = 0;
|
||||||
$weeklyPresenceCount = 0.0;
|
$weeklyTotalMinutes = 0;
|
||||||
$weeklyBreakfastCount = 0;
|
$weeklyPresenceCount = 0.0;
|
||||||
$weeklyLunchCount = 0;
|
$weeklyBreakfastCount = 0;
|
||||||
$weeklyOvernightCount = 0;
|
$weeklyLunchCount = 0;
|
||||||
$daily = [];
|
$weeklyDinnerCount = 0;
|
||||||
|
$weeklyOvernightCount = 0;
|
||||||
|
$daily = [];
|
||||||
// Les contrats au suivi "présence" ne manipulent pas les heures, mais des demi-journées.
|
// Les contrats au suivi "présence" ne manipulent pas les heures, mais des demi-journées.
|
||||||
$weekAnchorContract = $contractsByEmployeeDate[$employeeId][$anchorDateYmd]
|
$weekAnchorContract = $contractsByEmployeeDate[$employeeId][$anchorDateYmd]
|
||||||
?? $contractsByEmployeeDate[$employeeId][$days[0]]
|
?? $contractsByEmployeeDate[$employeeId][$days[0]]
|
||||||
@@ -217,21 +221,27 @@ final readonly class WorkHourWeeklySummaryProvider implements ProviderInterface
|
|||||||
|
|
||||||
$hasBreakfast = false;
|
$hasBreakfast = false;
|
||||||
$hasLunch = false;
|
$hasLunch = false;
|
||||||
|
$hasDinner = false;
|
||||||
$hasOvernight = false;
|
$hasOvernight = false;
|
||||||
|
|
||||||
if ($isDateDriver) {
|
if ($isDateDriver) {
|
||||||
$dayMinutes = ($entry['dayHoursMinutes'] ?? 0);
|
$dayMinutes = ($entry['dayHoursMinutes'] ?? 0);
|
||||||
$nightMinutes = ($entry['nightHoursMinutes'] ?? 0);
|
$nightMinutes = ($entry['nightHoursMinutes'] ?? 0);
|
||||||
$totalMinutes = $dayMinutes + $nightMinutes;
|
$workshopMinutes = ($entry['workshopHoursMinutes'] ?? 0);
|
||||||
$hasBreakfast = $entry['hasBreakfast'] ?? false;
|
$totalMinutes = $dayMinutes + $nightMinutes + $workshopMinutes;
|
||||||
$hasLunch = $entry['hasLunch'] ?? false;
|
$hasBreakfast = $entry['hasBreakfast'] ?? false;
|
||||||
$hasOvernight = $entry['hasOvernight'] ?? false;
|
$hasLunch = $entry['hasLunch'] ?? false;
|
||||||
|
$hasDinner = $entry['hasDinner'] ?? false;
|
||||||
|
$hasOvernight = $entry['hasOvernight'] ?? false;
|
||||||
if ($hasBreakfast) {
|
if ($hasBreakfast) {
|
||||||
++$weeklyBreakfastCount;
|
++$weeklyBreakfastCount;
|
||||||
}
|
}
|
||||||
if ($hasLunch) {
|
if ($hasLunch) {
|
||||||
++$weeklyLunchCount;
|
++$weeklyLunchCount;
|
||||||
}
|
}
|
||||||
|
if ($hasDinner) {
|
||||||
|
++$weeklyDinnerCount;
|
||||||
|
}
|
||||||
if ($hasOvernight) {
|
if ($hasOvernight) {
|
||||||
++$weeklyOvernightCount;
|
++$weeklyOvernightCount;
|
||||||
}
|
}
|
||||||
@@ -239,9 +249,10 @@ final readonly class WorkHourWeeklySummaryProvider implements ProviderInterface
|
|||||||
$metrics = $entry['metrics'] ?? new WorkMetrics();
|
$metrics = $entry['metrics'] ?? new WorkMetrics();
|
||||||
// Les absences "comptées comme travaillées" alimentent le total du jour.
|
// Les absences "comptées comme travaillées" alimentent le total du jour.
|
||||||
$metrics->addCreditedMinutes($creditedMinutes);
|
$metrics->addCreditedMinutes($creditedMinutes);
|
||||||
$dayMinutes = $metrics->dayMinutes;
|
$dayMinutes = $metrics->dayMinutes;
|
||||||
$nightMinutes = $metrics->nightMinutes;
|
$nightMinutes = $metrics->nightMinutes;
|
||||||
$totalMinutes = $metrics->totalMinutes;
|
$workshopMinutes = 0;
|
||||||
|
$totalMinutes = $metrics->totalMinutes;
|
||||||
}
|
}
|
||||||
|
|
||||||
$present = null;
|
$present = null;
|
||||||
@@ -256,6 +267,7 @@ final readonly class WorkHourWeeklySummaryProvider implements ProviderInterface
|
|||||||
|
|
||||||
$weeklyDayMinutes += $dayMinutes;
|
$weeklyDayMinutes += $dayMinutes;
|
||||||
$weeklyNightMinutes += $nightMinutes;
|
$weeklyNightMinutes += $nightMinutes;
|
||||||
|
$weeklyWorkshopMinutes += $workshopMinutes;
|
||||||
$weeklyTotalMinutes += $totalMinutes;
|
$weeklyTotalMinutes += $totalMinutes;
|
||||||
if (null !== $present) {
|
if (null !== $present) {
|
||||||
$weeklyPresenceCount += $present;
|
$weeklyPresenceCount += $present;
|
||||||
@@ -265,6 +277,7 @@ final readonly class WorkHourWeeklySummaryProvider implements ProviderInterface
|
|||||||
date: $date,
|
date: $date,
|
||||||
dayMinutes: $dayMinutes,
|
dayMinutes: $dayMinutes,
|
||||||
nightMinutes: $nightMinutes,
|
nightMinutes: $nightMinutes,
|
||||||
|
workshopMinutes: $workshopMinutes,
|
||||||
totalMinutes: $totalMinutes,
|
totalMinutes: $totalMinutes,
|
||||||
present: $present,
|
present: $present,
|
||||||
hasAbsence: $absenceByEmployeeDate[$employeeId][$date] ?? false,
|
hasAbsence: $absenceByEmployeeDate[$employeeId][$date] ?? false,
|
||||||
@@ -272,6 +285,7 @@ final readonly class WorkHourWeeklySummaryProvider implements ProviderInterface
|
|||||||
absenceColor: $absenceColorByEmployeeDate[$employeeId][$date] ?? null,
|
absenceColor: $absenceColorByEmployeeDate[$employeeId][$date] ?? null,
|
||||||
hasBreakfast: $hasBreakfast,
|
hasBreakfast: $hasBreakfast,
|
||||||
hasLunch: $hasLunch,
|
hasLunch: $hasLunch,
|
||||||
|
hasDinner: $hasDinner,
|
||||||
hasOvernight: $hasOvernight,
|
hasOvernight: $hasOvernight,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@@ -304,6 +318,7 @@ final readonly class WorkHourWeeklySummaryProvider implements ProviderInterface
|
|||||||
daily: $daily,
|
daily: $daily,
|
||||||
weeklyDayMinutes: $weeklyDayMinutes,
|
weeklyDayMinutes: $weeklyDayMinutes,
|
||||||
weeklyNightMinutes: $weeklyNightMinutes,
|
weeklyNightMinutes: $weeklyNightMinutes,
|
||||||
|
weeklyWorkshopMinutes: $weeklyWorkshopMinutes,
|
||||||
weeklyTotalMinutes: $weeklyTotalMinutes,
|
weeklyTotalMinutes: $weeklyTotalMinutes,
|
||||||
weeklyPresenceCount: $weeklyPresenceCount,
|
weeklyPresenceCount: $weeklyPresenceCount,
|
||||||
weeklyOvertimeTotalMinutes: $weeklyOvertimeTotalMinutes,
|
weeklyOvertimeTotalMinutes: $weeklyOvertimeTotalMinutes,
|
||||||
@@ -313,6 +328,7 @@ final readonly class WorkHourWeeklySummaryProvider implements ProviderInterface
|
|||||||
isDriver: $isDriver,
|
isDriver: $isDriver,
|
||||||
weeklyBreakfastCount: $weeklyBreakfastCount,
|
weeklyBreakfastCount: $weeklyBreakfastCount,
|
||||||
weeklyLunchCount: $weeklyLunchCount,
|
weeklyLunchCount: $weeklyLunchCount,
|
||||||
|
weeklyDinnerCount: $weeklyDinnerCount,
|
||||||
weeklyOvernightCount: $weeklyOvernightCount,
|
weeklyOvernightCount: $weeklyOvernightCount,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user