diff --git a/publicode/rules/dirigeant.yaml b/publicode/rules/dirigeant.yaml index 05ce2099b..47d73e51c 100644 --- a/publicode/rules/dirigeant.yaml +++ b/publicode/rules/dirigeant.yaml @@ -1006,9 +1006,12 @@ dirigeant . indépendant . cotisations et contributions . exonérations . ZFU: formule: 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) taux: taux - plafond: 3042 heures/an * SMIC horaire + # TODO : Le plafond est proratisé en début / fin d'exonération + plafond: + recalcul: + avec: + indépendant . revenu net de cotisations: 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" diff --git a/publicode/rules/salarié.yaml b/publicode/rules/salarié.yaml index 09b2f869e..f3548ada7 100644 --- a/publicode/rules/salarié.yaml +++ b/publicode/rules/salarié.yaml @@ -1217,9 +1217,13 @@ contrat salarié . SMIC temps plein: décret: https://www.legifrance.gouv.fr/affichTexte.do?cidTexte=JORFTEXT000037833206 contrat salarié . SMIC temps plein . net imposable: - description: Montant du SMIC net imposable - formule: 1247.55 €/mois - note: Ce montant est codé en dur, il faudrait le calculer à partir du montant du SMIC brut + titre: SMIC net imposable + description: Montant du SMIC net imposable pour un temps plein. + formule: + recalcul: + règle: rémunération . net imposable . base + avec: + rémunération . brut de base: SMIC temps plein références: barème PAS: https://bofip.impots.gouv.fr/bofip/11255-PGP.html @@ -1753,10 +1757,11 @@ contrat salarié . statut JEI . exonération de cotisations: unité: €/mois formule: - # TODO - le plafonnement à 4,5 SMIC, précalculé pour 09/2017; cette approximation n'est bien sûr pas satisfaisante, - # il faut fournir un mécanisme "exonération" capable de recalculer une règle en introduisant un plafond encadrement: - plafond: 1634.39 €/mois + plafond: + recalcul: + avec: + rémunération . brut de base: 4.5 * SMIC valeur: somme: - allocations familiales diff --git a/source/engine/known-mecanisms.yaml b/source/engine/known-mecanisms.yaml index 2564d1a6f..56cbeda1e 100644 --- a/source/engine/known-mecanisms.yaml +++ b/source/engine/known-mecanisms.yaml @@ -96,6 +96,14 @@ arrondi: description: | On arrondi à l'euro le plus proche +recalcul: + type: numeric + description: >- + Relance le calcul d'une règle dans une situation différente de la situation + courante. Permet par exemple de calculer le montant des cotisations au + niveau du SMIC, même si le salaire est plus élevé dans la situation + actuelle. + ########################################## # Ce qu'on appelle aujourd'hui des RuleProp # Et qui deviendront des mécanismes classiques normalement par la suite #TODO diff --git a/source/engine/mecanismViews/Recalcul.tsx b/source/engine/mecanismViews/Recalcul.tsx new file mode 100644 index 000000000..215cda42e --- /dev/null +++ b/source/engine/mecanismViews/Recalcul.tsx @@ -0,0 +1,41 @@ +import RuleLink from 'Components/RuleLink' +import { makeJsx } from 'Engine/evaluation' +import React from 'react' +import { Trans } from 'react-i18next' +import { DottedName } from 'Types/rule' +import { Node } from './common' + +export default function Recalcul(nodeValue, explanation) { + return ( + + {explanation.règle && ( + + Calcul de avec : + + )} + + + } + /> + ) +} diff --git a/source/engine/mecanisms.js b/source/engine/mecanisms.js index 9110577eb..4bac52105 100644 --- a/source/engine/mecanisms.js +++ b/source/engine/mecanisms.js @@ -36,6 +36,7 @@ import Allègement from './mecanismViews/Allègement' import { Node, SimpleRuleLink } from './mecanismViews/common' import InversionNumérique from './mecanismViews/InversionNumérique' import Product from './mecanismViews/Product' +import Recalcul from './mecanismViews/Recalcul' import Somme from './mecanismViews/Somme' import { disambiguateRuleReference, findRuleByDottedName } from './rules' import { anyNull, val } from './traverse-common-functions' @@ -286,6 +287,70 @@ export let mecanismInversion = dottedName => (recurse, k, v) => { } } +export let mecanismRecalcul = dottedNameContext => (recurse, k, v) => { + let evaluate = (currentCache, situationGate, parsedRules, node) => { + let defaultRuleToEvaluate = dottedNameContext + let nodeToEvaluate = recurse(node?.règle ?? defaultRuleToEvaluate) + let cache = { _meta: currentCache._meta, _metaInRecalcul: true } // Create an empty cache + let amendedSituation = Object.fromEntries( + Object.keys(node.avec).map(dottedName => [ + disambiguateRuleReference( + parsedRules, + { dottedName: dottedNameContext }, + dottedName + ), + node.avec[dottedName] + ]) + ) + + if (currentCache._metaInRecalcul) { + return defaultNode(false) + } + + let amendedSituationGate = dottedName => + Object.keys(amendedSituation).includes(dottedName) + ? evaluateNode( + cache, + amendedSituationGate, + parsedRules, + recurse(amendedSituation[dottedName]) + ).nodeValue + : situationGate(dottedName) + + let evaluatedNode = evaluateNode( + cache, + amendedSituationGate, + parsedRules, + nodeToEvaluate + ) + + return { + ...evaluatedNode, + explanation: { + ...evaluateNode.explanation, + unit: evaluatedNode.unit, + amendedSituation: Object.fromEntries( + Object.keys(amendedSituation).map(dottedName => [ + dottedName, + evaluateNode( + cache, + amendedSituationGate, + parsedRules, + recurse(amendedSituation[dottedName]) + ) + ]) + ) + }, + jsx: Recalcul + } + } + + return { + ...v, + evaluate + } +} + export let mecanismSum = (recurse, k, v) => { let explanation = v.map(recurse) diff --git a/source/engine/parse.js b/source/engine/parse.js index 9d3f5d8ee..f898b08e7 100644 --- a/source/engine/parse.js +++ b/source/engine/parse.js @@ -35,6 +35,7 @@ import { mecanismOneOf, mecanismOnePossibility, mecanismProduct, + mecanismRecalcul, mecanismReduction, mecanismSum, mecanismSynchronisation @@ -103,6 +104,7 @@ Cela vient probablement d'une erreur dans l'indentation ...statelessParseFunction, 'une possibilité': mecanismOnePossibility(rule.dottedName), 'inversion numérique': mecanismInversion(rule.dottedName), + recalcul: mecanismRecalcul(rule.dottedName), filter: () => parseReferenceTransforms( rules, diff --git a/source/locales/en.yaml b/source/locales/en.yaml index e73ff2898..5e787e5d7 100644 --- a/source/locales/en.yaml +++ b/source/locales/en.yaml @@ -217,6 +217,7 @@ autoentrepreneur: titre: Auto-entrepeneur back: Resume simulation barème: scale +calcul-avec: 'Calculation from <1>with :' cancelExample: Back to your situation car dépend de: because it depends on cible: target diff --git a/source/locales/rules-en.yaml b/source/locales/rules-en.yaml index eaa827e1f..8a5b4763c 100644 --- a/source/locales/rules-en.yaml +++ b/source/locales/rules-en.yaml @@ -713,16 +713,10 @@ contrat salarié . SMIC temps plein: titre.en: full-time mimimum wage (SMIC) titre.fr: SMIC temps plein contrat salarié . SMIC temps plein . net imposable: - description.en: Amount of the net taxable minimum wage (SMIC net imposable) - description.fr: Montant du SMIC net imposable - note.en: >- - [automatic] This amount is hard-coded, it should be calculated from the - amount of the gross minimum wage. - note.fr: >- - Ce montant est codé en dur, il faudrait le calculer à partir du montant du - SMIC brut - titre.en: net taxable - titre.fr: net imposable + description.en: '[automatic] Amount of the net taxable SMIC for a full-time employee.' + description.fr: Montant du SMIC net imposable pour un temps plein. + titre.en: '[automatic] minimum net taxable income' + titre.fr: SMIC net imposable contrat salarié . aides employeur: description.en: > Some aids can be requested by the employer to help hires. diff --git a/test/mécanismes/recalcul.yaml b/test/mécanismes/recalcul.yaml new file mode 100644 index 000000000..d50fd25f3 --- /dev/null +++ b/test/mécanismes/recalcul.yaml @@ -0,0 +1,28 @@ +salaire brut: + formule: 2000€ + +salaire net: + formule: 0.5 * salaire brut + +SMIC brut: + formule: 1000€ + +SMIC net: + formule: + recalcul: + règle: salaire net + avec: + salaire brut: SMIC brut + exemples: + - valeur attendue: 500 + +Recalcule règle courante: + formule: + encadrement: + valeur: 10% * salaire brut + plafond: + recalcul: + avec: + salaire brut: 100€ + exemples: + - valeur attendue: 10 diff --git a/test/regressions/__snapshots__/simulations.jest.js.snap b/test/regressions/__snapshots__/simulations.jest.js.snap index fed0cfbeb..af426dd3d 100644 --- a/test/regressions/__snapshots__/simulations.jest.js.snap +++ b/test/regressions/__snapshots__/simulations.jest.js.snap @@ -190,7 +190,7 @@ exports[`calculate simulations-rémunération-dirigeant: Indépendant - échell exports[`calculate simulations-salarié: JEI 1`] = `"[3440,0,0,3000,2353,2187]"`; -exports[`calculate simulations-salarié: JEI 2`] = `"[26710,0,0,20000,15969,10681]"`; +exports[`calculate simulations-salarié: JEI 2`] = `"[26635,0,0,20000,15969,10681]"`; exports[`calculate simulations-salarié: JEI 3`] = `"[4517,0,0,4000,3141,2741]"`;