Merge pull request #894 from betagouv/better-barèmes

⚙️ 🔥 grosse modification des barèmes
branche-desactivée
Johan Girod 2020-02-25 11:30:09 +01:00 committed by GitHub
commit 8e98c998b9
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
52 changed files with 1561 additions and 1840 deletions

View File

@ -144,19 +144,15 @@ impôt sur le revenu:
barème:
assiette: revenu imposable
tranches:
- en-dessous de: 9807
taux: 0%
- de: 9807
à: 27086
taux: 14%
- de: 27086
à: 72617
taux: 30%
- de: 72617
à: 153783
taux: 41%
- au-dessus de: 153783
taux: 45%
- taux: 0%
plafond: 9807
- taux: 14%
plafond: 27086
- taux: 30%
plafond: 72617
- taux: 41%
plafond: 153783
- taux: 45%
```
La syntaxe hiérarchique de Yaml permet d'imbriquer les mécanismes :

View File

@ -95,8 +95,8 @@ artiste-auteur . cotisations . vieillesse:
assiette: assiette
composantes:
- nom: plafonnée
plafond: contrat salarié . plafond sécurité sociale
taux: contrat salarié . vieillesse . taux salarié plafonné - 0.75%
plafond: contrat salarié . plafond sécurité sociale
- nom: non plafonnée
taux: contrat salarié . vieillesse . taux salarié non plafonné - 0.4%
@ -116,8 +116,8 @@ artiste-auteur . cotisations . CSG-CRDS . abattement:
formule:
multiplication:
assiette: revenus . traitements et salaires
plafond: 4 * contrat salarié . plafond sécurité sociale
taux: 1.75%
plafond: 4 * contrat salarié . plafond sécurité sociale
artiste-auteur . cotisations . CSG-CRDS . CSG:
formule:

View File

@ -39,15 +39,13 @@ contrat salarié . convention collective . HCR . majoration heures supplémentai
formule:
barème:
assiette: temps de travail . heures supplémentaires
multiplicateur: 1 heure/semaine * période . semaines par mois
multiplicateur: période . semaines par mois
tranches:
- en-dessous de: 4
taux: 10%
- de: 4
à: 8
taux: 20%
- au-dessus de: 8
taux: 50%
- taux: 10%
plafond: 4 heures/semaine
- taux: 20%
plafond: 8 heures/semaine
- taux: 50%
contrat salarié . convention collective . SVP:
titre: Spectacle vivant privé (beta)
@ -298,28 +296,21 @@ contrat salarié . convention collective . sport . cotisations . assiette forfai
applicable si: assiette franchisée < SMIC horaire * 115 heures/mois
remplace: contrat salarié . cotisations . assiette forfaitaire
formule:
multiplication:
assiette: SMIC horaire
facteur:
barème linéaire:
assiette: assiette franchisée [€/mois]
multiplicateur: SMIC horaire
unité: heures/mois
tranches:
- en-dessous de: 45
montant: 5
- de: 45
à: 60
montant: 15
- de: 60
à: 80
montant: 25
- de: 80
à: 100
montant: 35
- de: 100
à: 115
montant: 50
grille:
assiette: assiette franchisée [€/mois]
multiplicateur: SMIC horaire / 1 mois
unité: €/mois
tranches:
- montant: 5 * SMIC horaire
plafond: 45 heures
- montant: 15 * SMIC horaire
plafond: 60 heures
- montant: 25 * SMIC horaire
plafond: 80 heures
- montant: 35 * SMIC horaire
plafond: 100 heures
- montant: 50 * SMIC horaire
plafond: 115 heures
contrat salarié . convention collective . sport . primes . nombre de manifestations:
question: Combien de manifestations rémunérées le joueur a-t'il effectué ?

View File

@ -182,10 +182,9 @@ dirigeant . auto-entrepreneur . cotisations et contributions . cotisations:
assiette: base des cotisations
multiplicateur: plafond ACRE
tranches:
- en-dessous de: 1
taux: taux de cotisation * (100% - taux ACRE)
- au-dessus de: 1
taux: taux de cotisation
- taux: taux de cotisation * (100% - taux ACRE)
plafond: 1
- taux: taux de cotisation
références:
La protection sociale du micro-entrepreneur: https://bpifrance-creation.fr/encyclopedie/micro-entreprise-regime-auto-entrepreneur/fiscal-social-comptable/protection-sociale
@ -409,14 +408,14 @@ dirigeant . indépendant . cotisations et contributions . exonérations . ACRE .
dirigeant . indépendant . cotisations et contributions . exonérations . ACRE . taux:
formule:
barème continu:
assiette: revenu professionnel
taux progressif:
assiette: assiette des cotisations
multiplicateur: PSS proratisé
points:
0: 100%
0.75: 100%
1: 0%
retourne seulement le taux: oui
tranches:
- taux: 100%
plafond: 75%
- taux: 0%
plafond: 100%
dirigeant . indépendant . revenu net de cotisations:
formule:
@ -430,16 +429,7 @@ dirigeant . indépendant . revenu net de cotisations:
unité par défaut: €/an
dirigeant . indépendant . revenu professionnel:
titre: revenu professionnel (net imposable)
unité par défaut: €/an
description: |
C'est le revenu net de cotisations déductibles du travailleur indépendant, qui sert de base au calcul des cotisations et de l'impôt pour les indépendants.
Attention, **notre calcul est fait au régime de croisière**:
l'indépendant qui se lance paiera pendant ses 2 premières années un forfait relativement réduit de cotisations sociales. Il devra ensuite régulariser cette situation par rapport au revenu qu'il a vraiment perçu.
Il faut donc voir ce calcul comme *le montant qui devra de toute façon être payé* à court terme après 2 ans d'exercice.
formule:
inversion numérique:
avec:
@ -449,6 +439,13 @@ dirigeant . indépendant . revenu professionnel:
- entreprise . chiffre d'affaires
- entreprise . chiffre d'affaires minimum
dirigeant . indépendant . assiette des cotisations:
unité par défaut: €/an
formule:
encadrement:
plancher: 0
valeur: revenu professionnel
dirigeant . indépendant . conjoint collaborateur:
question: Avez-vous un conjoint collaborateur ?
description: |
@ -488,14 +485,14 @@ dirigeant . indépendant . conjoint collaborateur . assiette . revenu avec parta
**Cette option baisse le montant des cotisations à payer pour le gérant, mais elle diminue également ses contreparties sociales (pension de retraite, indemnité décès, etc)**
formule: assiette = 'revenu avec partage'
remplace:
règle: revenu professionnel
par: revenu professionnel - cotisations . assiette
règle: assiette des cotisations
par: assiette des cotisations - cotisations . assiette
dans:
- cotisations et contributions . cotisations . retraite de base
- cotisations et contributions . cotisations . retraite complémentaire
- cotisations et contributions . cotisations . invalidité et décès
dirigeant . indépendant . conjoint collaborateur . assiette . revenu sans partage:
description: Le conjoint collaborateur paiera des cotisations sociales calculées sur une base d'un pourcentage du revenu professionnel du gérant de l'entreprise (un tiers ou la moitié).
description: Le conjoint collaborateur paiera des cotisations sociales calculées sur une base d'un pourcentage du assiette des cotisations du gérant de l'entreprise (un tiers ou la moitié).
formule: assiette = 'revenu sans partage'
dirigeant . indépendant . conjoint collaborateur . assiette . pourcentage:
@ -520,7 +517,7 @@ dirigeant . indépendant . conjoint collaborateur . cotisations . assiette:
titre: assiette conjoint collaborateur
formule:
multiplication:
assiette: revenu professionnel
assiette: assiette des cotisations
taux: 1 / 3
variations:
- si: assiette . forfaitaire
@ -553,21 +550,19 @@ dirigeant . indépendant . conjoint collaborateur . cotisations . retraite de ba
assiette: assiette retraite
multiplicateur: plafond sécurité sociale temps plein
tranches:
- en-dessous de: 1
taux: 17.75%
- au-dessus de: 1
taux: 0.6%
- taux: 17.75%
plafond: 1
- taux: 0.6%
dirigeant . indépendant . conjoint collaborateur . cotisations . retraite complémentaire:
formule:
barème:
assiette: retraite complémentaire . assiette [€/an]
assiette: retraite complémentaire . assiette
tranches:
- en-dessous de: 37960
taux: 7%
- de: 37960
à: 162096
taux: 8%
- taux: 7%
plafond: cotisations et contributions . cotisations . retraite complémentaire . plafond
- taux: 8%
plafond: 4 * plafond sécurité sociale temps plein
dirigeant . indépendant . conjoint collaborateur . cotisations . retraite complémentaire . assiette:
titre: assiette retraite complémentaire
@ -589,8 +584,8 @@ dirigeant . indépendant . conjoint collaborateur . cotisations . invalidité et
formule:
multiplication:
assiette: assiette
plafond: plafond sécurité sociale temps plein
taux: 1.3%
plafond: plafond sécurité sociale temps plein
dirigeant . indépendant . conjoint collaborateur . cotisations . indemnités journalières maladie:
formule:
@ -634,16 +629,19 @@ dirigeant . indépendant . cotisations et contributions . cotisations . déducti
par défaut: 0
dirigeant . indépendant . cotisations et contributions . cotisations . déduction tabac . revenus déduits:
titre: revenu professionnel (avec déduction tabac)
titre: assiette des cotisations (avec déduction tabac)
applicable si: déduction tabac
remplace:
règle: revenu professionnel
règle: assiette des cotisations
dans:
- retraite de base
- retraite complémentaire
- invalidité et décès
- conjoint collaborateur
formule: revenu professionnel - déduction tabac
formule:
allègement:
assiette: assiette des cotisations
abattement: déduction tabac
dirigeant . rattachement CIPAV:
description: |
@ -686,12 +684,17 @@ dirigeant . indépendant . cotisations et contributions . cotisations . maladie
remplace: maladie
rend non applicable: indemnités journalières maladie
formule:
barème continu:
assiette: indépendant . revenu professionnel
multiplicateur: plafond sécurité sociale temps plein
points:
0: 1.5%
1.1: 6.5%
multiplication:
assiette: indépendant . assiette des cotisations
taux:
taux progressif:
assiette: indépendant . assiette des cotisations
multiplicateur: plafond sécurité sociale temps plein
tranches:
- plafond: 0%
taux: 1.5%
- plafond: 110%
taux: 6.5%
références:
secu-independants.fr: https://www.secu-independants.fr/cotisations/calcul-des-cotisations/taux-de-cotisations
guide urssaf (pdf): https://www.urssaf.fr/portail/files/live/sites/urssaf/files/documents/Guide-Professions-liberales.pdf
@ -702,11 +705,11 @@ dirigeant . indépendant . cotisations et contributions . cotisations . maladie
formule:
variations:
- si: situation personnelle . RSA
alors: revenu professionnel
alors: assiette des cotisations
- sinon:
encadrement:
plancher: 40% * plafond sécurité sociale temps plein
valeur: revenu professionnel
valeur: assiette des cotisations
références:
secu-independants.fr: https://www.secu-independants.fr/cotisations/calcul-des-cotisations/cotisations-minimales/
@ -725,10 +728,9 @@ dirigeant . indépendant . cotisations et contributions . cotisations . maladie:
assiette: assiette
multiplicateur: plafond sécurité sociale temps plein
tranches:
- en-dessous de: 5
taux: taux variable
- au-dessus de: 5
taux: 6.5%
- taux: taux variable
plafond: 5
- taux: 6.5%
références:
décret formule de calcul: https://www.legifrance.gouv.fr/affichTexte.do?cidTexte=JORFTEXT000036342439&categorieLien=id
note: |
@ -757,7 +759,7 @@ dirigeant . indépendant . cotisations et contributions . cotisations . maladie
formule:
multiplication:
assiette: 5%
taux: revenu professionnel / seuil supérieur de réduction
taux: assiette des cotisations / seuil supérieur de réduction
dirigeant . indépendant . cotisations et contributions . cotisations . maladie . seuil supérieur de réduction:
formule:
@ -767,14 +769,16 @@ dirigeant . indépendant . cotisations et contributions . cotisations . maladie
dirigeant . indépendant . cotisations et contributions . cotisations . maladie . taux:
formule:
barème continu:
retourne seulement le taux: oui
assiette: revenu professionnel
taux progressif:
assiette: assiette des cotisations
multiplicateur: plafond sécurité sociale temps plein
points:
0: 0%
0.4: 3.168%
1.1: 6.35%
tranches:
- plafond: 0%
taux: 0%
- plafond: 40%
taux: 3.168%
- plafond: 110%
taux: 6.35%
note: |
Cette cotisation est à la base très simple : la cotisation maladie est de 7.2% jusqu'à 5 fois le plafond de la sécurité sociale, puis 6.5% au-delà. Cependant :
- pour son recouvrement, cette cotisation est séparée en 2 : maladie et maladie 2, cette deuxième au taux de 0.85%. Ainsi le montant de 7.2% cité dans le décret est rabaissé à 6.35% dans nos calculs.
@ -791,11 +795,11 @@ dirigeant . rattachement CIPAV . retraite de base:
multiplicateur:
composantes:
- nom: tranche 1
plafond: plafond sécurité sociale temps plein
taux: 8.23%
plafond: plafond sécurité sociale temps plein
- nom: tranche 2
plafond: 5 * plafond sécurité sociale temps plein
taux: 1.87%
plafond: 5 * plafond sécurité sociale temps plein
dirigeant . indépendant . cotisations et contributions . cotisations . retraite de base:
formule:
@ -803,21 +807,20 @@ dirigeant . indépendant . cotisations et contributions . cotisations . retraite
assiette: assiette
multiplicateur: plafond sécurité sociale temps plein
tranches:
- en-dessous de: 1
taux: 17.75%
- au-dessus de: 1
taux: 0.6%
- taux: 17.75%
plafond: 1
- taux: 0.6%
dirigeant . indépendant . cotisations et contributions . cotisations . retraite de base . assiette:
titre: assiette retraite de base
formule:
variations:
- si: situation personnelle . RSA
alors: revenu professionnel
alors: assiette des cotisations
- sinon:
encadrement:
plancher: 11.5% * plafond sécurité sociale temps plein
valeur: revenu professionnel
valeur: assiette des cotisations
références:
secu-independants.fr: https://www.secu-independants.fr/cotisations/calcul-des-cotisations/cotisations-minimales/
@ -825,31 +828,25 @@ dirigeant . indépendant . cotisations et contributions . cotisations . retraite
dirigeant . rattachement CIPAV . retraite complémentaire:
remplace: indépendant . cotisations et contributions . cotisations . retraite complémentaire
formule:
barème linéaire:
assiette: indépendant . revenu professionnel [€/an]
grille:
assiette: indépendant . assiette des cotisations
unité: €/an
tranches:
- en-dessous de: 26580
montant: 1315
- de: 26581
à: 49280
montant: 2630
- de: 49281
à: 57850
montant: 3945
- de: 57851
à: 66400
montant: 6575
- de: 66401
à: 83060
montant: 9205
- de: 83061
à: 103180
montant: 14465
- de: 103181
à: 123300
montant: 15780
- au-dessus de: 123300
montant: 17095
- montant: 1315
plafond: 26581 €/an
- montant: 2630
plafond: 49281 €/an
- montant: 3945
plafond: 57851 €/an
- montant: 6575
plafond: 66401 €/an
- montant: 9205
plafond: 83061 €/an
- montant: 14465
plafond: 103181 €/an
- montant: 15780
plafond: 123301 €/an
- montant: 17095
dirigeant . indépendant . cotisations et contributions . cotisations . retraite complémentaire . taux spécifique PLNR:
applicable si:
@ -869,30 +866,35 @@ dirigeant . indépendant . cotisations et contributions . cotisations . retraite
remplace: retraite complémentaire
formule:
barème:
assiette: revenu professionnel
assiette: assiette des cotisations
multiplicateur: plafond sécurité sociale temps plein
tranches:
- de: 1
à: 4
taux: 14%
- taux: 0%
plafond: 1
- taux: 14%
plafond: 4
dirigeant . indépendant . cotisations et contributions . cotisations . retraite complémentaire:
formule:
barème:
assiette: revenu professionnel [€/an]
assiette: assiette des cotisations
tranches:
- en-dessous de: 37960
taux: 7%
- de: 37960
à: 162096
taux: 8%
- taux: 7%
plafond: plafond
- taux: 8%
plafond: 4 * plafond sécurité sociale temps plein
dirigeant . indépendant . cotisations et contributions . cotisations . retraite complémentaire . plafond:
acronyme: PRCI
titre: plafond retraite complémentaire des indépendants
formule: 38340 €/an
dirigeant . indépendant . cotisations et contributions . cotisations . invalidité et décès:
formule:
multiplication:
assiette: assiette
plafond: plafond sécurité sociale temps plein
taux: 1.3%
plafond: plafond sécurité sociale temps plein
# TODO invalidité décès pour les libéraux
# 3 classes, 76, 228, 380€
@ -901,10 +903,10 @@ dirigeant . indépendant . cotisations et contributions . cotisations . invalidi
formule:
variations:
- si: situation personnelle . RSA
alors: revenu professionnel
alors: assiette des cotisations
- sinon:
encadrement:
valeur: revenu professionnel
valeur: assiette des cotisations
plancher: 11.5% * plafond sécurité sociale temps plein
références:
secu-independants.fr: https://www.secu-independants.fr/cotisations/calcul-des-cotisations/cotisations-minimales/
@ -980,13 +982,17 @@ dirigeant . indépendant . cotisations et contributions . formation professionne
dirigeant . indépendant . cotisations et contributions . cotisations . allocations familiales:
formule:
barème continu:
assiette: revenu professionnel
multiplicateur: plafond sécurité sociale temps plein
points:
0: 0%
1.1: 0%
1.4: 3.1%
multiplication:
assiette: assiette des cotisations
taux:
taux progressif:
assiette: assiette des cotisations
multiplicateur: plafond sécurité sociale temps plein
tranches:
- plafond: 110%
taux: 0%
- plafond: 140%
taux: 3.1%
dirigeant . indépendant . cotisations et contributions . exonérations:
période: flexible
@ -1001,8 +1007,8 @@ dirigeant . indépendant . cotisations et contributions . exonérations . ZFU:
multiplication:
assiette: cotisations . maladie
# TODO : ceci n'est pas bon (le plafond est sur le revenu exonéré, et est proratisé en début / fin d'éxo)
plafond: 3042 heures/an * SMIC horaire
taux: taux
plafond: 3042 heures/an * SMIC horaire
dirigeant . indépendant . cotisations et contributions . exonérations . âge:
question: Bénéficiez-vous du dispositif d'exonération "âge"
@ -1024,30 +1030,41 @@ dirigeant . indépendant . cotisations et contributions . exonérations . invali
dirigeant . indépendant . cotisations et contributions . exonérations . ZFU . taux:
titre: taux exonération ZFU
formule:
barème continu:
taux progressif:
assiette: établissement . ZFU . durée d'implantation en fin d'année [an]
retourne seulement le taux: oui
variations:
- si: entreprise . effectif < 5
alors:
points:
0: 100%
5: 100%
6: 60%
10: 60%
11: 40%
12: 40%
13: 20%
14: 20%
15: 0%
tranches:
- plafond: 5 ans
taux: 100%
- plafond: 6 ans
taux: 60%
- plafond: 10 ans
taux: 60%
- plafond: 11 ans
taux: 40%
- plafond: 12 ans
taux: 40%
- plafond: 13 ans
taux: 20%
- plafond: 14 ans
taux: 20%
- plafond: 15 ans
taux: 0%
- sinon:
points:
0: 100%
5: 100%
6: 60%
7: 40%
8: 20%
9: 0%
tranches:
- plafond: 5 ans
taux: 100%
- plafond: 6 ans
taux: 60%
- plafond: 7 ans
taux: 40%
- plafond: 8 ans
taux: 20%
- plafond: 9 ans
taux: 0%
dirigeant . indépendant . cotisations et contributions . cotisations . maladie domiciliation fiscale étranger:
applicable si: situation personnelle . domiciliation fiscale à l'étranger

View File

@ -83,15 +83,13 @@ entreprise . impôt sur les sociétés:
unité: €/an
formule:
barème:
assiette: bénéfice [€/an]
assiette: bénéfice
tranches:
- en-dessous de: 38120
taux: 15%
- de: 38120
à: 500000
taux: 28%
- au-dessus de: 500000
taux: 33.3%
- taux: 15%
plafond: 38120 €/an
- taux: 28%
plafond: 500000 €/an
- taux: 33.3%
références:
fiche service-public.fr: https://www.service-public.fr/professionnels-entreprises/vosdroits/F23575
@ -279,8 +277,8 @@ entreprise . taxe sur les salaires:
assiette: barème
franchise: 1200 €/an
décote:
plafond: 2040 €/an
taux: 75%
plafond: 2040 €/an
abattement: abattement associations
entreprise . catégorie d'activité:

View File

@ -128,21 +128,17 @@ impôt . impôt sur le revenu:
unité par défaut: €/an
formule:
barème:
assiette: revenu abattu [€/an]
assiette: revenu abattu
tranches:
- en-dessous de: 10064
taux: 0%
- de: 10064
à: 25659
taux: 11%
- de: 25659
à: 73369
taux: 30%
- de: 73369
à: 157806
taux: 41%
- au-dessus de: 157806
taux: 45%
- taux: 0%
plafond: 10064 €/an
- taux: 11%
plafond: 25659 €/an
- taux: 30%
plafond: 73369 €/an
- taux: 41%
plafond: 157806 €/an
- taux: 45%
exemples:
- nom: Haut salaire de ~ 10 000€ mensuels
situation:
@ -158,8 +154,8 @@ impôt . impôt sur le revenu à payer:
allègement:
assiette: impôt sur le revenu
décote:
plafond: 1717 €/an
taux: 45.25%
plafond: 1717 €/an
exemples:
- nom: Salaire d'un cadre
situation:
@ -185,15 +181,13 @@ impôt . CEHR:
note: Attention, ce barème concerne les foyers célibataires. Pour les couples, le barème est adapté pour ne pas leur appliquer la même imposition alors qu'ils sont individuellement deux fois moins riches.
formule:
barème:
assiette: revenu fiscal de référence [€/an]
assiette: revenu fiscal de référence
tranches:
- en-dessous de: 250000
taux: 0%
- de: 250000
à: 500000
taux: 3%
- au-dessus de: 500000
taux: 4%
- taux: 0%
plafond: 250000 €/an
- taux: 3%
plafond: 500000 €/an
- taux: 4%
références:
contribution exceptionnelle sur les hauts revenus: https://www.service-public.fr/particuliers/vosdroits/F31130
Article 223 sexies du Code général des impôts: https://www.legifrance.gouv.fr/affichCode.do?idSectionTA=LEGISCTA000025049019&cidTexte=LEGITEXT000006069577
@ -202,174 +196,94 @@ impôt . CEHR:
impôt . taux neutre d'impôt sur le revenu . barème Guadeloupe Réunion Martinique:
icônes: 🇬🇵🇷🇪 🇲🇶
formule:
barème linéaire:
assiette: revenu imposable [€/mois]
retourne seulement le taux: oui
grille:
assiette: revenu imposable
tranches:
- de: 0
à: 1625
taux: 0%
- de: 1626
à: 1723
taux: 0.5%
- de: 1724
à: 1899
taux: 1.3%
- de: 1900
à: 2074
taux: 2.1%
- de: 2075
à: 2291
taux: 2.9%
- de: 2292
à: 2416
taux: 3.5%
- de: 2417
à: 2499
taux: 4.1%
- de: 2500
à: 2749
taux: 5.3%
- de: 2750
à: 3399
taux: 7.5%
- de: 3400
à: 4349
taux: 9.9%
- de: 4350
à: 4941
taux: 11.9%
- de: 4942
à: 5724
taux: 13.8%
- de: 5725
à: 6857
taux: 15.8%
- de: 6858
à: 7624
taux: 17.9%
- de: 7625
à: 8666
taux: 20%
- de: 8667
à: 11916
taux: 24%
- de: 11917
à: 15832
taux: 28%
- de: 15833
à: 24166
taux: 33%
- de: 24167
à: 52824
taux: 38%
- au-dessus de: 52825
taux: 43%
- montant: 0%
plafond: 1626 €/mois
- montant: 0.5%
plafond: 1724 €/mois
- montant: 1.3%
plafond: 1900 €/mois
- montant: 2.1%
plafond: 2075 €/mois
- montant: 2.9%
plafond: 2292 €/mois
- montant: 3.5%
plafond: 2417 €/mois
- montant: 4.1%
plafond: 2500 €/mois
- montant: 5.3%
plafond: 2750 €/mois
- montant: 7.5%
plafond: 3400 €/mois
- montant: 9.9%
plafond: 4350 €/mois
- montant: 11.9%
plafond: 4942 €/mois
- montant: 13.8%
plafond: 5725 €/mois
- montant: 15.8%
plafond: 6858 €/mois
- montant: 17.9%
plafond: 7625 €/mois
- montant: 20%
plafond: 8667 €/mois
- montant: 24%
plafond: 11917 €/mois
- montant: 28%
plafond: 15833 €/mois
- montant: 33%
plafond: 24167 €/mois
- montant: 38%
plafond: 52825 €/mois
- montant: 43%
impôt . taux neutre d'impôt sur le revenu . barème Guyane Mayotte:
icônes: 🇬🇾 🇾🇹
formule:
barème linéaire:
assiette: revenu imposable [€/mois]
retourne seulement le taux: oui
grille:
assiette: revenu imposable
tranches:
- de: 0
à: 1740
taux: 0%
- de: 1741
à: 1882
taux: 0.5%
- de: 1883
à: 2099
taux: 1.3%
- de: 2100
à: 2366
taux: 2.1%
- de: 2367
à: 2457
taux: 2.9%
- de: 2458
à: 2541
taux: 3.5%
- de: 2542
à: 2624
taux: 4.1%
- de: 2625
à: 2916
taux: 5.3%
- de: 2917
à: 4024
taux: 7.5%
- de: 4025
à: 5207
taux: 9.9%
- de: 5208
à: 5874
taux: 11.9%
- de: 5875
à: 6816
taux: 13.8%
- de: 6817
à: 7499
taux: 15.8%
- de: 7500
à: 8307
taux: 17.9%
- de: 8308
à: 9641
taux: 20%
- de: 9642
à: 12970
taux: 24%
- de: 12971
à: 16499
taux: 28%
- de: 16500
à: 26442
taux: 33%
- de: 26443
à: 55814
taux: 38%
- au-dessus de: 55815
taux: 43%
- montant: 0%
plafond: 1740 €/mois
- montant: 0.5%
plafond: 1883 €/mois
- montant: 1.3%
plafond: 2100 €/mois
- montant: 2.1%
plafond: 2367 €/mois
- montant: 2.9%
plafond: 2458 €/mois
- montant: 3.5%
plafond: 2542 €/mois
- montant: 4.1%
plafond: 2625 €/mois
- montant: 5.3%
plafond: 2917 €/mois
- montant: 7.5%
plafond: 4025 €/mois
- montant: 9.9%
plafond: 5208 €/mois
- montant: 11.9%
plafond: 5875 €/mois
- montant: 13.8%
plafond: 6817 €/mois
- montant: 15.8%
plafond: 7500 €/mois
- montant: 17.9%
plafond: 8308 €/mois
- montant: 20%
plafond: 9642 €/mois
- montant: 24%
plafond: 12971 €/mois
- montant: 28%
plafond: 16500 €/mois
- montant: 33%
plafond: 26443 €/mois
- montant: 38%
plafond: 55815 €/mois
- montant: 43%
impôt . taux neutre d'impôt sur le revenu:
description: >
@ -386,89 +300,48 @@ impôt . taux neutre d'impôt sur le revenu:
- établissement . localisation . département = 'Mayotte'
alors: barème Guyane Mayotte
- sinon:
barème linéaire:
assiette: revenu imposable [€/mois]
retourne seulement le taux: oui
grille:
assiette: revenu imposable
tranches:
- de: 0
à: 1417
taux: 0%
- de: 1418
à: 1471
taux: 0.5%
- de: 1472
à: 1566
taux: 1.3%
- de: 1567
à: 1672
taux: 2.1%
- de: 1673
à: 1786
taux: 2.9%
- de: 1787
à: 1882
taux: 3.5%
- de: 1883
à: 2007
taux: 4.1%
- de: 2008
à: 2375
taux: 5.3%
- de: 2376
à: 2719
taux: 7.5%
- de: 2720
à: 3097
taux: 9.9%
- de: 3098
à: 3486
taux: 11.9%
- de: 3487
à: 4068
taux: 13.8%
- de: 4069
à: 4877
taux: 15.8%
- de: 4878
à: 6103
taux: 17.9%
- de: 6104
à: 7624
taux: 20%
- de: 7625
à: 10582
taux: 24%
- de: 10583
à: 14332
taux: 28%
- de: 14333
à: 22499
taux: 33%
- de: 22500
à: 48195
taux: 38%
- au-dessus de: 48196
taux: 43%
- montant: 0%
plafond: 1418 €/mois
- montant: 0.5%
plafond: 1472 €/mois
- montant: 1.3%
plafond: 1567 €/mois
- montant: 2.1%
plafond: 1673 €/mois
- montant: 2.9%
plafond: 1787 €/mois
- montant: 3.5%
plafond: 1883 €/mois
- montant: 4.1%
plafond: 2008 €/mois
- montant: 5.3%
plafond: 2376 €/mois
- montant: 7.5%
plafond: 2720 €/mois
- montant: 9.9%
plafond: 3098 €/mois
- montant: 11.9%
plafond: 3487 €/mois
- montant: 13.8%
plafond: 4069 €/mois
- montant: 15.8%
plafond: 4878 €/mois
- montant: 17.9%
plafond: 6104 €/mois
- montant: 20%
plafond: 7625 €/mois
- montant: 24%
plafond: 10583 €/mois
- montant: 28%
plafond: 14333 €/mois
- montant: 33%
plafond: 22500 €/mois
- montant: 38%
plafond: 48196 €/mois
- montant: 43%
références:
Explication de l'impôt neutre: https://www.economie.gouv.fr/prelevement-a-la-source/taux-prelevement#taux-non-personnalise
BOFIP: http://bofip.impots.gouv.fr/bofip/11255-PGP.html

View File

@ -49,8 +49,8 @@ protection sociale . retraite . base:
titre: pension de retraite de base
formule:
multiplication:
plafond: plafond sécurité sociale temps plein
taux: taux de la pension
plafond: plafond sécurité sociale temps plein
assiette: revenu moyen
note: Les impatriés bénéficient d'une exonération de cotisation vieillesse. En contrepartie, ils n'acquièrent aucun droit pendant la durée d'exonération.
références:
@ -98,24 +98,20 @@ protection sociale . retraite . trimestres validés par an . trimestres indépen
protection sociale . retraite . trimestres validés par an . barème trimestres générique:
unité: trimestres validés/an
formule:
barème linéaire:
grille:
unité: trimestres validés/an
assiette: revenu moyen [€/an]
assiette: revenu moyen
multiplicateur: SMIC horaire
tranches:
- en-dessous de: 150
montant: 0
- de: 150
à: 300
montant: 1
- de: 300
à: 450
montant: 2
- de: 450
à: 600
montant: 3
- au-dessus de: 600
montant: 4
- montant: 0
plafond: 150 heures/an
- montant: 1
plafond: 300 heures/an
- montant: 2
plafond: 450 heures/an
- montant: 3
plafond: 600 heures/an
- montant: 4
références:
cnav.fr: https://www.legislation.cnav.fr/Pages/bareme.aspx?Nom=salaire_validant_un_trimestre_montant_bar
@ -127,63 +123,51 @@ protection sociale . retraite . trimestres validés par an . trimestres auto-ent
variations:
- si: entreprise . catégorie d'activité = 'libérale'
alors:
barème linéaire:
grille:
unité: trimestres validés/an
assiette: entreprise . chiffre d'affaires [€/an]
assiette: entreprise . chiffre d'affaires
tranches:
- en-dessous de: 2880
montant: 0
- de: 2880
à: 5062
montant: 1
- de: 5062
à: 7266
montant: 2
- de: 7266
à: 9675
montant: 3
- au-dessus de: 9675
montant: 4
- montant: 0
plafond: 2880 €/an
- montant: 1
plafond: 5062 €/an
- montant: 2
plafond: 7266 €/an
- montant: 3
plafond: 9675 €/an
- montant: 4
- si:
une de ces conditions:
- entreprise . catégorie d'activité . service ou vente = 'vente'
- entreprise . catégorie d'activité . restauration ou hébergement
alors:
barème linéaire:
grille:
unité: trimestres validés/an
assiette: entreprise . chiffre d'affaires [€/an]
assiette: entreprise . chiffre d'affaires
tranches:
- en-dessous de: 4137
montant: 0
- de: 4137
à: 7286
montant: 1
- de: 7286
à: 10426
montant: 2
- de: 10426
à: 20740
montant: 3
- au-dessus de: 20740
montant: 4
- montant: 0
plafond: 4137 €/an
- montant: 1
plafond: 7286 €/an
- montant: 2
plafond: 10426 €/an
- montant: 3
plafond: 20740 €/an
- montant: 4
- sinon:
barème linéaire:
grille:
unité: trimestres validés/an
assiette: entreprise . chiffre d'affaires [€/an]
assiette: entreprise . chiffre d'affaires
tranches:
- en-dessous de: 2412
montant: 0
- de: 2412
à: 4239
montant: 1
- de: 4239
à: 6071
montant: 2
- de: 6071
à: 12030
montant: 3
- au-dessus de: 12030
montant: 4
- montant: 0
plafond: 2412 €/an
- montant: 1
plafond: 4239 €/an
- montant: 2
plafond: 6071 €/an
- montant: 3
plafond: 12030 €/an
- montant: 4
références:
service-public.fr: https://www.service-public.fr/professionnels-entreprises/vosdroits/F23369
@ -334,8 +318,8 @@ protection sociale . santé . indemnités journalières . salarié:
formule:
multiplication:
assiette: revenu moyen [€/jour]
plafond: 1.8 * contrat salarié . SMIC temps plein [€/jour]
taux: 50%
plafond: 1.8 * contrat salarié . SMIC temps plein [€/jour]
reférences:
service-public.fr: https://www.service-public.fr/particuliers/vosdroits/F3053

View File

@ -255,38 +255,46 @@ contrat salarié . CDD . CPF:
valeur attendue: null
contrat salarié . CDD . compensation pour congés non pris:
titre: indemnité de congés payés
indemnité:
destinataire: salarié
dû par: employeur
description: |
Le salarié en CDD bénéficie des mêmes droits à congés payés que le salarié en CDI. Il acquiert et prend ses congés payés dans les mêmes conditions.
description: >-
Le salarié en CDD bénéficie des mêmes droits à congés payés que le salarié
en CDI. Il acquiert et prend ses congés payés dans les mêmes conditions.
Il est cependant courant que le salarié ne puisse pas prendre tous ses
congés avant le terme de son contrat, il bénéficie alors d'une indemnité
compensatrice de congés payés versée par l'employeur.
Il existe deux méthodes pour calculer l'indemnité de congés non pris.
### Méthode "du dixième"
Ce mode de calcul sera le plus souvent favorable au salarié lorsque celui-ci
a accompli des heures supplémentaires. Une indemnité égale au dixième de la
rémunération brute totale perçue par le salarié au cours de la période de
référence.
### Méthode "maintien du salaire"
Cette méthode sera le plus souvent favorable au salarié lorsque celui-ci a
bénéficié dune augmentation de salaire.
Pour effectuer le calcul, l'employeur peut tenir compte soit :
- de l'horaire réel du mois,
- du nombre moyen de jours ouvrables (ou ouvrés),
- du nombre réel de jours ouvrables (ou ouvrés).
Il est cependant courant que le salarié ne puisse pas prendre tous ses congés avant le terme de son contrat, il bénéficie alors d'une indemnité compensatrice de congés payés versée par l'employeur.
unité: €/mois
non applicable si:
une de ces conditions:
- événement . poursuite du CDD en CDI
# TODO Y a-t-il d'autres conditions ? Sinon supprimer la liste
non applicable si: événement . poursuite du CDD en CDI
formule:
le maximum de:
- description: Méthode "du dixième"
note: |
Ce mode de calcul sera le plus souvent favorable au salarié lorsque celui-ci a accompli des heures supplémentaires.
> Une indemnité égale au dixième de la rémunération brute totale perçue par le salarié au cours de la période de référence.
multiplication:
- multiplication:
assiette: assiette mensuelle
taux: 10%
facteur: proportion congés non pris
- description: Méthode "maintien du salaire"
note: |
Cette méthode sera le plus souvent favorable au salarié lorsque celui-ci a bénéficié dune augmentation de salaire.
> Pour effectuer le calcul, l'employeur peut tenir compte soit :
- de l'horaire réel du mois,
- du nombre moyen de jours ouvrables (ou ouvrés),
- du nombre réel de jours ouvrables (ou ouvrés).
référence: https://www.service-public.fr/particuliers/vosdroits/F33359
multiplication:
- multiplication:
assiette: salaire journalier
facteur: congés non pris / durée contrat
exemples:
@ -316,13 +324,14 @@ contrat salarié . CDD . compensation pour congés non pris:
note: |
L'indemnité est versée à la fin du contrat, sauf si le CDD se poursuit par un CDI.
À noter, la loi El Khomri modifie l'article L3141-12:
- avant : Les congés peuvent être pris dès l'ouverture des droits [...]
- maintenant : Les congés peuvent être pris dès lembauche [...]
- avant : Les congés peuvent être pris dès l'ouverture des droits
- maintenant : Les congés peuvent être pris dès lembauche
références:
Fiche service-public.gouv.fr: https://www.service-public.fr/particuliers/vosdroits/F2931
Code du travail - Article L3141-24: https://www.legifrance.gouv.fr/affichCodeArticle.do?cidTexte=LEGITEXT000006072050&idArticle=LEGIARTI000006902661&dateTexte=&categorieLien=cid
Congés payés et contrat CDD: https://www.easycdd.com/LEGISLATION-CDD/L-embauche-le-suivi-du-contrat-CDD-les-incidents-frequents/Conges-payes-et-contrat-CDD
assiette de l'indemnité, circulaire DRT 18 du 30 octobre 1990: http://conseillerdusalarie.free.fr/Docs/TextesFrance/19901030Circulaire_DRT_90_18_du_30_octobre_1990_CDD_Travail_temporaire.htm
Méthode du maintien de salaire: https://www.service-public.fr/particuliers/vosdroits/F33359
contrat salarié . CDD . compensation pour congés non pris . proportion congés non pris:
unité: '%'
@ -1449,14 +1458,14 @@ contrat salarié . réduction ACRE:
contrat salarié . réduction ACRE . taux:
titre: taux ACRE
formule:
barème continu:
taux progressif:
assiette: cotisations . assiette
multiplicateur: plafond sécurité sociale temps plein
points:
0: 100%
0.75: 100%
1: 0%
retourne seulement le taux: oui
tranches:
- plafond: 75%
taux: 100%
- plafond: 100%
taux: 0%
contrat salarié . cotisations . salariales . réduction heures supplémentaires:
cotisation:
@ -1639,12 +1648,11 @@ contrat salarié . temps de travail . heures supplémentaires . majoration:
formule:
barème:
assiette: heures supplémentaires
multiplicateur: 1 heure/semaine * période . semaines par mois
multiplicateur: période . semaines par mois
tranches:
- en-dessous de: 8
taux: 25%
- au-dessus de: 8
taux: 50%
- taux: 25%
plafond: 8 heures/semaine
- taux: 50%
contrat salarié . temps de travail . heures complémentaires:
description: >
@ -1848,21 +1856,19 @@ contrat salarié . contribution d'équilibre général:
- attributs:
dû par: employeur
tranches:
- en-dessous de: 1
taux: 1.29%
- de: 1
à: 8
taux: 1.62%
- taux: 1.29%
plafond: 1
- taux: 1.62%
plafond: 8
- attributs:
dû par: salarié
assiette: cotisations . assiette . salariale
tranches:
- en-dessous de: 1
taux: 0.86%
- de: 1
à: 8
taux: 1.08%
- taux: 0.86%
plafond: 1
- taux: 1.08%
plafond: 8
références:
calcul des cotisations: https://www.agirc-arrco.fr/ce-qui-change-au-1er-janvier-2019/vous-etes-une-entreprise-tiers-declarant/
@ -1902,20 +1908,18 @@ contrat salarié . retraite complémentaire:
- attributs:
dû par: employeur
tranches:
- en-dessous de: 1
taux: taux employeur tranche 1
- de: 1
à: 8
taux: taux employeur tranche 2
- taux: taux employeur tranche 1
plafond: 1
- taux: taux employeur tranche 2
plafond: 8
- attributs:
dû par: salarié
assiette: cotisations . assiette . salariale
tranches:
- en-dessous de: 1
taux: taux salarié tranche 1
- de: 1
à: 8
taux: taux salarié tranche 2
- taux: taux salarié tranche 1
plafond: 1
- taux: taux salarié tranche 2
plafond: 8
références:
calcul des cotisations: https://www.agirc-arrco.fr/ce-qui-change-au-1er-janvier-2019/vous-etes-une-entreprise-tiers-declarant/
@ -1941,8 +1945,8 @@ contrat salarié . AGS:
formule:
multiplication:
assiette: cotisations . assiette
plafond: 4 * plafond sécurité sociale
taux: 0.15%
plafond: 4 * plafond sécurité sociale
contrat salarié . allocations familiales:
cotisation:
@ -2170,14 +2174,13 @@ contrat salarié . assiette CSG et CRDS:
contrat salarié . assiette CSG et CRDS . assiette abattue:
formule:
barème:
assiette: cotisations . assiette [€/mois]
assiette: cotisations . assiette
multiplicateur: plafond sécurité sociale
# c'est en fait un abattement de 1,75% sur la partie en-dessous de 4 fois le plafond
tranches:
- en-dessous de: 4
taux: 98.25%
- au-dessus de: 4
taux: 100%
- taux: 98.25%
plafond: 4
- taux: 100%
contrat salarié . CSG . assiette heures supplémentaires et complémentaires défiscalisées:
formule:
@ -2410,8 +2413,8 @@ contrat salarié . prévoyance obligatoire cadre:
formule:
multiplication:
assiette: cotisations . assiette
plafond: plafond sécurité sociale
taux: 1.5%
plafond: plafond sécurité sociale
# TODO attention : il semblerait que ce 1.5% englobe aussi la complémentaire santé ! La confusion serait entretenue par les organismes de prévoyance...
contrat salarié . taxe d'apprentissage:
@ -2528,15 +2531,13 @@ contrat salarié . taxe sur les salaires . barème:
formule:
barème:
assiette: assiette [€/an]
assiette: assiette
tranches:
- en-dessous de: 7799
taux: 4.25%
- de: 7799
à: 15572
taux: 8.5%
- au-dessus de: 15572
taux: 13.6%
- taux: 4.25%
plafond: 7799 €/an
- taux: 8.5%
plafond: 15572 €/an
- taux: 13.6%
exemples:
- nom: salaire médian
situation:
@ -2641,16 +2642,16 @@ contrat salarié . vieillesse:
taux: taux salarié non plafonné
- nom: plafonnée
plafond: plafond sécurité sociale
taux: taux salarié plafonné
plafond: plafond sécurité sociale
- attributs:
dû par: employeur
composantes:
- nom: non plafonnée
taux: taux employeur non plafonné
- nom: plafonnée
plafond: plafond sécurité sociale
taux: taux employeur plafonné
plafond: plafond sécurité sociale
exemples:
- nom: SMIC

View File

@ -89,7 +89,7 @@ export default function CurrencyInput({
}
onValueChange={({ value }) => {
setCurrentValue(value)
nextValue.current = value.toString().replace(/^-/, '')
nextValue.current = value.toString().replace('-', '')
}}
onChange={handleChange}
value={currentValue.toString().replace('.', decimalSeparator)}

View File

@ -62,11 +62,11 @@ function integerAndDecimalParts(value: number) {
// returned values is always 100. For instance: [60, 30, 10].
export function roundedPercentages(values: Array<number>) {
const sum = (a: number = 0, b: number) => a + b
const total = values.reduce(sum)
const total = values.reduce(sum, 0)
const percentages = values.map(value =>
integerAndDecimalParts((value / total) * 100)
)
const totalRoundedPercentage = percentages.map(v => v.integer).reduce(sum)
const totalRoundedPercentage = percentages.map(v => v.integer).reduce(sum, 0)
const indexesToIncrement = percentages
.map((percentage, index) => ({ ...percentage, index }))
.sort((a, b) => b.decimal - a.decimal)

View File

@ -19,7 +19,7 @@ let Conditions = ({
parentDependency.nodeValue === false && (
<ShowIfDisabled
dependency={parentDependency}
key="parent dependency"
key={parentDependency.dottedName}
/>
)
),
@ -63,7 +63,6 @@ export default function Algorithm({ rule, showValues }) {
!!Object.keys(formula).length &&
!path(['formule', 'explanation', 'une possibilité'], rule) &&
formula.explanation?.category !== 'number'
return (
<div id="algorithm">
<section id="rule-rules" className={classNames({ showValues })}>

View File

@ -1,12 +1,26 @@
import { coerceArray } from '../utils'
export function syntaxError(
dottedName: string,
rules: string[] | string,
message: string,
originalError: Error
) {
throw new Error(
`[ Erreur syntaxique ]
Dans la règle \`${dottedName}\`,
`\n[ Erreur syntaxique ]
Dans la règle \`${coerceArray(rules).slice(-1)[0]}\`
${message}
${originalError && originalError.message}
`
)
}
export function evaluationError(
rules: string[] | string,
message: string,
originalError?: Error
) {
throw new Error(
`\n[ Erreur d'évaluation ]
Dans la règle \`${coerceArray(rules).slice(-1)[0]}\`
${message}
${originalError && originalError.message}
`
@ -19,18 +33,22 @@ export function typeWarning(
originalError?: Error
) {
console.warn(
`[ Erreur de type ]
Dans la règle \`${coerceArray(rules).slice(-1)[0]}\`,
`\n[ Erreur de type ]
Dans la règle \`${coerceArray(rules).slice(-1)[0]}\`
${message}
${originalError && originalError.message}
`
)
}
export function warning(dottedName: string, message: string, solution: string) {
export function warning(
rules: string[] | string,
message: string,
solution: string
) {
console.warn(
`[ Avertissement ]
Dans la règle \`${dottedName}\`,
`\n[ Avertissement ]
Dans la règle \`${coerceArray(rules).slice(-1)[0]}\`
${message}
💡${solution}
`

View File

@ -43,20 +43,25 @@ export let evaluateNode = (cache, situationGate, parsedRules, node) => {
return evaluatedNode
}
const sameUnitValues = (explanation, contextRule, mecanismName) => {
const unit = explanation.map(n => n.unit).find(Boolean)
const firstNodeWithUnit = explanation.find(node => !!node.unit)
if (!firstNodeWithUnit) {
return [undefined, explanation.map(({ nodeValue }) => nodeValue)]
}
const values = explanation.map(node => {
try {
return convertNodeToUnit(unit, node).nodeValue
return convertNodeToUnit(firstNodeWithUnit?.unit, node).nodeValue
} catch (e) {
typeWarning(
contextRule,
`'${node.name}' a une unité incompatible avec celle du mécanisme ${mecanismName}`,
`Dans le mécanisme ${mecanismName}, les unités des éléments suivants sont incompatibles entre elles : \n\t\t${node?.name ||
node?.rawNode}\n\t\t${firstNodeWithUnit?.name ||
firstNodeWithUnit?.rawNode}'`,
e
)
return node.nodeValue
}
})
return [unit, values]
return [firstNodeWithUnit.unit, values]
}
export let evaluateArray = (reducer, start) => (
@ -144,7 +149,12 @@ export let evaluateObject = (objectShape, effect) => (
let transforms = map(k => [k, evaluateOne], keys(objectShape)),
automaticExplanation = evolve(fromPairs(transforms))(node.explanation)
// the result of effect can either be just a nodeValue, or an object {additionalExplanation, nodeValue}. The latter is useful for a richer JSX visualisation of the mecanism : the view should not duplicate code to recompute intermediate values (e.g. for a marginal 'barème', the marginal 'tranche')
let evaluated = effect(automaticExplanation, cache, situationGate, parsedRules),
let evaluated = effect(
automaticExplanation,
cache,
situationGate,
parsedRules
),
explanation = is(Object, evaluated)
? { ...automaticExplanation, ...evaluated.additionalExplanation }
: automaticExplanation,

View File

@ -24,6 +24,7 @@ const lexer = moo.compile({
'[': '[',
']': ']',
comparison: ['>','<','>=','<=','=','!='],
infinity: 'Infinity',
words: new RegExp(words),
number: new RegExp(numberRegExp),
string: /'[ \t\.'a-zA-Z\-\u00C0-\u017F0-9 ]+'/,
@ -106,6 +107,7 @@ boolean ->
number ->
%number {% number %}
| %infinity {% number %}
| %number (%space):? Unit {% numberWithUnit %}
string -> %string {% string %}

View File

@ -134,37 +134,41 @@ rend non applicable:
barème:
type: numeric
description: |
C'est un barème en taux marginaux, mécanisme de calcul connu son utilisation dans le calcul de l'impôt sur le revenu.
description: >-
C'est un barème en taux marginaux, mécanisme de calcul connu son utilisation
dans le calcul de l'impôt sur le revenu.
L'assiette est décomposée en plusieurs tranches, qui sont multipliées par un taux spécifique.
L'assiette est décomposée en plusieurs tranches, qui sont multipliées par un
taux spécifique.
Les tranches sont souvent exprimées sous forme de facteurs d'une variable que l'on appelle `multiplicateur`, par exemple `1 x le plafond de la sécurité sociale`.
Les tranches sont souvent exprimées sous forme de facteurs d'une variable
que l'on appelle `multiplicateur`, par exemple `1 x le plafond de la
sécurité sociale`.
barème linéaire:
grille:
type: numeric
description: |
C'est un barème en taux non marginaux, très simple. C'est le mécanisme de calcul de l'impôt neutre, aussi appelé impôt non personnalisé.
Il est composé de tranches qui se suivent. Il suffit de trouver l'assiette qui correspond à la tranche, et de multiplier le taux associé avec l'assiette.
Un montant fixe pour chaque tranche peut aussi remplacer le taux, rendant le barème encore plus simple, mais moins "juste", car moins continu.
description: >-
C'est un barème sous la forme d'une grille de correspondance. C'est le
mécanisme de calcul de l'impôt neutre, aussi appelé impôt non personnalisé.
barème continu:
Il est composé de tranches qui se suivent. Il suffit de trouver l'assiette
qui correspond à la tranche, et de selectionner le montant associé à
l'assiette.
taux progressif:
type: numeric
description: |
Désolé, ce mécanisme est quelque peu compliqué, accrochez-vous !
description: >-
Ce mécanisme permet de calculer un taux progressif. On spécifie pour chaque
tranche le plafond et le taux associé. Le taux effectif renvoyé est calculé
en lissant la différence de taux entre la borne inférieure et supérieure de
l'assiette
> Par exemple, si nous nous avons les tranches suivantes :
- taux: 50% / plafond: 0
- taux: 100% / plafond: 1000
Le barème définit des points, constitués d'un seuil et d'un taux correspondant. Par exemple `1 : 6%` et `2 : 8%`. L'assiette du barème est placée au bon endroit entre ces seuils, et on en déduit le taux qui lui correspond.
Dans notre exemple, si l'assiette vaut 1, le taux est 6%. Si elle vaut 2, le taux est 8%. Et si elle est entre les deux, par exemple 1.5, alors le taux est 7%. Et ainsi de suite.
Dans ce type de barème, il y a souvent un multiplicateur de l'assiette, par exemple le plafond de la sécurité sociale : les seuils sont des petits nombres comme 1 ou 1.5, mais ils correspondent en réalité à `1 * 3311€` et `1.5 * 3311€`.
L'option `retourne seulement le taux: oui` permet d'obtenir un taux pour une assiette donnée, pour l'appliquer ensuite à une autre assiette.
complément:
type: numeric
description: |
Complète une base pour atteindre un seuil minimal
> Pour une assiette de 500, le taux retourné sera 75%, car il correspond au
taux situé à la moitié de la tranche correspondante.
composantes:
type: numeric
@ -196,5 +200,12 @@ durée:
synchronisation:
type: object
description: |
Pour éviter trop de saisies à l'utilisateur, certaines informations sont récupérées à partir de ce que l'on appelle des API. Ce sont des services auxquels ont fait appel pour obtenir des informations sur un sujet précis. Par exemple, l'État français fournit gratuitement l'API géo, qui permet à partir du nom d'une ville, d'obtenir son code postal, son département, la population etc.
Ce mécanismes `synchronisation` permet de faire le lien entre les règles de notre système et les réponses de ces API.
Pour éviter trop de saisies à l'utilisateur, certaines informations sont
récupérées à partir de ce que l'on appelle des API. Ce sont des services
auxquels ont fait appel pour obtenir des informations sur un sujet précis.
Par exemple, l'État français fournit gratuitement l'API géo, qui permet à
partir du nom d'une ville, d'obtenir son code postal, son département, la
population etc.
Ce mécanismes `synchronisation` permet de faire le lien entre les règles de
notre système et les réponses de ces API.

View File

@ -1,238 +0,0 @@
import classNames from 'classnames'
import { ShowValuesContext } from 'Components/rule/ShowValuesContext'
import RuleLink from 'Components/RuleLink'
import { numberFormatter } from 'Engine/format'
import { trancheValue } from 'Engine/mecanisms/barème'
import { inferUnit, serializeUnit } from 'Engine/units'
import { identity } from 'ramda'
import React, { useContext } from 'react'
import { Trans } from 'react-i18next'
import { makeJsx } from '../evaluation'
import './Barème.css'
import { Node, NodeValuePointer } from './common'
export let BarèmeAttributes = ({ explanation, lazyEval = identity }) => {
const multiplicateur = lazyEval(explanation['multiplicateur'])
const multiplicateurAcronym = multiplicateur?.explanation?.acronyme
return (
<>
<li key="assiette">
<span className="key">
<Trans>assiette</Trans>:{' '}
</span>
<span className="value">{makeJsx(lazyEval(explanation.assiette))}</span>
</li>
{explanation['multiplicateur'] &&
explanation['multiplicateur'].nodeValue !== 1 &&
!multiplicateurAcronym && (
<li key="multiplicateur">
<span className="key">
<Trans>multiplicateur</Trans>:{' '}
</span>
<span className="value">{makeJsx(multiplicateur)}</span>
</li>
)}
</>
)
}
let Component = function Barème({
language,
nodeValue,
explanation,
barèmeType,
lazyEval,
unit
}) {
const showValues = useContext(ShowValuesContext)
const multiplicateur = lazyEval(explanation?.multiplicateur)
return (
<Node
classes="mecanism barème"
name={barèmeType === 'marginal' ? 'barème' : 'barème linéaire'}
value={nodeValue}
unit={unit}
child={
<ul className="properties">
<BarèmeAttributes explanation={explanation} lazyEval={lazyEval} />
<table className="tranches">
<thead>
<tr>
<th>
<Trans>Tranche de l&apos;assiette</Trans>
</th>
<th>
{typeof unit === 'string' ? (
unit
) : (
<Trans>
{explanation.tranches[0].taux != null
? 'Taux'
: 'Montant'}
</Trans>
)}
</th>
{showValues &&
!explanation.returnRate &&
explanation.tranches[0].taux != null && (
<th>
<Trans>Résultat</Trans>
</th>
)}
</tr>
</thead>
<tbody>
{explanation.tranches.map(tranche => (
<Tranche
key={tranche['de'] + tranche['à']}
{...{
language,
tranche,
showValues,
tranchesUnit: inferUnit('/', [
explanation.assiette.unit,
explanation.multiplicateur?.unit
]),
resultUnit: explanation.assiette.unit,
returnRate: explanation.returnRate,
multiplicateur,
trancheValue:
barèmeType === 'marginal'
? tranche.value
: trancheValue(
explanation['assiette'],
explanation['multiplicateur']
)(tranche)
}}
/>
))}
</tbody>
</table>
{/* nous avons remarqué que la notion de taux moyen pour un barème à 2 tranches est moins pertinent pour les règles de calcul des indépendants. Règle empirique à faire évoluer ! */}
{showValues &&
!explanation.returnRate &&
barèmeType === 'marginal' &&
explanation.tranches.length > 2 && (
<>
<b>
<Trans>Taux moyen</Trans> :{' '}
</b>
<NodeValuePointer
data={
(100 * nodeValue) /
lazyEval(explanation['assiette']).nodeValue
}
unit="%"
/>
</>
)}
{explanation.returnRate && (
<p>
Ce barème <strong>ne retourne que le taux</strong>.
</p>
)}
</ul>
}
/>
)
}
let Tranche = ({
tranche: {
'en-dessous de': maxOnly,
'au-dessus de': minOnly,
de: min,
à: max,
taux,
nodeValue,
montant
},
multiplicateur,
tranchesUnit,
resultUnit,
returnRate,
trancheValue,
showValues,
language
}) => {
const trancheFormatter = value => (
<TrancheFormatter
{...{
language,
tranchesUnit,
resultUnit,
multiplicateur,
value
}}
/>
)
let activated = trancheValue > 0
return (
<tr className={classNames('tranche', { activated })}>
<td key="tranche">
{maxOnly ? (
<>
<Trans>En-dessous de</Trans> {trancheFormatter(maxOnly)}
</>
) : minOnly ? (
<>
<Trans>Au-dessus de</Trans> {trancheFormatter(minOnly)}
</>
) : (
<>
<Trans>De</Trans> {trancheFormatter(min)} <Trans>à</Trans>{' '}
{trancheFormatter(max)}
</>
)}
</td>
<td key="taux"> {taux != null ? makeJsx(taux) : makeJsx(montant)}</td>
{showValues && !returnRate && taux != null && (
<td key="value">
<NodeValuePointer data={nodeValue} unit={resultUnit} />
</td>
)}
</tr>
)
}
function TrancheFormatter({
language,
tranchesUnit,
resultUnit,
multiplicateur,
value
}) {
const multiplicateurAcronym = multiplicateur?.explanation?.acronyme
if (!multiplicateurAcronym) {
return numberFormatter({
language,
style: serializeUnit(tranchesUnit) === '€' ? 'currency' : undefined
})(value)
} else {
return (
<>
{value}&nbsp;
<RuleLink
{...multiplicateur.explanation}
title={multiplicateur.explanation.name}
>
{multiplicateurAcronym}
</RuleLink>{' '}
<NodeValuePointer
data={value * multiplicateur.nodeValue}
unit={resultUnit}
/>
</>
)
}
}
//eslint-disable-next-line
export default barèmeType => (
nodeValue,
explanation,
lazyEval = identity,
unit
) => <Component {...{ nodeValue, explanation, barèmeType, lazyEval, unit }} />

View File

@ -0,0 +1,123 @@
import classNames from 'classnames'
import React from 'react'
import { Trans } from 'react-i18next'
import { makeJsx } from '../evaluation'
import './Barème.css'
import { Node, NodeValuePointer } from './common'
export default function Barème(nodeValue, explanation, _, unit) {
return (
<Node
classes="mecanism barème"
name="barème"
value={nodeValue}
unit={unit}
child={
<ul className="properties">
<BarèmeAttributes explanation={explanation} />
<TrancheTable
tranches={explanation.tranches}
multiplicateur={explanation.multiplicateur}
/>
{/* nous avons remarqué que la notion de taux moyen pour un barème à 2 tranches est moins pertinent pour les règles de calcul des indépendants. Règle empirique à faire évoluer ! */}
{nodeValue !== null && explanation.tranches.length > 2 && (
<>
<b>
<Trans>Taux moyen</Trans> :{' '}
</b>
<NodeValuePointer
data={(100 * nodeValue) / explanation.assiette.nodeValue}
unit="%"
/>
</>
)}
</ul>
}
/>
)
}
export let BarèmeAttributes = ({ explanation }) => {
const multiplicateur = explanation.multiplicateur
return (
<>
<li key="assiette">
<span className="key">
<Trans>assiette</Trans>:{' '}
</span>
<span className="value">{makeJsx(explanation.assiette)}</span>
</li>
{multiplicateur && !multiplicateur.isDefault && (
<li key="multiplicateur">
<span className="key">
<Trans>multiplicateur</Trans>:{' '}
</span>
<span className="value">{makeJsx(multiplicateur)}</span>
</li>
)}
</>
)
}
export const TrancheTable = ({ tranches, multiplicateur }) => {
const activeTranche = tranches.find(({ isActive }) => isActive)
return (
<table className="tranches">
<thead>
<tr>
<th>
<Trans>Plafonds des tranches</Trans>
</th>
{tranches[0].taux && (
<th>
<Trans>Taux</Trans>
</th>
)}
{(tranches[0].montant || activeTranche?.nodeValue != null) && (
<th>
<Trans>Montant</Trans>
</th>
)}
</tr>
</thead>
<tbody>
{tranches.map((tranche, i) => (
<Tranche key={i} tranche={tranche} multiplicateur={multiplicateur} />
))}
</tbody>
</table>
)
}
const Tranche = ({ tranche, multiplicateur }) => {
const isHighlighted = tranche.isActive
return (
<tr className={classNames('tranche', { activated: isHighlighted })}>
<td key="tranche">
{tranche.plafond.nodeValue === Infinity ? (
<Trans>Au-delà du dernier plafond</Trans>
) : (
<>
{makeJsx(tranche.plafond)}
{multiplicateur && !multiplicateur.isDefault && (
<>
{' × '}
{makeJsx(multiplicateur)}
</>
)}
</>
)}
</td>
{tranche.taux && <td key="taux">{makeJsx(tranche.taux)}</td>}
{(tranche.nodeValue != null || tranche.montant) && (
<td key="value">
{tranche.montant ? (
makeJsx(tranche.montant)
) : (
<NodeValuePointer data={tranche.nodeValue} unit={tranche.unit} />
)}
</td>
)}
</tr>
)
}

View File

@ -1,106 +0,0 @@
import { ShowValuesConsumer } from 'Components/rule/ShowValuesContext'
import RuleLink from 'Components/RuleLink'
import { formatValue } from 'Engine/format'
import { sortObjectByKeys } from 'Engine/mecanismViews/common'
import { serializeUnit } from 'Engine/units'
import React from 'react'
import { Trans, useTranslation } from 'react-i18next'
import { BarèmeAttributes } from './Barème'
import './Barème.css'
import { Node, NodeValuePointer } from './common'
let Comp = function Barème({ nodeValue, explanation, unit }) {
return (
<ShowValuesConsumer>
{showValues => (
<Node
classes="mecanism barème"
name="barème continu"
value={nodeValue}
unit={unit}
child={
<ul className="properties">
<BarèmeAttributes explanation={explanation} />
<table className="tranches">
<thead>
<tr>
<th>
<Trans>Seuil</Trans>
</th>
<th>
<Trans>Taux</Trans>
</th>
</tr>
</thead>
<tbody>
{sortObjectByKeys(explanation.points).map(([seuil, taux]) => (
<tr key={seuil} className="tranche">
<td key="tranche">
<SeuilFormatteur
value={Number(seuil)}
unit={explanation.assiette.unit}
multiplicateur={
explanation.multiplicateur?.explanation
}
/>
</td>
<td key="taux"> {taux}</td>
</tr>
))}
</tbody>
</table>
{showValues && (
<span style={{ background: 'yellow' }}>
<b>
<Trans>Votre taux </Trans> :{' '}
</b>
<NodeValuePointer data={100 * explanation.taux} unit="%" />
</span>
)}
{explanation.returnRate && (
<p>
Ce barème <strong>ne retourne que le taux</strong>.
</p>
)}
</ul>
}
/>
)}
</ShowValuesConsumer>
)
}
function SeuilFormatteur({ value, multiplicateur, unit }) {
const { language } = useTranslation().i18n
if (value === 0) {
return '0'
} else {
return (
<>
{formatValue({
value,
language
})}{' '}
{multiplicateur ? (
<>
&nbsp;
<RuleLink {...multiplicateur} title={multiplicateur.name}>
{multiplicateur.acronyme}
</RuleLink>
<NodeValuePointer
data={value * multiplicateur.nodeValue}
unit={multiplicateur.unit}
/>
</>
) : (
serializeUnit(unit)
)}{' '}
</>
)
}
}
//eslint-disable-next-line
export default (nodeValue, explanation, _, unit) => (
<Comp {...{ nodeValue, explanation, unit }} />
)

View File

@ -0,0 +1,24 @@
import React from 'react'
import { BarèmeAttributes, TrancheTable } from './Barème'
import './Barème.css'
import { Node } from './common'
export default function Grille(nodeValue, explanation, _, unit) {
return (
<Node
classes="mecanism barème"
name="grille"
value={nodeValue}
unit={unit}
child={
<ul className="properties">
<BarèmeAttributes explanation={explanation} />
<TrancheTable
tranches={explanation.tranches}
multiplicateur={explanation.multiplicateur}
/>
</ul>
}
/>
)
}

View File

@ -0,0 +1,33 @@
import React from 'react'
import { Trans } from 'react-i18next'
import { BarèmeAttributes, TrancheTable } from './Barème'
import './Barème.css'
import { Node, NodeValuePointer } from './common'
export default function TauxProgressif(nodeValue, explanation, _, unit) {
return (
<Node
classes="mecanism barème"
name="taux progressif"
value={nodeValue}
unit={unit}
child={
<ul className="properties">
<BarèmeAttributes explanation={explanation} />
<TrancheTable
tranches={explanation.tranches}
multiplicateur={explanation.multiplicateur}
/>
{nodeValue != null && (
<>
<b>
<Trans>Taux calculé</Trans> :{' '}
</b>{' '}
<NodeValuePointer data={nodeValue * 100} unit="%" />
</>
)}
</ul>
}
/>
)
}

View File

@ -7,9 +7,9 @@ const colors = {
'toutes ces conditions': '#3498db',
composantes: '#3498db',
variations: '#FF9800',
complément: '#795548',
'taux progressif': '#795548',
barème: '#607D8B',
barèmeLinéaire: '#AD1457'
grille: '#AD1457'
}
export default (name: string) => colors[name] || '#34495e'

View File

@ -129,14 +129,19 @@ export function Leaf({
const sitePaths = useContext(SitePathsContext)
const flatRules = useSelector(flatRulesSelector)
let rule = findRuleByDottedName(flatRules, dottedName)
const title = rule.title || capitalise0(name)
return (
<span className={classNames(classes, 'leaf')}>
{dottedName && (
<span className="nodeHead">
<Link to={sitePaths.documentation.rule(dottedName)}>
<span className="name">
{rule.title || capitalise0(name)} {filter}
{rule.acronyme ? (
<abbr title={title}>{rule.acronyme}</abbr>
) : (
title
)}{' '}
{filter}
</span>
</Link>
{!isNil(nodeValue) && (

View File

@ -54,8 +54,8 @@ export let mecanismOneOf = (recurse, k, v) => {
value={nodeValue}
child={
<ul>
{explanation.map(item => (
<li key={item.name || item.text}>{makeJsx(item)}</li>
{explanation.map((item, i) => (
<li key={i}>{makeJsx(item)}</li>
))}
</ul>
}
@ -105,8 +105,8 @@ export let mecanismAllOf = (recurse, k, v) => {
value={nodeValue}
child={
<ul>
{explanation.map(item => (
<li key={item.name || item.text}>{makeJsx(item)}</li>
{explanation.map((item, i) => (
<li key={i}>{makeJsx(item)}</li>
))}
</ul>
}
@ -623,9 +623,6 @@ export let mecanismSynchronisation = (recurse, k, v) => {
}
}
export let mecanismError = (recurse, k, v) => {
throw new Error("Le mécanisme '" + k + "' est inconnu !" + v)
}
export let mecanismOnePossibility = dottedName => (recurse, k, v) => ({
...v,
'une possibilité': 'oui',

View File

@ -1,82 +0,0 @@
import { defaultNode, evaluateObject, parseObject } from 'Engine/evaluation'
import { decompose } from 'Engine/mecanisms/utils'
import variations from 'Engine/mecanisms/variations'
import BarèmeContinu from 'Engine/mecanismViews/BarèmeContinu'
import { anyNull, val } from 'Engine/traverse-common-functions'
import { parseUnit } from 'Engine/units'
import {
aperture,
isEmpty,
last,
pipe,
reduce,
reduced,
sort,
toPairs
} from 'ramda'
export default (recurse, k, v) => {
if (v.composantes) {
return decompose(recurse, k, v)
}
if (v.variations) {
return variations(recurse, k, v, true)
}
if (!v.points || typeof v.points !== 'object' || isEmpty(v.points)) {
throw new Error(
'Le mécanisme `barème linéaire` doit avoir un paramètre `points` valide'
)
}
let objectShape = {
assiette: false,
multiplicateur: defaultNode(1)
}
let returnRate = v['retourne seulement le taux'] === 'oui'
let effect = ({ assiette, multiplicateur, points }) => {
if (anyNull([assiette, multiplicateur])) return null
//We'll build a linear function given the two constraints that must be respected
let result = pipe(
toPairs,
// we don't rely on the sorting of objects
sort(([k1], [k2]) => k1 - k2),
points => [...points, [Infinity, last(points)[1]]],
aperture(2),
reduce((_, [[lowerLimit, lowerRate], [upperLimit, upperRate]]) => {
let x1 = val(multiplicateur) * lowerLimit,
x2 = val(multiplicateur) * upperLimit,
y1 = (val(assiette) * val(recurse(lowerRate))) / 100,
y2 = (val(assiette) * val(recurse(upperRate))) / 100
if (val(assiette) > x1 && val(assiette) <= x2) {
// Outside of these 2 limits, it's a linear function a * x + b
let a = (y2 - y1) / (x2 - x1),
b = y1 - x1 * a,
nodeValue = a * val(assiette) + b,
taux = nodeValue / val(assiette)
return reduced({
nodeValue: returnRate ? taux * 100 : nodeValue,
additionalExplanation: {
seuil: val(assiette) / val(multiplicateur),
taux,
unit: returnRate ? parseUnit('%') : assiette.unit
}
})
}
}, 0)
)(points)
return result
}
let explanation = {
...parseObject(recurse, objectShape, v),
points: v.points,
returnRate
},
evaluate = evaluateObject(objectShape, effect)
return {
evaluate,
jsx: BarèmeContinu,
explanation,
category: 'mecanism',
name: 'barème continu',
type: 'numeric',
unit: returnRate ? parseUnit('%') : explanation.assiette.unit
}
}

View File

@ -1,87 +0,0 @@
import { defaultNode, evaluateObject, parseObject } from 'Engine/evaluation'
import { decompose } from 'Engine/mecanisms/utils'
import variations from 'Engine/mecanisms/variations'
import Barème from 'Engine/mecanismViews/Barème'
import { evaluateNode } from 'Engine/evaluation'
import { val } from 'Engine/traverse-common-functions'
import { parseUnit } from 'Engine/units'
import { desugarScale } from './barème'
/* on réécrit en une syntaxe plus bas niveau mais plus régulière les tranches :
`en-dessous de: 1`
devient
```
de: 0
à: 1
```
*/
export default (recurse, k, v) => {
if (v.composantes) {
//mécanisme de composantes. Voir known-mecanisms.md/composantes
return decompose(recurse, k, v)
}
if (v.variations) {
return variations(recurse, k, v, true)
}
let returnRate = v['retourne seulement le taux'] === 'oui'
let tranches = desugarScale(recurse)(v['tranches']),
objectShape = {
assiette: false,
multiplicateur: defaultNode(1)
}
let effect = ({ assiette, multiplicateur, tranches }, cache, situationGate, parsedRules) => {
if (val(assiette) === null) return null
let roundedAssiette = Math.round(val(assiette))
let matchedTranche = tranches.find(
({ de: min, à: max }) =>
roundedAssiette >= val(multiplicateur) * min &&
roundedAssiette <= max * val(multiplicateur)
)
let nodeValue
if (!matchedTranche) {
nodeValue = 0
} else if (matchedTranche.taux) {
nodeValue = returnRate
? matchedTranche.taux.nodeValue
: (matchedTranche.taux.nodeValue / 100) * val(assiette)
} else {
matchedTranche.montant = evaluateNode(cache, situationGate, parsedRules, matchedTranche.montant)
nodeValue = matchedTranche.montant.nodeValue
}
return {
nodeValue,
additionalExplanation: {
matchedTranche,
unit: returnRate
? parseUnit('%')
: (v['unité'] && parseUnit(v['unité'])) || explanation.assiette.unit
}
}
}
let explanation = {
...parseObject(recurse, objectShape, v),
returnRate,
tranches
},
evaluate = evaluateObject(objectShape, effect),
unit = returnRate
? parseUnit('%')
: (v['unité'] && parseUnit(v['unité'])) || explanation.assiette.unit
return {
evaluate,
jsx: Barème('linéaire'),
explanation,
category: 'mecanism',
name: 'barème linéaire',
barème: 'en taux',
type: 'numeric',
unit
}
}

View File

@ -1,138 +0,0 @@
import { defaultNode, evaluateNode, mergeAllMissing } from 'Engine/evaluation'
import { decompose } from 'Engine/mecanisms/utils'
import variations from 'Engine/mecanisms/variations'
import Barème from 'Engine/mecanismViews/Barème'
import { evolve, has } from 'ramda'
import { typeWarning } from '../error'
import { convertNodeToUnit } from '../nodeUnits'
import { parseUnit } from '../units'
export let desugarScale = recurse => tranches =>
tranches
.map(t =>
has('en-dessous de')(t)
? { ...t, de: 0, à: t['en-dessous de'] }
: has('au-dessus de')(t)
? { ...t, de: t['au-dessus de'], à: Infinity }
: t
)
.map(evolve({ taux: recurse, montant: recurse }))
// This function was also used for marginal barèmes, but now only for linear ones
export let trancheValue = (assiette, multiplicateur) => ({
de: min,
à: max,
taux,
montant
}) =>
Math.round(assiette.nodeValue) >= min * multiplicateur.nodeValue &&
(!max || Math.round(assiette.nodeValue) <= max * multiplicateur.nodeValue)
? taux != null
? assiette.nodeValue * taux.nodeValue
: montant
: 0
export default (recurse, k, v) => {
// Barème en taux marginaux.
if (v.composantes) {
//mécanisme de composantes. Voir known-mecanisms.md/composantes
return decompose(recurse, k, v)
}
if (v.variations) {
return variations(recurse, k, v, true)
}
let { assiette, multiplicateur } = v,
tranches = desugarScale(recurse)(v['tranches'])
let explanation = {
assiette: recurse(assiette),
multiplicateur: multiplicateur ? recurse(multiplicateur) : defaultNode(1),
tranches
}
let evaluate = (cache, situationGate, parsedRules, node) => {
let { assiette, multiplicateur } = node.explanation
assiette = evaluateNode(cache, situationGate, parsedRules, assiette)
multiplicateur = evaluateNode(
cache,
situationGate,
parsedRules,
multiplicateur
)
try {
multiplicateur = convertNodeToUnit(assiette.unit, multiplicateur)
} catch (e) {
typeWarning(
cache._meta.contextRule,
`L'unité du multiplicateur du barème doit être compatible avec celle de son assiette`,
e
)
}
const tranches = node.explanation.tranches.map(tranche => {
let { de: min, à: max, taux } = tranche
if (
[assiette, multiplicateur].every(
({ nodeValue }) => nodeValue != null
) &&
assiette.nodeValue < min * multiplicateur.nodeValue
) {
return { ...tranche, nodeValue: 0 }
}
taux = convertNodeToUnit(
parseUnit(''),
evaluateNode(cache, situationGate, parsedRules, taux)
)
if (
[assiette, multiplicateur, taux].some(
({ nodeValue }) => nodeValue == null
)
) {
return {
...tranche,
nodeValue: null,
missingVariables: taux.missingVariables
}
}
return {
...tranche,
nodeValue:
(Math.min(assiette.nodeValue, max * multiplicateur.nodeValue) -
min * multiplicateur.nodeValue) *
taux.nodeValue
}
})
const nodeValue = tranches.reduce(
(value, { nodeValue }) => (nodeValue == null ? null : value + nodeValue),
0
)
const missingVariables = mergeAllMissing([
assiette,
multiplicateur,
...tranches
])
return {
...node,
nodeValue,
explanation: {
...explanation,
tranches
},
missingVariables,
unit: assiette.unit,
lazyEval: node => evaluateNode(cache, situationGate, parsedRules, node)
}
}
return {
explanation,
evaluate,
jsx: Barème('marginal'),
category: 'mecanism',
name: 'barème',
barème: 'marginal',
unit: explanation.assiette.unit
}
}

View File

@ -0,0 +1,91 @@
import { defaultNode, evaluateNode, mergeAllMissing } from 'Engine/evaluation'
import { decompose } from 'Engine/mecanisms/utils'
import variations from 'Engine/mecanisms/variations'
import Barème from 'Engine/mecanismViews/Barème'
import { convertUnit, parseUnit } from '../units'
import {
evaluatePlafondUntilActiveTranche,
parseTranches
} from './trancheUtils'
export default function parse(parse, k, v) {
// Barème en taux marginaux.
if (v.composantes) {
//mécanisme de composantes. Voir known-mecanisms.md/composantes
return decompose(parse, k, v)
}
if (v.variations) {
return variations(parse, k, v, true)
}
const explanation = {
assiette: parse(v.assiette),
multiplicateur: v.multiplicateur ? parse(v.multiplicateur) : defaultNode(1),
tranches: parseTranches(parse, v.tranches)
}
return {
explanation,
evaluate,
jsx: Barème,
category: 'mecanism',
name: 'barème',
type: 'numeric',
unit: explanation.assiette.unit
}
}
const evaluate = (
cache,
situationGate,
parsedRules,
node: ReturnType<typeof parse>
) => {
const evaluate = evaluateNode.bind(null, cache, situationGate, parsedRules)
const assiette = evaluate(node.explanation.assiette)
const multiplicateur = evaluate(node.explanation.multiplicateur)
const tranches = evaluatePlafondUntilActiveTranche(
evaluate,
{
parsedTranches: node.explanation.tranches,
assiette,
multiplicateur
},
cache
).map(tranche => {
if (tranche.isAfterActive) {
return { ...tranche, nodeValue: 0 }
}
const taux = evaluate(tranche.taux)
if ([taux.nodeValue, tranche.nodeValue].some(value => value === null)) {
return {
...tranche,
taux,
nodeValue: null,
missingVariables: mergeAllMissing([taux, tranche])
}
}
return {
...tranche,
taux,
unit: assiette.unit,
nodeValue:
(Math.min(assiette.nodeValue, tranche.plafondValue) -
tranche.plancherValue) *
convertUnit(taux.unit, parseUnit(''), taux.nodeValue)
}
})
return {
...node,
nodeValue: tranches.reduce(
(value, { nodeValue }) => (nodeValue == null ? null : value + nodeValue),
0
),
missingVariables: mergeAllMissing(tranches),
explanation: {
assiette,
multiplicateur,
tranches
},
unit: assiette.unit
}
}

View File

@ -0,0 +1,85 @@
import { defaultNode, evaluateNode, mergeAllMissing } from 'Engine/evaluation'
import { decompose } from 'Engine/mecanisms/utils'
import variations from 'Engine/mecanisms/variations'
import grille from 'Engine/mecanismViews/Grille'
import { parseUnit } from 'Engine/units'
import { lensPath, over } from 'ramda'
import {
evaluatePlafondUntilActiveTranche,
parseTranches
} from './trancheUtils'
export default function parse(parse, k, v) {
if (v.composantes) {
//mécanisme de composantes. Voir known-mecanisms.md/composantes
return decompose(parse, k, v)
}
if (v.variations) {
return variations(parse, k, v, true)
}
const defaultUnit = v['unité'] && parseUnit(v['unité'])
let explanation = {
assiette: parse(v.assiette),
multiplicateur: v.multiplicateur ? parse(v.multiplicateur) : defaultNode(1),
tranches: parseTranches(parse, v.tranches).map(
over(lensPath(['montant', 'unit']), unit => unit ?? defaultUnit)
)
}
return {
explanation,
evaluate,
jsx: grille,
category: 'mecanism',
name: 'grille',
type: 'numeric',
unit: explanation.tranches[0].montant.unit
}
}
const evaluate = (
cache,
situationGate,
parsedRules,
node: ReturnType<typeof parse>
) => {
const evaluate = evaluateNode.bind(null, cache, situationGate, parsedRules)
const assiette = evaluate(node.explanation.assiette)
const multiplicateur = evaluate(node.explanation.multiplicateur)
const tranches = evaluatePlafondUntilActiveTranche(
evaluate,
{
parsedTranches: node.explanation.tranches,
assiette,
multiplicateur
},
cache
).map(tranche => {
if (tranche.isActive === false) {
return tranche
}
const montant = evaluate(tranche.montant)
return {
...tranche,
montant,
nodeValue: montant.nodeValue,
unit: montant.unit,
missingVariables: mergeAllMissing([montant, tranche])
}
})
const activeTranches = tranches.filter(({ isActive }) => isActive != false)
const missingVariables = mergeAllMissing(activeTranches)
const nodeValue = activeTranches.length ? activeTranches[0].nodeValue : false
return {
...node,
explanation: {
tranches,
assiette,
multiplicateur
},
missingVariables,
nodeValue,
unit: activeTranches[0]?.unit ?? node.unit
}
}

View File

@ -0,0 +1,134 @@
import { defaultNode, evaluateNode, mergeAllMissing } from 'Engine/evaluation'
import { decompose } from 'Engine/mecanisms/utils'
import variations from 'Engine/mecanisms/variations'
import tauxProgressif from 'Engine/mecanismViews/TauxProgressif'
import { convertNodeToUnit } from 'Engine/nodeUnits'
import { anyNull } from 'Engine/traverse-common-functions'
import { parseUnit } from 'Engine/units'
import {
evaluatePlafondUntilActiveTranche,
parseTranches
} from './trancheUtils'
export default function parse(parse, k, v) {
if (v.composantes) {
//mécanisme de composantes. Voir known-mecanisms.md/composantes
return decompose(parse, k, v)
}
if (v.variations) {
return variations(parse, k, v, true)
}
let explanation = {
assiette: parse(v.assiette),
multiplicateur: v.multiplicateur ? parse(v.multiplicateur) : defaultNode(1),
tranches: parseTranches(parse, v.tranches)
}
return {
evaluate,
jsx: tauxProgressif,
explanation,
category: 'mecanism',
name: 'taux progressif',
type: 'numeric',
unit: parseUnit('%')
}
}
const evaluate = (
cache,
situationGate,
parsedRules,
node: ReturnType<typeof parse>
) => {
const evaluate = evaluateNode.bind(null, cache, situationGate, parsedRules)
const assiette = evaluate(node.explanation.assiette)
const multiplicateur = evaluate(node.explanation.multiplicateur)
const tranches = evaluatePlafondUntilActiveTranche(
evaluate,
{
parsedTranches: node.explanation.tranches,
assiette,
multiplicateur
},
cache
)
const evaluatedNode = {
...node,
explanation: {
tranches,
assiette,
multiplicateur
},
unit: parseUnit('%')
}
const lastTranche = tranches[tranches.length - 1]
if (
tranches.every(({ isActive }) => isActive === false) ||
(lastTranche.isActive && lastTranche.plafond.nodeValue === Infinity)
) {
const taux = convertNodeToUnit(parseUnit('%'), evaluate(lastTranche.taux))
const { nodeValue, missingVariables } = taux
lastTranche.taux = taux
lastTranche.nodeValue = nodeValue
lastTranche.missingVariables = missingVariables
return {
...evaluatedNode,
nodeValue,
missingVariables
}
}
if (tranches.every(({ isActive }) => isActive !== true)) {
return {
...evaluatedNode,
nodeValue: null,
missingVariables: mergeAllMissing(tranches)
}
}
const activeTrancheIndex = tranches.findIndex(
({ isActive }) => isActive === true
)
const activeTranche = tranches[activeTrancheIndex]
activeTranche.taux = convertNodeToUnit(
parseUnit('%'),
evaluate(activeTranche.taux)
)
const previousTranche = tranches[activeTrancheIndex - 1]
if (previousTranche) {
previousTranche.taux = convertNodeToUnit(
parseUnit('%'),
evaluate(previousTranche.taux)
)
previousTranche.isActive = true
}
const previousTaux = previousTranche
? previousTranche.taux
: activeTranche.taux
const calculationValues = [previousTaux, activeTranche.taux, activeTranche]
if (anyNull(calculationValues)) {
activeTranche.nodeValue = null
activeTranche.missingVariables = mergeAllMissing(calculationValues)
return {
...evaluatedNode,
nodeValue: null,
activeTranche: activeTranche.missingVariables
}
}
const lowerTaux = previousTaux.nodeValue
const upperTaux = activeTranche.taux.nodeValue
const plancher = activeTranche.plancherValue
const plafond = activeTranche.plafondValue
const coeff = (upperTaux - lowerTaux) / (plafond - plancher)
const nodeValue = lowerTaux + (assiette.nodeValue - plancher) * coeff
activeTranche.nodeValue = nodeValue
return {
...evaluatedNode,
nodeValue
}
}

View File

@ -0,0 +1,97 @@
import { mergeAllMissing } from 'Engine/evaluation'
import { evolve } from 'ramda'
import { evaluationError, typeWarning } from '../error'
import { convertUnit, inferUnit } from '../units'
export const parseTranches = (parse, tranches) => {
return tranches
.map((t, i) => {
if (!t.plafond && i > tranches.length) {
console.log(t, i)
throw new SyntaxError(
`La tranche n°${i} du barème n'a pas de plafond précisé. Seule la dernière tranche peut ne pas être plafonnée`
)
}
return { ...t, plafond: t.plafond ?? Infinity }
})
.map(evolve({ taux: parse, montant: parse, plafond: parse }))
}
export function evaluatePlafondUntilActiveTranche(
evaluate,
{ multiplicateur, assiette, parsedTranches },
cache
) {
return parsedTranches.reduce(
([tranches, activeTrancheFound], parsedTranche, i: number) => {
if (activeTrancheFound) {
return [
[...tranches, { ...parsedTranche, isAfterActive: true }],
activeTrancheFound
]
}
const plafond = evaluate(parsedTranche.plafond)
const plancher = tranches[i - 1]
? tranches[i - 1].plafond
: { nodeValue: 0 }
const calculationValues = [plafond, assiette, multiplicateur, plancher]
if (calculationValues.some(node => node.nodeValue === null)) {
return [
[
...tranches,
{
...parsedTranche,
plafond,
nodeValue: null,
isActive: null,
isAfterActive: false,
missingVariables: mergeAllMissing(calculationValues)
}
],
false
]
}
let plafondValue = plafond.nodeValue * multiplicateur.nodeValue
try {
plafondValue = [Infinity || 0].includes(plafondValue)
? plafondValue
: convertUnit(
inferUnit('*', [plafond.unit, multiplicateur.unit]),
assiette.unit,
plafondValue
)
} catch (e) {
typeWarning(
cache._meta.contextRule,
`L'unité du plafond de la tranche n°${i +
1} n'est pas compatible avec celle l'assiette`,
e
)
}
let plancherValue = tranches[i - 1] ? tranches[i - 1].plafondValue : 0
if (!!tranches[i - 1] && plafondValue <= plancherValue) {
evaluationError(
cache._meta.contextRule,
`Le plafond de la tranche n°${i +
1} a une valeur inférieure à celui de la tranche précédente`
)
}
const tranche = {
...parsedTranche,
plafond,
plancherValue,
plafondValue,
isAfterActive: false,
isActive:
assiette.nodeValue >= plancherValue &&
assiette.nodeValue < plafondValue
}
return [[...tranches, tranche], tranche.isActive]
},
[[], false]
)[0]
}

View File

@ -5,38 +5,30 @@
import { formatValue } from 'Engine/format'
import mecanismRound from 'Engine/mecanisms/arrondi'
import barème from 'Engine/mecanisms/barème'
import barèmeContinu from 'Engine/mecanisms/barème-continu'
import barèmeLinéaire from 'Engine/mecanisms/barème-linéaire'
import durée from 'Engine/mecanisms/durée'
import encadrement from 'Engine/mecanisms/encadrement'
import grille from 'Engine/mecanisms/grille'
import operation from 'Engine/mecanisms/operation'
import tauxProgressif from 'Engine/mecanisms/tauxProgressif'
import variations from 'Engine/mecanisms/variations'
import { Grammar, Parser } from 'nearley'
import {
add,
cond,
divide,
equals,
fromPairs,
gt,
gte,
is,
keys,
lt,
lte,
multiply,
propOr,
subtract,
T,
without
subtract
} from 'ramda'
import React from 'react'
import { syntaxError } from './error.ts'
import grammar from './grammar.ne'
import {
mecanismAllOf,
mecanismComplement,
mecanismError,
mecanismInversion,
mecanismMax,
mecanismMin,
@ -49,154 +41,156 @@ import {
} from './mecanisms'
import { parseReferenceTransforms } from './parseReference'
export let parse = (rules, rule, parsedRules) => rawNode => {
let onNodeType = cond([
[is(String), parseString(rules, rule, parsedRules)],
[is(Number), parseNumber],
[is(Object), parseObject(rules, rule, parsedRules)],
[T, parseOther]
])
export const parse = (rules, rule, parsedRules) => rawNode => {
if (rawNode == null) {
syntaxError(
rule.dottedName,
`
Une des valeurs de la formule est vide.
Vérifiez que tous les champs à droite des deux points sont remplis`
)
}
if (typeof rawNode === 'boolean') {
syntaxError(
rule.dottedName,
`
Les valeure booléenes true / false ne sont acceptée.
Utilisez leur contrepartie française : 'oui' / 'non'`
)
}
const node =
typeof rawNode === 'object' ? rawNode : parseExpression(rule, '' + rawNode)
let defaultEvaluate = (cache, situationGate, parsedRules, node) => node
let parsedNode = onNodeType(rawNode)
return parsedNode.evaluate
? parsedNode
: { ...parsedNode, evaluate: defaultEvaluate }
const parsedNode = parseMecanism(rules, rule, parsedRules)(node)
parsedNode.evaluate = parsedNode.evaluate ?? ((_, __, ___, node) => node)
return parsedNode
}
const compiledGrammar = Grammar.fromCompiled(grammar)
export let parseString = (rules, rule, parsedRules) => rawNode => {
const parseExpression = (rule, rawNode) => {
/* Strings correspond to infix expressions.
* Indeed, a subset of expressions like simple arithmetic operations `3 + (quantity * 2)` or like `salary [month]` are more explicit that their prefixed counterparts.
* This function makes them prefixed operations. */
try {
let [parseResult] = new Parser(compiledGrammar).feed(rawNode).results
return parseObject(rules, rule, parsedRules)(parseResult)
return parseResult
} catch (e) {
syntaxError(
rule.dottedName,
`\`${rawNode}\` n'est pas une formule valide`,
`\`${rawNode}\` n'est pas une expression valide`,
e
)
}
}
export let parseNumber = rawNode => ({
text: '' + rawNode,
category: 'number',
nodeValue: rawNode,
type: 'numeric',
jsx: <span className="number">{rawNode}</span>
})
export let parseOther = rawNode => {
throw new Error(
'Cette donnée : ' + rawNode + ' doit être un Number, String ou Object'
)
}
export let parseObject = (rules, rule, parsedRules) => rawNode => {
/* TODO instead of describing mecanisms in knownMecanisms.yaml, externalize the mecanisms themselves in an individual file and describe it
let mecanisms = intersection(keys(rawNode), keys(knownMecanisms))
if (mecanisms.length != 1) {
}
*/
let attributes = keys(rawNode),
descriptiveAttributes = ['description', 'note', 'référence'],
relevantAttributes = without(descriptiveAttributes, attributes)
if (relevantAttributes.length !== 1)
throw new Error(`OUPS : On ne devrait reconnaître que un et un seul mécanisme dans cet objet (au-delà des attributs descriptifs tels que "description", "commentaire", etc.)
Objet YAML : ${JSON.stringify(rawNode)}
Cette liste doit avoir un et un seul élément.
Si vous venez tout juste d'ajouter un nouveau mécanisme, vérifier qu'il est bien intégré dans le dispatch de parse.js
`)
let k = relevantAttributes[0],
v = rawNode[k]
let knownOperations = {
'*': [multiply, '×'],
'/': [divide, ''],
'+': [add],
'-': [subtract, ''],
'<': [lt],
'<=': [lte, '≤'],
'>': [gt],
'>=': [gte, '≥'],
'=': [equals],
'!=': [(a, b) => !equals(a, b), '≠']
},
operationDispatch = fromPairs(
Object.entries(knownOperations).map(([k, [f, symbol]]) => [
k,
operation(k, f, symbol)
])
const parseMecanism = (rules, rule, parsedRules) => rawNode => {
if (Object.keys(rawNode).length > 1) {
syntaxError(
rule.dottedName,
`
Les mécanismes suivants se situent au même niveau : ${Object.keys(rawNode)
.map(x => `'${x}'`)
.join(', ')}
Cela vient probablement d'une erreur dans l'indentation
`
)
}
const mecanismName = Object.keys(rawNode)[0]
const values = rawNode[mecanismName]
let dispatch = {
'une de ces conditions': mecanismOneOf,
'toutes ces conditions': mecanismAllOf,
somme: mecanismSum,
multiplication: mecanismProduct,
arrondi: mecanismRound,
barème,
'barème linéaire': barèmeLinéaire,
'barème continu': barèmeContinu,
encadrement,
durée,
'le maximum de': mecanismMax,
'le minimum de': mecanismMin,
complément: mecanismComplement,
'une possibilité': mecanismOnePossibility(rule.dottedName),
'inversion numérique': mecanismInversion(rule.dottedName),
allègement: mecanismReduction,
variations,
synchronisation: mecanismSynchronisation,
...operationDispatch,
filter: () =>
parseReferenceTransforms(
rules,
rule,
parsedRules
)({
filter: v.filter,
variable: v.explanation
}),
variable: () =>
parseReferenceTransforms(rules, rule, parsedRules)({ variable: v }),
unitConversion: () =>
parseReferenceTransforms(
rules,
rule,
parsedRules
)({
variable: v.explanation,
unit: v.unit
}),
constant: () => ({
type: v.type,
nodeValue: v.nodeValue,
unit: v.unit,
// eslint-disable-next-line
jsx: () => (
<span className={v.type}>
{formatValue({
unit: v.unit,
value: v.nodeValue,
// TODO : handle localization here
language: 'fr',
// We want to display constants with full precision,
// espacilly for percentages like APEC 0,036 %
maximumFractionDigits: 5
})}
</span>
)
const parseFunctions = {
...statelessParseFunction,
'une possibilité': mecanismOnePossibility(rule.dottedName),
'inversion numérique': mecanismInversion(rule.dottedName),
filter: () =>
parseReferenceTransforms(
rules,
rule,
parsedRules
)({
filter: values.filter,
variable: values.explanation
}),
variable: () =>
parseReferenceTransforms(rules, rule, parsedRules)({ variable: values }),
unitConversion: () =>
parseReferenceTransforms(
rules,
rule,
parsedRules
)({
variable: values.explanation,
unit: values.unit
})
},
action = propOr(mecanismError, k, dispatch)
}
return action(parse(rules, rule, parsedRules), k, v)
const parseFn = parseFunctions[mecanismName]
if (!parseFn) {
syntaxError(
rule.dottedName,
`
Le mécanisme ${mecanismName} est inconnu.
Vérifiez qu'il n'y ait pas d'erreur dans l'orthographe du nom.`
)
}
return parseFn(parse(rules, rule, parsedRules), mecanismName, values)
}
const knownOperations = {
'*': [multiply, '×'],
'/': [divide, ''],
'+': [add],
'-': [subtract, ''],
'<': [lt],
'<=': [lte, '≤'],
'>': [gt],
'>=': [gte, '≥'],
'=': [equals],
'!=': [(a, b) => !equals(a, b), '≠']
}
const operationDispatch = fromPairs(
Object.entries(knownOperations).map(([k, [f, symbol]]) => [
k,
operation(k, f, symbol)
])
)
const statelessParseFunction = {
...operationDispatch,
'une de ces conditions': mecanismOneOf,
'toutes ces conditions': mecanismAllOf,
somme: mecanismSum,
multiplication: mecanismProduct,
arrondi: mecanismRound,
barème,
grille,
'taux progressif': tauxProgressif,
encadrement,
durée,
'le maximum de': mecanismMax,
'le minimum de': mecanismMin,
allègement: mecanismReduction,
variations,
synchronisation: mecanismSynchronisation,
constant: (_, __, v) => ({
type: v.type,
nodeValue: v.nodeValue,
unit: v.unit,
// eslint-disable-next-line
jsx: (nodeValue, _, __, unit) => (
<span className={v.type}>
{formatValue({
unit: unit,
value: nodeValue,
// TODO : handle localization here
language: 'fr',
// We want to display constants with full precision,
// espacilly for percentages like APEC 0,036 %
maximumFractionDigits: 5
})}
</span>
)
})
}

View File

@ -50,7 +50,7 @@ let printUnits = (units: Array<string>, count: number, lng): string =>
const plural = 2
export let serializeUnit = (
rawUnit: Unit | null | string,
rawUnit: Unit | undefined | string,
count: number = plural,
lng: string = 'fr'
) => {
@ -88,7 +88,7 @@ type SupportedOperators = '*' | '/' | '+' | '-'
let noUnit = { numerators: [], denominators: [] }
export let inferUnit = (
operator: SupportedOperators,
rawUnits: Array<Unit>
rawUnits: Array<Unit | undefined>
): Unit | undefined => {
let units = rawUnits.map(u => u || noUnit)
if (operator === '*')
@ -182,7 +182,11 @@ function unitsConversionFactor(from: string[], to: string[]): number {
return factor
}
export function convertUnit(from: Unit, to: Unit, value: number) {
export function convertUnit(
from: Unit | undefined,
to: Unit | undefined,
value: number
) {
if (!areUnitConvertible(from, to)) {
throw new Error(
`Impossible de convertir l'unité '${serializeUnit(
@ -193,8 +197,8 @@ export function convertUnit(from: Unit, to: Unit, value: number) {
if (!value) {
return value
}
const [fromSimplified, factorTo] = simplifyUnitWithValue(from)
const [toSimplified, factorFrom] = simplifyUnitWithValue(to)
const [fromSimplified, factorTo] = simplifyUnitWithValue(from || noUnit)
const [toSimplified, factorFrom] = simplifyUnitWithValue(to || noUnit)
return round(
((value * factorTo) / factorFrom) *
unitsConversionFactor(
@ -239,7 +243,7 @@ export function simplifyUnitWithValue(
value ? round(value * factor) : value
]
}
export function areUnitConvertible(a: Unit, b: Unit) {
export function areUnitConvertible(a: Unit | undefined, b: Unit | undefined) {
if (a == null || b == null) {
return true
}

View File

@ -7,6 +7,7 @@ Accueil: Home
Alors: Then
Année d'activité: Years of activity
Assimilé salarié: '"Assimilé-salarié"'
Au-delà du dernier plafond: Beyond the last ceiling
Au-dessus de: Above
Aucun résultat: No result$
Auto-entrepreneur: Auto-entrepreneur
@ -62,6 +63,7 @@ Modifier: Modify
Modifier mes réponses: Change my answers
Mon entreprise: My company
Mon revenu: My income
Montant: Amount
Montant des cotisations: Amount of contributions
'Nom de l''entreprise ou SIREN ': Company name or SIREN code
Non: 'No'
@ -76,6 +78,7 @@ Pas en auto-entrepreneur: Not in auto-entrepreneur
Pas implémenté: Not implemented
Passer: Skip
Personnalisez l'integration: Customize the integration
Plafonds des tranches: Wafer ceilings
Plein écran: Fullscreen
Plus d'informations: More information (fr)
Plusieurs associés: Several partners
@ -115,6 +118,7 @@ Simulations personnalisées: Customized simulations
Sinon: Else
Suivant: Next
Taux: Rate
Taux calculé: Calculated rate
Taux moyen: Average rate
Total des retenues: Total withheld
Tout effacer: Delete all

View File

@ -248,42 +248,76 @@ contrat salarié . CDD . CPF:
titre.fr: CPF
contrat salarié . CDD . compensation pour congés non pris:
description.en: >-
The employee on a fixed-term contract has the same rights for paid leave as
the employee on a permanent contract. He acquires and takes his paid leave
under the same conditions.
[automatic] The employee with a fixed-term contract has the same paid
vacation entitlements as the employee with a permanent contract. He acquires
and takes his paid leave under the same conditions.
However, it is common that the employee may not take all of his or her leave
before the end of the contract, in which case he or she will receive a
compensatory allowance for paid leave paid by the employer.
It is however common that the employee can not take all his leave before the
end of his contract. In that case, he receives a compensatory alowance paid
by the employer.
description.fr: >
There are two methods of calculating the allowance for leave without pay.
### Tenths method
This method of calculation will most often be favourable to the employee
when he or she has worked overtime. An allowance equal to one tenth of the
total gross remuneration received by the employee during the reference
period.
## Salary continuance method ##
This method will most often be favourable to the employee when he or she has
received a salary increase.
In making the calculation, the employer may take into account either : - the
actual time of the month, - the average number of working days (or working
days), - the actual number of working days (or working days).
description.fr: >-
Le salarié en CDD bénéficie des mêmes droits à congés payés que le salarié
en CDI. Il acquiert et prend ses congés payés dans les mêmes conditions.
Il est cependant courant que le salarié ne puisse pas prendre tous ses
congés avant le terme de son contrat, il bénéficie alors d'une indemnité
compensatrice de congés payés versée par l'employeur.
Il existe deux méthodes pour calculer l'indemnité de congés non pris.
### Méthode "du dixième"
Ce mode de calcul sera le plus souvent favorable au salarié lorsque celui-ci
a accompli des heures supplémentaires. Une indemnité égale au dixième de la
rémunération brute totale perçue par le salarié au cours de la période de
référence.
### Méthode "maintien du salaire"
Cette méthode sera le plus souvent favorable au salarié lorsque celui-ci a
bénéficié dune augmentation de salaire.
Pour effectuer le calcul, l'employeur peut tenir compte soit : - de
l'horaire réel du mois, - du nombre moyen de jours ouvrables (ou ouvrés), -
du nombre réel de jours ouvrables (ou ouvrés).
note.en: >
[automatic] The indemnity is paid at the end of the contract, unless the
fixed-term contract is continued by a permanent contract.
Note that the El Khomri law modifies article L3141-12:
- before: Leave can be taken as soon as you become entitled....
- before: Leave can be taken as soon as the entitlement arises.
- now: Leaves can be taken as soon as you are hired...
- now: Leaves can be taken as soon as you are hired.
note.fr: >
L'indemnité est versée à la fin du contrat, sauf si le CDD se poursuit par
un CDI.
À noter, la loi El Khomri modifie l'article L3141-12:
- avant : Les congés peuvent être pris dès l'ouverture des droits [...]
- avant : Les congés peuvent être pris dès l'ouverture des droits
- maintenant : Les congés peuvent être pris dès lembauche [...]
titre.en: untaken vacation compensation
titre.fr: compensation pour congés non pris
- maintenant : Les congés peuvent être pris dès lembauche
titre.en: '[automatic] holiday pay'
titre.fr: indemnité de congés payés
contrat salarié . CDD . compensation pour congés non pris . assiette mensuelle:
titre.en: monthly basis
titre.fr: assiette mensuelle
@ -3223,6 +3257,9 @@ dirigeant . indépendant . PLNR régime général:
question.fr: Avez-vous opté pour le rattachement au régime général des indépendants ?
titre.en: PLNR general scheme
titre.fr: PLNR régime général
dirigeant . indépendant . assiette des cotisations:
titre.en: '[automatic] contribution base'
titre.fr: assiette des cotisations
dirigeant . indépendant . conjoint collaborateur:
description.en: >
[automatic] Allows the executive's spouse to be covered by social protection
@ -3309,12 +3346,12 @@ dirigeant . indépendant . conjoint collaborateur . assiette . revenu avec parta
dirigeant . indépendant . conjoint collaborateur . assiette . revenu sans partage:
description.en: >-
[automatic] The collaborating spouse will pay social security contributions
calculated on the basis of a percentage of the professional income of the
manager of the company (one third or one half).
calculated on the basis of a percentage of the contribution base of the
company manager (one third or one half).
description.fr: >-
Le conjoint collaborateur paiera des cotisations sociales calculées sur une
base d'un pourcentage du revenu professionnel du gérant de l'entreprise (un
tiers ou la moitié).
base d'un pourcentage du assiette des cotisations du gérant de l'entreprise
(un tiers ou la moitié).
titre.en: undivided income
titre.fr: revenu sans partage
dirigeant . indépendant . conjoint collaborateur . cotisations:
@ -3384,8 +3421,8 @@ dirigeant . indépendant . cotisations et contributions . cotisations . déducti
titre.en: tobacco deduction
titre.fr: déduction tabac
dirigeant . indépendant . cotisations et contributions . cotisations . déduction tabac . revenus déduits:
titre.en: professional income (with tobacco deduction)
titre.fr: revenu professionnel (avec déduction tabac)
titre.en: '[automatic] contribution base (with tobacco deduction)'
titre.fr: assiette des cotisations (avec déduction tabac)
dirigeant . indépendant . cotisations et contributions . cotisations . indemnités journalières maladie:
description.en: >-
Contributions for the daily allowances of self-employed people. If the state
@ -3515,6 +3552,9 @@ dirigeant . indépendant . cotisations et contributions . cotisations . maladie
dirigeant . indépendant . cotisations et contributions . cotisations . retraite complémentaire:
titre.en: supplementary pension
titre.fr: retraite complémentaire
dirigeant . indépendant . cotisations et contributions . cotisations . retraite complémentaire . plafond:
titre.en: '[automatic] supplementary pension ceiling for self-employed persons'
titre.fr: plafond retraite complémentaire des indépendants
dirigeant . indépendant . cotisations et contributions . cotisations . retraite complémentaire . taux spécifique PLNR:
description.en: >
Non-regulated liberal professions who started their activity as of 1 January
@ -3634,36 +3674,8 @@ dirigeant . indépendant . revenu net de cotisations:
titre.en: net contribution income
titre.fr: revenu net de cotisations
dirigeant . indépendant . revenu professionnel:
description.en: >
It is the net income of the self-employed person from deductible
contributions that is used as the basis for calculating contributions and
taxes for the self-employed.
Attention, **our calculation is valid for a standard scheme**:
The self-employed person who starts out will pay a fixed contributions
during his first months. He will then have to regularize this situation in
relation to the income he actually received.
This calculation should therefore be seen as *the amount that should in any
case be paid* in the short term after several months of activity.
description.fr: >
C'est le revenu net de cotisations déductibles du travailleur indépendant,
qui sert de base au calcul des cotisations et de l'impôt pour les
indépendants.
Attention, **notre calcul est fait au régime de croisière**:
l'indépendant qui se lance paiera pendant ses 2 premières années un forfait
relativement réduit de cotisations sociales. Il devra ensuite régulariser
cette situation par rapport au revenu qu'il a vraiment perçu.
Il faut donc voir ce calcul comme *le montant qui devra de toute façon être
payé* à court terme après 2 ans d'exercice.
titre.en: Professional income
titre.fr: revenu professionnel (net imposable)
titre.en: '[automatic] occupational income'
titre.fr: revenu professionnel
dirigeant . indépendant . revenus étrangers:
description.en: >
[automatic] Foreign income is income declared by self-employed persons in

View File

@ -1,5 +1,5 @@
revenu imposable:
format: euros
unité:
revenu abattu:
formule:
@ -12,25 +12,20 @@ impôt sur le revenu:
barème:
assiette: revenu abattu
tranches:
- en-dessous de: 9807
taux: 0%
- de: 9807
à: 27086
taux: 14%
- de: 27086
à: 72617
taux: 30%
- de: 72617
à: 153783
taux: 41%
- au-dessus de: 153783
taux: 45%
- taux: 0%
plafond: 9807
- taux: 14%
plafond: 27086
- taux: 30%
plafond: 72617
- taux: 41%
plafond: 153783
- taux: 45%
impôt final:
formule:
allègement:
assiette: impôt sur le revenu
décote:
plafond: 1177
taux: 75%
plafond: 1177

View File

@ -18,8 +18,8 @@ describe('conversation', function() {
{ nom: 'top . startHere', formule: { somme: ['a', 'b'] } },
{ nom: 'top . a', formule: 'aa' },
{ nom: 'top . b', formule: 'bb' },
{ nom: 'top . aa', question: '?', titre: 'a' },
{ nom: 'top . bb', question: '?', titre: 'b' }
{ nom: 'top . aa', question: '?', titre: 'a', unité: '€' },
{ nom: 'top . bb', question: '?', titre: 'b', unité: '€' }
],
rules = rawRules.map(enrichRule),
state = merge(baseState, {
@ -40,9 +40,9 @@ describe('conversation', function() {
{ nom: 'top . a', formule: 'aa' },
{ nom: 'top . b', formule: 'bb' },
{ nom: 'top . c', formule: 'cc' },
{ nom: 'top . aa', question: '?', titre: 'a' },
{ nom: 'top . bb', question: '?', titre: 'b' },
{ nom: 'top . cc', question: '?', titre: 'c' }
{ nom: 'top . aa', question: '?', titre: 'a', unité: '€' },
{ nom: 'top . bb', question: '?', titre: 'b', unité: '€' },
{ nom: 'top . cc', question: '?', titre: 'c', unité: '€' }
],
rules = rawRules.map(enrichRule)

View File

@ -159,9 +159,9 @@ describe('collectMissingVariables', function() {
alors: {
multiplicateur: 'deux',
tranches: [
{ 'en-dessous de': 1, taux: 0.1 },
{ de: 1, à: 2, taux: 'trois' },
{ 'au-dessus de': 2, taux: 10 }
{ plafond: 1, taux: 0.1 },
{ plafond: 2, taux: 'trois' },
{ taux: 10 }
]
}
},
@ -170,8 +170,8 @@ describe('collectMissingVariables', function() {
alors: {
multiplicateur: 'quatre',
tranches: [
{ 'en-dessous de': 1, taux: 0.1 },
{ de: 1, à: 2, taux: 1.8 },
{ plafond: 1, taux: 0.1 },
{ plafond: 2, taux: 1.8 },
{ 'au-dessus de': 2, taux: 10 }
]
}

View File

@ -92,19 +92,15 @@ ya:
barème:
assiette: revenu abattu
tranches:
- en-dessous de: 9807
taux: 0%
- de: 9807
à: 27086
taux: 14%
- de: 27086
à: 72617
taux: 30%
- de: 72617
à: 153783
taux: 41%
- au-dessus de: 153783
taux: 45%
- taux: 0%
plafond: 9807
- taux: 14%
plafond: 27086
- taux: 30%
plafond: 72617
- taux: 41%
plafond: 153783
- taux: 45%
- nom: impôt sur le revenu à payer
@ -112,8 +108,8 @@ ya:
allègement:
assiette: impôt sur le revenu
décote:
plafond: 1177
taux: 75%
plafond: 1177
`
let target = 'impôt sur le revenu à payer'

View File

@ -6,10 +6,10 @@
*/
import { expect } from 'chai'
import { serializeUnit } from 'Engine/units'
import { collectMissingVariables } from '../source/engine/generateQuestions'
import { enrichRule } from '../source/engine/rules'
import { analyse, parseAll } from '../source/engine/traverse'
import { parseUnit } from '../source/engine/units'
import testSuites from './load-mecanism-tests'
describe('Mécanismes', () =>
@ -59,7 +59,7 @@ describe('Mécanismes', () =>
if (unit) {
expect(target.unit).not.to.be.equal(undefined)
expect(serializeUnit(target.unit)).to.eql(unit)
expect(target.unit).to.deep.equal(parseUnit(unit))
}
})
))

View File

@ -23,8 +23,8 @@ montant décoté:
allègement:
assiette: montant
décote:
plafond: 2040
taux: 100%
plafond: 2040
exemples:
- situation:
montant: 1000
@ -37,8 +37,8 @@ montant franchisé et décoté:
assiette: montant
franchise: 1200
décote:
plafond: 2040
taux: 75%
plafond: 2040
exemples:
- situation:
montant: 100
@ -103,8 +103,8 @@ montant franchisé, décote, abattu:
assiette: montant
franchise: 1200
décote:
plafond: 2040
taux: 75%
plafond: 2040
abattement: 20507
exemples:
- situation:

View File

@ -1,79 +0,0 @@
base:
unité: £
formule: 300
assiette:
unité: £
Simple:
formule:
barème continu:
assiette: assiette
multiplicateur: base
points:
0: 0%
0.4: 3.16%
1.1: 6.35%
unité attendue: £
exemples:
- nom: Premier point
situation:
assiette: 10
valeur attendue: 0.026
- nom: Deuxième point
situation:
assiette: 120
valeur attendue: 3.792
- nom: Premier point
situation:
assiette: 150
valeur attendue: 5.423
- nom: Troisième point
situation:
assiette: 330
valeur attendue: 20.955
- nom: Au-delà
situation:
assiette: 1000
valeur attendue: 63.5
base deux:
unité: µ
formule: 300
assiette deux:
unité: µ
Retour de taux, pas d'assiette:
unité: '%'
formule:
barème continu:
assiette: assiette deux
multiplicateur: base deux
points:
0: 100%
0.75: 100%
1: 0%
retourne seulement le taux: oui
unité attendue: '%'
exemples:
- nom: Premier point
situation:
assiette deux: 200
valeur attendue: 100
- nom: Deuxième point
situation:
assiette deux: 225
valeur attendue: 100
- nom: Troisième point
situation:
assiette deux: 262.5
valeur attendue: 50
- nom: Quatrième point
situation:
assiette deux: 300
valeur attendue: 0
- nom: Cinquième point
situation:
assiette deux: 300
valeur attendue: 0

View File

@ -1,76 +0,0 @@
assiette:
unité:
Barème linéaire en taux:
formule:
barème linéaire:
assiette: assiette
tranches:
- de: 0
à: 999
taux: 5%
- de: 1000
à: 1999
taux: 10%
- au-dessus de: 2000
taux: 15%
unité attendue:
exemples:
- nom: 'petite assiette'
situation:
assiette: 200
valeur attendue: 10
- nom: 'moyenne assiette'
situation:
assiette: 1500
valeur attendue: 150
- nom: 'grande assiette'
situation:
assiette: 10000
valeur attendue: 1500
- nom: "pour choisir la tranche, l'assiette est arrondie au préalable"
situation:
assiette: 999.3
valeur attendue: 49.965
- nom: "pour choisir la tranche, l'assiette est arrondie au préalable (2)"
situation:
assiette: 999.6
valeur attendue: 99.96
Barème linéaire en montant:
formule:
barème linéaire:
assiette: assiette
tranches:
- de: 0
à: 999
montant: 50
- de: 1000
à: 1999
montant: 170
- au-dessus de: 2000
montant: 400
unité attendue:
exemples:
- nom: 'petite assiette'
situation:
assiette: 200
valeur attendue: 50
- nom: 'moyenne assiette'
situation:
assiette: 1500
valeur attendue: 170
- nom: 'grande assiette'
situation:
assiette: 10000
valeur attendue: 400
- nom: "pour choisir la tranche, l'assiette est arrondie au préalable"
situation:
assiette: 999.3
valeur attendue: 50
- nom: "pour choisir la tranche, l'assiette est arrondie au préalable (2)"
situation:
assiette: 999.6
valeur attendue: 170

View File

@ -1,8 +1,8 @@
assiette:
unité:
unité: /mois
base:
unité:
unité: /mois
Barème en taux marginaux:
formule:
@ -10,14 +10,12 @@ Barème en taux marginaux:
assiette: assiette
multiplicateur: base
tranches:
- en-dessous de: 1
taux: 4.65%
- de: 1
à: 3
taux: 3%
- au-dessus de: 3
taux: 1%
unité attendue:
- taux: 4.65%
plafond: 1
- taux: 3%
plafond: 3
- taux: 1%
unité attendue: €/mois
exemples:
- nom: 'petite assiette'
situation:
@ -42,16 +40,14 @@ Barème à composantes:
multiplicateur: base
composantes:
- tranches:
- en-dessous de: 1
taux: 2%
- au-dessus de: 1
taux: 0%
- taux: 2%
plafond: 1
- taux: 0%
- tranches:
- en-dessous de: 2
taux: 9%
- au-dessus de: 2
taux: 29%
unité attendue:
- taux: 9%
plafond: 2
- taux: 29%
unité attendue: €/mois
exemples:
- nom:
@ -78,11 +74,10 @@ deuxième barème:
assiette: assiette
multiplicateur: base
tranches:
- en-dessous de: 1
taux: taux variable
- au-dessus de: 1
taux: 90%
unité attendue:
- taux: taux variable
plafond: 1
- taux: 90%
unité attendue: '€/mois'
exemples:
- nom: taux faible
@ -109,3 +104,37 @@ deuxième barème:
base: 100
variables manquantes:
- ma condition
tranche 1:
formule: 100 €/mois
tranche 2:
unité: €/an
tranches variables:
formule:
barème:
assiette: assiette
tranches:
- taux: 10%
plafond: tranche 1
- taux: 50%
plafond: tranche 2
exemples:
- nom: tranche 2 manquante non active
situation:
assiette: 40
valeur attendue: 4
- nom: tranche 2 manquante active
situation:
assiette: 200
variables manquantes:
- tranche 2
- nom: tranche 2 active
situation:
assiette: 200
tranche 2: 12000
valeur attendue: 60 # 10% * 100 + 50% * 100
- nom: tranche 2 dépassée
situation:
assiette: 2000
tranche 2: 12000
valeur attendue: 460 # 10% * 100 + 50% * 900

View File

@ -1,35 +0,0 @@
ma cotisation:
unité:
Complément:
formule:
complément:
cible: ma cotisation
montant: 100
exemples:
- nom:
situation:
ma cotisation: 33
valeur attendue: 67
autre cotisation:
unité:
Complément à composantes:
formule:
complément:
composantes:
- nom: A
cible: ma cotisation
montant: 100
- nom: B
cible: autre cotisation
montant: 200
exemples:
- nom:
situation:
ma cotisation: 33
autre cotisation: 133
valeur attendue: 134

View File

@ -72,15 +72,13 @@ Conversion de mécanisme 1:
unité: €/an
formule:
barème:
assiette: assiette mensuelle [€/an]
assiette: assiette mensuelle
tranches:
- en-dessous de: 30000
taux: 4.65%
- de: 30000
à: 90000
taux: 3%
- au-dessus de: 90000
taux: 1%
- taux: 4.65%
plafond: 30000 €/an
- taux: 3%
plafond: 90000 €/an
- taux: 1%
exemples:
- situation:
@ -93,15 +91,13 @@ assiette annuelle:
Conversion de mécanisme 2:
formule:
barème:
assiette: assiette annuelle [€/mois]
assiette: assiette annuelle
tranches:
- en-dessous de: 2500
taux: 4.65%
- de: 2500
à: 7500
taux: 3%
- au-dessus de: 7500
taux: 1%
- taux: 4.65%
plafond: 2500 €/mois
- taux: 3%
plafond: 7500 €/mois
- taux: 1%
exemples:
- situation:
assiette annuelle: 36000
@ -126,8 +122,8 @@ retraite:
formule:
multiplication:
assiette: assiette annuelle
plafond: 12 k€/an
taux: 10%
plafond: 12 k€/an
Conversion dans une somme compliquée:
formule:

View File

@ -98,7 +98,7 @@ nombre de personnes:
division trois:
formule: salaire de base / nombre de personnes
unité attendue: $ / personne
unité attendue: $/personne
exemples:
- situation:
salaire de base: 3000

View File

@ -0,0 +1,33 @@
assiette:
unité:
Grille:
formule:
grille:
assiette: assiette
unité:
tranches:
- montant: 50
plafond: 1000
- montant: 170
plafond: 2000
- montant: 400
unité attendue:
exemples:
- nom: 'petite assiette'
situation:
assiette: 200
valeur attendue: 50
- nom: 'moyenne assiette'
situation:
assiette: 1500
valeur attendue: 170
- nom: 'grande assiette'
situation:
assiette: 10000
valeur attendue: 400
- nom: 'assiette limite'
situation:
assiette: 999.3
valeur attendue: 50

View File

@ -70,8 +70,8 @@ Multiplication complète:
multiplication:
assiette: mon assiette
facteur: mon facteur
plafond: mon plafond
taux: 0.5%
plafond: mon plafond
unité attendue: €.patates
exemples:

View File

@ -0,0 +1,47 @@
base:
unité: £
formule: 300
assiette:
unité: £
base deux:
unité: µ
formule: 300
assiette deux:
unité: µ
Simple:
unité: '%'
formule:
taux progressif:
assiette: assiette deux
multiplicateur: base deux
tranches:
- plafond: 0.75
taux: 100%
- plafond: 1
taux: 0%
unité attendue: '%'
exemples:
- nom: Premier point
situation:
assiette deux: 200
valeur attendue: 100
- nom: Deuxième point
situation:
assiette deux: 225
valeur attendue: 100
- nom: Troisième point
situation:
assiette deux: 262.5
valeur attendue: 50
- nom: Quatrième point
situation:
assiette deux: 300
valeur attendue: 0
- nom: Cinquième point
situation:
assiette deux: 300
valeur attendue: 0

View File

@ -44,7 +44,7 @@ exports[`calculate simulations-auto-entrepreneur: échelle de revenus 9`] = `"[
exports[`calculate simulations-auto-entrepreneur: échelle de revenus 10`] = `"[1148303,148303,1000000,131979,868021]"`;
exports[`calculate simulations-indépendant: acre 1`] = `"[73028,23028,50000,51980,8052,41948,null,73028]"`;
exports[`calculate simulations-indépendant: acre 1`] = `"[73024,23024,50000,51980,8052,41948,null,73024]"`;
exports[`calculate simulations-indépendant: activité 1`] = `"[28923,8923,20000,20783,604,19396,null,28923]"`;
@ -52,7 +52,7 @@ exports[`calculate simulations-indépendant: activité 2`] = `"[29101,9101,2000
exports[`calculate simulations-indépendant: impôt sur le revenu 1`] = `"[29085,9085,20000,20787,603,19397,null,29085]"`;
exports[`calculate simulations-indépendant: impôt sur le revenu 2`] = `"[73028,23028,50000,51980,8213,41787,null,73028]"`;
exports[`calculate simulations-indépendant: impôt sur le revenu 2`] = `"[73024,23024,50000,51980,8213,41787,null,73024]"`;
exports[`calculate simulations-indépendant: impôt sur le revenu 3`] = `"[29085,9085,20000,20787,2079,17921,null,29085]"`;
@ -62,7 +62,7 @@ exports[`calculate simulations-indépendant: inversions 2`] = `"[50000,16003,33
exports[`calculate simulations-indépendant: inversions 3`] = `"[14596,4596,10000,10394,0,10000,null,14596]"`;
exports[`calculate simulations-indépendant: inversions 4`] = `"[88551,27364,61187,63588,11187,50000,null,88551]"`;
exports[`calculate simulations-indépendant: inversions 4`] = `"[88547,27360,61187,63588,11187,50000,null,88547]"`;
exports[`calculate simulations-indépendant: inversions 5`] = `"[14596,4596,10000,10394,0,10000,null,15596]"`;
@ -82,9 +82,9 @@ exports[`calculate simulations-indépendant: échelle de revenus 5`] = `"[7427,
exports[`calculate simulations-indépendant: échelle de revenus 6`] = `"[14596,4596,10000,10394,0,10000,null,14596]"`;
exports[`calculate simulations-indépendant: échelle de revenus 7`] = `"[139598,39598,100000,103788,24245,75755,null,139598]"`;
exports[`calculate simulations-indépendant: échelle de revenus 7`] = `"[139594,39594,100000,103788,24245,75755,null,139594]"`;
exports[`calculate simulations-indépendant: échelle de revenus 8`] = `"[1239743,239743,1000000,1033661,467503,532497,null,1239743]"`;
exports[`calculate simulations-indépendant: échelle de revenus 8`] = `"[1239955,239955,1000000,1033666,467505,532495,null,1239955]"`;
exports[`calculate simulations-rémunération-dirigeant: Assimilé salarié - ACRE 1`] = `"[7257,7257,7184,4,13,16]"`;
@ -186,7 +186,7 @@ exports[`calculate simulations-rémunération-dirigeant: Indépendant - échell
exports[`calculate simulations-rémunération-dirigeant: Indépendant - échelle de rémunération 6`] = `"[30434,33997,24912,4,48,0]"`;
exports[`calculate simulations-rémunération-dirigeant: Indépendant - échelle de rémunération 7`] = `"[56271,69892,36442,4,56,0]"`;
exports[`calculate simulations-rémunération-dirigeant: Indépendant - échelle de rémunération 7`] = `"[56273,69895,36431,4,56,0]"`;
exports[`calculate simulations-salarié: JEI 1`] = `"[3440,0,0,3000,2353,2187]"`;

View File

@ -15,15 +15,13 @@ répartition salaire sur dividendes:
impôt sur les sociétés:
formule:
barème:
assiette: bénéfice [€/an]
assiette: bénéfice
tranches:
- en-dessous de: 38120
taux: 15%
- de: 38120
à: 500000
taux: 28%
- au-dessus de: 500000
taux: 33.3%
- taux: 15%
plafond: 38120 €/an
- taux: 28%
plafond: 500000 €/an
- taux: 33.3%
références:
fiche service-public.fr: https://www.service-public.fr/professionnels-entreprises/vosdroits/F23575

View File

@ -196,9 +196,9 @@ describe('analyse with mecanisms', function() {
assiette: 2008,
multiplicateur: 1000,
tranches: [
{ 'en-dessous de': 1, taux: 0.1 },
{ de: 1, à: 2, taux: 1.2 },
{ 'au-dessus de': 2, taux: 10 }
{ plafond: 1, taux: 0.1 },
{ plafond: 2, taux: 1.2 },
{ taux: 10 }
]
}
}
@ -221,16 +221,16 @@ describe('analyse with mecanisms', function() {
composantes: [
{
tranches: [
{ 'en-dessous de': 1, taux: 0.05 },
{ de: 1, à: 2, taux: 0.4 },
{ 'au-dessus de': 2, taux: 5 }
{ plafond: 1, taux: 0.05 },
{ plafond: 2, taux: 0.4 },
{ taux: 5 }
]
},
{
tranches: [
{ 'en-dessous de': 1, taux: 0.05 },
{ de: 1, à: 2, taux: 0.8 },
{ 'au-dessus de': 2, taux: 5 }
{ plafond: 1, taux: 0.05 },
{ plafond: 2, taux: 0.8 },
{ taux: 5 }
]
}
]
@ -257,9 +257,9 @@ describe('analyse with mecanisms', function() {
si: '3 > 4',
alors: {
tranches: [
{ 'en-dessous de': 1, taux: 0.1 },
{ de: 1, à: 2, taux: 1.2 },
{ 'au-dessus de': 2, taux: 10 }
{ plafond: 1, taux: 0.1 },
{ plafond: 2, taux: 1.2 },
{ taux: 10 }
]
}
},
@ -267,9 +267,9 @@ describe('analyse with mecanisms', function() {
si: '3 > 2',
alors: {
tranches: [
{ 'en-dessous de': 1, taux: 0.1 },
{ de: 1, à: 2, taux: 1.8 },
{ 'au-dessus de': 2, taux: 10 }
{ plafond: 1, taux: 0.1 },
{ plafond: 2, taux: 1.8 },
{ taux: 10 }
]
}
}
@ -294,41 +294,6 @@ describe('analyse with mecanisms', function() {
).to.have.property('nodeValue', 3200)
})
it('should handle complements', function() {
let rawRules = [
{ nom: 'top' },
{
nom: 'top . startHere',
formule: { complément: { cible: 'dix', montant: 93 } }
},
{ nom: 'top . dix', formule: 17 }
],
rules = parseAll(rawRules.map(enrichRule))
expect(
analyse(rules, 'startHere')(stateSelector).targets[0]
).to.have.property('nodeValue', 93 - 17)
})
it('should handle components in complements', function() {
let rawRules = [
{ nom: 'top' },
{
nom: 'top . startHere',
formule: {
complément: {
cible: 'dix',
composantes: [{ montant: 93 }, { montant: 93 }]
}
}
},
{ nom: 'top . dix', formule: 17 }
],
rules = parseAll(rawRules.map(enrichRule))
expect(
analyse(rules, 'startHere')(stateSelector).targets[0]
).to.have.property('nodeValue', 2 * (93 - 17))
})
it('should handle filtering on components', function() {
let rawRules = [
{ nom: 'top' },
@ -342,17 +307,17 @@ describe('analyse with mecanisms', function() {
composantes: [
{
tranches: [
{ 'en-dessous de': 1, taux: 0.05 },
{ de: 1, à: 2, taux: 0.4 },
{ 'au-dessus de': 2, taux: 5 }
{ plafond: 1, taux: 0.05 },
{ plafond: 2, taux: 0.4 },
{ taux: 5 }
],
attributs: { 'dû par': 'salarié' }
},
{
tranches: [
{ 'en-dessous de': 1, taux: 0.05 },
{ de: 1, à: 2, taux: 0.8 },
{ 'au-dessus de': 2, taux: 5 }
{ plafond: 1, taux: 0.05 },
{ plafond: 2, taux: 0.8 },
{ taux: 5 }
],
attributs: { 'dû par': 'employeur' }
}
@ -384,17 +349,17 @@ describe('analyse with mecanisms', function() {
composantes: [
{
tranches: [
{ 'en-dessous de': 1, taux: 0.05 },
{ de: 1, à: 2, taux: 0.4 },
{ 'au-dessus de': 2, taux: 5 }
{ plafond: 1, taux: 0.05 },
{ plafond: 2, taux: 0.4 },
{ taux: 5 }
],
attributs: { 'dû par': 'salarié' }
},
{
tranches: [
{ 'en-dessous de': 1, taux: 0.05 },
{ de: 1, à: 2, taux: 0.8 },
{ 'au-dessus de': 2, taux: 5 }
{ plafond: 1, taux: 0.05 },
{ plafond: 2, taux: 0.8 },
{ taux: 5 }
],
attributs: { 'dû par': 'employeur' }
}