diff --git a/config/version.yaml b/config/version.yaml
index 58ebf50..bb12e2d 100644
--- a/config/version.yaml
+++ b/config/version.yaml
@@ -1,2 +1,2 @@
parameters:
- app.version: '0.1.15'
+ app.version: '0.1.17'
diff --git a/frontend/components/PeriodStepperPicker.vue b/frontend/components/PeriodStepperPicker.vue
new file mode 100644
index 0000000..c88b66d
--- /dev/null
+++ b/frontend/components/PeriodStepperPicker.vue
@@ -0,0 +1,77 @@
+
+
+
+
+
+
+
+
+
+
diff --git a/frontend/components/hours/HoursToolbar.vue b/frontend/components/hours/HoursToolbar.vue
index 991ccfa..f833eb6 100644
--- a/frontend/components/hours/HoursToolbar.vue
+++ b/frontend/components/hours/HoursToolbar.vue
@@ -64,41 +64,17 @@
-
-
-
-
-
-
+
@@ -145,6 +121,7 @@ import type { Site } from '~/services/dto/site'
import type { AbsenceType } from '~/services/dto/absence-type'
import EmployeeNameFilterInput from '~/components/EmployeeNameFilterInput.vue'
import SiteFilterSelector from '~/components/SiteFilterSelector.vue'
+import PeriodStepperPicker from '~/components/PeriodStepperPicker.vue'
import { weekInputValueToYmd, ymdToWeekInputValue } from '~/utils/date'
const selectedDate = defineModel
('selectedDate', { required: true })
@@ -172,7 +149,6 @@ const emit = defineEmits<{
(e: 'shift-date', value: number): void
}>()
-const nativeDateInput = ref(null)
const pickerValue = computed(() => {
if (viewMode.value === 'week') return ymdToWeekInputValue(selectedDate.value)
return selectedDate.value
@@ -186,19 +162,7 @@ const viewModeButtonClass = (mode: 'day' | 'week') => {
return 'bg-white text-primary-500 hover:bg-tertiary-500'
}
-const openDatePicker = () => {
- const input = nativeDateInput.value
- if (!input) return
- if (typeof input.showPicker === 'function') {
- input.showPicker()
- return
- }
- input.focus()
- input.click()
-}
-
-const onPickerInput = (event: Event) => {
- const value = (event.target as HTMLInputElement).value
+const onPickerValue = (value: string) => {
if (!value) return
if (viewMode.value === 'week') {
diff --git a/frontend/composables/useHoursPage.ts b/frontend/composables/useHoursPage.ts
index b8379ad..36ffa21 100644
--- a/frontend/composables/useHoursPage.ts
+++ b/frontend/composables/useHoursPage.ts
@@ -427,8 +427,11 @@ export const useHoursPage = () => {
const getPresenceDayValue = (employeeId: number) => {
const row = rows.value[employeeId]
- const basePresence = (row?.isPresentMorning ? 0.5 : 0) + (row?.isPresentAfternoon ? 0.5 : 0)
- const creditedPresence = dayContextByEmployeeId.value.get(employeeId)?.creditedPresenceUnits ?? 0
+ const dayRow = dayContextByEmployeeId.value.get(employeeId)
+ const absentMorning = dayRow?.absentMorning ?? false
+ const absentAfternoon = dayRow?.absentAfternoon ?? false
+ const basePresence = ((row?.isPresentMorning && !absentMorning) ? 0.5 : 0) + ((row?.isPresentAfternoon && !absentAfternoon) ? 0.5 : 0)
+ const creditedPresence = dayRow?.creditedPresenceUnits ?? 0
const total = Math.min(1, basePresence + creditedPresence)
return Number.isInteger(total) ? String(total) : total.toFixed(1)
}
diff --git a/frontend/pages/calendar.vue b/frontend/pages/calendar.vue
index 88c183a..77d17f6 100644
--- a/frontend/pages/calendar.vue
+++ b/frontend/pages/calendar.vue
@@ -30,22 +30,17 @@
-
-
+
@@ -111,6 +106,7 @@ import CalendarGrid from '~/components/CalendarGrid.vue'
import AbsenceFormDrawer from '~/components/AbsenceFormDrawer.vue'
import AbsencePrintDrawer from '~/components/AbsencePrintDrawer.vue'
import EmployeeNameFilterInput from '~/components/EmployeeNameFilterInput.vue'
+import PeriodStepperPicker from '~/components/PeriodStepperPicker.vue'
import SiteFilterSelector from '~/components/SiteFilterSelector.vue'
useHead({
@@ -195,8 +191,8 @@ const months = [
{value: 11, label: 'Décembre'}
]
-const years = Array.from({length: 5}, (unusedValue, index) => now.getFullYear() - 2 + index)
-
+const selectedMonthLabel = computed(() => `${months[selectedMonth.value]?.label ?? ''}`)
+const monthPickerValue = computed(() => `${selectedYear.value}-${String(selectedMonth.value + 1).padStart(2, '0')}`)
// Infos de calendrier calculées.
const daysInMonth = computed(() => getDaysInMonth(selectedYear.value, selectedMonth.value))
@@ -316,6 +312,22 @@ const addMonths = (date: Date, months: number) => {
return next
}
+const shiftMonth = (delta: number) => {
+ const next = new Date(selectedYear.value, selectedMonth.value + delta, 1)
+ selectedYear.value = next.getFullYear()
+ selectedMonth.value = next.getMonth()
+}
+
+const onMonthPickerValue = (value: string) => {
+ if (!value) return
+ const [yearStr, monthStr] = value.split('-')
+ const year = Number(yearStr)
+ const month = Number(monthStr)
+ if (!Number.isInteger(year) || !Number.isInteger(month) || month < 1 || month > 12) return
+ selectedYear.value = year
+ selectedMonth.value = month - 1
+}
+
// Limite l'intervalle d'impression à 2 mois max.
const enforcePrintRange = () => {
if (!printForm.from) return
diff --git a/src/Service/WorkHours/WorkedHoursCreditPolicy.php b/src/Service/WorkHours/WorkedHoursCreditPolicy.php
index 9454e86..7a4f72f 100644
--- a/src/Service/WorkHours/WorkedHoursCreditPolicy.php
+++ b/src/Service/WorkHours/WorkedHoursCreditPolicy.php
@@ -60,11 +60,6 @@ final readonly class WorkedHoursCreditPolicy
bool $absentMorning,
bool $absentAfternoon
): float {
- $type = $absence->getType();
- if (!$type?->getCountAsWorkedHours()) {
- return 0.0;
- }
-
$employee = $absence->getEmployee();
if (null === $employee) {
return 0.0;
@@ -74,9 +69,14 @@ final readonly class WorkedHoursCreditPolicy
return 0.0;
}
- $halfUnits = ($absentMorning ? 1 : 0) + ($absentAfternoon ? 1 : 0);
+ // Règle forfait:
+ // - demi-journée d'absence => 0.5 travaillé
+ // - journée complète d'absence => 0 travaillé
+ if ($absentMorning xor $absentAfternoon) {
+ return 0.5;
+ }
- return $halfUnits * 0.5;
+ return 0.0;
}
public function resolveContractDayMinutes(?int $weeklyHours, int $isoWeekDay): int
diff --git a/src/State/WorkHourWeeklySummaryProvider.php b/src/State/WorkHourWeeklySummaryProvider.php
index b11922b..526e2fe 100644
--- a/src/State/WorkHourWeeklySummaryProvider.php
+++ b/src/State/WorkHourWeeklySummaryProvider.php
@@ -135,6 +135,8 @@ final readonly class WorkHourWeeklySummaryProvider implements ProviderInterface
$creditedByEmployeeDate = [];
$creditedPresenceByEmployeeDate = [];
$absenceByEmployeeDate = [];
+ $absentMorningByEmployeeDate = [];
+ $absentAfternoonByEmployeeDate = [];
$absenceLabelByEmployeeDate = [];
$absenceColorByEmployeeDate = [];
foreach ($absences as $absence) {
@@ -153,7 +155,9 @@ final readonly class WorkHourWeeklySummaryProvider implements ProviderInterface
[$absentMorning, $absentAfternoon] = $this->absenceSegmentsResolver->resolveForDate($absence, $date);
if ($absentMorning || $absentAfternoon) {
- $absenceByEmployeeDate[$employeeId][$date] = true;
+ $absenceByEmployeeDate[$employeeId][$date] = true;
+ $absentMorningByEmployeeDate[$employeeId][$date] = ($absentMorningByEmployeeDate[$employeeId][$date] ?? false) || $absentMorning;
+ $absentAfternoonByEmployeeDate[$employeeId][$date] = ($absentAfternoonByEmployeeDate[$employeeId][$date] ?? false) || $absentAfternoon;
if (!isset($absenceLabelByEmployeeDate[$employeeId][$date])) {
$absenceLabelByEmployeeDate[$employeeId][$date] = $absence->getType()?->getLabel();
}
@@ -202,8 +206,10 @@ final readonly class WorkHourWeeklySummaryProvider implements ProviderInterface
$metrics->addCreditedMinutes($creditedMinutes);
$present = null;
if ($isPresenceTracking) {
- $morning = ($entry['isPresentMorning'] ?? false) ? 0.5 : 0.0;
- $afternoon = ($entry['isPresentAfternoon'] ?? false) ? 0.5 : 0.0;
+ $absentMorning = $absentMorningByEmployeeDate[$employeeId][$date] ?? false;
+ $absentAfternoon = $absentAfternoonByEmployeeDate[$employeeId][$date] ?? false;
+ $morning = (($entry['isPresentMorning'] ?? false) && !$absentMorning) ? 0.5 : 0.0;
+ $afternoon = (($entry['isPresentAfternoon'] ?? false) && !$absentAfternoon) ? 0.5 : 0.0;
$creditedPresence = $creditedPresenceByEmployeeDate[$employeeId][$date] ?? 0.0;
$present = min(1.0, $morning + $afternoon + $creditedPresence);
}
diff --git a/tests/State/WorkHourWeeklySummaryProviderTest.php b/tests/State/WorkHourWeeklySummaryProviderTest.php
index 24ac5ab..a3a441d 100644
--- a/tests/State/WorkHourWeeklySummaryProviderTest.php
+++ b/tests/State/WorkHourWeeklySummaryProviderTest.php
@@ -135,7 +135,7 @@ final class WorkHourWeeklySummaryProviderTest extends TestCase
self::assertSame(210, $result->rows[0]->weeklyOvertime50Minutes);
self::assertSame(1230, $result->rows[0]->weeklyRecoveryMinutes);
- self::assertSame(1.0, $result->rows[1]->weeklyPresenceCount);
+ self::assertSame(0.0, $result->rows[1]->weeklyPresenceCount);
self::assertTrue($result->rows[1]->daily[0]->hasAbsence);
self::assertSame('Congé', $result->rows[1]->daily[0]->absenceLabel);
self::assertSame('#000', $result->rows[1]->daily[0]->absenceColor);