espace-paie-odentas/lib/pdf/templates/ContratCDDU.tsx
odentas 6485db4a75 feat(naa): Amélioration UX modal EditNAA - replier/déplier
- Tous les clients repliés par défaut à l'ouverture du modal
- Boutons 'Tout replier' / 'Tout déplier' pour gérer tous les clients
- Section factures repliable avec bouton Afficher/Masquer
- Affichage résumé facture sélectionnée quand section repliée
- Nouveau client déplié automatiquement pour faciliter la saisie
- Améliore la lisibilité pour NAA avec nombreux clients
2025-10-31 15:28:44 +01:00

592 lines
27 KiB
TypeScript

import React from 'react';
import { Document, Page, Text, View, Image, StyleSheet, Font } from '@react-pdf/renderer';
import { ContratCDDUData } from '../types';
// Styles du document (conversion du CSS)
const styles = StyleSheet.create({
page: {
fontFamily: 'Helvetica',
fontSize: 12,
paddingHorizontal: 50,
paddingVertical: 40,
},
logoContainer: {
display: 'flex',
justifyContent: 'center',
alignItems: 'center',
marginBottom: 20,
},
logo: {
width: 180,
},
title: {
textAlign: 'center',
marginBottom: 30,
fontSize: 14,
fontWeight: 'bold',
},
bold: {
fontWeight: 'bold',
},
section: {
marginBottom: 30,
},
sectionObjet: {
marginBottom: 20,
},
sectionTitle: {
fontWeight: 'bold',
marginBottom: 10,
fontSize: 12,
},
paragraph: {
marginBottom: 0,
textAlign: 'justify',
},
list: {
marginLeft: 0,
},
listItem: {
marginBottom: 5,
},
infoLabel: {
fontWeight: 'bold',
marginBottom: 5,
},
infoValue: {
textAlign: 'left',
marginBottom: 5,
},
infoDelegation: {
textAlign: 'left',
fontStyle: 'italic',
marginBottom: 5,
},
signatureSpace: {
marginTop: 20,
marginBottom: 60,
},
});
interface ContratCDDUProps {
data: ContratCDDUData;
}
export const ContratCDDU: React.FC<ContratCDDUProps> = ({ data }) => {
// Helpers pour la logique conditionnelle
const isMadame = data.employee_civ === 'Madame';
const isMonsieur = data.employee_civ === 'Monsieur';
const isArtiste = data.employee_catpro === 'Artiste';
const isTechnicien = data.employee_catpro === 'Technicien';
const isMetteurEnScene = data.employee_catpro === 'Metteur en scène';
// Titre du contrat selon la catégorie
const getTitreContrat = () => {
if (isArtiste) return 'ARTISTE';
if (isMadame && isTechnicien) return 'TECHNICIENNE';
if (isMonsieur && isTechnicien) return 'TECHNICIEN';
if (isMadame && isMetteurEnScene) return '\nARTISTE CADRE';
return 'ARTISTE CADRE';
};
// Formatage des dates travaillées
const getDatesFormatted = () => {
if (!data.dates_travaillees || data.dates_travaillees === '00') return null;
return data.dates_travaillees.split(';').map(d => d.trim());
};
// Manipulation du lieu de naissance (retirer "Le ")
const getCobFormatted = () => {
const cob = data.employee_cob;
if (cob.startsWith('Le ')) {
return { prefix: 'au', ville: cob.replace(/^Le /, '') };
}
return { prefix: 'à', ville: cob };
};
// Manipulation de la ville (pour signature)
const getVilleSignature = () => {
const ville = data.structure_ville;
if (ville.includes('Le ')) {
return { prefix: 'Au', ville: ville.replace(/^Le /, '') };
}
return { prefix: 'À', ville };
};
// Convention collective formatée
const getCCNFormatted = () => {
if (Array.isArray(data.CCN)) {
return data.CCN.join(', ');
}
return data.CCN;
};
const cobData = getCobFormatted();
const villeSignature = getVilleSignature();
const datesArray = getDatesFormatted();
const ccnFormatted = getCCNFormatted();
return (
<Document>
<Page size="A4" style={styles.page}>
{/* Logo */}
{data.imageUrl && (
<View style={styles.logoContainer}>
<Image src={data.imageUrl} style={styles.logo} />
</View>
)}
{/* Titre */}
<Text style={styles.title}>
CONTRAT D'ENGAGEMENT {getTitreContrat()}
</Text>
{/* Entre les soussignés */}
<Text style={[styles.paragraph, styles.bold]}>
Entre les {isMonsieur ? 'soussignés' : 'soussignées'} :
</Text>
<View style={styles.list}>
<Text style={[styles.listItem, styles.bold]}>{data.structure_name}</Text>
<Text style={styles.listItem}>{data.forme_juridique}</Text>
<Text style={styles.listItem}>{data.structure_adresse}</Text>
<Text style={styles.listItem}>{data.structure_cpville} {data.structure_ville}</Text>
<Text style={styles.listItem}>SIRET : {data.structure_siret}</Text>
{data.structure_licence !== 'n/a' && (
<Text style={styles.listItem}>
Licence d'entrepreneur de spectacles : {data.structure_licence}
</Text>
)}
<Text style={styles.listItem}>
représentée par {data.structure_signataire}, en sa qualité{' '}
{data.structure_signatairequalite === 'Administrateur' ? "d'" : 'de '}
{data.structure_signatairequalite}
{data.delegation === 'Oui'
? ', pour le représentant légal et par délégation.'
: '.'}
</Text>
</View>
<Text style={[styles.paragraph, styles.bold]}>d'une part,</Text>
<Text style={[styles.paragraph, styles.bold]}>et :</Text>
{/* Salarié */}
<View style={styles.list}>
<Text style={[styles.listItem, styles.bold]}>
{data.employee_civ} {data.employee_firstname} {data.employee_lastname}
{data.employee_birthname !== data.employee_lastname && (
<>
{isMonsieur ? ', ' : ', née '}
{data.employee_birthname}
</>
)}
{data.employee_pseudo !== 'n/a' && (
<>
, {isMonsieur ? 'dit' : 'dite'} "{data.employee_pseudo}"
</>
)}
</Text>
<Text style={styles.listItem}>
{isMonsieur ? '' : 'née'} le {data.employee_dob} {cobData.prefix} {cobData.ville}
</Text>
<Text style={styles.listItem}>demeurant {data.employee_address}</Text>
{(!data.employee_ss || data.employee_ss === 0 || data.employee_ss === '') ? (
<Text style={styles.listItem}>
Le numéro de Sécurité Sociale du salarié est en cours d'attribution.
</Text>
) : (
<Text style={styles.listItem}>
N° de Sécurité Sociale : {data.employee_ss}
</Text>
)}
<Text style={styles.listItem}>N° Congés Spectacles : {data.employee_cs}</Text>
{/* Représentant légal si mineur */}
{data.mineur1618 === 'Oui' && (
<Text style={styles.listItem}>
dont {data.representant_civ === 'Monsieur' ? 'le représentant légal' : 'la représentante légale'} est{' '}
{data.representant_civ} {data.representant_nom},{' '}
{data.representant_civ === 'Monsieur' ? 'né' : 'née'} le {data.representant_dob} à{' '}
{data.representant_cob}, demeurant {data.representant_adresse}.
</Text>
)}
</View>
<Text style={[styles.paragraph, styles.bold]}>d'autre part.</Text>
{/* Préambule */}
<Text style={styles.paragraph}>
Le présent contrat est conclu dans le cadre de la législation du travail, des usages en vigueur dans la
profession, de l'article L. 1242-2° du Code du travail et de l'accord interbranche sur le recours au
contrat à durée déterminée d'usage dans le spectacle du 12/10/1998. Il est, en outre, régi par les
dispositions de la {ccnFormatted}
{ccnFormatted.includes('Convention Collective Nationale de l\'Édition') &&
' et de ses annexes afférentes à l\'Édition Phonographique'}.
</Text>
<Text style={styles.paragraph}>Il a é convenu et arrêté ce qui suit :</Text>
{/* Section OBJET */}
<View style={styles.section}>
<Text style={styles.sectionTitle}>OBJET</Text>
<Text style={styles.paragraph}>
{data.employee_civ} {data.employee_firstname} {data.employee_lastname} est{' '}
{isMonsieur ? 'engagé' : 'engagée'} selon l'objet suivant :
</Text>
<View style={styles.list}>
<Text style={styles.listItem}><Text style={styles.bold}>Profession</Text> : {data.employee_profession}</Text>
<Text style={styles.listItem}><Text style={styles.bold}>Code emploi</Text> : {data.employee_codeprofession}</Text>
{(data.structure_spectacle === 'Oui' && data.type_numobjet !== 'Administratif') ||
ccnFormatted.includes('Convention Collective Nationale de la Production Audiovisuelle') ||
ccnFormatted.includes('Convention Collective Nationale de l\'Édition') ? (
<Text style={styles.listItem}>
<Text style={styles.bold}>
{data.structure_spectacle === 'Oui' && data.type_numobjet !== 'Administratif'
? 'Spectacle'
: 'Production'}
</Text> : {data.spectacle}
</Text>
) : null}
{data.numobjet ? (
<Text style={styles.listItem}>
<Text style={styles.bold}>Numéro d'objet</Text> : {data.numobjet}
</Text>
) : (
<Text style={styles.listItem}>
Le <Text style={styles.bold}>numéro d'objet</Text> de cette production est en cours d'attribution.
</Text>
)}
</View>
</View>
{/* Section DURÉE DE L'ENGAGEMENT - Partie 1 */}
<View style={styles.sectionObjet}>
<Text style={styles.sectionTitle}>DURÉE DE L'ENGAGEMENT</Text>
<Text style={styles.paragraph}>
{data.date_debut === data.date_fin ? (
<>Le présent engagement couvre la journée du {data.date_debut}, pour </>
) : (
<>
{datesArray ? (
<>Le présent engagement couvre la période du {data.date_debut} au {data.date_fin} pour les dates travaillées suivantes :</>
) : (
<>Le présent engagement couvre la période du {data.date_debut} au {data.date_fin}.</>
)}
</>
)}
</Text>
{/* Dates travaillées */}
{datesArray && (
<View style={styles.list}>
{datesArray.map((date, index) => (
<Text key={index} style={styles.listItem}>
- {date}{index < datesArray.length - 1 ? ' ;' : ''}
</Text>
))}
</View>
)}
{/* Suite selon catégorie professionnelle */}
{data.date_debut !== data.date_fin && <Text style={styles.paragraph}>Pour </Text>}
{/* Artiste */}
{isArtiste && (
<Text style={styles.paragraph}>
un total de{' '}
{data.cachets.representations >= 1 && data.cachets.repetitions >= 1 && (
<>
{data.cachets.representations} {data.cachets.representations === 1 ? 'cachet' : 'cachets'} de représentation et{' '}
{data.cachets.repetitions} {data.cachets.repetitions === 1 ? 'service' : 'services'} de répétition.
</>
)}
{data.cachets.representations >= 1 && data.cachets.repetitions === 0 && (
<>
{data.cachets.representations} {data.cachets.representations === 1 ? 'cachet' : 'cachets'}
{ccnFormatted.includes('Convention Collective Nationale de la Production Audiovisuelle') ||
ccnFormatted.includes('Convention Collective Nationale de l\'Édition')
? ' d\'enregistrement.'
: ' de représentation.'}
</>
)}
{data.cachets.representations === 0 && data.cachets.repetitions >= 1 && (
<>
{data.cachets.repetitions} {data.cachets.repetitions === 1 ? 'service' : 'services'} de répétition.
</>
)}
</Text>
)}
{/* Technicien */}
{isTechnicien && (
<Text style={styles.paragraph}>
un total de {data.cachets.heures} heures de travail
{data.cachets.heuresparjour === 0 ? '.' : `, à raison de ${data.cachets.heuresparjour} heures par jour de travail.`}
</Text>
)}
{/* Metteur en scène */}
{isMetteurEnScene && (
<Text style={styles.paragraph}>
{data.cachets.representations >= 1 && data.cachets.heures > 0 ? (
<>
un total de {data.cachets.representations} {data.cachets.representations === 1 ? 'cachet' : 'cachets'} de représentation et{' '}
{data.cachets.heures} heures de travail.
</>
) : data.cachets.representations === 0 ? (
<>un total de {data.cachets.heures} heures de travail.</>
) : (
<>un total de {data.cachets.representations} {data.cachets.representations === 1 ? 'cachet' : 'cachets'} de représentation.</>
)}
</Text>
)}
{/* Durée répétitions */}
{data.cachets.repetitions >= 1 && (
<Text style={styles.paragraph}>
La durée totale des répétitions sera de {data.cachets.heures} heures
{data.cachets.heuresparjour === 0
? '.'
: `, à raison de ${data.cachets.heuresparjour} heures par journée de répétition.`}
</Text>
)}
</View>
<View style={styles.section}>
<Text style={styles.paragraph}>
Il ne nous sera, en aucun cas, fait obligation de proroger le présent engagement à expiration. La fin de la période d'engagement prévue
aux présentes, prorogée éventuellement de la durée de dépassement, en constitue le terme. Il n'y a lieu à aucun préavis.
</Text>
</View>
{/* LIEUX D'ENGAGEMENT ET HORAIRES DE TRAVAIL */}
<View style={styles.section}>
<Text style={styles.sectionTitle}>LIEUX D'ENGAGEMENT ET HORAIRES DE TRAVAIL</Text>
<Text style={styles.paragraph}>
{data.structure_name} communiquera à {data.employee_firstname} {data.employee_lastname} les lieux{' '}
{ccnFormatted.includes('Convention Collective Nationale de la Production Audiovisuelle') ||
ccnFormatted.includes('Convention Collective Nationale de l\'Édition') ? (
<></>
) : data.cachets.representations >= 1 && data.cachets.repetitions === 0 ? (
<>des représentations</>
) : data.cachets.representations === 0 && data.cachets.repetitions >= 1 ? (
<>des répétitions</>
) : isTechnicien ? (
<>d'engagement</>
) : data.cachets.representations >= 1 && data.cachets.repetitions >= 1 ? (
<>des répétitions et des représentations</>
) : null}
{isMetteurEnScene && data.cachets.representations === 0 && <>d'exercice de sa fonction</>}
, ainsi que ses horaires de travail.
</Text>
</View>
{/* RÉMUNÉRATION */}
<View style={styles.section}>
<Text style={styles.sectionTitle}>RÉMUNÉRATION</Text>
<Text style={styles.paragraph}>
Il sera alloué à {data.employee_firstname} {data.employee_lastname} à titre de salaire la somme de {data.salaire_brut} euros bruts.
</Text>
{data.precisions_salaire && (
<Text style={styles.paragraph}>
À titre informatif, la répartition de ce salaire brut est la suivante : {data.precisions_salaire}.
</Text>
)}
{data.panierrepas && data.hebergement && (
<Text style={styles.paragraph}>
{data.employee_firstname} {data.employee_lastname} percevra {data.panierrepas}{' '}
{data.panierrepas === '1' ? 'panier repas principal ' : 'paniers repas principaux, '}
et {data.hebergement} {data.hebergement === '1' ? 'indemnité' : 'indemnités'} d'hébergement et petit-déjeuner,{' '}
{data.panierrepasccn === 'Oui' && data.hebergementccn === 'Oui' ? (
<>selon les conditions prévues par la Convention Collective.</>
) : data.panierrepasccn === 'Non' && data.hebergementccn === 'Oui' ? (
<>
à hauteur de {data.montantpanierrepas} euros par panier repas principal, et selon les conditions prévues par la Convention Collective pour l'indemnité hébergement et petit-déjeuner.
</>
) : data.panierrepasccn === 'Oui' && data.hebergementccn === 'Non' ? (
<>
selon les conditions prévues par la Convention Collective pour les paniers repas principaux, et à hauteur de {data.montanthebergement} euros par indemnité hébergement et petit-déjeuner.
</>
) : (
<>
à hauteur de {data.montantpanierrepas} euros par panier repas principal et à hauteur de {data.montanthebergement} euros par indemnité hébergement et petit-déjeuner.
</>
)}
</Text>
)}
{data.autreprecision && (
<Text style={styles.paragraph}>{data.autreprecision}</Text>
)}
</View>
{/* RETRAITE ET CONGÉS PAYÉS */}
<View style={styles.section}>
<Text style={styles.sectionTitle}>RETRAITE ET CONGÉS PAYÉS</Text>
<Text style={styles.paragraph}>
Les cotisations de retraite seront versées à AUDIENS - 7 rue Jean Bleuzen - 92177 VANVES Cedex. L'employeur acquittera ses
contributions à la caisse des Congés Spectacles conformément à la législation et dans la limite des plafonds applicables en vigueur.
</Text>
</View>
{/* ABSENCE-MALADIE */}
<View style={styles.section}>
<Text style={styles.sectionTitle}>ABSENCE-MALADIE</Text>
<Text style={styles.paragraph}>
En cas de maladie ou d'empêchement d'assurer{' '}
{isMetteurEnScene ? (
<>ses missions de mise en scène,</>
) : ccnFormatted.includes('Convention Collective Nationale de la Production Audiovisuelle') ? (
<>ses missions de {data.employee_profession},</>
) : ccnFormatted.includes('Convention Collective Nationale de l\'Édition') ? (
<>un enregistrement,</>
) : (
<>une répétition ou une représentation,</>
)}{' '}
{data.employee_firstname} {data.employee_lastname} sera {isMonsieur ? 'tenu' : 'tenue'} d'en aviser {data.structure_name} dans un délai de 24 heures en précisant la durée probable de son absence. En cas de prolongation d'arrêt de travail,
{' '}{data.employee_firstname} {data.employee_lastname} devra transmettre à {data.structure_name}, dans les plus brefs délais, le certificat médical
justifiant de cette prolongation. En tout état de cause, les parties conviennent expressément qu'en cas de maladie de {data.employee_firstname} {data.employee_lastname},
le présent contrat pourra être résilié de plein droit par {data.structure_name} et ce, dans le respect des dispositions de la convention collective applicable.
</Text>
</View>
{/* DROIT DE PRIORITÉ ET D'EXCLUSIVITÉ */}
<View style={styles.section}>
<Text style={styles.sectionTitle}>DROIT DE PRIORITÉ ET D'EXCLUSIVITÉ</Text>
<Text style={styles.paragraph}>
Le présent contrat donne à {data.structure_name} une priorité absolue sur tous les autres engagements que pourrait conclure par ailleurs {data.employee_firstname} {data.employee_lastname}, sur la période de l'engagement.
La dérogation éventuelle à cette clause devra faire l'objet d'un accord écrit de {data.structure_name}.
</Text>
<Text style={styles.paragraph}>
{data.employee_firstname} {data.employee_lastname} ne pourra en aucun cas refuser sa présence{' '}
{isMetteurEnScene ? (
<>sur ses lieux de travail et aux répétitions</>
) : ccnFormatted.includes('Convention Collective Nationale de la Production Audiovisuelle') ? (
<>sur les lieux de production</>
) : ccnFormatted.includes('Convention Collective Nationale de l\'Édition') ? (
<>sur les lieux d'enregistrement</>
) : (
<>à une répétition ou à une représentation</>
)}{' '}
pour cause d'engagement extérieur, à quelque moment qu'il·elle ait été prévenu{' '}
{isMetteurEnScene ? (
<>de ses horaires et jours de travail et de l'existence de répétitons.</>
) : ccnFormatted.includes('Convention Collective Nationale de la Production Audiovisuelle') ? (
<>de ses horaires, jours et lieux de travail.</>
) : ccnFormatted.includes('Convention Collective Nationale de l\'Édition') ? (
<>de cet session d'enregistrement.</>
) : (
<>de l'existence de cette répétition ou représentation.</>
)}
</Text>
</View>
{/* MÉDECINE DU TRAVAIL */}
<View style={styles.section}>
<Text style={styles.sectionTitle}>MÉDECINE DU TRAVAIL</Text>
<Text style={styles.paragraph}>
{data.employee_firstname} {data.employee_lastname} déclare avoir satisfait aux obligations relatives à la Médecine du travail et communiquera
à {data.structure_name} l'attestation annuelle qui lui a été délivrée par cet organisme.
</Text>
</View>
{/* ASSURANCES */}
<View style={styles.section}>
<Text style={styles.sectionTitle}>ASSURANCES</Text>
<Text style={styles.paragraph}>
{data.employee_firstname} {data.employee_lastname} est {isMonsieur ? 'tenu' : 'tenue'} d'assurer contre tous les risques tous les objets lui appartenant. {data.structure_name}
déclare avoir souscrit les assurances nécessaires à la couverture des risques liés
{ccnFormatted.includes('Convention Collective Nationale de la Production Audiovisuelle') ? (
<> à la production audiovisuelle.</>
) : ccnFormatted.includes('Convention Collective Nationale de l\'Édition') ? (
<> à l'édition phonographique.</>
) : (
<> aux représentations du spectacle.</>
)}
</Text>
</View>
{/* LITIGES */}
<View style={styles.section}>
<Text style={styles.sectionTitle}>LITIGES</Text>
<Text style={styles.paragraph}>
En cas de litige portant sur l'interprétation ou l'application du présent contrat, les parties conviennent de s'en remettre à l'appréciation des
tribunaux compétents, mais seulement après épuisement des voies amiables (conciliation, arbitrage).
</Text>
</View>
{/* PROTECTION DES DONNÉES PERSONNELLES */}
<View style={styles.section}>
<Text style={styles.sectionTitle}>PROTECTION DES DONNÉES PERSONNELLES</Text>
<Text style={styles.paragraph}>
Aux fins de gestion du personnel et de traitement des rémunérations, nous sommes amenés à solliciter des données personnelles vous concernant
à l'occasion de la conclusion, l'exécution et le cas échéant, la rupture de votre contrat de travail.
</Text>
<Text style={styles.paragraph}>
La signature du présent contrat vaut autorisation pour la société de collecter, d'enregistrer et de stocker les données nécessaires.
</Text>
<Text style={styles.paragraph}>
Outre les services internes de {data.structure_name}, les destinataires de ces données sont, à ce jour, les organismes de sécurité sociale,
les caisses de retraite et de prévoyance, la mutuelle, France Travail Spectacle, les services des impôts, le service de médecine du travail, les organismes conventionnels et la société
Odentas Media SAS, notre prestataire de gestion de la paie.
</Text>
<Text style={styles.paragraph}>
Ces informations sont réservées à l'usage des services concernés et ne peuvent être communiquées qu'à ces destinataires.
</Text>
<Text style={styles.paragraph}>
Vous bénéficiez notamment d'un droit d'accès, de rectification et d'effacement des informations vous concernant, que vous pouvez exercer
en adressant directement une demande au responsable de ces traitements : {data.nom_responsable_traitement}, {data.qualite_responsable_traitement}, {data.email_responsable_traitement}.
</Text>
</View>
{/* Fait à / Date signature */}
<View style={styles.section}>
<Text style={styles.paragraph}>Fait en double exemplaire,</Text>
<Text style={styles.paragraph}>
{villeSignature.prefix} {villeSignature.ville}, le {data.date_signature}.
</Text>
</View>
{/* Signatures */}
<View style={styles.section}>
<View style={styles.signatureSpace}>
<Text style={styles.infoLabel}>
{isMonsieur ? 'Le salarié :' : 'La salariée :'}
</Text>
<Text style={styles.infoValue}>
{data.employee_civ} {data.employee_firstname} {data.employee_lastname}
</Text>
<Text style={styles.paragraph}>(Signature électronique via DocuSeal)</Text>
</View>
{/* Signature représentant légal si mineur */}
{data.mineur1618 === 'Oui' && (
<View style={styles.signatureSpace}>
<Text style={styles.infoLabel}>
{data.representant_civ === 'Monsieur' ? 'Le représentant légal' : 'La représentante légale'}
{isMadame ? ' de la salariée :' : ' du salarié :'}
</Text>
<Text style={styles.infoValue}>
{data.representant_civ} {data.representant_nom}
</Text>
<Text style={styles.paragraph}>(Signature électronique via DocuSeal)</Text>
</View>
)}
{/* Signature employeur */}
<View style={styles.signatureSpace}>
<Text style={styles.infoLabel}>L'employeur:</Text>
<Text style={styles.infoValue}>Pour {data.structure_name},</Text>
{data.delegation === 'Oui' && (
<Text style={styles.infoDelegation}>
Pour le représentant légal et par délégation,
</Text>
)}
<Text style={styles.infoValue}>{data.structure_signataire},</Text>
<Text style={styles.infoValue}>{data.structure_signatairequalite}.</Text>
<Text style={styles.paragraph}>(Signature électronique via DocuSeal)</Text>
</View>
</View>
</Page>
</Document>
);
};