Skip to content

Commit

Permalink
feat(simulateurs): Nouveau simulateur réduction générale
Browse files Browse the repository at this point in the history
  • Loading branch information
liliced committed Aug 30, 2024
1 parent 25fb6ec commit 003e48c
Show file tree
Hide file tree
Showing 21 changed files with 818 additions and 7 deletions.
1 change: 1 addition & 0 deletions modele-social/règles/entreprise/entreprise.publicodes
Original file line number Diff line number Diff line change
Expand Up @@ -370,6 +370,7 @@ entreprise . capital social:
unité: €

entreprise . salariés:

entreprise . salariés . effectif:
unité: employés
variations:
Expand Down
22 changes: 21 additions & 1 deletion modele-social/règles/salarié/cotisations.publicodes
Original file line number Diff line number Diff line change
Expand Up @@ -295,6 +295,7 @@ salarié . cotisations . exonérations . réduction générale:
urssaf.fr: https://www.urssaf.fr/portail/home/employeur/beneficier-dune-exoneration/exonerations-generales/la-reduction-generale.html
calcul: https://www.urssaf.fr/portail/home/employeur/beneficier-dune-exoneration/exonerations-generales/la-reduction-generale/le-calcul-de-la-reduction.html
cumuls: https://www.legisocial.fr/actualites-sociales/2068-comment-declarer-les-cotisations-dallocations-familiales-si-lentreprise-beneficie-du-regime-jei.html
BOSS: https://boss.gouv.fr/portail/accueil/exonerations/allegements-generaux.html#titre-chapitre-1--la-reduction-general
produit:
- cotisations . assiette
- coefficient
Expand All @@ -305,14 +306,16 @@ salarié . cotisations . exonérations . réduction générale:
coefficient:
privé: oui
produit:
- temps de travail . SMIC / cotisations . assiette * 1.6 - 1
- 1.6 * temps de travail . SMIC / cotisations . assiette - 1
- T / 0.6
plancher: 0%
plafond: T
unité: '%'
arrondi: 4 décimales
références:
urssaf.fr: https://www.urssaf.fr/portail/home/employeur/beneficier-dune-exoneration/exonerations-generales/la-reduction-generale/le-calcul-de-la-reduction/etape-1--determination-du-coeffi.html
Code de la sécurité sociale: https://www.legifrance.gouv.fr/affichCodeArticle.do?idArticle=LEGIARTI000025103779&cidTexte=LEGITEXT000006073189
Bulletin Officiel de la Sécurité Sociale: https://boss.gouv.fr/portail/accueil/exonerations/allegements-generaux.html#titre-chapitre-1--la-reduction-general-section-2---determination-du-mon-ii-calcul-du-montant-de-la-reduc-a-formule-de-calcul-de-la-reduct

imputation retraite complémentaire:
privé: oui
Expand All @@ -334,6 +337,23 @@ salarié . cotisations . exonérations . réduction générale:
régimes spécifiques . DFS: non
- 130%

part Urssaf:
produit:
- réduction générale
- T . sécurité sociale et chômage
- 1 / T

avec:
part chômage:
produit:
- réduction générale
- chômage . employeur . taux
- 1 / T

part retraite:
valeur: réduction générale
abattement: part Urssaf

salarié . cotisations . exonérations . T:
privé: oui
titre: Coefficient T
Expand Down
1 change: 1 addition & 0 deletions site/build/prerender.ts
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,7 @@ export const pagesToPrerender: {
sitePathFr.simulateurs['profession-libérale'].avocat,
sitePathFr.simulateurs['profession-libérale']['chirurgien-dentiste'],
sitePathFr.simulateurs['profession-libérale'].index,
sitePathFr.simulateurs['réduction-générale'],
].map((val) => encodeURI(val)),
infrance: [
sitePathEn.index,
Expand Down
2 changes: 1 addition & 1 deletion site/cypress/integration/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,4 +3,4 @@
Les jeux de tests qu'on souhaite faire tourner avec le langage FR et EN doivent aller dans le
sous-dossier `mon-entreprise/english`.

Les autres jeux ne seront testés qu'avent le langage FR.
Les autres jeux ne seront testés qu'avec le langage FR.
110 changes: 110 additions & 0 deletions site/cypress/integration/mon-entreprise/reduction-generale.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,110 @@
import { checkA11Y, fr } from '../../support/utils'

describe('Réduction générale', { testIsolation: false }, function () {
if (!fr) {
return
}

const inputSelector = 'div[id="simulator-legend"] input[inputmode="numeric"]'

before(function () {
return cy.visit('/simulateurs/réduction-générale')
})

it('should not crash', function () {
cy.contains('Salaire brut')
})

it('should allow to select a company size', function () {
cy.get('button').contains('SMIC').click()

cy.contains('Plus de 50 salariés').click()
cy.contains('Modifier mes réponses').click()
cy.get('div[data-cy="modal"]')
.eq(0)
.contains('Effectif')
.next()
.contains('100')
cy.get('div[data-cy="modal"]').eq(0).contains('Fermer').click()

cy.contains('Moins de 50 salariés').click()
cy.contains('Modifier mes réponses').click()
cy.get('div[data-cy="modal"]')
.eq(0)
.contains('Effectif')
.next()
.contains('10')
cy.get('div[data-cy="modal"]').eq(0).contains('Fermer').click()
})

it('should allow to change time period', function () {
cy.contains('Montant mensuel').click()
cy.get(inputSelector).first().type('{selectall}2000')

cy.contains('Montant annuel').click()
cy.get(inputSelector).first().should('have.value', '24 000 €')
})

it('should have median salary and SMIC buttons', function () {
cy.contains('Montant mensuel').click()
cy.get('button').contains('SMIC')
cy.get('button').contains('salaire médian').click()
cy.get(inputSelector).first().should('have.value', '2 600 €')
})

it('should display values for the réduction générale', function () {
cy.contains('Montant mensuel').click()
cy.get(inputSelector).first().type('{selectall}1900')

cy.get(
'p[id="salarié___cotisations___exonérations___réduction_générale-value"]'
).should('include.text', '494 €')
cy.get(
'p[id="salarié___cotisations___exonérations___réduction_générale___part_retraite-value"]'
).should('include.text', '93 €')
cy.get(
'p[id="salarié___cotisations___exonérations___réduction_générale___part_Urssaf-value"]'
).should('include.text', '401 €')
cy.get(
'p[id="salarié___cotisations___exonérations___réduction_générale___part_Urssaf___part_chômage-value"]'
).should('include.text', '63 €')

cy.contains('Plus de 50 salariés').click()
cy.get(
'p[id="salarié___cotisations___exonérations___réduction_générale-value"]'
).should('include.text', '500 €')
cy.get(
'p[id="salarié___cotisations___exonérations___réduction_générale___part_retraite-value"]'
).should('include.text', '93 €')
cy.get(
'p[id="salarié___cotisations___exonérations___réduction_générale___part_Urssaf-value"]'
).should('include.text', '407 €')
cy.get(
'p[id="salarié___cotisations___exonérations___réduction_générale___part_Urssaf___part_chômage-value"]'
).should('include.text', '63 €')
})

it('should display a warning for a salary too high', function () {
cy.contains('Montant mensuel').click()
cy.get(inputSelector).first().type('{selectall}3000')

cy.get('div[id="simulator-legend"]').should(
'include.text',
'La RGCP concerne uniquement les salaires inférieurs à 1,6 SMIC.'
)

cy.get(
'p[id="salarié___cotisations___exonérations___réduction_générale___part_retraite-value"]'
).should('include.text', '0 €')
cy.get(
'p[id="salarié___cotisations___exonérations___réduction_générale___part_Urssaf-value"]'
).should('include.text', '0 €')
cy.get(
'p[id="salarié___cotisations___exonérations___réduction_générale___part_Urssaf___part_chômage-value"]'
).should('include.text', '0 €')
})

it('should be RGAA compliant', function () {
checkA11Y()
})
})
9 changes: 9 additions & 0 deletions site/source/components/SimulateurWarning.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -151,6 +151,15 @@ export default function SimulateurWarning({
</Trans>
</Body>
)}
{simulateur === 'réduction-générale' && (
<Body>
<Trans i18nKey="simulateurs.warning.réduction-générale">
La réduction générale des cotisations patronales (RGCP) est
applicable jusqu’à 1,6 fois le SMIC et pour tout salarié cotisant à
l’assurance chômage.
</Trans>
</Body>
)}
</Warning>
)
}
Expand Down
2 changes: 1 addition & 1 deletion site/source/components/Simulation/SimulationGoal.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -161,7 +161,7 @@ export function SimulationGoal({
</Grid>
) : (
<Grid item>
<Body>
<Body id={`${dottedName.replace(/\s|\./g, '_')}-value`}>
{formatValue(evaluation, {
displayedUnit,
precision: round ? 0 : 2,
Expand Down
130 changes: 130 additions & 0 deletions site/source/components/Simulation/SimulationValue.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,130 @@
import { DottedName } from 'modele-social'
import { formatValue } from 'publicodes'
import React from 'react'
import { useSelector } from 'react-redux'
import { styled } from 'styled-components'

import { Grid } from '@/design-system/layout'
import { Body } from '@/design-system/typography/paragraphs'
import { targetUnitSelector } from '@/store/selectors/simulationSelectors'

import { Appear } from '../ui/animate'
import AnimatedTargetValue from '../ui/AnimatedTargetValue'
import { useEngine } from '../utils/EngineContext'
import { useInitialRender } from '../utils/useInitialRender'

type SimulationValueProps = {
dottedName: DottedName
label?: React.ReactNode
appear?: boolean
isTypeBoolean?: boolean
displayedUnit?: string
round?: boolean
}

export function SimulationValue({
dottedName,
label,
displayedUnit = '€',
round = true,
appear = true,
isTypeBoolean = false, // TODO : remove when type inference works in publicodes
}: SimulationValueProps) {
const engine = useEngine()
const currentUnit = useSelector(targetUnitSelector)
const evaluation = engine.evaluate({
valeur: dottedName,
arrondi: round ? 'oui' : 'non',
...(!isTypeBoolean ? { unité: currentUnit } : {}),
})
const rule = engine.getRule(dottedName)
const initialRender = useInitialRender()
if (evaluation.nodeValue === null) {
return null
}
if (evaluation.nodeValue === undefined) {
return null
}

return (
<Appear unless={!appear || initialRender}>
<StyledValue>
<Grid
container
style={{
alignItems: 'baseline',
justifyContent: 'space-between',
}}
spacing={2}
>
<Grid item md="auto" sm={9} xs={8}>
<StyledValueHeader>
<Grid
container
style={{
alignItems: 'center',
}}
>
<Grid item>
<StyledBody id={`${dottedName.replace(/\s|\./g, '_')}-label`}>
{label || rule.title}
</StyledBody>
</Grid>
</Grid>
</StyledValueHeader>
</Grid>

<StyledGuideLectureContainer item md>
<StyledGuideLecture />
</StyledGuideLectureContainer>

<Grid item>
<AnimatedTargetValue value={evaluation.nodeValue as number} />
<StyledBody id={`${dottedName.replace(/\s|\./g, '_')}-value`}>
{formatValue(evaluation, {
displayedUnit,
precision: round ? 0 : 2,
})}
</StyledBody>
</Grid>
</Grid>
</StyledValue>
</Appear>
)
}

const StyledGuideLectureContainer = styled(Grid)`
display: none;
@media (min-width: ${({ theme }) => theme.breakpointsWidth.md}) {
display: block;
}
`

const StyledGuideLecture = styled.div.attrs({ 'aria-hidden': true })`
border-bottom: 1px dashed
${({ theme }) =>
theme.darkMode
? theme.colors.extended.grey[100]
: theme.colors.extended.grey[700]};
align-self: baseline;
opacity: 50%;
flex: 1;
`
const StyledValueHeader = styled.div``

const StyledValue = styled.div`
position: relative;
z-index: 1;
padding: ${({ theme }) => theme.spacings.xxs} 0;
@media print {
padding: 0;
}
`

const StyledBody = styled(Body)`
color: ${({ theme }) => theme.colors.extended.grey[100]};
margin: 0;
padding: ${({ theme }) => `${theme.spacings.xs} ${theme.spacings.sm} 0 0`};
`
10 changes: 10 additions & 0 deletions site/source/locales/rules-en.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -9314,6 +9314,16 @@ salarié . cotisations . exonérations . réduction générale:
imputation sécurité sociale:
titre.en: '[automatic] social security charge'
titre.fr: imputation sécurité sociale
part Urssaf:
avec:
part chômage:
titre.en: '[automatic] unemployment share'
titre.fr: part chômage
titre.en: '[automatic] Urssaf share'
titre.fr: part Urssaf
part retraite:
titre.en: '[automatic] pension portion'
titre.fr: part retraite
plafond avec application de la DFS:
titre.en: '[automatic] ceiling with application of the DFS'
titre.fr: plafond avec application de la DFS
Expand Down
Loading

0 comments on commit 003e48c

Please sign in to comment.