Ajoute la gestion €/an et €/mois dans le simulateur auto-entrepreneur

pull/1421/head
Johan Girod 2021-02-03 18:39:16 +01:00
parent 1cebad45e4
commit 31f5f1889f
7 changed files with 111 additions and 61 deletions

View File

@ -10,7 +10,7 @@ export default function PeriodSwitch() {
const units = ['€/mois', '€/an']
return (
<span id="PeriodSwitch">
<div id="PeriodSwitch">
<span className="base ui__ small radio toggle">
{units.map((unit) => (
<label key={unit}>
@ -27,6 +27,6 @@ export default function PeriodSwitch() {
</label>
))}
</span>
</span>
</div>
)
}

View File

@ -5,7 +5,10 @@ import { DottedName } from 'modele-social'
import { formatValue, UNSAFE_isNotApplicable } from 'publicodes'
import { createContext, useContext, useEffect, useState } from 'react'
import { useDispatch, useSelector } from 'react-redux'
import { situationSelector } from 'Selectors/simulationSelectors'
import {
situationSelector,
targetUnitSelector,
} from 'Selectors/simulationSelectors'
import RuleInput from './conversation/RuleInput'
import RuleLink from './RuleLink'
import AnimatedTargetValue from './ui/AnimatedTargetValue'
@ -26,7 +29,10 @@ export function SimulationGoals({
return (
<InitialRenderContext.Provider value={initialRender}>
<section className={`ui__ card ${className}`}>
<section
className={`ui__ card ${className}`}
style={{ marginTop: '0.6rem' }}
>
<div id="targetSelection">
<ul className="targets">{children}</ul>
</div>
@ -45,7 +51,7 @@ function useInitialRender() {
type SimulationGoalProps = {
dottedName: DottedName
labelWithTitle?: boolean
labelWithQuestion?: boolean
small?: boolean
appear?: boolean
editable?: boolean
@ -53,16 +59,21 @@ type SimulationGoalProps = {
export function SimulationGoal({
dottedName,
labelWithTitle = false,
labelWithQuestion = false,
small = false,
appear = true,
editable = true,
}: SimulationGoalProps) {
const dispatch = useDispatch()
const engine = useEngine()
const currentUnit = useSelector(targetUnitSelector)
const situation = useSelector(situationSelector)
const isNotApplicable = UNSAFE_isNotApplicable(engine, dottedName)
const evaluation = engine.evaluate(dottedName)
const evaluation = engine.evaluate({
valeur: dottedName,
unité: currentUnit,
arrondi: 'oui',
})
const rule = engine.getRule(dottedName)
const initialRender = useContext(InitialRenderContext)
const [isFocused, setFocused] = useState(false)
@ -79,6 +90,12 @@ export function SimulationGoal({
isFocused ||
initialRender ||
Object.keys(situation).length === 0
if (
!editable &&
(evaluation.nodeValue === false || evaluation.nodeValue === null)
) {
return null
}
return (
<li className={small ? 'small-target' : ''}>
<Animate.appear unless={!appear || initialRender}>
@ -86,7 +103,9 @@ export function SimulationGoal({
<div className="header">
<label htmlFor={dottedName}>
<span className="optionTitle">
{(!labelWithTitle && rule.rawNode.question) || rule.title}
{(labelWithQuestion && rule.rawNode.question) || (
<RuleLink dottedName={dottedName} />
)}
</span>
<p className="ui__ notice">{rule.rawNode.résumé}</p>
</label>
@ -101,6 +120,10 @@ export function SimulationGoal({
{ focused: isFocused }
)}
isTarget
modifiers={{
unité: currentUnit,
arrondi: 'oui',
}}
dottedName={dottedName}
onFocus={() => setFocused(true)}
onBlur={() => setFocused(false)}

View File

@ -27,6 +27,7 @@ export type Props<Name extends string = DottedName> = Omit<
useSwitch?: boolean
isTarget?: boolean
onSubmit?: (source: string) => void
modifiers?: Record<string, string>
}
export type InputProps<Name extends string = string> = Props<Name> &
@ -51,11 +52,12 @@ export default function RuleInput({
useSwitch = false,
isTarget = false,
onSubmit = () => null,
modifiers = {},
...props
}: Props<DottedName>) {
const engine = useContext(EngineContext)
const rule = engine.getRule(dottedName)
const evaluation = engine.evaluate(dottedName)
const evaluation = engine.evaluate({ valeur: dottedName, ...modifiers })
const language = useTranslation().i18n.language
const value = evaluation.nodeValue
const commonProps: InputProps<DottedName> = {

View File

@ -22,14 +22,29 @@ export default function ArtisteAuteur() {
<>
<SimulateurWarning simulateur="artiste-auteur" />
<SimulationGoals className="light">
<SimulationGoal dottedName="artiste-auteur . revenus . traitements et salaires" />
<SimulationGoal dottedName="artiste-auteur . revenus . BNC . recettes" />
<SimulationGoal dottedName="artiste-auteur . revenus . BNC . micro-bnc" />
<SimulationGoal
labelWithQuestion
dottedName="artiste-auteur . revenus . traitements et salaires"
/>
<SimulationGoal
labelWithQuestion
dottedName="artiste-auteur . revenus . BNC . recettes"
/>
<SimulationGoal
labelWithQuestion
dottedName="artiste-auteur . revenus . BNC . micro-bnc"
/>
<Warning dottedName="artiste-auteur . revenus . BNC . contrôle micro-bnc" />
<Condition expression="artiste-auteur . revenus . BNC . micro-bnc = non">
<SimulationGoal dottedName="artiste-auteur . revenus . BNC . frais réels" />
<SimulationGoal
labelWithQuestion
dottedName="artiste-auteur . revenus . BNC . frais réels"
/>
</Condition>
<SimulationGoal dottedName="artiste-auteur . cotisations . option surcotisation" />
<SimulationGoal
labelWithQuestion
dottedName="artiste-auteur . cotisations . option surcotisation"
/>
</SimulationGoals>
<CotisationsResult />
</>

View File

@ -1,6 +1,7 @@
import { updateSituation } from 'Actions/actions'
import Conversation from 'Components/conversation/Conversation'
import { Condition } from 'Components/EngineValue'
import PeriodSwitch from 'Components/PeriodSwitch'
import SearchButton from 'Components/SearchButton'
import SimulateurWarning from 'Components/SimulateurWarning'
import { SimulationGoal, SimulationGoals } from 'Components/SimulationGoals'
@ -18,14 +19,14 @@ export default function AutoEntrepreneur() {
<>
<SimulateurWarning simulateur="auto-entrepreneur" />
<SearchButton invisibleButton />
<PeriodSwitch />
<br />
<SimulationGoals className="plain">
<SimulationGoal
appear={false}
editable={
engine.evaluate('entreprise . activité . mixte').nodeValue !== true
}
labelWithTitle
dottedName="entreprise . chiffre d'affaires"
/>
<ActivitéMixte />
@ -34,31 +35,29 @@ export default function AutoEntrepreneur() {
<ul>
<SimulationGoal
small
labelWithTitle
dottedName="entreprise . chiffre d'affaires . vente restauration hébergement"
/>
<SimulationGoal
labelWithTitle
small
dottedName="entreprise . chiffre d'affaires . prestations de service . BIC"
/>
<SimulationGoal
labelWithTitle
small
dottedName="entreprise . chiffre d'affaires . prestations de service . BNC"
/>
</ul>
</li>
</Condition>
<SimulationGoal
labelWithTitle
dottedName="dirigeant . auto-entrepreneur . net de cotisations"
/>
<SimulationGoal
labelWithTitle
dottedName="dirigeant . auto-entrepreneur . net après impôt"
small
editable={false}
dottedName="dirigeant . auto-entrepreneur . cotisations et contributions"
/>
<SimulationGoal dottedName="dirigeant . auto-entrepreneur . net de cotisations" />
<Condition expression="impôt > 0">
<SimulationGoal small editable={false} dottedName="impôt" />
</Condition>
<SimulationGoal dottedName="dirigeant . auto-entrepreneur . net après impôt" />
</SimulationGoals>
<Conversation />
<Explanation />

View File

@ -1,4 +1,5 @@
import { Evaluation, Unit } from './AST/types'
import { simplifyNodeUnit } from './nodeUnits'
import { formatUnit, serializeUnit } from './units'
export const numberFormatter = ({
@ -29,27 +30,27 @@ export const numberFormatter = ({
}).format(value)
}
export const formatCurrency = (value: number | undefined, language: string) => {
return value == null
export const formatCurrency = (nodeValue: number | undefined, language: string) => {
return nodeValue == null
? ''
: (formatNumber({ unit: '€', language, value }) ?? '').replace(
: (formatNumber({ unit: '€', language, nodeValue }) ?? '').replace(
/^(-)?€/,
'$1€\u00A0'
)
}
export const formatPercentage = (value: number | undefined) =>
value == null
export const formatPercentage = (nodeValue: number | undefined) =>
nodeValue == null
? ''
: formatNumber({ unit: '%', value, maximumFractionDigits: 2 })
: formatNumber({ unit: '%', nodeValue, maximumFractionDigits: 2 })
export type formatValueOptions = {
type formatValueOptions = {
maximumFractionDigits?: number
minimumFractionDigits?: number
language?: string
unit?: Unit | string
formatUnit?: formatUnit
value: number
nodeValue: number
}
function formatNumber({
@ -58,13 +59,13 @@ function formatNumber({
language,
formatUnit,
unit,
value,
nodeValue,
}: formatValueOptions) {
if (typeof value !== 'number') {
return value
if (typeof nodeValue !== 'number') {
return nodeValue
}
const serializedUnit = unit
? serializeUnit(unit, value, formatUnit)
? serializeUnit(unit, nodeValue, formatUnit)
: undefined
switch (serializedUnit) {
case '€':
@ -73,13 +74,13 @@ function formatNumber({
maximumFractionDigits,
minimumFractionDigits,
language,
})(value)
})(nodeValue)
case '%':
return numberFormatter({
style: 'percent',
maximumFractionDigits,
language,
})(value / 100)
})(nodeValue / 100)
default:
return (
numberFormatter({
@ -87,7 +88,7 @@ function formatNumber({
minimumFractionDigits,
maximumFractionDigits,
language,
})(value) +
})(nodeValue) +
(typeof serializedUnit === 'string' ? `\u00A0${serializedUnit}` : '')
)
}
@ -116,18 +117,11 @@ export function formatValue(
{ language = 'fr', displayedUnit, formatUnit, precision = 2 }: Options = {}
) {
const nodeValue =
let nodeValue =
typeof value === 'number' || typeof value === 'undefined'
? value
: value.nodeValue
const unit =
displayedUnit ??
(typeof value === 'number' ||
typeof value === 'undefined' ||
!('unit' in value)
? undefined
: value.unit)
if (
(typeof nodeValue === 'number' && Number.isNaN(nodeValue)) ||
@ -135,22 +129,38 @@ export function formatValue(
) {
return '-'
}
return typeof nodeValue === 'string'
? capitalise0(nodeValue.replace('\\n', '\n'))
: typeof nodeValue === 'object'
? (nodeValue as any).nom
: typeof nodeValue === 'boolean'
? booleanTranslations[language][nodeValue]
: typeof nodeValue === 'number'
? formatNumber({
if (typeof nodeValue === 'string'){
return capitalise0(nodeValue.replace('\\n', '\n'))
}
if (typeof nodeValue === 'object')
return (nodeValue as any).nom
if (typeof nodeValue === 'boolean')
return booleanTranslations[language][nodeValue]
if (typeof nodeValue === 'number'){
let unit =
(typeof value === 'number' ||
typeof value === 'undefined' ||
!('unit' in value)
? undefined
: value.unit)
if (unit) {
const simplifiedNode = simplifyNodeUnit({
unit,
nodeValue
})
unit = simplifiedNode.unit
nodeValue = simplifiedNode.nodeValue as number
}
return formatNumber({
minimumFractionDigits: 0,
maximumFractionDigits: precision,
language,
formatUnit,
unit,
value: nodeValue,
nodeValue,
unit: displayedUnit ?? unit
})
: null
}
return null
}
export function serializeValue(

View File

@ -1,8 +1,9 @@
import { EvaluatedNode, Unit } from './AST/types'
import { mapTemporal } from './temporal'
import { convertUnit, simplifyUnit } from './units'
import { EvaluatedNode, Unit } from './AST/types'
export function simplifyNodeUnit(node) {
console.log('jhjoiijoijo', node)
if (!node.unit) {
return node
}