diff --git a/SIMULATEUR_POSTHOG_TRACKING.md b/SIMULATEUR_POSTHOG_TRACKING.md
index 61448a7..fbfbbff 100644
--- a/SIMULATEUR_POSTHOG_TRACKING.md
+++ b/SIMULATEUR_POSTHOG_TRACKING.md
@@ -33,23 +33,63 @@ PostHog
### Nom de l'événement
`simulateur_calculation`
-### Propriétés
+### Propriétés complètes
| Propriété | Type | Description | Exemple |
|-----------|------|-------------|---------|
+| **Paramètres du formulaire** ||||
+| `ccn_id` | string | ID de la CCN sélectionnée | `"1285"` |
+| `ccn_nom` | string | Nom complet de la CCN | `"1285 – Entreprises Artistiques & Culturelles (CCNEAC)"` |
| `categorie` | string | Catégorie du salarié | `"artiste"` ou `"technicien"` |
-| `type_calcul` | string | Type de calcul | `"brut"`, `"net"` ou `"cost"` |
-| `montant` | number | Montant saisi | `2000` |
-| `timestamp` | string | Date/heure de l'événement | `"2025-10-15T14:30:00.000Z"` |
+| `statut` | string | Statut professionnel | `"non-cadre"` ou `"cadre"` |
+| `abattement_active` | boolean | Abattement pour frais pro activé | `true` ou `false` |
+| `abattement_profession` | string | Type de profession (si abattement) | `"drama"`, `"musique"` ou `""` |
+| **Cachets, heures et dates** ||||
+| `cachets` | number | Nombre de cachets | `10` |
+| `heures` | number | Nombre d'heures | `35` |
+| `dates_travail` | array | Liste des dates de travail | `["2025-10-15", "2025-10-16"]` |
+| `nombre_jours` | number | Nombre de jours travaillés | `2` |
+| **Montant et type de calcul** ||||
+| `montant_saisi` | number | Montant saisi par l'utilisateur | `2000` |
+| `type_calcul` | string | Type de calcul effectué | `"brut"`, `"net"` ou `"cost"` |
+| **Résultats calculés** ||||
+| `resultat_brut` | number | Salaire brut calculé | `2000.00` |
+| `resultat_net` | number | Salaire net avant PAS calculé | `1580.50` |
+| `resultat_cost` | number | Coût total employeur calculé | `2456.80` |
+| **Métadonnées** ||||
+| `plafond_urssaf` | number | Plafond URSSAF appliqué | `3864` |
+| `timestamp` | string | Date/heure de la simulation | `"2025-10-16T14:30:00.000Z"` |
-### Exemple d'événement
+### Exemple d'événement complet
```javascript
{
event: 'simulateur_calculation',
properties: {
+ // Paramètres du formulaire
+ ccn_id: '1285',
+ ccn_nom: '1285 – Entreprises Artistiques & Culturelles (CCNEAC)',
categorie: 'artiste',
+ statut: 'non-cadre',
+ abattement_active: true,
+ abattement_profession: 'drama',
+
+ // Cachets, heures et dates
+ cachets: 10,
+ heures: 35,
+ dates_travail: ['2025-10-15', '2025-10-16', '2025-10-17'],
+ nombre_jours: 3,
+
+ // Montant et type de calcul
+ montant_saisi: 2000,
type_calcul: 'brut',
- montant: 2000,
- timestamp: '2025-10-15T14:30:00.000Z'
+
+ // Résultats calculés
+ resultat_brut: 2000.00,
+ resultat_net: 1580.50,
+ resultat_cost: 2456.80,
+
+ // Métadonnées
+ plafond_urssaf: 3864,
+ timestamp: '2025-10-16T14:30:00.000Z'
}
}
```
@@ -57,17 +97,56 @@ PostHog
## Fichiers modifiés
### 1. `/public/simulateur-embed.html`
-**Ligne 1120+** : Ajout de l'envoi du message via `postMessage` dans le gestionnaire du bouton "Calculer"
+**Ligne 1326+** : Ajout de l'envoi du message enrichi via `postMessage` dans le gestionnaire du bouton "Calculer"
```javascript
-// 📊 Envoyer un événement PostHog via postMessage au parent
+// 📊 Envoyer un événement PostHog enrichi via postMessage au parent
try {
+ // Récupération de toutes les données du formulaire
+ const ccnElement = document.getElementById('conventionSelect');
+ const ccnValue = ccnElement?.value || '';
+ const ccnText = ccnElement?.options[ccnElement.selectedIndex]?.text || '';
+
+ const categorieValue = isTechnicien() ? 'technicien' : 'artiste';
+ const statutValue = document.getElementById('statutSelect')?.value || 'non-cadre';
+
+ // Abattement
+ const abattementChecked = document.querySelector('input[name="abattement"]:checked')?.value || 'non';
+ const professionValue = document.getElementById('professionSelect')?.value || '';
+
+ // Dates
+ const datesInput = document.getElementById('datesInput')?.value || '';
+ const datesArray = datesInput ? datesInput.split(',').map(d => d.trim()).filter(Boolean) : [];
+
window.parent.postMessage({
type: 'simulateur_calculation',
data: {
- categorie: isTechnicien() ? 'technicien' : 'artiste',
- type: document.querySelector('input[name="type"]:checked').value,
- montant: montant
+ // Paramètres du formulaire
+ ccn_id: ccnValue,
+ ccn_nom: ccnText,
+ categorie: categorieValue,
+ statut: statutValue,
+ abattement_active: abattementChecked === 'oui',
+ abattement_profession: professionValue,
+
+ // Cachets, heures, dates
+ cachets: cachets,
+ heures: heures,
+ dates_travail: datesArray,
+ nombre_jours: datesArray.length,
+
+ // Montant saisi et type
+ montant_saisi: montant,
+ type_calcul: type,
+
+ // Résultats calculés
+ resultat_brut: parseFloat(brut.toFixed(2)),
+ resultat_net: parseFloat(net.toFixed(2)),
+ resultat_cost: parseFloat(costEmployer.toFixed(2)),
+
+ // Métadonnées
+ plafond_urssaf: plafondUrssaf,
+ timestamp: new Date().toISOString()
}
}, '*');
} catch (e) {
@@ -76,28 +155,49 @@ try {
```
### 2. `/app/(app)/simulateur/page.tsx`
-**Lignes 1-35** : Ajout du hook PostHog et de l'écouteur de messages
+**Lignes 12-50** : Mise à jour du hook PostHog pour capturer toutes les propriétés enrichies
```typescript
-import { usePostHog } from 'posthog-js/react';
-
-export default function SimulateurPage() {
- const posthog = usePostHog();
-
- // 📊 Écouter les messages de l'iframe pour tracker les calculs
- useEffect(() => {
- const handleMessage = (event: MessageEvent) => {
- if (event.data?.type === 'simulateur_calculation') {
- const { categorie, type, montant } = event.data.data;
+useEffect(() => {
+ const handleMessage = (event: MessageEvent) => {
+ if (event.data?.type === 'simulateur_calculation') {
+ const data = event.data.data;
+
+ posthog?.capture('simulateur_calculation', {
+ // Paramètres du formulaire
+ ccn_id: data.ccn_id,
+ ccn_nom: data.ccn_nom,
+ categorie: data.categorie,
+ statut: data.statut,
+ abattement_active: data.abattement_active,
+ abattement_profession: data.abattement_profession,
- posthog?.capture('simulateur_calculation', {
- categorie,
- type_calcul: type,
- montant,
- timestamp: new Date().toISOString()
- });
- }
- };
+ // Cachets, heures, dates
+ cachets: data.cachets,
+ heures: data.heures,
+ dates_travail: data.dates_travail,
+ nombre_jours: data.nombre_jours,
+
+ // Montant saisi et type
+ montant_saisi: data.montant_saisi,
+ type_calcul: data.type_calcul,
+
+ // Résultats calculés
+ resultat_brut: data.resultat_brut,
+ resultat_net: data.resultat_net,
+ resultat_cost: data.resultat_cost,
+
+ // Métadonnées
+ plafond_urssaf: data.plafond_urssaf,
+ timestamp: data.timestamp
+ });
+ }
+ };
+
+ window.addEventListener('message', handleMessage);
+ return () => window.removeEventListener('message', handleMessage);
+}, [posthog]);
+```
window.addEventListener('message', handleMessage);
return () => window.removeEventListener('message', handleMessage);
@@ -136,7 +236,17 @@ Targeting:
### Dans la console du navigateur
Lors d'un clic sur "Calculer", vous devriez voir :
```
-📊 PostHog: Événement simulateur_calculation envoyé { categorie: 'artiste', type: 'brut', montant: 2000 }
+📊 PostHog: Événement simulateur_calculation enrichi envoyé {
+ categorie: 'artiste',
+ ccn: '1285 – Entreprises Artistiques & Culturelles (CCNEAC)',
+ type_calcul: 'brut',
+ montant_saisi: 2000,
+ resultats: {
+ brut: 2000,
+ net: 1580.5,
+ cost: 2456.8
+ }
+}
```
### Dans PostHog
@@ -168,27 +278,81 @@ Lors d'un clic sur "Calculer", vous devriez voir :
## Évolutions futures
-### Idées d'améliorations
-- Tracker également les modifications de paramètres (CCN, catégorie, etc.)
-- Ajouter le temps passé sur le simulateur
-- Tracker les exports PDF (si fonctionnalité ajoutée)
-- Ajouter des événements pour les erreurs de validation
+### Analyses possibles avec les données enrichies
+Avec toutes ces propriétés, vous pouvez maintenant analyser :
-### Autres métriques possibles
-```javascript
-// Exemple d'événements supplémentaires
-posthog.capture('simulateur_ccn_changed', { ccn: '1285' });
-posthog.capture('simulateur_export_pdf', { format: 'pdf' });
-posthog.capture('simulateur_error', { error_type: 'invalid_amount' });
+- **Par CCN** : Quelle convention collective est la plus utilisée ?
+- **Par catégorie** : Artiste vs Technicien - ratio d'utilisation
+- **Par type de calcul** : Les utilisateurs partent-ils plutôt du brut, net ou coût ?
+- **Abattement** : Combien d'utilisateurs utilisent l'abattement pour frais professionnels ?
+- **Montants moyens** : Salaire brut moyen, net moyen, coût moyen par CCN
+- **Heures et cachets** : Patterns d'utilisation (nombre moyen de cachets, heures par simulation)
+- **Périodes** : Analyse temporelle des simulations (jours, semaines, mois)
+
+### Exemples de requêtes PostHog
+
+**Salaire brut moyen par CCN** :
+```sql
+SELECT
+ ccn_nom,
+ AVG(resultat_brut) as brut_moyen,
+ COUNT(*) as nb_simulations
+FROM events
+WHERE event = 'simulateur_calculation'
+GROUP BY ccn_nom
+ORDER BY nb_simulations DESC
```
+**Répartition Artiste vs Technicien** :
+```sql
+SELECT
+ categorie,
+ COUNT(*) as total,
+ AVG(resultat_brut) as brut_moyen
+FROM events
+WHERE event = 'simulateur_calculation'
+GROUP BY categorie
+```
+
+**Taux d'utilisation de l'abattement** :
+```sql
+SELECT
+ abattement_active,
+ COUNT(*) as total,
+ (COUNT(*) * 100.0 / SUM(COUNT(*)) OVER ()) as pourcentage
+FROM events
+WHERE event = 'simulateur_calculation'
+ AND categorie = 'artiste'
+GROUP BY abattement_active
+```
+
+### Idées d'améliorations futures
+- Tracker les exports PDF (si fonctionnalité ajoutée)
+- Ajouter le temps passé sur le simulateur
+- Tracker les erreurs de validation
+- Capturer le device type (mobile/desktop)
+- Ajouter des événements pour les modifications de paramètres sans calcul
+
## Notes techniques
- ✅ Le simulateur est maintenant actif dans la sidebar
-- ✅ Les événements PostHog sont trackés pour analyse
+- ✅ Les événements PostHog sont trackés avec **toutes les données de simulation**
- ✅ Compatible avec les surveys PostHog
- ✅ Aucune dépendance externe supplémentaire requise
- ✅ Fonctionne en mode production et développement
+- ✅ **Données enrichies** : CCN, catégorie, statut, abattement, cachets, heures, dates, montants et résultats complets
+
+## Données capturées (résumé)
+| Catégorie | Données |
+|-----------|---------|
+| **CCN** | ID et nom complet de la convention |
+| **Profil** | Catégorie (artiste/technicien), statut (cadre/non-cadre) |
+| **Abattement** | Activation et type de profession |
+| **Volumes** | Cachets, heures, dates de travail, nombre de jours |
+| **Calcul** | Montant saisi, type de calcul (brut/net/cost) |
+| **Résultats** | Brut, Net avant PAS, Coût Total Employeur |
+| **Contexte** | Plafond URSSAF, timestamp |
## Date de mise en place
-15 octobre 2025
+- **Première version** : 15 octobre 2025 (tracking basique)
+- **Version enrichie** : 16 octobre 2025 (toutes les données)
diff --git a/app/(app)/simulateur/page.tsx b/app/(app)/simulateur/page.tsx
index bcb76c3..7c84274 100644
--- a/app/(app)/simulateur/page.tsx
+++ b/app/(app)/simulateur/page.tsx
@@ -14,17 +14,49 @@ export default function SimulateurPage() {
const handleMessage = (event: MessageEvent) => {
// Vérifier que le message vient de notre iframe
if (event.data?.type === 'simulateur_calculation') {
- const { categorie, type, montant } = event.data.data;
+ const data = event.data.data;
- // Envoyer l'événement à PostHog
+ // Envoyer l'événement enrichi à PostHog
posthog?.capture('simulateur_calculation', {
- categorie,
- type_calcul: type,
- montant,
- timestamp: new Date().toISOString()
+ // Paramètres du formulaire
+ ccn_id: data.ccn_id,
+ ccn_nom: data.ccn_nom,
+ categorie: data.categorie,
+ statut: data.statut,
+ abattement_active: data.abattement_active,
+ abattement_profession: data.abattement_profession,
+
+ // Cachets, heures, dates
+ cachets: data.cachets,
+ heures: data.heures,
+ dates_travail: data.dates_travail,
+ nombre_jours: data.nombre_jours,
+
+ // Montant saisi et type
+ montant_saisi: data.montant_saisi,
+ type_calcul: data.type_calcul,
+
+ // Résultats calculés
+ resultat_brut: data.resultat_brut,
+ resultat_net: data.resultat_net,
+ resultat_cost: data.resultat_cost,
+
+ // Métadonnées
+ plafond_urssaf: data.plafond_urssaf,
+ timestamp: data.timestamp
});
- console.log('📊 PostHog: Événement simulateur_calculation envoyé', { categorie, type, montant });
+ console.log('📊 PostHog: Événement simulateur_calculation enrichi envoyé', {
+ categorie: data.categorie,
+ ccn: data.ccn_nom,
+ type_calcul: data.type_calcul,
+ montant_saisi: data.montant_saisi,
+ resultats: {
+ brut: data.resultat_brut,
+ net: data.resultat_net,
+ cost: data.resultat_cost
+ }
+ });
}
};
diff --git a/components/Sidebar.tsx b/components/Sidebar.tsx
index 745efa6..8388b11 100644
--- a/components/Sidebar.tsx
+++ b/components/Sidebar.tsx
@@ -458,7 +458,7 @@ export default function Sidebar({ clientInfo, isStaff = false, mobile = false, o
onNavigate && onNavigate()} className={`block px-3 py-2 rounded-xl text-sm transition truncate ${
isActivePath(pathname, "/simulateur") ? "bg-gradient-to-r from-indigo-200 via-purple-200 to-pink-200 text-slate-700 font-semibold" : "hover:bg-slate-50"
diff --git a/public/simulateur-embed.html b/public/simulateur-embed.html
index 2b69bc8..a603e1d 100644
--- a/public/simulateur-embed.html
+++ b/public/simulateur-embed.html
@@ -141,6 +141,9 @@
+
+
+
Résultat de la simulation
@@ -314,6 +317,27 @@ function getPlafondUrssaf() {
return PMSS * (diffDays / daysInMonth);
}
+// ===== IRC Tranche 1 Max (T1 cap, AGIRC-ARRCO) =====
+function getIrcT1Max(){
+ // Mode intermittent ajusté : 12 × PMSS × (span / (daysInMonth × 0.95)), plafonné à 12 × PMSS
+ const dates = getSelectedDatesArray();
+ if (!dates.length) return 0;
+ const first = dates[0], last = dates[dates.length-1];
+ const diffDays = Math.round((last - first) / (1000*60*60*24)) + 1;
+ const y = first.getFullYear();
+ const m = first.getMonth();
+ const daysInMonth = new Date(y, m + 1, 0).getDate();
+ const adjusted = PMSS * 12 * (diffDays / (daysInMonth * 0.95));
+ return Math.min(adjusted, PMSS * 12);
+}
+// ===== Helper: split IRC base between T1 and T2 (avant abattement) =====
+function getIrcBasesBeforeAbattement(brut){
+ const t1Max = getIrcT1Max();
+ const baseT1 = Math.min(brut, Math.max(0, t1Max));
+ const baseT2 = Math.max(0, brut - Math.max(0, t1Max));
+ return { baseT1, baseT2 };
+}
+
/* ===== Compléments maladie / AF jour par jour ===== */
const HOURS_PER_CACHET_FOR_COMPLEMENT = 8; // pour les seuils
const HOURS_PER_DAY_FOR_COMPLEMENT_IF_CACHET = 6.7; // forfait si cachet ce jour
@@ -346,15 +370,40 @@ function getAssietteChomageMaxPourMoisCourant() {
const m = dates[0].getMonth();
const daysInMonth = new Date(y, m + 1, 0).getDate();
+ // Ajout : nombre de cachets et nbJoursChomage (max des deux)
+ const cachetsRaw = parseInt(document.getElementById('cachetsInput')?.value, 10);
+ const cachets = (isNaN(cachetsRaw) || cachetsRaw < 0 || isTechnicien()) ? 0 : cachetsRaw;
+ const nbJoursChomage = Math.max(nbJours, cachets);
+
// Règle attendue :
// - < 5 jours : plafond chômage = 4 × plafond URSSAF de la période
- // - ≥ 5 jours : plafond chômage = 4 × PMSS × (nbJours / daysInMonth)
+ // - ≥ 5 jours : plafond chômage = 4 × PMSS × (span en jours / daysInMonth)
if (diffDays < 5) {
const plafUrssaf = getPlafondUrssaf();
return 4 * plafUrssaf;
}
- return 4 * PMSS * (nbJours / daysInMonth);
+ return 4 * PMSS * (diffDays / daysInMonth);
+}
+
+// === Debug panel helper for chômage assiette max
+function getChomageDebugInfo() {
+ const dates = getSelectedDatesArray();
+ if (!dates.length) return null;
+ const first = dates[0], last = dates[dates.length-1];
+ const diffDays = Math.round((last - first) / (1000*60*60*24)) + 1;
+ const nbJours = dates.length;
+ const y = dates[0].getFullYear();
+ const m = dates[0].getMonth();
+ const daysInMonth = new Date(y, m + 1, 0).getDate();
+ const cachetsRaw = parseInt(document.getElementById('cachetsInput')?.value, 10);
+ const cachets = (isNaN(cachetsRaw) || cachetsRaw < 0 || isTechnicien()) ? 0 : cachetsRaw;
+ const nbJoursChomage = Math.max(nbJours, cachets);
+ const plafondShort = 4 * getPlafondUrssaf();
+ // Use diffDays for plafondLong as per new rule
+ const plafondLong = 4 * PMSS * (diffDays / daysInMonth);
+ const assiette = (diffDays < 5) ? plafondShort : plafondLong;
+ return { diffDays, nbJours, cachets, nbJoursChomage, daysInMonth, assiette, branch: (diffDays < 5) ? '<5 jours' : '>=5 jours', usedRatioDays: (diffDays < 5) ? null : diffDays };
}
// Taux complémentaires selon catégorie
@@ -478,6 +527,9 @@ function baseLibelle(code, cat){
"ags": "AGS intermittent",
"retraite_t1": isTech ? "Retraite non-cadre Int. T1" : "Retraite artiste Tranche 1",
"ceg_t1": isTech ? "CEG non-cadre Int. T1" : "CEG artiste intermittent Tranche 1",
+ "retraite_t2": isTech ? "Retraite non-cadre Int. T2" : "Retraite artiste Tranche 2",
+ "ceg_t2": isTech ? "CEG non-cadre Int. T2" : "CEG artiste intermittent Tranche 2",
+ "cet": isTech ? "CET non-cadre Int." : "CET artiste intermittent",
"prevoyance_ta": isTech ? "Prévoyance non-cadre Int. TA" : "Prévoyance non-cadre Artiste Int. TA",
"conges_spectacles": "Congés spectacles",
"medecine_t1": isTech ? "Médecine du travail int. T1" : "Médecine du travail int.",
@@ -514,19 +566,23 @@ function getProfileRates(cat){
}
};
}
- // Artiste = base existante
+ // Artiste
return {
sal: {
contrib_solidarite: 0, maladie: 0, vieillesse: 0.28, alloc_fam: 0, at: 0,
vieillesse_ta: 4.83, fnal_plaf: 0, maj_chomage: 0, chomage: 2.4, ags: 0,
- ceg_t1: 0.86, retraite_t1: 4.44, prevoyance_ta: 0.12, conges_spectacles: 0,
+ ceg_t1: 0.86, retraite_t1: 4.44, retraite_t2: 10.79, ceg_t2: 1.08,
+ cet: 0.14,
+ prevoyance_ta: 0.12, conges_spectacles: 0,
casc_svp: 0, fnas: 0, fcap: 0, medecine_t1: 0, cfpc_conv: 0, cfp_ta: 0,
paritarisme: 0, csg_deductible: 6.8, csg_imposable: 2.4, rds: 0.5
},
emp: {
contrib_solidarite: 0.3, maladie: 4.90, vieillesse: 1.41, alloc_fam: 2.42, at: 1.18,
vieillesse_ta: 5.99, fnal_plaf: 0.07, maj_chomage: 0.5, chomage: 9.0, ags: 0.25,
- ceg_t1: 1.29, retraite_t1: 4.45, prevoyance_ta: 1.04, conges_spectacles: 15.5,
+ ceg_t1: 1.29, retraite_t1: 4.45, retraite_t2: 10.80, ceg_t2: 1.62,
+ cet: 0.21,
+ prevoyance_ta: 1.04, conges_spectacles: 15.5,
casc_svp: 0.4, fnas: 1.45, fcap: 0.25, medecine_t1: 0.32, cfpc_conv: 0.1,
cfp_ta: 2.0, paritarisme: 0.016, csg_deductible: 0, csg_imposable: 0, rds: 0
}
@@ -538,13 +594,20 @@ function getCotisations(){
const cc = document.getElementById('conventionSelect')?.value;
const rates = getProfileRates(cat);
- const codes = [
+ let codes = [
"contrib_solidarite","maladie","vieillesse","alloc_fam","at",
"vieillesse_ta","fnal_plaf","maj_chomage","chomage","ags",
"retraite_t1","ceg_t1","prevoyance_ta","conges_spectacles",
"medecine_t1","cfpc_conv","cfp_ta","paritarisme",
"csg_deductible","csg_imposable","rds"
];
+ // Pour ARTISTE, insérer T2 après ceg_t1, puis CET
+ if (cat === 'artiste') {
+ const idx = codes.indexOf("ceg_t1");
+ if (idx !== -1) {
+ codes.splice(idx + 1, 0, "retraite_t2","ceg_t2","cet");
+ }
+ }
if (cc === "3090") codes.push("casc_svp"); // CCNSVP
if (cc === "1285") { codes.push("fnas","fcap"); } // CCNEAC
@@ -574,6 +637,47 @@ function getCotisations(){
return { sal, emp, cat };
}
+/* ===== Somme des contributions employeur incluses dans l'assiette CSG/CRDS ===== */
+function employerContribsForCSGBase(brut){
+ const { emp } = getCotisations();
+ const dates = getSelectedDatesArray();
+ if (!dates.length) return 0;
+
+ const prevBase = getPrevoyanceBase(brut);
+ const assietteChomageMax = getAssietteChomageMaxPourMoisCourant();
+
+ let total = 0;
+ for (const e of emp){
+ let amount = 0;
+ switch (e.code){
+ // ✅ Inclure : chômage / maj. chômage / AGS sur leur assiette plafonnée
+ case 'chomage':
+ case 'maj_chomage':
+ case 'ags': {
+ const base = Math.min(brut, assietteChomageMax);
+ amount = base * (e.taux / 100);
+ break;
+ }
+ // ✅ Inclure : prévoyance TA (sur base plafonnée jour)
+ case 'prevoyance_ta':
+ amount = prevBase * (e.taux / 100);
+ break;
+ // ✅ Inclure : contributions conventionnelles simples
+ case 'cfpc_conv':
+ case 'paritarisme':
+ case 'apec':
+ amount = brut * (e.taux / 100);
+ break;
+
+ // ❌ Exclure : IRC (AGIRC-ARRCO) T1/T2 (retraite & CEG), FNAL, maladie, vieillesse, AF, AT, etc.
+ default:
+ break;
+ }
+ total += amount;
+ }
+ return total;
+}
+
/* ===== Prévoyance employeur ===== */
function prevoyanceEmployerAmount(brut) {
const { emp } = getCotisations();
@@ -591,7 +695,8 @@ function calculateNet(brut, plafondUrssaf) {
const nonAbattementCodes = [
"maj_chomage","chomage","ags","conges_spectacles",
"csg_deductible","csg_imposable","rds",
- "prevoyance_ta" // ✅ ne pas abattre la prévoyance
+ "prevoyance_ta",
+ "cet"
];
const assietteChomageMax = getAssietteChomageMaxPourMoisCourant();
@@ -610,7 +715,17 @@ const nonAbattementCodes = [
} else if (c.code === "prevoyance_ta" || (c.libelle && c.libelle.includes("Prévoyance"))) {
base = getPrevoyanceBase(brut);
} else if (["csg_deductible","csg_imposable","rds"].includes(c.code)) {
- base = (brut + prevEmp) * 0.9825;
+ const incEmp = employerContribsForCSGBase(brut);
+ base = (brut + incEmp) * 0.9825;
+ } else if (c.code === "retraite_t1" || c.code === "ceg_t1") {
+ const { baseT1 } = getIrcBasesBeforeAbattement(brut);
+ base = baseT1; // abattement éventuel appliqué plus bas si autorisé
+ } else if (c.code === "retraite_t2" || c.code === "ceg_t2") {
+ const { baseT2 } = getIrcBasesBeforeAbattement(brut);
+ base = baseT2; // abattement éventuel appliqué plus bas si autorisé
+ } else if (c.code === "cet") {
+ base = brut;
+ appliedAbattement = true;
} else {
base = brut;
}
@@ -632,7 +747,8 @@ function computeEmployerCharges(brut, cachets, heures, plafondUrssaf) {
const nonAbattementCodes = [
"maj_chomage","chomage","ags","conges_spectacles",
"csg_deductible","csg_imposable","rds",
- "prevoyance_ta" // ✅ ne pas abattre la prévoyance
+ "prevoyance_ta",
+ "cet"
];
// Fusionne codes salariaux/patronaux pour calculer la part patronale
@@ -664,7 +780,17 @@ const nonAbattementCodes = [
} else if (c.code === "prevoyance_ta" || (c.libelle && c.libelle.includes("Prévoyance"))) {
base = getPrevoyanceBase(brut);
} else if (["csg_deductible","csg_imposable","rds"].includes(c.code)) {
- base = (brut + prevEmp) * 0.9825;
+ const incEmp = employerContribsForCSGBase(brut);
+ base = (brut + incEmp) * 0.9825;
+ } else if (c.code === "retraite_t1" || c.code === "ceg_t1") {
+ const { baseT1 } = getIrcBasesBeforeAbattement(brut);
+ base = baseT1; // abattement éventuel appliqué plus bas si autorisé
+ } else if (c.code === "retraite_t2" || c.code === "ceg_t2") {
+ const { baseT2 } = getIrcBasesBeforeAbattement(brut);
+ base = baseT2; // abattement éventuel appliqué plus bas si autorisé
+ } else if (c.code === "cet") {
+ base = brut;
+ appliedAbattement = true;
} else {
base = brut;
}
@@ -864,7 +990,8 @@ function generateDetailTable(brut, cachets, heures, plafondUrssaf) {
const nonAbattementCodes = [
"maj_chomage","chomage","ags","conges_spectacles",
"csg_deductible","csg_imposable","rds",
- "prevoyance_ta" // ✅ ne pas abattre la prévoyance
+ "prevoyance_ta",
+ "cet"
];
const { sal, emp, cat } = getCotisations();
const assietteChomageMax = getAssietteChomageMaxPourMoisCourant();
@@ -907,7 +1034,17 @@ const nonAbattementCodes = [
} else if (c.code === "prevoyance_ta" || (c.libelle && c.libelle.includes("Prévoyance"))) {
base = getPrevoyanceBase(brut);
} else if (["csg_deductible","csg_imposable","rds"].includes(c.code)) {
- base = (brut + prevEmp) * 0.9825;
+ const incEmp = employerContribsForCSGBase(brut);
+ base = (brut + incEmp) * 0.9825;
+ } else if (c.code === "retraite_t1" || c.code === "ceg_t1") {
+ const { baseT1 } = getIrcBasesBeforeAbattement(brut);
+ base = baseT1; // abattement éventuel appliqué plus bas si autorisé
+ } else if (c.code === "retraite_t2" || c.code === "ceg_t2") {
+ const { baseT2 } = getIrcBasesBeforeAbattement(brut);
+ base = baseT2; // abattement éventuel appliqué plus bas si autorisé
+ } else if (c.code === "cet") {
+ base = brut;
+ appliedAbattement = true;
} else {
base = brut;
}
@@ -983,7 +1120,8 @@ function buildContributionsStructured(brut, cachets, heures, plafondUrssaf){
const nonAbattementCodes = [
"maj_chomage","chomage","ags","conges_spectacles",
"csg_deductible","csg_imposable","rds",
- "prevoyance_ta" // ✅ ne pas abattre la prévoyance
+ "prevoyance_ta",
+ "cet"
];
const { sal, emp, cat } = getCotisations();
const assietteChomageMax = getAssietteChomageMaxPourMoisCourant();
@@ -1100,7 +1238,17 @@ if (fillonTotal > 0) {
} else if (c.code === "prevoyance_ta" || (c.libelle && c.libelle.includes("Prévoyance"))) {
base = getPrevoyanceBase(brut);
} else if (["csg_deductible","csg_imposable","rds"].includes(c.code)) {
- base = (brut + prevoyanceEmployerAmount(brut)) * 0.9825;
+ const incEmp = employerContribsForCSGBase(brut);
+ base = (brut + incEmp) * 0.9825;
+ } else if (c.code === "retraite_t1" || c.code === "ceg_t1") {
+ const { baseT1 } = getIrcBasesBeforeAbattement(brut);
+ base = baseT1; // abattement éventuel appliqué plus bas si autorisé
+ } else if (c.code === "retraite_t2" || c.code === "ceg_t2") {
+ const { baseT2 } = getIrcBasesBeforeAbattement(brut);
+ base = baseT2; // abattement éventuel appliqué plus bas si autorisé
+ } else if (c.code === "cet") {
+ base = brut;
+ appliedAbattement = true;
} else {
base = brut;
}
@@ -1159,20 +1307,6 @@ document.getElementById('calcBtn').addEventListener('click', function() {
return;
}
- // 📊 Envoyer un événement PostHog via postMessage au parent
- try {
- window.parent.postMessage({
- type: 'simulateur_calculation',
- data: {
- categorie: isTechnicien() ? 'technicien' : 'artiste',
- type: document.querySelector('input[name="type"]:checked').value,
- montant: montant
- }
- }, '*');
- } catch (e) {
- console.error('Erreur lors de l\'envoi de l\'événement PostHog:', e);
- }
-
const plafondUrssaf = getPlafondUrssaf();
const type = document.querySelector('input[name="type"]:checked').value;
@@ -1191,6 +1325,119 @@ document.getElementById('calcBtn').addEventListener('click', function() {
({ net, costEmployer } = calculateSalaries(brut, cachets, heures, plafondUrssaf));
}
+ // 📊 Envoyer un événement PostHog enrichi via postMessage au parent
+ try {
+ // Récupération de toutes les données du formulaire
+ const ccnElement = document.getElementById('conventionSelect');
+ const ccnValue = ccnElement?.value || '';
+ const ccnText = ccnElement?.options[ccnElement.selectedIndex]?.text || '';
+
+ const categorieValue = isTechnicien() ? 'technicien' : 'artiste';
+ const statutValue = document.getElementById('statutSelect')?.value || 'non-cadre';
+
+ // Abattement
+ const abattementChecked = document.querySelector('input[name="abattement"]:checked')?.value || 'non';
+ const professionValue = document.getElementById('professionSelect')?.value || '';
+
+ // Dates
+ const datesInput = document.getElementById('datesInput')?.value || '';
+ const datesArray = datesInput ? datesInput.split(',').map(d => d.trim()).filter(Boolean) : [];
+
+ window.parent.postMessage({
+ type: 'simulateur_calculation',
+ data: {
+ // Paramètres du formulaire
+ ccn_id: ccnValue,
+ ccn_nom: ccnText,
+ categorie: categorieValue,
+ statut: statutValue,
+ abattement_active: abattementChecked === 'oui',
+ abattement_profession: professionValue,
+
+ // Cachets, heures, dates
+ cachets: cachets,
+ heures: heures,
+ dates_travail: datesArray,
+ nombre_jours: datesArray.length,
+
+ // Montant saisi et type
+ montant_saisi: montant,
+ type_calcul: type, // 'brut', 'net', ou 'cost'
+
+ // Résultats calculés
+ resultat_brut: parseFloat(brut.toFixed(2)),
+ resultat_net: parseFloat(net.toFixed(2)),
+ resultat_cost: parseFloat(costEmployer.toFixed(2)),
+
+ // Métadonnées
+ plafond_urssaf: plafondUrssaf,
+ timestamp: new Date().toISOString()
+ }
+ }, '*');
+
+ console.log('📊 PostHog: Événement simulateur_calculation enrichi envoyé');
+ } catch (e) {
+ console.error('Erreur lors de l\'envoi de l\'événement PostHog:', e);
+ }
+
+ // —— Alerts & hard stop on extreme simulations ——
+ const alertsEl = document.getElementById('alerts');
+ if (alertsEl) alertsEl.innerHTML = '';
+
+ // Hard stop > 100 000 €
+ if (brut > 100000) {
+ try {
+ swal({
+ title: "Montant très élevé",
+ text: "Cette simulation dépasse le périmètre de l'outil. Merci de nous contacter pour confirmer ou valider la simulation.",
+ icon: "warning",
+ buttons: {
+ cancel: "Fermer",
+ support: {
+ text: "Contacter le support",
+ value: "support",
+ closeModal: true
+ }
+ }
+ }).then(value => {
+ if (value === "support") window.location.href = "/support";
+ });
+ } catch(e){
+ // Fallback simple si SweetAlert indisponible
+ if (confirm("Montant très élevé. Ouvrir la page support ?")) window.location.href = "/support";
+ }
+ // Stop calculation rendering
+ document.getElementById('result').innerHTML = '';
+ document.getElementById('detailTable').innerHTML = '';
+ return;
+ }
+
+ // Alerte non bloquante > 10 000 €
+ if (brut > 10000) {
+ if (alertsEl) {
+ alertsEl.innerHTML = `
+
+
+
+ Simulation extrême : le montant saisi est très élevé.\n
+ Nous vous invitons à nous contacter pour confirmer ou valider la simulation.
+
+
Contacter le support
+
+
`;
+ }
+ }
+
+ // Ajout du debug chômage
+ const dbg = getChomageDebugInfo();
+ const dbgHtml = dbg ? `
+
+
Debug chômage
+
Branche: ${dbg.branch} — Jours sélectionnés: ${dbg.nbJours}, Cachets: ${dbg.cachets}, Jours retenus chômage: ${dbg.nbJoursChomage}
+
Jours dans le mois: ${dbg.daysInMonth}, DiffDays (span): ${dbg.diffDays}
+
Assiette chômage max calculée: ${fmtEuro(dbg.assiette)} €
+
` : '';
+
const resultTable = `
@@ -1205,6 +1452,7 @@ document.getElementById('calcBtn').addEventListener('click', function() {
Plafond URSSAF : ${fmtEuro(plafondUrssaf)} €
+ ${dbgHtml}
`;
document.getElementById('result').innerHTML = resultTable;
document.getElementById('detailTable').innerHTML = generateDetailTable(brut, cachets, heures, plafondUrssaf);