diff --git a/apps/ops/src/pages/PlanificationPage.vue b/apps/ops/src/pages/PlanificationPage.vue index 2373307..8b524cf 100644 --- a/apps/ops/src/pages/PlanificationPage.vue +++ b/apps/ops/src/pages/PlanificationPage.vue @@ -224,6 +224,7 @@ + Maternité, invalidité… → à remplacer (pas juste sauter comme des vacances)
Une demande approuvée rend le tech indisponible pour le solveur sur ces dates.
@@ -367,7 +368,7 @@ const showShiftEditor = ref(false); const editTpls = ref([]) const showTeamEditor = ref(false); const editTechs = ref([]) const notifySms = ref(false) const showLeave = ref(false); const leaveRows = ref([]); const leaveFilter = ref('Demandé') -const newLeave = reactive({ technician: '', availability_type: 'Congé', from_date: '', to_date: '', reason: '' }) +const newLeave = reactive({ technician: '', availability_type: 'Congé', from_date: '', to_date: '', reason: '', long_term: 0 }) const newTpl = reactive({ template_name: '', start: '08:00', end: '16:00', color: '#1976d2', on_call: 0 }) function numToTime (h) { const hh = Math.floor(h); const mm = Math.round((h - hh) * 60); return String(hh).padStart(2, '0') + ':' + String(mm).padStart(2, '0') } // Slider à 2 poignées pour le nouveau modèle (heures custom) ↔ newTpl.start/end @@ -532,7 +533,7 @@ async function approveLeave (l, reject) { try { await roster.approveAvailability async function createLeave () { if (!newLeave.technician || !newLeave.from_date || !newLeave.to_date) { $q.notify({ type: 'warning', message: 'Technicien + dates requis' }); return } const t = techs.value.find(x => x.id === newLeave.technician) - try { await roster.requestAvailability({ technician: newLeave.technician, technician_name: t ? t.name : '', availability_type: newLeave.availability_type, from_date: newLeave.from_date, to_date: newLeave.to_date, reason: newLeave.reason }); $q.notify({ type: 'positive', message: 'Demande créée' }); newLeave.from_date = ''; newLeave.to_date = ''; newLeave.reason = ''; await loadLeave() } catch (e) { err(e) } + try { await roster.requestAvailability({ technician: newLeave.technician, technician_name: t ? t.name : '', availability_type: newLeave.availability_type, from_date: newLeave.from_date, to_date: newLeave.to_date, reason: newLeave.reason, long_term: newLeave.long_term }); $q.notify({ type: 'positive', message: 'Demande créée' }); newLeave.from_date = ''; newLeave.to_date = ''; newLeave.reason = ''; newLeave.long_term = 0; await loadLeave() } catch (e) { err(e) } } async function saveEff (t) { const eff = Number(t.efficiency) || 1; try { await roster.setTechEfficiency(t.id, eff); const tt = techs.value.find(x => x.id === t.id); if (tt) tt.efficiency = eff; $q.notify({ type: 'positive', message: t.name + ' : cadence ' + eff }) } catch (e) { err(e) } } function loadedCost (t) { return Math.round(((Number(t.salary) || 0) * (1 + (Number(t.charges) || 0) / 100) + (Number(t.other) || 0)) * 100) / 100 } @@ -680,7 +681,7 @@ function applyTemplate (tm) { const t = techs.value.find(x => x.id === techId); const name = (t ? t.name : techId) const type = absByTechDay.value[techId + '|' + (dayList.value.find(d => isAbsent(techId, d.iso)) || {}).iso] || '' const lbl = name + (type ? ' (' + type + ')' : '') - if (skipped[techId] >= countPatternDays(tm, techId)) fullOut.push(lbl); else partial.push(lbl) + if (/longue durée/i.test(type) || skipped[techId] >= countPatternDays(tm, techId)) fullOut.push(lbl); else partial.push(lbl) } let msg = 'Modèle « ' + tm.name + ' » appliqué (' + applied + ' assignations)' if (partial.length) msg += ' · absence partielle ignorée : ' + partial.join(', ') diff --git a/services/targo-hub/lib/roster.js b/services/targo-hub/lib/roster.js index 754fb7b..0c4d384 100644 --- a/services/targo-hub/lib/roster.js +++ b/services/targo-hub/lib/roster.js @@ -499,9 +499,9 @@ async function absencesByTechDay (start, days) { for (const t of techs) if (t.status === PAUSE_STATUS) for (const d of dates) m[t.id + '|' + d] = 'En pause' const avs = await erp.list('Tech Availability', { filters: [['status', '=', 'Approuvé'], ['from_date', '<=', hi], ['to_date', '>=', lo]], - fields: ['technician', 'from_date', 'to_date', 'availability_type'], limit: 1000, + fields: ['technician', 'from_date', 'to_date', 'availability_type', 'long_term'], limit: 1000, }) - for (const a of avs) for (const d of dates) if (d >= a.from_date && d <= a.to_date) m[a.technician + '|' + d] = a.availability_type || 'Absent' + for (const a of avs) for (const d of dates) if (d >= a.from_date && d <= a.to_date) m[a.technician + '|' + d] = (a.availability_type || 'Absent') + (a.long_term ? ' (longue durée)' : '') return m } @@ -734,6 +734,7 @@ async function handle (req, res, method, path, url) { technician: b.technician, technician_name: b.technician_name || '', availability_type: b.availability_type || 'Congé', status: 'Demandé', from_date: b.from_date, to_date: b.to_date, reason: b.reason || '', + long_term: b.long_term ? 1 : 0, }) return json(res, r.ok ? 200 : 500, r) }