feat: Préparation migration PDFMonkey vers Gotenberg
- Ajout helpers Handlebars pour remplacer filtres Liquid - Conversion template CDDU de Liquid vers Handlebars - Nouvelle API route /api/generate-contract-pdf pour Gotenberg - Configuration Docker Compose pour auto-héberger Gotenberg - Documentation complète de migration - Variables d'environnement exemple Note: Le bouton 'Créer PDF' utilise encore PDFMonkey. Pour activer Gotenberg, modifier l'appel dans ContractEditor.tsx
This commit is contained in:
parent
a12bf0ca0e
commit
c6faceb038
6 changed files with 1129 additions and 0 deletions
8
.env.gotenberg.example
Normal file
8
.env.gotenberg.example
Normal file
|
|
@ -0,0 +1,8 @@
|
|||
# Variables d'environnement pour Gotenberg
|
||||
# Ajouter ces lignes dans .env.local
|
||||
|
||||
# URL de Gotenberg (local pour développement)
|
||||
GOTENBERG_URL=http://localhost:3001
|
||||
|
||||
# En production (Railway, Coolify, VPS, etc.)
|
||||
# GOTENBERG_URL=https://gotenberg.votre-domaine.com
|
||||
400
MIGRATION_PDFMONKEY_GOTENBERG.md
Normal file
400
MIGRATION_PDFMONKEY_GOTENBERG.md
Normal file
|
|
@ -0,0 +1,400 @@
|
|||
# Migration PDFMonkey → Gotenberg
|
||||
|
||||
## Contexte
|
||||
|
||||
Ce guide explique comment migrer la génération de PDF des contrats de travail de **PDFMonkey** (SaaS avec templates Liquid) vers **Gotenberg** (auto-hébergé).
|
||||
|
||||
## Avantages de Gotenberg
|
||||
|
||||
- Auto-hébergé : contrôle total, pas de dépendance externe
|
||||
- Gratuit et open-source
|
||||
- Rapide et performant
|
||||
- Supporte HTML/CSS nativement
|
||||
- API simple et bien documentée
|
||||
- Déployable sur Vercel, Railway, Coolify, etc.
|
||||
|
||||
## Architecture de la Solution
|
||||
|
||||
```
|
||||
┌─────────────────┐
|
||||
│ Next.js App │
|
||||
│ (Formulaire) │
|
||||
└────────┬────────┘
|
||||
│ 1. Envoie données JSON
|
||||
↓
|
||||
┌─────────────────────────┐
|
||||
│ API Route Next.js │
|
||||
│ /api/generate-contract │
|
||||
│ │
|
||||
│ - Compile template │
|
||||
│ Handlebars │
|
||||
│ - Génère HTML │
|
||||
└────────┬────────────────┘
|
||||
│ 2. Envoie HTML
|
||||
↓
|
||||
┌─────────────────────────┐
|
||||
│ Gotenberg Service │
|
||||
│ (Docker/Auto-hébergé) │
|
||||
│ │
|
||||
│ - Convertit HTML → PDF │
|
||||
│ - Retourne le PDF │
|
||||
└────────┬────────────────┘
|
||||
│ 3. Reçoit PDF
|
||||
↓
|
||||
┌─────────────────────────┐
|
||||
│ Supabase Storage │
|
||||
│ (Stockage PDF) │
|
||||
└─────────────────────────┘
|
||||
```
|
||||
|
||||
## Fichiers Créés
|
||||
|
||||
### 1. Helpers Handlebars
|
||||
**Fichier** : `lib/handlebars-helpers.ts`
|
||||
|
||||
Remplace les filtres Liquid par des helpers Handlebars :
|
||||
- `removeFirst` : équivalent de `remove_first`
|
||||
- `contains` : vérifier si une chaîne contient un pattern
|
||||
- `split` : diviser une chaîne
|
||||
- `eq`, `ne`, `gte`, `gt` : comparaisons
|
||||
- `isEmpty`, `isNotEmpty` : vérifier les valeurs vides
|
||||
- `includesAny` : vérifier plusieurs valeurs (pour les CCN)
|
||||
|
||||
### 2. Template Handlebars
|
||||
**Fichier** : `templates-contrats/cddu-handlebars.html`
|
||||
|
||||
Template HTML/Handlebars qui remplace le template Liquid PDFMonkey.
|
||||
|
||||
**Principales conversions** :
|
||||
|
||||
| Liquid | Handlebars |
|
||||
|--------|------------|
|
||||
| `{% if condition %}` | `{{#if condition}}` |
|
||||
| `{% elsif %}` | `{{else if}}` |
|
||||
| `{% assign var = value %}` | Variables pré-calculées en JS |
|
||||
| `{{ var \| filter }}` | `{{helper var}}` |
|
||||
| `{% for item in array %}` | `{{#each array}}` |
|
||||
|
||||
### 3. API Route Gotenberg
|
||||
**Fichier** : `app/api/generate-contract-pdf/route.ts`
|
||||
|
||||
Route API qui :
|
||||
1. Authentifie l'utilisateur
|
||||
2. Charge le template Handlebars
|
||||
3. Compile le template avec les données
|
||||
4. Envoie le HTML à Gotenberg
|
||||
5. Reçoit le PDF
|
||||
6. Upload sur Supabase Storage
|
||||
7. Retourne l'URL du PDF
|
||||
|
||||
### 4. Docker Compose
|
||||
**Fichier** : `docker-compose.gotenberg.yml`
|
||||
|
||||
Configuration Docker pour auto-héberger Gotenberg.
|
||||
|
||||
## Installation
|
||||
|
||||
### Étape 1 : Installer les dépendances
|
||||
|
||||
```bash
|
||||
npm install handlebars
|
||||
npm install --save-dev @types/handlebars
|
||||
```
|
||||
|
||||
### Étape 2 : Configurer les variables d'environnement
|
||||
|
||||
Ajouter dans `.env.local` :
|
||||
|
||||
```bash
|
||||
# URL de Gotenberg (local ou auto-hébergé)
|
||||
GOTENBERG_URL=http://localhost:3001
|
||||
|
||||
# Ou en production (exemple avec Railway/Coolify)
|
||||
# GOTENBERG_URL=https://gotenberg.votre-domaine.com
|
||||
```
|
||||
|
||||
### Étape 3 : Déployer Gotenberg localement
|
||||
|
||||
```bash
|
||||
docker-compose -f docker-compose.gotenberg.yml up -d
|
||||
```
|
||||
|
||||
Vérifier que Gotenberg fonctionne :
|
||||
```bash
|
||||
curl http://localhost:3001/health
|
||||
```
|
||||
|
||||
### Étape 4 : Tester l'API
|
||||
|
||||
Créer un fichier de test `test-generate-pdf.ts` :
|
||||
|
||||
```typescript
|
||||
const response = await fetch('/api/generate-contract-pdf', {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
},
|
||||
body: JSON.stringify({
|
||||
contractId: 'abc123',
|
||||
contractType: 'cddu',
|
||||
data: {
|
||||
structure_name: 'Association Compagnie Lazara',
|
||||
employee_firstname: 'Jean',
|
||||
employee_lastname: 'GOLTIER',
|
||||
// ... autres données
|
||||
},
|
||||
}),
|
||||
});
|
||||
|
||||
const result = await response.json();
|
||||
console.log('PDF généré:', result.pdfUrl);
|
||||
```
|
||||
|
||||
## Déploiement en Production
|
||||
|
||||
### Option 1 : Railway (recommandé)
|
||||
|
||||
1. Créer un nouveau service sur Railway
|
||||
2. Déployer l'image Docker : `gotenberg/gotenberg:8`
|
||||
3. Exposer le port `3000`
|
||||
4. Récupérer l'URL publique
|
||||
5. Mettre à jour `GOTENBERG_URL` dans Vercel
|
||||
|
||||
### Option 2 : Coolify
|
||||
|
||||
1. Créer un nouveau service
|
||||
2. Source : Docker Image
|
||||
3. Image : `gotenberg/gotenberg:8`
|
||||
4. Port : `3000`
|
||||
5. Générer un domaine public
|
||||
6. Mettre à jour `GOTENBERG_URL`
|
||||
|
||||
### Option 3 : VPS auto-hébergé
|
||||
|
||||
```bash
|
||||
# Sur le VPS
|
||||
git clone <votre-repo>
|
||||
cd Projet\ Nouvel\ Espace\ Paie
|
||||
docker-compose -f docker-compose.gotenberg.yml up -d
|
||||
|
||||
# Configurer Nginx reverse proxy
|
||||
sudo nano /etc/nginx/sites-available/gotenberg
|
||||
|
||||
# Contenu :
|
||||
server {
|
||||
listen 80;
|
||||
server_name gotenberg.votre-domaine.com;
|
||||
|
||||
location / {
|
||||
proxy_pass http://localhost:3001;
|
||||
proxy_set_header Host $host;
|
||||
proxy_set_header X-Real-IP $remote_addr;
|
||||
}
|
||||
}
|
||||
|
||||
# Activer le site
|
||||
sudo ln -s /etc/nginx/sites-available/gotenberg /etc/nginx/sites-enabled/
|
||||
sudo systemctl reload nginx
|
||||
```
|
||||
|
||||
## Intégration dans le Code Existant
|
||||
|
||||
### Modifier le formulaire de création de contrat
|
||||
|
||||
Dans `app/staff/contrats/[id]/page.tsx` ou le composant concerné :
|
||||
|
||||
```typescript
|
||||
const handleGeneratePDF = async () => {
|
||||
setIsGenerating(true);
|
||||
|
||||
try {
|
||||
const response = await fetch('/api/generate-contract-pdf', {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
},
|
||||
body: JSON.stringify({
|
||||
contractId: contract.id,
|
||||
contractType: contract.regime === 'CDDU' ? 'cddu' : 'rg',
|
||||
data: prepareContractData(contract),
|
||||
}),
|
||||
});
|
||||
|
||||
const result = await response.json();
|
||||
|
||||
if (result.success) {
|
||||
toast.success('PDF généré avec succès');
|
||||
// Ouvrir le PDF dans un nouvel onglet
|
||||
window.open(result.pdfUrl, '_blank');
|
||||
} else {
|
||||
toast.error('Erreur lors de la génération du PDF');
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('Erreur:', error);
|
||||
toast.error('Erreur lors de la génération du PDF');
|
||||
} finally {
|
||||
setIsGenerating(false);
|
||||
}
|
||||
};
|
||||
|
||||
// Fonction pour préparer les données
|
||||
function prepareContractData(contract: Contract) {
|
||||
return {
|
||||
structure_name: contract.organization.name,
|
||||
structure_adresse: contract.organization.address,
|
||||
structure_cpville: contract.organization.postal_code,
|
||||
structure_ville: contract.organization.city,
|
||||
structure_siret: contract.organization.siret,
|
||||
employee_firstname: contract.employee.first_name,
|
||||
employee_lastname: contract.employee.last_name,
|
||||
employee_dob: formatDate(contract.employee.birth_date),
|
||||
// ... mapper toutes les données
|
||||
imageUrl: contract.organization.logo_data_uri,
|
||||
};
|
||||
}
|
||||
```
|
||||
|
||||
## Différences avec PDFMonkey
|
||||
|
||||
| Aspect | PDFMonkey | Gotenberg |
|
||||
|--------|-----------|-----------|
|
||||
| **Hébergement** | SaaS externe | Auto-hébergé |
|
||||
| **Coût** | Payant (par document) | Gratuit |
|
||||
| **Template** | Liquid (éditeur en ligne) | Handlebars (code local) |
|
||||
| **Latence** | Variable (réseau externe) | Faible (interne) |
|
||||
| **Contrôle** | Limité | Total |
|
||||
| **Maintenance** | Aucune | Docker + monitoring |
|
||||
|
||||
## Gestion des Templates
|
||||
|
||||
### Modifier un template
|
||||
|
||||
1. Éditer `templates-contrats/cddu-handlebars.html`
|
||||
2. Tester localement
|
||||
3. Commit + push
|
||||
4. Déploiement automatique via Vercel
|
||||
|
||||
### Créer un nouveau template
|
||||
|
||||
1. Dupliquer `cddu-handlebars.html`
|
||||
2. Adapter le contenu
|
||||
3. Ajouter le cas dans l'API route :
|
||||
|
||||
```typescript
|
||||
case 'nouveau-type':
|
||||
templatePath = path.join(process.cwd(), 'templates-contrats', 'nouveau-type.html');
|
||||
break;
|
||||
```
|
||||
|
||||
## Monitoring et Logs
|
||||
|
||||
### Vérifier les logs Gotenberg
|
||||
|
||||
```bash
|
||||
docker logs odentas-gotenberg --tail 100 -f
|
||||
```
|
||||
|
||||
### Health check
|
||||
|
||||
```bash
|
||||
curl http://localhost:3001/health
|
||||
```
|
||||
|
||||
### Métriques de performance
|
||||
|
||||
Ajouter dans l'API route :
|
||||
|
||||
```typescript
|
||||
const startTime = Date.now();
|
||||
// ... génération PDF
|
||||
const duration = Date.now() - startTime;
|
||||
console.log(`PDF généré en ${duration}ms`);
|
||||
```
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
### Erreur : Gotenberg inaccessible
|
||||
|
||||
**Symptôme** : `fetch failed` ou timeout
|
||||
|
||||
**Solutions** :
|
||||
1. Vérifier que Gotenberg est démarré : `docker ps`
|
||||
2. Vérifier le health check : `curl http://localhost:3001/health`
|
||||
3. Vérifier les logs : `docker logs odentas-gotenberg`
|
||||
4. Vérifier la variable `GOTENBERG_URL`
|
||||
|
||||
### Erreur : Template non trouvé
|
||||
|
||||
**Symptôme** : `ENOENT: no such file or directory`
|
||||
|
||||
**Solutions** :
|
||||
1. Vérifier le chemin du template
|
||||
2. Vérifier que le template existe bien dans `templates-contrats/`
|
||||
3. En production Vercel, vérifier que les templates sont inclus dans le build
|
||||
|
||||
### Erreur : Rendu incorrect du PDF
|
||||
|
||||
**Symptôme** : Mise en page cassée, polices manquantes
|
||||
|
||||
**Solutions** :
|
||||
1. Vérifier le CSS dans le template
|
||||
2. Utiliser des polices web-safe ou inclure les fonts en base64
|
||||
3. Tester le HTML seul dans un navigateur
|
||||
4. Ajuster les marges dans l'API route
|
||||
|
||||
### Performance lente
|
||||
|
||||
**Symptôme** : Génération de PDF > 5 secondes
|
||||
|
||||
**Solutions** :
|
||||
1. Augmenter les ressources Docker
|
||||
2. Optimiser les images (compression, taille)
|
||||
3. Réduire la complexité du HTML/CSS
|
||||
4. Utiliser un cache pour les templates compilés
|
||||
|
||||
## Checklist de Migration
|
||||
|
||||
- [ ] Installer les dépendances npm
|
||||
- [ ] Créer les helpers Handlebars
|
||||
- [ ] Convertir le template CDDU
|
||||
- [ ] Créer l'API route
|
||||
- [ ] Déployer Gotenberg localement
|
||||
- [ ] Tester la génération de PDF en local
|
||||
- [ ] Déployer Gotenberg en production
|
||||
- [ ] Configurer la variable `GOTENBERG_URL` sur Vercel
|
||||
- [ ] Intégrer dans le formulaire de contrat
|
||||
- [ ] Tester en production
|
||||
- [ ] Migrer les autres templates (RG, Avenants)
|
||||
- [ ] Désactiver PDFMonkey
|
||||
|
||||
## Rollback
|
||||
|
||||
Si besoin de revenir à PDFMonkey :
|
||||
|
||||
1. Réactiver les appels à l'API PDFMonkey
|
||||
2. Commenter les appels à `/api/generate-contract-pdf`
|
||||
3. Garder le code Gotenberg en standby
|
||||
|
||||
## Next Steps
|
||||
|
||||
Une fois la migration terminée :
|
||||
|
||||
1. **Migrer les autres templates** : RG, Avenants, etc.
|
||||
2. **Optimiser les performances** : cache de templates, parallélisation
|
||||
3. **Ajouter des analytics** : temps de génération, taux de succès
|
||||
4. **Backup automatique** : sauvegarder les PDFs générés
|
||||
5. **Versioning des templates** : Git + tags pour suivre les changements
|
||||
|
||||
## Support
|
||||
|
||||
En cas de problème, consulter :
|
||||
- [Documentation Gotenberg](https://gotenberg.dev/)
|
||||
- [Documentation Handlebars](https://handlebarsjs.com/)
|
||||
- Logs Docker : `docker logs odentas-gotenberg`
|
||||
- Logs Next.js : Console Vercel
|
||||
|
||||
---
|
||||
|
||||
**Auteur** : Équipe Odentas
|
||||
**Date** : Décembre 2025
|
||||
**Version** : 1.0
|
||||
163
app/api/generate-contract-pdf/route.ts
Normal file
163
app/api/generate-contract-pdf/route.ts
Normal file
|
|
@ -0,0 +1,163 @@
|
|||
import { createRouteHandlerClient } from '@supabase/auth-helpers-nextjs';
|
||||
import { cookies } from 'next/headers';
|
||||
import { NextRequest, NextResponse } from 'next/server';
|
||||
import Handlebars from 'handlebars';
|
||||
import fs from 'fs/promises';
|
||||
import path from 'path';
|
||||
import { registerHandlebarsHelpers } from '@/lib/handlebars-helpers';
|
||||
|
||||
registerHandlebarsHelpers();
|
||||
|
||||
/**
|
||||
* API Route pour générer un contrat PDF via Gotenberg
|
||||
* POST /api/generate-contract-pdf
|
||||
*
|
||||
* Body attendu :
|
||||
* {
|
||||
* contractId: string,
|
||||
* contractType: "cddu" | "rg" | "avenant",
|
||||
* data: object (données du contrat)
|
||||
* }
|
||||
*/
|
||||
export async function POST(request: NextRequest) {
|
||||
try {
|
||||
const supabase = createRouteHandlerClient({ cookies });
|
||||
|
||||
const {
|
||||
data: { session },
|
||||
} = await supabase.auth.getSession();
|
||||
|
||||
if (!session) {
|
||||
return NextResponse.json(
|
||||
{ error: 'Non authentifié' },
|
||||
{ status: 401 }
|
||||
);
|
||||
}
|
||||
|
||||
const body = await request.json();
|
||||
const { contractId, contractType, data } = body;
|
||||
|
||||
if (!contractId || !contractType || !data) {
|
||||
return NextResponse.json(
|
||||
{ error: 'Données manquantes (contractId, contractType, data)' },
|
||||
{ status: 400 }
|
||||
);
|
||||
}
|
||||
|
||||
// Vérifier que l'utilisateur a accès à ce contrat
|
||||
const { data: contract, error: contractError } = await supabase
|
||||
.from('contracts')
|
||||
.select('*')
|
||||
.eq('id', contractId)
|
||||
.single();
|
||||
|
||||
if (contractError || !contract) {
|
||||
return NextResponse.json(
|
||||
{ error: 'Contrat introuvable' },
|
||||
{ status: 404 }
|
||||
);
|
||||
}
|
||||
|
||||
// Charger le template Handlebars selon le type de contrat
|
||||
let templatePath: string;
|
||||
switch (contractType) {
|
||||
case 'cddu':
|
||||
templatePath = path.join(process.cwd(), 'templates-contrats', 'cddu-handlebars.html');
|
||||
break;
|
||||
case 'rg':
|
||||
templatePath = path.join(process.cwd(), 'templates-contrats', 'rg-handlebars.html');
|
||||
break;
|
||||
case 'avenant':
|
||||
templatePath = path.join(process.cwd(), 'templates-contrats', 'avenant-handlebars.html');
|
||||
break;
|
||||
default:
|
||||
return NextResponse.json(
|
||||
{ error: 'Type de contrat invalide' },
|
||||
{ status: 400 }
|
||||
);
|
||||
}
|
||||
|
||||
const templateContent = await fs.readFile(templatePath, 'utf-8');
|
||||
const template = Handlebars.compile(templateContent);
|
||||
|
||||
// Générer le HTML avec les données
|
||||
const html = template(data);
|
||||
|
||||
// Envoyer le HTML à Gotenberg pour conversion en PDF
|
||||
const gotenbergUrl = process.env.GOTENBERG_URL || 'http://localhost:3001';
|
||||
|
||||
const formData = new FormData();
|
||||
formData.append('files', new Blob([html], { type: 'text/html' }), 'index.html');
|
||||
|
||||
// Options Gotenberg
|
||||
formData.append('marginTop', '1');
|
||||
formData.append('marginBottom', '1');
|
||||
formData.append('marginLeft', '1');
|
||||
formData.append('marginRight', '1');
|
||||
formData.append('paperWidth', '21');
|
||||
formData.append('paperHeight', '29.7');
|
||||
formData.append('preferCssPageSize', 'false');
|
||||
|
||||
const gotenbergResponse = await fetch(`${gotenbergUrl}/forms/chromium/convert/html`, {
|
||||
method: 'POST',
|
||||
body: formData,
|
||||
});
|
||||
|
||||
if (!gotenbergResponse.ok) {
|
||||
const errorText = await gotenbergResponse.text();
|
||||
console.error('Erreur Gotenberg:', errorText);
|
||||
return NextResponse.json(
|
||||
{ error: 'Erreur lors de la génération du PDF', details: errorText },
|
||||
{ status: 500 }
|
||||
);
|
||||
}
|
||||
|
||||
// Récupérer le PDF généré
|
||||
const pdfBuffer = await gotenbergResponse.arrayBuffer();
|
||||
const pdfBlob = new Blob([pdfBuffer], { type: 'application/pdf' });
|
||||
|
||||
// Uploader le PDF sur Supabase Storage
|
||||
const fileName = `contract_${contractId}_${Date.now()}.pdf`;
|
||||
const { data: uploadData, error: uploadError } = await supabase.storage
|
||||
.from('contracts')
|
||||
.upload(fileName, pdfBlob, {
|
||||
contentType: 'application/pdf',
|
||||
upsert: false,
|
||||
});
|
||||
|
||||
if (uploadError) {
|
||||
console.error('Erreur upload Supabase:', uploadError);
|
||||
return NextResponse.json(
|
||||
{ error: 'Erreur lors de l\'upload du PDF' },
|
||||
{ status: 500 }
|
||||
);
|
||||
}
|
||||
|
||||
// Mettre à jour le contrat avec l'URL du PDF
|
||||
const { data: publicUrl } = supabase.storage
|
||||
.from('contracts')
|
||||
.getPublicUrl(fileName);
|
||||
|
||||
const { error: updateError } = await supabase
|
||||
.from('contracts')
|
||||
.update({ pdf_url: publicUrl.publicUrl })
|
||||
.eq('id', contractId);
|
||||
|
||||
if (updateError) {
|
||||
console.error('Erreur mise à jour contrat:', updateError);
|
||||
}
|
||||
|
||||
return NextResponse.json({
|
||||
success: true,
|
||||
pdfUrl: publicUrl.publicUrl,
|
||||
fileName,
|
||||
});
|
||||
|
||||
} catch (error) {
|
||||
console.error('Erreur génération PDF:', error);
|
||||
return NextResponse.json(
|
||||
{ error: 'Erreur interne du serveur' },
|
||||
{ status: 500 }
|
||||
);
|
||||
}
|
||||
}
|
||||
54
docker-compose.gotenberg.yml
Normal file
54
docker-compose.gotenberg.yml
Normal file
|
|
@ -0,0 +1,54 @@
|
|||
version: '3.8'
|
||||
|
||||
services:
|
||||
gotenberg:
|
||||
image: gotenberg/gotenberg:8
|
||||
container_name: odentas-gotenberg
|
||||
restart: unless-stopped
|
||||
ports:
|
||||
- "3001:3000"
|
||||
environment:
|
||||
# Configuration de sécurité
|
||||
- GOTENBERG_API_TIMEOUT=30s
|
||||
- GOTENBERG_API_ROOT_PATH=/
|
||||
|
||||
# Limites de ressources pour éviter la surcharge
|
||||
- GOTENBERG_CHROMIUM_MAX_QUEUE_SIZE=10
|
||||
- GOTENBERG_CHROMIUM_AUTO_START=true
|
||||
|
||||
# Options de conversion PDF
|
||||
- GOTENBERG_CHROMIUM_DISABLE_JAVASCRIPT=false
|
||||
- GOTENBERG_CHROMIUM_ALLOW_LIST=^file:///tmp/.*
|
||||
|
||||
# Limites de ressources Docker
|
||||
deploy:
|
||||
resources:
|
||||
limits:
|
||||
cpus: '2'
|
||||
memory: 2G
|
||||
reservations:
|
||||
cpus: '0.5'
|
||||
memory: 512M
|
||||
|
||||
# Health check pour vérifier que Gotenberg est opérationnel
|
||||
healthcheck:
|
||||
test: ["CMD", "curl", "-f", "http://localhost:3000/health"]
|
||||
interval: 30s
|
||||
timeout: 10s
|
||||
retries: 3
|
||||
start_period: 40s
|
||||
|
||||
# Volumes pour logs et cache (optionnel)
|
||||
volumes:
|
||||
- gotenberg-cache:/tmp/gotenberg
|
||||
|
||||
networks:
|
||||
- odentas-network
|
||||
|
||||
volumes:
|
||||
gotenberg-cache:
|
||||
driver: local
|
||||
|
||||
networks:
|
||||
odentas-network:
|
||||
driver: bridge
|
||||
114
lib/handlebars-helpers.ts
Normal file
114
lib/handlebars-helpers.ts
Normal file
|
|
@ -0,0 +1,114 @@
|
|||
/**
|
||||
* Helpers Handlebars personnalisés pour remplacer les filtres Liquid
|
||||
* Utilisés pour la génération de contrats PDF avec Gotenberg
|
||||
*/
|
||||
|
||||
import Handlebars from 'handlebars';
|
||||
|
||||
/**
|
||||
* Enregistre tous les helpers Handlebars personnalisés
|
||||
*/
|
||||
export function registerHandlebarsHelpers() {
|
||||
// Helper pour remplacer "remove_first" de Liquid
|
||||
Handlebars.registerHelper('removeFirst', function(str: string, pattern: string) {
|
||||
if (!str) return '';
|
||||
return str.replace(pattern, '');
|
||||
});
|
||||
|
||||
// Helper pour remplacer "contains" de Liquid
|
||||
Handlebars.registerHelper('contains', function(str: string, pattern: string) {
|
||||
if (!str) return false;
|
||||
return str.includes(pattern);
|
||||
});
|
||||
|
||||
// Helper pour remplacer "strip" de Liquid (trim)
|
||||
Handlebars.registerHelper('strip', function(str: string) {
|
||||
if (!str) return '';
|
||||
return str.trim();
|
||||
});
|
||||
|
||||
// Helper pour remplacer "join" de Liquid
|
||||
Handlebars.registerHelper('join', function(array: any[], separator: string) {
|
||||
if (!Array.isArray(array)) return '';
|
||||
return array.join(separator);
|
||||
});
|
||||
|
||||
// Helper pour split (diviser une chaîne)
|
||||
Handlebars.registerHelper('split', function(str: string, delimiter: string) {
|
||||
if (!str) return [];
|
||||
return str.split(delimiter);
|
||||
});
|
||||
|
||||
// Helper pour vérifier l'égalité
|
||||
Handlebars.registerHelper('eq', function(a: any, b: any) {
|
||||
return a === b;
|
||||
});
|
||||
|
||||
// Helper pour vérifier l'inégalité
|
||||
Handlebars.registerHelper('ne', function(a: any, b: any) {
|
||||
return a !== b;
|
||||
});
|
||||
|
||||
// Helper pour vérifier >=
|
||||
Handlebars.registerHelper('gte', function(a: number, b: number) {
|
||||
return a >= b;
|
||||
});
|
||||
|
||||
// Helper pour vérifier >
|
||||
Handlebars.registerHelper('gt', function(a: number, b: number) {
|
||||
return a > b;
|
||||
});
|
||||
|
||||
// Helper pour vérifier ==
|
||||
Handlebars.registerHelper('lte', function(a: number, b: number) {
|
||||
return a <= b;
|
||||
});
|
||||
|
||||
// Helper pour vérifier <
|
||||
Handlebars.registerHelper('lt', function(a: number, b: number) {
|
||||
return a < b;
|
||||
});
|
||||
|
||||
// Helper pour vérifier si une valeur est vide
|
||||
Handlebars.registerHelper('isEmpty', function(value: any) {
|
||||
return !value || value === '' || value === 'n/a' || value === 0;
|
||||
});
|
||||
|
||||
// Helper pour vérifier si une valeur n'est pas vide
|
||||
Handlebars.registerHelper('isNotEmpty', function(value: any) {
|
||||
return value && value !== '' && value !== 'n/a' && value !== 0;
|
||||
});
|
||||
|
||||
// Helper OR logique
|
||||
Handlebars.registerHelper('or', function(...args: any[]) {
|
||||
// Enlever le dernier argument qui est l'objet options de Handlebars
|
||||
const values = args.slice(0, -1);
|
||||
return values.some(v => !!v);
|
||||
});
|
||||
|
||||
// Helper AND logique
|
||||
Handlebars.registerHelper('and', function(...args: any[]) {
|
||||
// Enlever le dernier argument qui est l'objet options de Handlebars
|
||||
const values = args.slice(0, -1);
|
||||
return values.every(v => !!v);
|
||||
});
|
||||
|
||||
// Helper pour formatter un nombre avec virgules
|
||||
Handlebars.registerHelper('formatNumber', function(value: number) {
|
||||
if (value === null || value === undefined) return '';
|
||||
return value.toString().replace('.', ',');
|
||||
});
|
||||
|
||||
// Helper raw pour éviter l'échappement HTML
|
||||
Handlebars.registerHelper('raw', function(options) {
|
||||
return options.fn();
|
||||
});
|
||||
|
||||
// Helper pour comparer avec plusieurs valeurs (utile pour les CCN)
|
||||
Handlebars.registerHelper('includesAny', function(str: string, ...values: any[]) {
|
||||
if (!str) return false;
|
||||
// Enlever le dernier argument (options de Handlebars)
|
||||
const searchValues = values.slice(0, -1);
|
||||
return searchValues.some(v => str.includes(v));
|
||||
});
|
||||
}
|
||||
390
templates-contrats/cddu-handlebars.html
Normal file
390
templates-contrats/cddu-handlebars.html
Normal file
|
|
@ -0,0 +1,390 @@
|
|||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<style>
|
||||
body {
|
||||
font-family: Arial, sans-serif;
|
||||
font-size: 12pt;
|
||||
line-height: 1.5;
|
||||
margin: 40px;
|
||||
}
|
||||
h1 {
|
||||
text-align: center;
|
||||
font-size: 16pt;
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
h2.section-title {
|
||||
font-size: 13pt;
|
||||
margin-top: 20px;
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
.bold {
|
||||
font-weight: bold;
|
||||
}
|
||||
.section {
|
||||
margin-bottom: 15px;
|
||||
}
|
||||
.section-objet {
|
||||
margin-bottom: 15px;
|
||||
}
|
||||
ul {
|
||||
list-style-type: none;
|
||||
padding-left: 0;
|
||||
}
|
||||
li {
|
||||
margin-bottom: 5px;
|
||||
}
|
||||
.info-paragraph {
|
||||
margin-top: 30px;
|
||||
}
|
||||
.info-row {
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
.info-label {
|
||||
font-weight: bold;
|
||||
margin-bottom: 5px;
|
||||
}
|
||||
.info-value {
|
||||
margin-bottom: 3px;
|
||||
}
|
||||
.info-delegation {
|
||||
font-style: italic;
|
||||
margin-bottom: 3px;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
|
||||
<div style="display: flex; justify-content: center;">
|
||||
<img src="{{imageUrl}}" style="width: 180px;" />
|
||||
</div>
|
||||
|
||||
<h1>CONTRAT D'ENGAGEMENT
|
||||
{{#eq employee_catpro "Artiste"}}ARTISTE{{/eq}}
|
||||
{{#if (and (eq employee_civ "Madame") (eq employee_catpro "Technicien"))}}TECHNICIENNE{{/if}}
|
||||
{{#if (and (eq employee_civ "Monsieur") (eq employee_catpro "Technicien"))}}TECHNICIEN{{/if}}
|
||||
{{#if (and (eq employee_civ "Madame") (eq employee_catpro "Metteur en scène"))}}<br>ARTISTE CADRE{{/if}}
|
||||
{{#if (and (eq employee_civ "Monsieur") (eq employee_catpro "Metteur en scène"))}}ARTISTE CADRE{{/if}}
|
||||
</h1>
|
||||
|
||||
<p class="bold">Entre les {{#eq employee_civ "Monsieur"}}soussignés{{else}}soussignées{{/eq}} :</p>
|
||||
<ul>
|
||||
<li class="bold">{{structure_name}}</li>
|
||||
<li>{{forme_juridique}}</li>
|
||||
<li>{{structure_adresse}}</li>
|
||||
<li>{{structure_cpville}} {{structure_ville}}</li>
|
||||
<li>SIRET : {{structure_siret}}</li>
|
||||
{{#ne structure_licence "n/a"}}<li>Licence d'entrepreneur de spectacles : {{structure_licence}}</li>{{/ne}}
|
||||
<li>représentée par {{structure_signataire}}, en sa qualité {{#eq structure_signatairequalite "Administrateur"}}d'{{else}}de {{/eq}}{{structure_signatairequalite}}{{#eq delegation "Oui"}}, pour le représentant légal et par délégation.{{else}}.{{/eq}}</li>
|
||||
</ul>
|
||||
|
||||
<p class="bold">d'une part,</p>
|
||||
|
||||
<p class="bold">et :</p>
|
||||
|
||||
<ul>
|
||||
<li class="bold">{{employee_civ}} {{employee_firstname}} {{employee_lastname}}{{#ne employee_birthname employee_lastname}}{{#eq employee_civ "Monsieur"}}, né {{employee_birthname}}{{/eq}}{{#eq employee_civ "Madame"}}, née {{employee_birthname}}{{/eq}}{{/ne}}{{#ne employee_pseudo "n/a"}}, {{#eq employee_civ "Monsieur"}}dit{{else}}dite{{/eq}} "{{employee_pseudo}}"{{/ne}}</li>
|
||||
<li>
|
||||
{{#eq employee_civ "Monsieur"}}né{{else}}née{{/eq}}
|
||||
le {{ employee_dob }}
|
||||
{{#if (contains employee_cob "Le ")}}
|
||||
au {{removeFirst employee_cob "Le "}}
|
||||
{{else}}
|
||||
à {{ employee_cob }}
|
||||
{{/if}}
|
||||
</li>
|
||||
<li>demeurant {{employee_address}}</li>
|
||||
{{#if (or (eq employee_ss 0) (isEmpty employee_ss))}}
|
||||
<li>Le numéro de Sécurité Sociale du salarié est en cours d'attribution.</li>
|
||||
{{else}}
|
||||
<li>N° de Sécurité Sociale : {{ employee_ss }}</li>
|
||||
{{/if}}
|
||||
<li>N° Congés Spectacles : {{employee_cs}}</li>
|
||||
{{#eq mineur1618 "Oui"}}<li>dont {{#eq representant_civ "Monsieur"}}le représentant légal{{else}}la représentante légale{{/eq}} est {{representant_civ}} {{representant_nom}}, {{#eq representant_civ "Monsieur"}}né{{else}}née{{/eq}} le {{representant_dob}} à {{representant_cob}}, demeurant {{representant_adresse}}.</li>{{/eq}}
|
||||
</ul>
|
||||
|
||||
<p class="bold">d'autre part.</p>
|
||||
|
||||
<p>
|
||||
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 {{join CCN ', '}}{{#if (contains (join CCN ', ') "Convention Collective Nationale de l'Édition")}} et de ses annexes afférentes à l'Édition Phonographique{{/if}}.
|
||||
</p>
|
||||
|
||||
<p>
|
||||
Il a été convenu et arrêté ce qui suit :
|
||||
</p>
|
||||
|
||||
<div class="section">
|
||||
<h2 class="section-title">OBJET</h2>
|
||||
{{employee_civ}} {{employee_firstname}} {{employee_lastname}} est {{#eq employee_civ "Monsieur"}}engagé{{else}}engagée{{/eq}} selon l'objet suivant :
|
||||
<ul>
|
||||
<li><b>Profession</b> : {{employee_profession}}</li>
|
||||
<li><b>Code emploi</b> : {{employee_codeprofession}}</li>
|
||||
<li>
|
||||
{{#if (and (eq structure_spectacle "Oui") (ne type_numobjet "Administratif"))}}
|
||||
<b>Spectacle</b> : {{ spectacle }}
|
||||
{{else if (includesAny (join CCN ', ') "Convention Collective Nationale de la Production Audiovisuelle" "Convention Collective Nationale de l'Édition")}}
|
||||
<b>Production</b> : {{ spectacle }}
|
||||
{{/if}}
|
||||
</li>
|
||||
<li>{{#isNotEmpty numobjet}}<b>Numéro d'objet</b> : {{numobjet}}{{else}}Le <b>numéro d'objet</b> de cette production est en cours d'attribution.{{/isNotEmpty}}</li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
<div class="section-objet">
|
||||
<h2 class="section-title">DURÉE DE L'ENGAGEMENT</h2>
|
||||
<p>
|
||||
{{#eq date_debut date_fin}}
|
||||
Le présent engagement couvre la journée du {{ date_debut }}, pour
|
||||
{{else}}
|
||||
{{#isNotEmpty dates_travaillees}}
|
||||
Le présent engagement couvre la période du {{ date_debut }} au {{ date_fin }} pour les dates travaillées suivantes :
|
||||
<ul>
|
||||
{{#each (split dates_travaillees ";")}}
|
||||
<li>- {{strip this}}{{#unless @last}};{{/unless}}</li>
|
||||
{{/each}}
|
||||
</ul>
|
||||
{{else}}
|
||||
Le présent engagement couvre la période du {{ date_debut }} au {{ date_fin }}.
|
||||
{{/isNotEmpty}}
|
||||
Pour
|
||||
{{/eq}}
|
||||
|
||||
{{#eq employee_catpro "Artiste"}}
|
||||
un total de
|
||||
{{#if (and (gte cachets.representations 1) (gte cachets.repetitions 1))}}
|
||||
{{cachets.representations}} {{#eq cachets.representations 1}}cachet{{else}}cachets{{/eq}} de représentation et {{cachets.repetitions}} {{#eq cachets.repetitions 1}}service{{else}}services{{/eq}} de répétition.
|
||||
{{/if}}
|
||||
{{#if (and (gte cachets.representations 1) (eq cachets.repetitions 0))}}
|
||||
{{cachets.representations}} {{#eq cachets.representations 1}}cachet{{else}}cachets{{/eq}}{{#if (includesAny (join CCN ', ') "Convention Collective Nationale de la Production Audiovisuelle" "Convention Collective Nationale de l'Édition")}} d'enregistrement.{{else}} de représentation.{{/if}}
|
||||
{{/if}}
|
||||
{{#if (and (eq cachets.representations 0) (gte cachets.repetitions 1))}}
|
||||
{{cachets.repetitions}} {{#eq cachets.repetitions 1}}service{{else}}services{{/eq}} de répétition.
|
||||
{{/if}}
|
||||
{{/eq}}
|
||||
|
||||
{{#eq employee_catpro "Technicien"}}
|
||||
un total de {{cachets.heures}} heures de travail{{#eq cachets.heuresparjour 0}}.{{/eq}}{{#gte cachets.heuresparjour 1}}, à raison de {{cachets.heuresparjour}} heures par jour de travail.{{/gte}}
|
||||
{{/eq}}
|
||||
|
||||
{{#eq employee_catpro "Metteur en scène"}}
|
||||
{{#if (and (gte cachets.representations 1) (gt cachets.heures 0))}}
|
||||
un total de {{cachets.representations}} {{#eq cachets.representations 1}}cachet{{else}}cachets{{/eq}} de représentation et {{cachets.heures}} heures de travail.
|
||||
{{/if}}
|
||||
{{#if (eq cachets.representations 0)}}
|
||||
un total de {{cachets.heures}} heures de travail.
|
||||
{{/if}}
|
||||
{{#if (and (gte cachets.representations 1) (eq cachets.heures 0))}}
|
||||
un total de {{cachets.representations}} {{#eq cachets.representations 1}}cachet{{else}}cachets{{/eq}} de représentation.
|
||||
{{/if}}
|
||||
{{/eq}}
|
||||
</p>
|
||||
|
||||
<p>
|
||||
{{#if (gte cachets.repetitions 1)}}La durée totale des répétitions sera de {{cachets.heures}} heures{{#eq cachets.heuresparjour 0}}.{{/eq}}{{#gte cachets.heuresparjour 1}}, à raison de {{cachets.heuresparjour}} heures par journée de répétition.{{/gte}}{{/if}}
|
||||
</p>
|
||||
{{#isNotEmpty autreprecision_duree}}<p>{{autreprecision_duree}}</p>{{/isNotEmpty}}
|
||||
</div>
|
||||
|
||||
<div class="section">
|
||||
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.
|
||||
</div>
|
||||
|
||||
<div class="section">
|
||||
<h2 class="section-title">LIEUX D'ENGAGEMENT ET HORAIRES DE TRAVAIL</h2>
|
||||
<p>
|
||||
{{structure_name}} communiquera à {{employee_firstname}} {{employee_lastname}} les lieux
|
||||
{{#if (includesAny (join CCN ', ') "Convention Collective Nationale de la Production Audiovisuelle" "Convention Collective Nationale de l'Édition")}}
|
||||
de travail
|
||||
{{else if (and (gte cachets.representations 1) (eq cachets.repetitions 0))}}
|
||||
des représentations
|
||||
{{else if (and (eq cachets.representations 0) (gte cachets.repetitions 1))}}
|
||||
des répétitions
|
||||
{{else if (eq employee_catpro "Technicien")}}
|
||||
d'engagement
|
||||
{{else if (and (gte cachets.representations 1) (gte cachets.repetitions 1))}}
|
||||
des répétitions et des représentations
|
||||
{{/if}}
|
||||
{{#if (and (eq employee_catpro "Metteur en scène") (eq cachets.representations 0))}}d'exercice de sa fonction{{/if}}, ainsi que ses horaires de travail.
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div class="section">
|
||||
<h2 class="section-title">RÉMUNÉRATION</h2>
|
||||
<p>
|
||||
Il sera alloué à {{employee_firstname}} {{employee_lastname}} à titre de salaire la somme de {{salaire_brut}} euros bruts.
|
||||
</p>
|
||||
{{#isNotEmpty precisions_salaire}}
|
||||
<p>
|
||||
À titre informatif, la répartition de ce salaire brut est la suivante : {{precisions_salaire}}.
|
||||
</p>
|
||||
{{/isNotEmpty}}
|
||||
{{#if (and (isNotEmpty panierrepas) (isNotEmpty hebergement))}}
|
||||
<p>{{employee_firstname}} {{employee_lastname}} percevra {{panierrepas}}
|
||||
{{#eq panierrepas "1"}}panier repas principal{{else}}paniers repas principaux,{{/eq}} et {{hebergement}} {{#eq hebergement "1"}}indemnité{{else}}indemnités{{/eq}} d'hébergement et petit-déjeuner,
|
||||
{{#if (and (eq panierrepasccn "Oui") (eq hebergementccn "Oui"))}}
|
||||
selon les conditions prévues par la Convention Collective.
|
||||
{{else if (and (eq panierrepasccn "Non") (eq hebergementccn "Oui"))}}
|
||||
à hauteur de {{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.
|
||||
{{else if (and (eq panierrepasccn "Oui") (eq hebergementccn "Non"))}}
|
||||
selon les conditions prévues par la Convention Collective pour les paniers repas principaux, et à hauteur de {{montanthebergement}} euros par indemnité hébergement et petit-déjeuner.
|
||||
{{else if (and (eq panierrepasccn "Non") (eq hebergementccn "Non"))}}
|
||||
à hauteur de {{montantpanierrepas}} euros par panier repas principal et à hauteur de {{montanthebergement}} euros par indemnité hébergement et petit-déjeuner.
|
||||
{{/if}}
|
||||
</p>
|
||||
{{/if}}
|
||||
{{#isNotEmpty autreprecision_salaire}}<p>{{autreprecision_salaire}}</p>{{/isNotEmpty}}
|
||||
</div>
|
||||
|
||||
<div class="section">
|
||||
<h2 class="section-title">RETRAITE ET CONGÉS PAYÉS</h2>
|
||||
<p>
|
||||
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.
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div class="section">
|
||||
<h2 class="section-title">ABSENCE-MALADIE</h2>
|
||||
<p>
|
||||
En cas de maladie ou d'empêchement d'assurer
|
||||
{{#if (eq employee_catpro "Metteur en scène")}}
|
||||
ses missions de mise en scène,
|
||||
{{else if (contains (join CCN ', ') "Convention Collective Nationale de la Production Audiovisuelle")}}
|
||||
ses missions de {{employee_profession}},
|
||||
{{else if (contains (join CCN ', ') "Convention Collective Nationale de l'Édition")}}
|
||||
un enregistrement,
|
||||
{{else}}
|
||||
une répétition ou une représentation,
|
||||
{{/if}}
|
||||
{{employee_firstname}} {{employee_lastname}} sera {{#eq employee_civ "Monsieur"}}tenu{{else}}tenue{{/eq}}
|
||||
d'en aviser {{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,
|
||||
{{employee_firstname}} {{employee_lastname}} devra transmettre à {{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 {{employee_firstname}} {{employee_lastname}},
|
||||
le présent contrat pourra être résilié de plein droit par {{structure_name}} et ce, dans le respect des dispositions de la convention collective applicable.
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div class="section">
|
||||
<h2 class="section-title">DROIT DE PRIORITÉ ET D'EXCLUSIVITÉ</h2>
|
||||
<p>
|
||||
Le présent contrat donne à {{structure_name}} une priorité absolue sur tous les autres engagements que pourrait conclure par ailleurs {{employee_firstname}} {{employee_lastname}}, sur la période de l'engagement.
|
||||
La dérogation éventuelle à cette clause devra faire l'objet d'un accord écrit de {{structure_name}}.
|
||||
</p>
|
||||
<p>
|
||||
{{employee_firstname}} {{employee_lastname}} ne pourra en aucun cas refuser sa présence
|
||||
{{#if (eq employee_catpro "Metteur en scène")}}
|
||||
sur ses lieux de travail et aux répétitions
|
||||
{{else if (contains (join CCN ', ') "Convention Collective Nationale de la Production Audiovisuelle")}}
|
||||
sur les lieux de production
|
||||
{{else if (contains (join CCN ', ') "Convention Collective Nationale de l'Édition")}}
|
||||
sur les lieux d'enregistrement
|
||||
{{else}}
|
||||
à une répétition ou à une représentation
|
||||
{{/if}}
|
||||
pour cause d'engagement extérieur, à quelque moment qu'il·elle ait été prévenu
|
||||
{{#if (eq employee_catpro "Metteur en scène")}}
|
||||
de ses horaires et jours de travail et de l'existence de répétitons.
|
||||
{{else if (contains (join CCN ', ') "Convention Collective Nationale de la Production Audiovisuelle")}}
|
||||
de ses horaires, jours et lieux de travail.
|
||||
{{else if (contains (join CCN ', ') "Convention Collective Nationale de l'Édition")}}
|
||||
de cet session d'enregistrement.
|
||||
{{else}}
|
||||
de l'existence de cette répétition ou représentation.
|
||||
{{/if}}
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div class="section">
|
||||
<h2 class="section-title">MÉDECINE DU TRAVAIL</h2>
|
||||
<p>
|
||||
{{employee_firstname}} {{employee_lastname}} déclare avoir satisfait aux obligations relatives à la Médecine du travail et communiquera
|
||||
à {{structure_name}} l'attestation annuelle qui lui a été délivrée par cet organisme.
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div class="section">
|
||||
<h2 class="section-title">ASSURANCES</h2>
|
||||
<p>
|
||||
{{employee_firstname}} {{employee_lastname}} est {{#eq employee_civ "Monsieur"}}tenu{{else}}tenue{{/eq}} d'assurer contre tous les risques tous les objets lui appartenant. {{structure_name}}
|
||||
déclare avoir souscrit les assurances nécessaires à la couverture des risques liés{{#if (contains (join CCN ', ') "Convention Collective Nationale de la Production Audiovisuelle")}} à la production audiovisuelle.{{else if (contains (join CCN ', ') "Convention Collective Nationale de l'Édition")}} à l'édition phonographique.{{else}} aux représentations du spectacle.{{/if}}
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div class="section">
|
||||
<h2 class="section-title">LITIGES</h2>
|
||||
<p>
|
||||
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).
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div class="section">
|
||||
<h2 class="section-title">PROTECTION DES DONNÉES PERSONNELLES</h2>
|
||||
<p>
|
||||
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.
|
||||
</p>
|
||||
<p>
|
||||
La signature du présent contrat vaut autorisation pour la société de collecter, d'enregistrer et de stocker les données nécessaires.
|
||||
</p>
|
||||
<p>
|
||||
Outre les services internes de {{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.
|
||||
</p>
|
||||
<p>
|
||||
Ces informations sont réservées à l'usage des services concernés et ne peuvent être communiquées qu'à ces destinataires.
|
||||
</p>
|
||||
<p>
|
||||
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 : {{nom_responsable_traitement}}, {{qualite_responsable_traitement}}, {{email_responsable_traitement}}.
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div class="section">
|
||||
<p>
|
||||
Fait en double exemplaire,
|
||||
</p>
|
||||
<p>
|
||||
{{#if (contains structure_ville "Le ")}}
|
||||
Au {{removeFirst structure_ville "Le "}}, le {{ date_signature }}.
|
||||
{{else}}
|
||||
À {{ structure_ville }}, le {{ date_signature }}.
|
||||
{{/if}}
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div class="info-paragraph">
|
||||
<div class="info-row">
|
||||
<div class="info-label">{{#eq employee_civ "Monsieur"}}Le salarié :{{else}}La salariée :{{/eq}}</div>
|
||||
<div class="info-value">{{employee_civ}} {{employee_firstname}} {{employee_lastname}}</div>
|
||||
<br>
|
||||
<div>[Signature Employé - À intégrer via Docuseal]</div>
|
||||
<br><br><br><br>
|
||||
</div>
|
||||
{{#eq mineur1618 "Oui"}}
|
||||
<div class="info-row">
|
||||
<div class="info-label">{{#eq representant_civ "Monsieur"}}Le représentant légal{{else}}La représentante légale{{/eq}}{{#eq employee_civ "Madame"}} de la salariée :{{else}} du salarié :{{/eq}}</div>
|
||||
<div class="info-value">{{representant_civ}} {{representant_nom}}</div>
|
||||
<div class="info-signature">[Signature Représentant - À intégrer via Docuseal]</div>
|
||||
<br><br>
|
||||
</div>
|
||||
{{/eq}}
|
||||
<div class="info-row">
|
||||
<div class="info-label">L'employeur:</div>
|
||||
<div class="info-value">Pour {{structure_name}},</div>
|
||||
{{#eq delegation "Oui"}}<div class="info-delegation">Pour le représentant légal et par délégation,</div>{{/eq}}
|
||||
<div class="info-value">{{structure_signataire}},</div>
|
||||
<div class="info-value">{{structure_signatairequalite}}.</div>
|
||||
<br>
|
||||
<div>[Signature Employeur - À intégrer via Docuseal]</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</body>
|
||||
</html>
|
||||
Loading…
Reference in a new issue