Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: ajoute un onglet "mois par mois" au simulateur RGCP #3163

Merged
merged 21 commits into from
Oct 31, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
21 commits
Select commit Hold shift + click to select a range
e8454f6
refactor: rend PeriodSwitch personnalisable
liliced Oct 15, 2024
0bc35fa
feat: ajoute un onglet mois par mois au simulateur RGCP
liliced Oct 15, 2024
e88c3ea
feat: affiche la réduction générale mois par mois
liliced Oct 15, 2024
42ee5d5
feat: recalcule la RGCP lorsque la situation change
liliced Oct 15, 2024
e032eec
feat: calcule la RGCP mois par mois à la saisie
liliced Oct 15, 2024
8f710a4
feat: met à jour la rémunération totale lors de la modification mois …
liliced Oct 15, 2024
40500ba
refactor: crée un composant avec la répartition de la RGCP
liliced Oct 15, 2024
32cc04d
refactor: ajoute une prop contexte à SimulationValue
liliced Oct 15, 2024
f6462a6
feat: affiche la répartition de la RGCP mois par mois
liliced Oct 15, 2024
ff29b59
refactor: crée un composant avec les avertissements sur la RGCP
liliced Oct 15, 2024
8e8c81b
feat: affiche les avertissements RGCP dans l'onglet mois par mois
liliced Oct 15, 2024
1e67eff
feat: ajoute la langue dans la mise en forme de SimulationValue et Si…
liliced Oct 15, 2024
eb01f1f
feat: ajout d'une icone à côté du montant RGCP mois par mois
liliced Oct 18, 2024
11fb3b5
fix: empêche le clignotement de la Tooltip
liliced Oct 18, 2024
6e2b733
feat: ajoute une flèche à Tooltip
liliced Oct 18, 2024
cc0d29e
fix: corrige le z-index de SimulationGoals
liliced Oct 18, 2024
03fd4ce
feat: ajoute la possibilité de mettre des décimales (RGCP mois par mois)
liliced Oct 18, 2024
c704430
feat: ajoute un avertissement salaire trop haut pour la RGCP mois par…
liliced Oct 25, 2024
b3e47dc
tests: ajoute des tests pour la réduction générale mois par mois
liliced Oct 25, 2024
0d7344c
refactor: utilise la répartition de RGCP pré-existante
liliced Oct 25, 2024
5b26e40
feat: modifie l'avertissement pour le simulateur RGCP
liliced Oct 25, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
28 changes: 8 additions & 20 deletions modele-social/règles/salarié/cotisations.publicodes
Original file line number Diff line number Diff line change
Expand Up @@ -341,14 +341,19 @@ salarié . cotisations . exonérations . réduction générale:
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
valeur: réduction générale - imputation sécurité sociale

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

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

plafond avec application de la DFS:
privé: oui
Expand All @@ -360,23 +365,6 @@ 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
238 changes: 152 additions & 86 deletions site/cypress/integration/mon-entreprise/reduction-generale.ts
Original file line number Diff line number Diff line change
@@ -1,89 +1,155 @@
import { checkA11Y, fr } from '../../support/utils'

describe('Réduction générale', { testIsolation: false }, function () {
if (!fr) {
return
}
describe(
'Simulateur 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(inputSelector).first().type('{selectall}2000')

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('Réduction mensuelle').click()
cy.get(inputSelector).first().type('{selectall}2000')

cy.contains('Réduction annuelle').click()
cy.get(inputSelector).first().should('have.value', '24 000 €')
})

it('should display values for the réduction générale', function () {
cy.contains('Réduction mensuelle').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', '493,43 €')
cy.get(
'p[id="salarié___cotisations___exonérations___réduction_générale___imputation_retraite_complémentaire-value"]'
).should('include.text', '92,85 €')
cy.get(
'p[id="salarié___cotisations___exonérations___réduction_générale___imputation_sécurité_sociale-value"]'
).should('include.text', '400,58 €')
cy.get(
'p[id="salarié___cotisations___exonérations___réduction_générale___imputation_chômage-value"]'
).should('include.text', '62,57 €')
})

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(inputSelector).first().type('{selectall}2000')

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 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', '493,43 €')
cy.get(
'p[id="salarié___cotisations___exonérations___réduction_générale___part_retraite-value"]'
).should('include.text', '92,85 €')
cy.get(
'p[id="salarié___cotisations___exonérations___réduction_générale___part_Urssaf-value"]'
).should('include.text', '400,58 €')
cy.get(
'p[id="salarié___cotisations___exonérations___réduction_générale___part_Urssaf___part_chômage-value"]'
).should('include.text', '62,57 €')
})

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()
})
})
it('should display a warning for a remuneration too high', function () {
cy.contains('Réduction mensuelle').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___imputation_retraite_complémentaire-value"]'
).should('include.text', '0 €')
cy.get(
'p[id="salarié___cotisations___exonérations___réduction_générale___imputation_sécurité_sociale-value"]'
).should('include.text', '0 €')
cy.get(
'p[id="salarié___cotisations___exonérations___réduction_générale___imputation_chômage-value"]'
).should('include.text', '0 €')
})

it('should display remuneration and RGCP month by month', function () {
cy.contains('Réduction annuelle').click()
cy.get(inputSelector).first().type('{selectall}30000')

cy.contains('Réduction mois par mois').click()
cy.contains('Réduction générale mois par mois :')
cy.get(inputSelector)
.should('have.length', 12)
.each(($input) => {
cy.wrap($input).should('have.value', '2 500 €')
})
cy.get(
'td[id^="salarié___cotisations___exonérations___réduction_générale-"]'
)
.should('have.length', 12)
.each(($td) => {
cy.wrap($td).should('include.text', '174 €')
})
})

it('should calculate RGCP month by month independently', function () {
cy.contains('Moins de 50 salariés').click()
cy.contains('Réduction mois par mois').click()

cy.get(inputSelector).first().type('{selectall}1900')
cy.get(inputSelector).last().type('{selectall}2000')
cy.get(
'td[id="salarié___cotisations___exonérations___réduction_générale-janvier"]'
).should('include.text', '493,43 €')
cy.get(
'td[id="salarié___cotisations___exonérations___réduction_générale-décembre"]'
).should('include.text', '440,20 €')
})

it('should save remuneration between tabs', function () {
cy.contains('Moins de 50 salariés').click()
cy.contains('Réduction mensuelle').click()
cy.get(inputSelector).first().type('{selectall}1900')
cy.contains('Réduction mois par mois').click()
cy.get(inputSelector).first().type('{selectall}2000')
cy.get(inputSelector).last().type('{selectall}2000')

cy.contains('Réduction mensuelle').click()
cy.get(inputSelector).first().should('have.value', '1 916,67 €')
cy.contains('Réduction annuelle').click()
cy.get(inputSelector).first().should('have.value', '23 000 €')
cy.contains('Réduction mois par mois').click()
cy.get(inputSelector).each(($input, index) => {
let expectedValue = '1 900 €'
if (index === 0 || index === 11) {
expectedValue = '2 000 €'
}
cy.wrap($input).should('have.value', expectedValue)
})
})

it('should be RGAA compliant', function () {
cy.contains('Réduction mensuelle').click()
checkA11Y()
cy.contains('Réduction annuelle').click()
checkA11Y()
cy.contains('Réduction mois par mois').click()
checkA11Y()
})
}
)
6 changes: 5 additions & 1 deletion site/source/components/EngineValue/Condition.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +5,14 @@ export function Condition({
expression,
children,
engine: engineFromProps,
contexte = {},
}: ConditionProps) {
const defaultEngine = useEngine()
const engine = engineFromProps ?? defaultEngine
const nodeValue = engine.evaluate({ '!=': [expression, 'non'] }).nodeValue
const nodeValue = engine.evaluate({
'!=': [expression, 'non'],
contexte,
}).nodeValue

if (!nodeValue) {
return null
Expand Down
3 changes: 3 additions & 0 deletions site/source/components/EngineValue/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@ import { DottedName } from 'modele-social'
import Engine, { ASTNode, PublicodesExpression } from 'publicodes'
import React from 'react'

import { Contexte } from '@/domaine/Contexte'

export type ValueProps<Names extends string> = {
expression: PublicodesExpression
unit?: string
Expand All @@ -17,4 +19,5 @@ export type ConditionProps = {
expression: PublicodesExpression | ASTNode
children: React.ReactNode
engine?: Engine<DottedName>
contexte?: Contexte
}
26 changes: 22 additions & 4 deletions site/source/components/PeriodSwitch.tsx
Original file line number Diff line number Diff line change
@@ -1,16 +1,25 @@
import { useCallback } from 'react'
import { useTranslation } from 'react-i18next'
import { useDispatch, useSelector } from 'react-redux'

import { Radio, ToggleGroup } from '@/design-system/field'
import { updateUnit } from '@/store/actions/actions'
import { targetUnitSelector } from '@/store/selectors/simulationSelectors'

export default function PeriodSwitch() {
type Props = {
periods?: Array<{
label: string
unit: string
}>
onSwitch?: (unit: string) => void
}

export default function PeriodSwitch({ periods, onSwitch }: Props) {
const dispatch = useDispatch()

const currentUnit = useSelector(targetUnitSelector)
const { t } = useTranslation()
const periods = [
const defaultPeriods = [
{
label: t('Montant mensuel'),
unit: '€/mois',
Expand All @@ -20,17 +29,26 @@ export default function PeriodSwitch() {
unit: '€/an',
},
]
const periodsValue = periods || defaultPeriods

const onChange = useCallback(
(unit: string) => {
dispatch(updateUnit(unit))
onSwitch?.(unit)
},
[dispatch, onSwitch]
)

return (
<div>
<ToggleGroup
value={currentUnit}
onChange={(unit: string) => dispatch(updateUnit(unit))}
onChange={onChange}
mode="tab"
hideRadio
aria-label={t("Mode d'affichage")}
>
{periods.map(({ label, unit }) => (
{periodsValue.map(({ label, unit }) => (
<span
key={unit}
className={currentUnit !== unit ? 'print-hidden' : ''}
Expand Down
Loading
Loading