feat(rgcp): ajout d'un récapitulatif trimestriel

pull/3263/head
Alice Dahan 2024-12-12 15:40:18 +01:00 committed by liliced
parent 6454050828
commit 1c6bcd358b
6 changed files with 328 additions and 46 deletions

View File

@ -330,6 +330,7 @@ Tout déplier: Unfold all
Tout plier: Fold everything
Tout réinitialiser: Reset all
Travailleurs Non Salariés (TNS): Self-employed workers (TNS)
Trimestre: Quarter
Type: Type
URSSAF Mon entreprise, accéder à la page d'accueil: URSSAF Mon entreprise, go to home page
Un <1>capital « orphelin »</1> est versé aux <4>enfants des travailleurs indépendants</4> décédés, sous certaines conditions.:
@ -1514,6 +1515,15 @@ pages:
rémunération-primes:
popover: <0>Indicate here any elements of remuneration not affected by the
absence, such as bonuses.</0>
recap:
T1: 1st quarter
T2: 2nd quarter
T3: 3rd quarter
T4: 4th quarter
caption: "Quarterly summary :"
code671: code 671(€)
code801: code 801(€)
header: Calculated reduction
régularisation:
annuelle: Annual adjustment
progressive: Progressive regularization

View File

@ -345,6 +345,7 @@ Tout déplier: Tout déplier
Tout plier: Tout plier
Tout réinitialiser: Tout réinitialiser
Travailleurs Non Salariés (TNS): Travailleurs Non Salariés (TNS)
Trimestre: Trimestre
Type: Type
URSSAF Mon entreprise, accéder à la page d'accueil: URSSAF Mon entreprise, accéder à la page d'accueil
Un <1>capital « orphelin »</1> est versé aux <4>enfants des travailleurs indépendants</4> décédés, sous certaines conditions.:
@ -1608,6 +1609,15 @@ pages:
rémunération-primes:
popover: <0>Indiquez ici les éléments de rémunération non affectés par
l'absence, comme les primes.</0>
recap:
T1: 1er trimestre
T2: 2ème trimestre
T3: 3ème trimestre
T4: 4ème trimestre
caption: "Récapitulatif trimestriel :"
code671: code 671(€)
code801: code 801(€)
header: Réduction calculée
régularisation:
annuelle: Régularisation annuelle
progressive: Régularisation progressive

View File

@ -2,11 +2,13 @@ import { useTranslation } from 'react-i18next'
import { styled } from 'styled-components'
import RuleLink from '@/components/RuleLink'
import { Spacing } from '@/design-system/layout'
import { baseTheme } from '@/design-system/theme'
import { Body } from '@/design-system/typography/paragraphs'
import { useMediaQuery } from '@/hooks/useMediaQuery'
import RéductionGénéraleMois from './components/Mois'
import RécapitulatifTrimestre from './components/RécapitulatifTrimestre'
import RéductionGénéraleMois from './components/RéductionGénéraleMois'
import Warnings from './components/Warnings'
import { MonthState, Options, réductionGénéraleDottedName } from './utils'
@ -41,52 +43,112 @@ export default function RéductionGénéraleMoisParMois({
t('décembre'),
]
const quarters = {
[t('pages.simulateurs.réduction-générale.recap.T1', '1er trimestre')]:
data.slice(0, 3),
[t('pages.simulateurs.réduction-générale.recap.T2', '2ème trimestre')]:
data.slice(3, 6),
[t('pages.simulateurs.réduction-générale.recap.T3', '3ème trimestre')]:
data.slice(6, 9),
[t('pages.simulateurs.réduction-générale.recap.T4', '4ème trimestre')]:
data.slice(9),
}
return (
<>
{isDesktop ? (
<StyledTable>
<caption>
{t(
'pages.simulateurs.réduction-générale.month-by-month.caption',
'Réduction générale mois par mois :'
)}
</caption>
<thead>
<tr>
<th scope="col">{t('Mois')}</th>
<th scope="col">
{/* TODO: remplacer par rémunérationBruteDottedName lorsque ... */}
<RuleLink dottedName="salarié . rémunération . brut" />
</th>
<th scope="col">
<RuleLink dottedName={réductionGénéraleDottedName} />
</th>
<th scope="col">
<RuleLink dottedName="salarié . cotisations . exonérations . réduction générale . régularisation" />
</th>
</tr>
</thead>
<tbody>
{data.length > 0 &&
months.map((monthName, monthIndex) => (
<RéductionGénéraleMois
key={`month-${monthIndex}`}
monthName={monthName}
data={data[monthIndex]}
index={monthIndex}
onRémunérationChange={(
monthIndex: number,
rémunérationBrute: number
) => {
onRémunérationChange(monthIndex, rémunérationBrute)
}}
onOptionsChange={(monthIndex: number, options: Options) => {
onOptionsChange(monthIndex, options)
}}
<>
<StyledTable>
<caption>
{t(
'pages.simulateurs.réduction-générale.month-by-month.caption',
'Réduction générale mois par mois :'
)}
</caption>
<thead>
<tr>
<th scope="col">{t('Mois')}</th>
<th scope="col">
{/* TODO: remplacer par rémunérationBruteDottedName lorsque ... */}
<RuleLink dottedName="salarié . rémunération . brut" />
</th>
<th scope="col">
<RuleLink dottedName={réductionGénéraleDottedName} />
</th>
<th scope="col">
<RuleLink dottedName="salarié . cotisations . exonérations . réduction générale . régularisation" />
</th>
</tr>
</thead>
<tbody>
{data.length > 0 &&
months.map((monthName, monthIndex) => (
<RéductionGénéraleMois
key={`month-${monthIndex}`}
monthName={monthName}
data={data[monthIndex]}
index={monthIndex}
onRémunérationChange={(
monthIndex: number,
rémunérationBrute: number
) => {
onRémunérationChange(monthIndex, rémunérationBrute)
}}
onOptionsChange={(monthIndex: number, options: Options) => {
onOptionsChange(monthIndex, options)
}}
/>
))}
</tbody>
</StyledTable>
<Spacing xxl />
<StyledRecapTable>
<caption>
{t(
'pages.simulateurs.réduction-générale.recap.caption',
'Récapitulatif trimestriel :'
)}
</caption>
<thead>
<tr>
<th scope="col">{t('Trimestre')}</th>
<th scope="col">
{t(
'pages.simulateurs.réduction-générale.recap.header',
'Réduction calculée'
)}
<br />
{t(
'pages.simulateurs.réduction-générale.recap.code671',
'code 671(€)'
)}
</th>
<th scope="col">
{t(
'pages.simulateurs.réduction-générale.recap.header',
'Réduction calculée'
)}
<br />
{t(
'pages.simulateurs.réduction-générale.recap.code801',
'code 801(€)'
)}
</th>
</tr>
</thead>
<tbody>
{Object.keys(quarters).map((label, index) => (
<RécapitulatifTrimestre
key={index}
label={label}
data={quarters[label]}
/>
))}
</tbody>
</StyledTable>
</tbody>
</StyledRecapTable>
</>
) : (
<>
<Body>
@ -114,6 +176,23 @@ export default function RéductionGénéraleMoisParMois({
mobileVersion={true}
/>
))}
<Spacing xxl />
<Body>
{t(
'pages.simulateurs.réduction-générale.recap.caption',
'Récapitulatif trimestriel :'
)}
</Body>
{Object.keys(quarters).map((label, index) => (
<RécapitulatifTrimestre
key={index}
label={label}
data={quarters[label]}
mobileVersion={true}
/>
))}
</>
)}
@ -148,3 +227,16 @@ const StyledTable = styled.table`
font-weight: normal;
}
`
const StyledRecapTable = styled(StyledTable)`
thead {
border-bottom: solid 1px;
}
thead th:not(:last-of-type),
tbody th,
td:not(:last-of-type) {
border-right: solid 1px;
}
thead th:not(:first-of-type) {
text-align: center;
}
`

View File

@ -22,6 +22,7 @@ type Props = {
displayedUnit: string
language: string
displayNull?: boolean
alignment?: 'center' | 'end'
}
export default function MontantRéduction({
@ -32,6 +33,7 @@ export default function MontantRéduction({
displayedUnit,
language,
displayNull = true,
alignment = 'end',
}: Props) {
const { t } = useTranslation()
@ -39,7 +41,7 @@ export default function MontantRéduction({
return réductionGénérale ? (
<StyledTooltip tooltip={tooltip}>
<FlexDiv id={id}>
<FlexDiv id={id} $alignment={alignment}>
{formatValue(
{
nodeValue: réductionGénérale,
@ -54,7 +56,7 @@ export default function MontantRéduction({
</StyledTooltip>
) : (
displayNull && (
<FlexDiv id={id}>
<FlexDiv id={id} $alignment={alignment}>
{formatValue(0, { displayedUnit, language })}
<Condition
@ -76,9 +78,9 @@ export default function MontantRéduction({
const StyledTooltip = styled(Tooltip)`
width: 100%;
`
const FlexDiv = styled.div`
const FlexDiv = styled.div<{ $alignment: 'end' | 'center' }>`
${FlexCenter}
justify-content: end;
justify-content: ${({ $alignment }) => $alignment};
`
const StyledSearchIcon = styled(SearchIcon)`
margin-left: ${({ theme }) => theme.spacings.sm};

View File

@ -0,0 +1,168 @@
import { sumAll } from 'effect/Number'
import { useTranslation } from 'react-i18next'
import { styled } from 'styled-components'
import { Grid } from '@/design-system/layout'
import { Body } from '@/design-system/typography/paragraphs'
import { MonthState } from '../utils'
import MontantRéduction from './MontantRéduction'
type Props = {
label: string
data: MonthState[]
mobileVersion?: boolean
}
export type RémunérationBruteInput = {
unité: string
valeur: number
}
export default function RécapitulatifTrimestre({
label,
data,
mobileVersion = false,
}: Props) {
const { t, i18n } = useTranslation()
const language = i18n.language
const displayedUnit = '€'
const rémunération = sumAll(
data.map((monthData) => monthData.rémunérationBrute)
)
const répartition = {
IRC: sumAll(
data.map(
(monthData) =>
monthData.réductionGénérale.répartition.IRC +
monthData.régularisation.répartition.IRC
)
),
Urssaf: sumAll(
data.map(
(monthData) =>
monthData.réductionGénérale.répartition.Urssaf +
monthData.régularisation.répartition.Urssaf
)
),
chômage: sumAll(
data.map(
(monthData) =>
monthData.réductionGénérale.répartition.chômage +
monthData.régularisation.répartition.chômage
)
),
}
let réduction = sumAll(
data.map((monthData) => monthData.réductionGénérale.value)
)
let régularisation = sumAll(
data.map((monthData) => monthData.régularisation.value)
)
if (réduction + régularisation > 0) {
réduction += régularisation
régularisation = 0
} else {
régularisation += réduction
réduction = 0
}
const Montant671 = () => {
return (
<MontantRéduction
rémunérationBrute={rémunération}
réductionGénérale={réduction}
répartition={répartition}
displayedUnit={displayedUnit}
language={language}
displayNull={false}
alignment="center"
/>
)
}
const Montant801 = () => {
return (
<MontantRéduction
rémunérationBrute={rémunération}
réductionGénérale={régularisation}
répartition={répartition}
displayedUnit={displayedUnit}
language={language}
displayNull={false}
alignment="center"
/>
)
}
return mobileVersion ? (
<div>
<StyledMonth>{label}</StyledMonth>
<GridContainer container spacing={2}>
<Grid item>
<StyledBody>
{t(
'pages.simulateurs.réduction-générale.recap.header',
'Réduction calculée'
)}
<br />
{t(
'pages.simulateurs.réduction-générale.recap.code671',
'code 671(€)'
)}
</StyledBody>
</Grid>
<Grid item>
<StyledBody>
<Montant671 />
</StyledBody>
</Grid>
</GridContainer>
<GridContainer container spacing={2}>
<Grid item>
<StyledBody>
{t(
'pages.simulateurs.réduction-générale.recap.header',
'Réduction calculée'
)}
<br />
{t(
'pages.simulateurs.réduction-générale.recap.code801',
'code 801(€)'
)}
</StyledBody>
</Grid>
<Grid item>
<StyledBody>
<Montant801 />
</StyledBody>
</Grid>
</GridContainer>
</div>
) : (
<tr>
<th scope="row">{label}</th>
<td>
<Montant671 />
</td>
<td>
<Montant801 />
</td>
</tr>
)
}
const StyledMonth = styled(Body)`
font-weight: bold;
text-transform: capitalize;
border-bottom: solid 1px ${({ theme }) => theme.colors.bases.primary[100]};
`
const GridContainer = styled(Grid)`
align-items: center;
justify-content: space-between;
`
const StyledBody = styled(Body)`
margin-top: 0;
`