Correctif bases spécifiques simulateur
This commit is contained in:
parent
2497a80f4d
commit
dd4e8d17b1
4 changed files with 525 additions and 81 deletions
|
|
@ -33,23 +33,63 @@ PostHog
|
||||||
### Nom de l'événement
|
### Nom de l'événement
|
||||||
`simulateur_calculation`
|
`simulateur_calculation`
|
||||||
|
|
||||||
### Propriétés
|
### Propriétés complètes
|
||||||
| Propriété | Type | Description | Exemple |
|
| 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"` |
|
| `categorie` | string | Catégorie du salarié | `"artiste"` ou `"technicien"` |
|
||||||
| `type_calcul` | string | Type de calcul | `"brut"`, `"net"` ou `"cost"` |
|
| `statut` | string | Statut professionnel | `"non-cadre"` ou `"cadre"` |
|
||||||
| `montant` | number | Montant saisi | `2000` |
|
| `abattement_active` | boolean | Abattement pour frais pro activé | `true` ou `false` |
|
||||||
| `timestamp` | string | Date/heure de l'événement | `"2025-10-15T14:30:00.000Z"` |
|
| `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
|
```javascript
|
||||||
{
|
{
|
||||||
event: 'simulateur_calculation',
|
event: 'simulateur_calculation',
|
||||||
properties: {
|
properties: {
|
||||||
|
// Paramètres du formulaire
|
||||||
|
ccn_id: '1285',
|
||||||
|
ccn_nom: '1285 – Entreprises Artistiques & Culturelles (CCNEAC)',
|
||||||
categorie: 'artiste',
|
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',
|
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
|
## Fichiers modifiés
|
||||||
|
|
||||||
### 1. `/public/simulateur-embed.html`
|
### 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
|
```javascript
|
||||||
// 📊 Envoyer un événement PostHog via postMessage au parent
|
// 📊 Envoyer un événement PostHog enrichi via postMessage au parent
|
||||||
try {
|
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({
|
window.parent.postMessage({
|
||||||
type: 'simulateur_calculation',
|
type: 'simulateur_calculation',
|
||||||
data: {
|
data: {
|
||||||
categorie: isTechnicien() ? 'technicien' : 'artiste',
|
// Paramètres du formulaire
|
||||||
type: document.querySelector('input[name="type"]:checked').value,
|
ccn_id: ccnValue,
|
||||||
montant: montant
|
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) {
|
} catch (e) {
|
||||||
|
|
@ -76,28 +155,49 @@ try {
|
||||||
```
|
```
|
||||||
|
|
||||||
### 2. `/app/(app)/simulateur/page.tsx`
|
### 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
|
```typescript
|
||||||
import { usePostHog } from 'posthog-js/react';
|
useEffect(() => {
|
||||||
|
const handleMessage = (event: MessageEvent) => {
|
||||||
export default function SimulateurPage() {
|
if (event.data?.type === 'simulateur_calculation') {
|
||||||
const posthog = usePostHog();
|
const data = event.data.data;
|
||||||
|
|
||||||
// 📊 Écouter les messages de l'iframe pour tracker les calculs
|
posthog?.capture('simulateur_calculation', {
|
||||||
useEffect(() => {
|
// Paramètres du formulaire
|
||||||
const handleMessage = (event: MessageEvent) => {
|
ccn_id: data.ccn_id,
|
||||||
if (event.data?.type === 'simulateur_calculation') {
|
ccn_nom: data.ccn_nom,
|
||||||
const { categorie, type, montant } = event.data.data;
|
categorie: data.categorie,
|
||||||
|
statut: data.statut,
|
||||||
|
abattement_active: data.abattement_active,
|
||||||
|
abattement_profession: data.abattement_profession,
|
||||||
|
|
||||||
posthog?.capture('simulateur_calculation', {
|
// Cachets, heures, dates
|
||||||
categorie,
|
cachets: data.cachets,
|
||||||
type_calcul: type,
|
heures: data.heures,
|
||||||
montant,
|
dates_travail: data.dates_travail,
|
||||||
timestamp: new Date().toISOString()
|
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);
|
window.addEventListener('message', handleMessage);
|
||||||
return () => window.removeEventListener('message', handleMessage);
|
return () => window.removeEventListener('message', handleMessage);
|
||||||
|
|
@ -136,7 +236,17 @@ Targeting:
|
||||||
### Dans la console du navigateur
|
### Dans la console du navigateur
|
||||||
Lors d'un clic sur "Calculer", vous devriez voir :
|
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
|
### Dans PostHog
|
||||||
|
|
@ -168,27 +278,81 @@ Lors d'un clic sur "Calculer", vous devriez voir :
|
||||||
|
|
||||||
## Évolutions futures
|
## Évolutions futures
|
||||||
|
|
||||||
### Idées d'améliorations
|
### Analyses possibles avec les données enrichies
|
||||||
- Tracker également les modifications de paramètres (CCN, catégorie, etc.)
|
Avec toutes ces propriétés, vous pouvez maintenant analyser :
|
||||||
- Ajouter le temps passé sur le simulateur
|
|
||||||
- Tracker les exports PDF (si fonctionnalité ajoutée)
|
|
||||||
- Ajouter des événements pour les erreurs de validation
|
|
||||||
|
|
||||||
### Autres métriques possibles
|
- **Par CCN** : Quelle convention collective est la plus utilisée ?
|
||||||
```javascript
|
- **Par catégorie** : Artiste vs Technicien - ratio d'utilisation
|
||||||
// Exemple d'événements supplémentaires
|
- **Par type de calcul** : Les utilisateurs partent-ils plutôt du brut, net ou coût ?
|
||||||
posthog.capture('simulateur_ccn_changed', { ccn: '1285' });
|
- **Abattement** : Combien d'utilisateurs utilisent l'abattement pour frais professionnels ?
|
||||||
posthog.capture('simulateur_export_pdf', { format: 'pdf' });
|
- **Montants moyens** : Salaire brut moyen, net moyen, coût moyen par CCN
|
||||||
posthog.capture('simulateur_error', { error_type: 'invalid_amount' });
|
- **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
|
## Notes techniques
|
||||||
|
|
||||||
- ✅ Le simulateur est maintenant actif dans la sidebar
|
- ✅ 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
|
- ✅ Compatible avec les surveys PostHog
|
||||||
- ✅ Aucune dépendance externe supplémentaire requise
|
- ✅ Aucune dépendance externe supplémentaire requise
|
||||||
- ✅ Fonctionne en mode production et développement
|
- ✅ 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
|
## 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)
|
||||||
|
|
|
||||||
|
|
@ -14,17 +14,49 @@ export default function SimulateurPage() {
|
||||||
const handleMessage = (event: MessageEvent) => {
|
const handleMessage = (event: MessageEvent) => {
|
||||||
// Vérifier que le message vient de notre iframe
|
// Vérifier que le message vient de notre iframe
|
||||||
if (event.data?.type === 'simulateur_calculation') {
|
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', {
|
posthog?.capture('simulateur_calculation', {
|
||||||
categorie,
|
// Paramètres du formulaire
|
||||||
type_calcul: type,
|
ccn_id: data.ccn_id,
|
||||||
montant,
|
ccn_nom: data.ccn_nom,
|
||||||
timestamp: new Date().toISOString()
|
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
|
||||||
|
}
|
||||||
|
});
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -458,7 +458,7 @@ export default function Sidebar({ clientInfo, isStaff = false, mobile = false, o
|
||||||
<DisabledMenuItem
|
<DisabledMenuItem
|
||||||
icon={Scale}
|
icon={Scale}
|
||||||
label="Minima CCN"
|
label="Minima CCN"
|
||||||
tooltipMessage="Le simulateur et les minima seront de retour dans quelques jours."
|
tooltipMessage="Les minima CCN seront de retour dans quelques jours."
|
||||||
/>
|
/>
|
||||||
<Link href="/simulateur" onClick={() => onNavigate && onNavigate()} className={`block px-3 py-2 rounded-xl text-sm transition truncate ${
|
<Link href="/simulateur" onClick={() => 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"
|
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"
|
||||||
|
|
|
||||||
|
|
@ -141,6 +141,9 @@
|
||||||
<button id="calcBtn">Calculer</button>
|
<button id="calcBtn">Calculer</button>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<!-- Alerts -->
|
||||||
|
<div id="alerts"></div>
|
||||||
|
|
||||||
<!-- Résultats -->
|
<!-- Résultats -->
|
||||||
<div class="result">
|
<div class="result">
|
||||||
<h4>Résultat de la simulation</h4>
|
<h4>Résultat de la simulation</h4>
|
||||||
|
|
@ -314,6 +317,27 @@ function getPlafondUrssaf() {
|
||||||
return PMSS * (diffDays / daysInMonth);
|
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 ===== */
|
/* ===== Compléments maladie / AF jour par jour ===== */
|
||||||
const HOURS_PER_CACHET_FOR_COMPLEMENT = 8; // pour les seuils
|
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
|
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 m = dates[0].getMonth();
|
||||||
const daysInMonth = new Date(y, m + 1, 0).getDate();
|
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 :
|
// Règle attendue :
|
||||||
// - < 5 jours : plafond chômage = 4 × plafond URSSAF de la période
|
// - < 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) {
|
if (diffDays < 5) {
|
||||||
const plafUrssaf = getPlafondUrssaf();
|
const plafUrssaf = getPlafondUrssaf();
|
||||||
return 4 * plafUrssaf;
|
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
|
// Taux complémentaires selon catégorie
|
||||||
|
|
@ -478,6 +527,9 @@ function baseLibelle(code, cat){
|
||||||
"ags": "AGS intermittent",
|
"ags": "AGS intermittent",
|
||||||
"retraite_t1": isTech ? "Retraite non-cadre Int. T1" : "Retraite artiste Tranche 1",
|
"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",
|
"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",
|
"prevoyance_ta": isTech ? "Prévoyance non-cadre Int. TA" : "Prévoyance non-cadre Artiste Int. TA",
|
||||||
"conges_spectacles": "Congés spectacles",
|
"conges_spectacles": "Congés spectacles",
|
||||||
"medecine_t1": isTech ? "Médecine du travail int. T1" : "Médecine du travail int.",
|
"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 {
|
return {
|
||||||
sal: {
|
sal: {
|
||||||
contrib_solidarite: 0, maladie: 0, vieillesse: 0.28, alloc_fam: 0, at: 0,
|
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,
|
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,
|
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
|
paritarisme: 0, csg_deductible: 6.8, csg_imposable: 2.4, rds: 0.5
|
||||||
},
|
},
|
||||||
emp: {
|
emp: {
|
||||||
contrib_solidarite: 0.3, maladie: 4.90, vieillesse: 1.41, alloc_fam: 2.42, at: 1.18,
|
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,
|
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,
|
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
|
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 cc = document.getElementById('conventionSelect')?.value;
|
||||||
const rates = getProfileRates(cat);
|
const rates = getProfileRates(cat);
|
||||||
|
|
||||||
const codes = [
|
let codes = [
|
||||||
"contrib_solidarite","maladie","vieillesse","alloc_fam","at",
|
"contrib_solidarite","maladie","vieillesse","alloc_fam","at",
|
||||||
"vieillesse_ta","fnal_plaf","maj_chomage","chomage","ags",
|
"vieillesse_ta","fnal_plaf","maj_chomage","chomage","ags",
|
||||||
"retraite_t1","ceg_t1","prevoyance_ta","conges_spectacles",
|
"retraite_t1","ceg_t1","prevoyance_ta","conges_spectacles",
|
||||||
"medecine_t1","cfpc_conv","cfp_ta","paritarisme",
|
"medecine_t1","cfpc_conv","cfp_ta","paritarisme",
|
||||||
"csg_deductible","csg_imposable","rds"
|
"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 === "3090") codes.push("casc_svp"); // CCNSVP
|
||||||
if (cc === "1285") { codes.push("fnas","fcap"); } // CCNEAC
|
if (cc === "1285") { codes.push("fnas","fcap"); } // CCNEAC
|
||||||
|
|
||||||
|
|
@ -574,6 +637,47 @@ function getCotisations(){
|
||||||
return { sal, emp, cat };
|
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 ===== */
|
/* ===== Prévoyance employeur ===== */
|
||||||
function prevoyanceEmployerAmount(brut) {
|
function prevoyanceEmployerAmount(brut) {
|
||||||
const { emp } = getCotisations();
|
const { emp } = getCotisations();
|
||||||
|
|
@ -591,7 +695,8 @@ function calculateNet(brut, plafondUrssaf) {
|
||||||
const nonAbattementCodes = [
|
const nonAbattementCodes = [
|
||||||
"maj_chomage","chomage","ags","conges_spectacles",
|
"maj_chomage","chomage","ags","conges_spectacles",
|
||||||
"csg_deductible","csg_imposable","rds",
|
"csg_deductible","csg_imposable","rds",
|
||||||
"prevoyance_ta" // ✅ ne pas abattre la prévoyance
|
"prevoyance_ta",
|
||||||
|
"cet"
|
||||||
];
|
];
|
||||||
|
|
||||||
const assietteChomageMax = getAssietteChomageMaxPourMoisCourant();
|
const assietteChomageMax = getAssietteChomageMaxPourMoisCourant();
|
||||||
|
|
@ -610,7 +715,17 @@ const nonAbattementCodes = [
|
||||||
} else if (c.code === "prevoyance_ta" || (c.libelle && c.libelle.includes("Prévoyance"))) {
|
} else if (c.code === "prevoyance_ta" || (c.libelle && c.libelle.includes("Prévoyance"))) {
|
||||||
base = getPrevoyanceBase(brut);
|
base = getPrevoyanceBase(brut);
|
||||||
} else if (["csg_deductible","csg_imposable","rds"].includes(c.code)) {
|
} 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 {
|
} else {
|
||||||
base = brut;
|
base = brut;
|
||||||
}
|
}
|
||||||
|
|
@ -632,7 +747,8 @@ function computeEmployerCharges(brut, cachets, heures, plafondUrssaf) {
|
||||||
const nonAbattementCodes = [
|
const nonAbattementCodes = [
|
||||||
"maj_chomage","chomage","ags","conges_spectacles",
|
"maj_chomage","chomage","ags","conges_spectacles",
|
||||||
"csg_deductible","csg_imposable","rds",
|
"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
|
// 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"))) {
|
} else if (c.code === "prevoyance_ta" || (c.libelle && c.libelle.includes("Prévoyance"))) {
|
||||||
base = getPrevoyanceBase(brut);
|
base = getPrevoyanceBase(brut);
|
||||||
} else if (["csg_deductible","csg_imposable","rds"].includes(c.code)) {
|
} 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 {
|
} else {
|
||||||
base = brut;
|
base = brut;
|
||||||
}
|
}
|
||||||
|
|
@ -864,7 +990,8 @@ function generateDetailTable(brut, cachets, heures, plafondUrssaf) {
|
||||||
const nonAbattementCodes = [
|
const nonAbattementCodes = [
|
||||||
"maj_chomage","chomage","ags","conges_spectacles",
|
"maj_chomage","chomage","ags","conges_spectacles",
|
||||||
"csg_deductible","csg_imposable","rds",
|
"csg_deductible","csg_imposable","rds",
|
||||||
"prevoyance_ta" // ✅ ne pas abattre la prévoyance
|
"prevoyance_ta",
|
||||||
|
"cet"
|
||||||
];
|
];
|
||||||
const { sal, emp, cat } = getCotisations();
|
const { sal, emp, cat } = getCotisations();
|
||||||
const assietteChomageMax = getAssietteChomageMaxPourMoisCourant();
|
const assietteChomageMax = getAssietteChomageMaxPourMoisCourant();
|
||||||
|
|
@ -907,7 +1034,17 @@ const nonAbattementCodes = [
|
||||||
} else if (c.code === "prevoyance_ta" || (c.libelle && c.libelle.includes("Prévoyance"))) {
|
} else if (c.code === "prevoyance_ta" || (c.libelle && c.libelle.includes("Prévoyance"))) {
|
||||||
base = getPrevoyanceBase(brut);
|
base = getPrevoyanceBase(brut);
|
||||||
} else if (["csg_deductible","csg_imposable","rds"].includes(c.code)) {
|
} 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 {
|
} else {
|
||||||
base = brut;
|
base = brut;
|
||||||
}
|
}
|
||||||
|
|
@ -983,7 +1120,8 @@ function buildContributionsStructured(brut, cachets, heures, plafondUrssaf){
|
||||||
const nonAbattementCodes = [
|
const nonAbattementCodes = [
|
||||||
"maj_chomage","chomage","ags","conges_spectacles",
|
"maj_chomage","chomage","ags","conges_spectacles",
|
||||||
"csg_deductible","csg_imposable","rds",
|
"csg_deductible","csg_imposable","rds",
|
||||||
"prevoyance_ta" // ✅ ne pas abattre la prévoyance
|
"prevoyance_ta",
|
||||||
|
"cet"
|
||||||
];
|
];
|
||||||
const { sal, emp, cat } = getCotisations();
|
const { sal, emp, cat } = getCotisations();
|
||||||
const assietteChomageMax = getAssietteChomageMaxPourMoisCourant();
|
const assietteChomageMax = getAssietteChomageMaxPourMoisCourant();
|
||||||
|
|
@ -1100,7 +1238,17 @@ if (fillonTotal > 0) {
|
||||||
} else if (c.code === "prevoyance_ta" || (c.libelle && c.libelle.includes("Prévoyance"))) {
|
} else if (c.code === "prevoyance_ta" || (c.libelle && c.libelle.includes("Prévoyance"))) {
|
||||||
base = getPrevoyanceBase(brut);
|
base = getPrevoyanceBase(brut);
|
||||||
} else if (["csg_deductible","csg_imposable","rds"].includes(c.code)) {
|
} 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 {
|
} else {
|
||||||
base = brut;
|
base = brut;
|
||||||
}
|
}
|
||||||
|
|
@ -1159,20 +1307,6 @@ document.getElementById('calcBtn').addEventListener('click', function() {
|
||||||
return;
|
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 plafondUrssaf = getPlafondUrssaf();
|
||||||
const type = document.querySelector('input[name="type"]:checked').value;
|
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));
|
({ 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 = `
|
||||||
|
<div class="alert alert-warning" role="alert" style="margin:16px 0">
|
||||||
|
<div style="display:flex; align-items:center; justify-content:space-between; gap:12px;">
|
||||||
|
<div>
|
||||||
|
<strong>Simulation extrême :</strong> le montant saisi est très élevé.\n
|
||||||
|
Nous vous invitons à nous contacter pour confirmer ou valider la simulation.
|
||||||
|
</div>
|
||||||
|
<a href="/support" class="btn btn-sm btn-primary">Contacter le support</a>
|
||||||
|
</div>
|
||||||
|
</div>`;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Ajout du debug chômage
|
||||||
|
const dbg = getChomageDebugInfo();
|
||||||
|
const dbgHtml = dbg ? `
|
||||||
|
<div style="margin-top:12px;padding:8px;border:1px dashed #cbd5e1;border-radius:8px;background:#f8fafc">
|
||||||
|
<div style="font-weight:600;margin-bottom:4px">Debug chômage</div>
|
||||||
|
<div>Branche: <strong>${dbg.branch}</strong> — Jours sélectionnés: <strong>${dbg.nbJours}</strong>, Cachets: <strong>${dbg.cachets}</strong>, Jours retenus chômage: <strong>${dbg.nbJoursChomage}</strong></div>
|
||||||
|
<div>Jours dans le mois: <strong>${dbg.daysInMonth}</strong>, DiffDays (span): <strong>${dbg.diffDays}</strong></div>
|
||||||
|
<div>Assiette chômage max calculée: <strong>${fmtEuro(dbg.assiette)}</strong> €</div>
|
||||||
|
</div>` : '';
|
||||||
|
|
||||||
const resultTable = `
|
const resultTable = `
|
||||||
<table class="result-table">
|
<table class="result-table">
|
||||||
<tr>
|
<tr>
|
||||||
|
|
@ -1205,6 +1452,7 @@ document.getElementById('calcBtn').addEventListener('click', function() {
|
||||||
</tr>
|
</tr>
|
||||||
</table>
|
</table>
|
||||||
<p>Plafond URSSAF : ${fmtEuro(plafondUrssaf)} €</p>
|
<p>Plafond URSSAF : ${fmtEuro(plafondUrssaf)} €</p>
|
||||||
|
${dbgHtml}
|
||||||
`;
|
`;
|
||||||
document.getElementById('result').innerHTML = resultTable;
|
document.getElementById('result').innerHTML = resultTable;
|
||||||
document.getElementById('detailTable').innerHTML = generateDetailTable(brut, cachets, heures, plafondUrssaf);
|
document.getElementById('detailTable').innerHTML = generateDetailTable(brut, cachets, heures, plafondUrssaf);
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue