Retour RH: vue jour par date, RTT mi-semaine, récap salaire & exports, panier de nuit (#21)
Auto Tag Develop / tag (push) Successful in 11s
Auto Tag Develop / tag (push) Successful in 11s
## Correctifs RH (branche fix/retour-rh) ### Vue Jour (Heures) - Mode saisie/présence, libellé de contrat et sauvegarde résolus **à la date affichée** (et non au contrat courant). Corrige les salariés passés 39h/35h → Forfait. ### RTT — heures supplémentaires - Proratisation du **plafond 25%/50%** pour les embauches en milieu de semaine (la bande +25% se décale au lieu de rester bloquée à 43h). Témoin Dylan : 4h à 25% + 3h à 50%. ### Récap salaire (PDF mensuel) - Forfait : congés imputés **N-1** non affichés et comptés en présence. - Colonne « Heures payés » **scindée 25% / 50%** (en-tête fusionné). - **Exclusion des salariés sans contrat** sur le mois (ex. Marine, contrat terminé). ### Exports heures annuelles (par salarié + tous) - **Tous les jours sous contrat** affichés, même vides/non saisis (corrige les lignes manquantes). - Samedis/dimanches en **gris plus foncé**. ### Panier de nuit - **Ne s'applique pas aux conducteurs** (vue semaine + récap salaire). ## Tests - 11 tests ajoutés. Suite verte hors un test legacy pré-existant dépendant de la date (`EmployeeRttSummaryProviderTest::testNoQueryParamsKeepsLegacyYearDefaulting`, non modifié par cette branche). ## À noter (hors scope) - L'export heures annuelles *tous salariés* peut dépasser `memory_limit=256M` (Dompdf) — limitation **pré-existante**, non corrigée ici. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Reviewed-on: #21 Co-authored-by: tristan <tristan@yuno.malio.fr> Co-committed-by: tristan <tristan@yuno.malio.fr>
This commit was merged in pull request #21.
This commit is contained in:
@@ -350,11 +350,30 @@ export const useHoursPage = () => {
|
||||
updatedAt: null
|
||||
})
|
||||
|
||||
const isPresenceTracking = (employee: Employee) => employee.contract?.trackingMode === TRACKING_MODES.PRESENCE
|
||||
// Résout le contrat à la date affichée (ligne du jour), avec repli sur le contrat courant.
|
||||
const resolveDayContract = (employee: Employee) => {
|
||||
const dayRow = dayContextByEmployeeId.value.get(employee.id)
|
||||
if (dayRow?.hasContractAtDate) {
|
||||
return {
|
||||
trackingMode: dayRow.trackingMode ?? null,
|
||||
weeklyHours: dayRow.weeklyHours ?? null,
|
||||
type: dayRow.contractType ?? null,
|
||||
name: dayRow.contractName ?? ''
|
||||
}
|
||||
}
|
||||
return {
|
||||
trackingMode: employee.contract?.trackingMode ?? null,
|
||||
weeklyHours: employee.contract?.weeklyHours ?? null,
|
||||
type: employee.contract?.type ?? null,
|
||||
name: employee.contract?.name ?? ''
|
||||
}
|
||||
}
|
||||
|
||||
const isPresenceTracking = (employee: Employee) => resolveDayContract(employee).trackingMode === TRACKING_MODES.PRESENCE
|
||||
const isTimeTracking = (employee: Employee) => !isPresenceTracking(employee)
|
||||
const is4hContract = (employeeId: number) => {
|
||||
const employee = employees.value.find((e) => e.id === employeeId)
|
||||
return employee?.contract?.weeklyHours === 4
|
||||
return employee ? resolveDayContract(employee).weeklyHours === 4 : false
|
||||
}
|
||||
const isRowLocked = (employeeId: number) => {
|
||||
const row = rows.value[employeeId]
|
||||
@@ -365,8 +384,8 @@ export const useHoursPage = () => {
|
||||
}
|
||||
|
||||
const contractLabel = (employee: Employee) => {
|
||||
const contract = employee.contract
|
||||
if (!contract) return '-'
|
||||
const contract = resolveDayContract(employee)
|
||||
if (!contract.type && !contract.name) return '-'
|
||||
if (contract.type === CONTRACT_TYPES.INTERIM) {
|
||||
return contract.name
|
||||
}
|
||||
|
||||
@@ -29,6 +29,7 @@ export const documentationSections: DocSection[] = [
|
||||
{ type: 'list', content: 'Boutons "Hier" / "Aujourd\'hui" / "Demain" pour naviguer rapidement\nSélecteur de date pour choisir une date spécifique\nFiltrage par site si vous avez accès à plusieurs sites' },
|
||||
{ type: 'paragraph', content: 'Seuls les employés ayant un contrat actif à la date sélectionnée sont affichés.' },
|
||||
{ type: 'note', content: 'Sous le nom de l\'employé, la nature du contrat (CDI / CDD / Intérim) affichée correspond à la période couvrant la date filtrée, et non à aujourd\'hui.' },
|
||||
{ type: 'paragraph', content: 'Sur la vue Jour, l\'affichage (saisie d\'heures ou cases de présence) et le libellé de contrat correspondent au contrat de l\'employé à la date consultée. Si un salarié a changé de type de contrat (par exemple un passage en forfait), les jours antérieurs à ce changement restent affichés selon l\'ancien contrat.' },
|
||||
],
|
||||
},
|
||||
{
|
||||
@@ -373,7 +374,7 @@ export const documentationSections: DocSection[] = [
|
||||
requiredLevel: 'admin',
|
||||
blocks: [
|
||||
{ type: 'paragraph', content: 'La vue semaine conducteurs affiche des colonnes spécifiques.' },
|
||||
{ type: 'list', content: 'Totaux jour / nuit / atelier par jour et par semaine\nPanier de nuit (PN) : affiché quand heures nuit > heures jour OU nuit ≥ 4h\nCompteurs hebdomadaires : petit déjeuner, déjeuner, dîner, nuitée\nRTT calculé sur jour + nuit + atelier (au lieu des créneaux classiques)' },
|
||||
{ type: 'list', content: 'Totaux jour / nuit / atelier par jour et par semaine\nPanier de nuit (PN) : ne s\'applique pas aux conducteurs (ils ont leurs propres primes repas/nuitée)\nCompteurs hebdomadaires : petit déjeuner, déjeuner, dîner, nuitée\nRTT calculé sur jour + nuit + atelier (au lieu des créneaux classiques)' },
|
||||
],
|
||||
},
|
||||
],
|
||||
@@ -512,6 +513,7 @@ export const documentationSections: DocSection[] = [
|
||||
blocks: [
|
||||
{ type: 'paragraph', content: 'Le RTT correspond aux heures supplémentaires accumulées, converties en temps de récupération. L\'exercice RTT va du 1er juin (N-1) au 31 mai (N).' },
|
||||
{ type: 'paragraph', content: 'L\'onglet RTT sur la fiche employé affiche le détail hebdomadaire regroupé par mois, avec un compteur global en heures (1 jour = 7h = 420 minutes).' },
|
||||
{ type: 'note', content: 'Pour un contrat débutant en milieu de semaine, le calcul RTT proratise les seuils d\'heures supplémentaires aux jours réellement contractés : le seuil de départ du +25 % et le plafond séparant le +25 % du +50 % sont décalés ensemble (la bande +25 % garde sa largeur : 4h pour un 39h, 8h pour un 35h). Une semaine d\'embauche peut donc générer à la fois des heures à 25 % et à 50 %.' },
|
||||
],
|
||||
},
|
||||
{
|
||||
@@ -619,7 +621,9 @@ export const documentationSections: DocSection[] = [
|
||||
requiredLevel: 'admin',
|
||||
blocks: [
|
||||
{ type: 'paragraph', content: 'Génère un PDF A4 paysage avec le détail mensuel pour la paie.' },
|
||||
{ type: 'list', content: 'Sélecteur de mois (défaut = mois courant)\nDonnées groupées par site\nColonnes : nom, base contrat, jours de présence cadre, heures de nuit, panier de nuit, heures RTT payées, congés (nombre + dates), maladie/AT (nombre + dates), primes conducteur (PDJ, repas, nuitée, samedi), observations\nColonne « Repas » chauffeur : somme déjeuner + dîner sur le mois (un jour avec les deux compte 2 repas)' },
|
||||
{ type: 'list', content: 'Sélecteur de mois (défaut = mois courant)\nDonnées groupées par site\nColonnes : nom, base contrat, jours de présence cadre, heures de nuit, panier de nuit, heures RTT payées (en-tête fusionné scindé en deux sous-colonnes 25 % et 50 %), congés (nombre + dates), maladie/AT (nombre + dates), primes conducteur (PDJ, repas, nuitée, samedi), observations\nColonne « Repas » chauffeur : somme déjeuner + dîner sur le mois (un jour avec les deux compte 2 repas)' },
|
||||
{ type: 'note', content: 'Seuls les salariés ayant un contrat couvrant tout ou partie du mois apparaissent : un salarié dont le contrat est terminé (ex. parti en février) n\'est pas listé sur le récap des mois suivants.' },
|
||||
{ type: 'note', content: 'Forfait : un congé imputé sur le stock de l\'année précédente (N-1) n\'apparaît pas dans la colonne congés et compte comme un jour de présence. Le budget N-1 est consommé dans l\'ordre chronologique depuis janvier, de façon cohérente avec la fiche employé (les jours payés réduisent le stock N-1 d\'abord). Au-delà du budget N-1, les congés s\'affichent normalement.' },
|
||||
],
|
||||
},
|
||||
{
|
||||
@@ -637,7 +641,7 @@ export const documentationSections: DocSection[] = [
|
||||
requiredLevel: 'admin',
|
||||
blocks: [
|
||||
{ type: 'paragraph', content: 'Génère un PDF par employé avec le détail jour par jour de ses heures sur une année.' },
|
||||
{ type: 'list', content: 'Accessible depuis la fiche employé (bouton imprimante)\nChoix de l\'année civile (janvier à décembre)\nColonnes adaptées au mode de suivi (TIME, PRESENCE, conducteur)\nSections séparées en cas de changement de contrat en cours d\'année\nLes jours fériés apparaissent toujours (ligne bleue) avec la mention « Férié : {nom} » dans la colonne Absence ; le total reprend les heures contractuelles créditées (hors Forfait)' },
|
||||
{ type: 'list', content: 'Accessible depuis la fiche employé (bouton imprimante)\nChoix de l\'année civile (janvier à décembre)\nColonnes adaptées au mode de suivi (TIME, PRESENCE, conducteur)\nSections séparées en cas de changement de contrat en cours d\'année\nTous les jours sous contrat sont affichés, même vides ou non saisis (jusqu\'à la date du jour) ; seuls les jours hors contrat (avant embauche, après départ) sont omis\nLes samedis et dimanches sont grisés (gris foncé), les jours fériés en bleu avec « Férié : {nom} »' },
|
||||
],
|
||||
},
|
||||
],
|
||||
|
||||
@@ -115,6 +115,10 @@ export type WorkHourDayContextRow = {
|
||||
formationLabel?: string | null
|
||||
virtualHolidayMinutes?: number
|
||||
contractNature?: 'CDI' | 'CDD' | 'INTERIM' | null
|
||||
trackingMode?: TrackingMode | null
|
||||
weeklyHours?: number | null
|
||||
contractType?: ContractType | null
|
||||
contractName?: string | null
|
||||
}
|
||||
|
||||
export type WorkHourDayContext = {
|
||||
|
||||
Reference in New Issue
Block a user