diff --git a/source/components/Distribution.js b/source/components/Distribution.js index f8879d7f6..7b9651d6b 100644 --- a/source/components/Distribution.js +++ b/source/components/Distribution.js @@ -6,7 +6,6 @@ import Value from 'Components/Value' import { findRuleByDottedName } from 'Engine/rules' import React, { useState } from 'react' import emoji from 'react-easy-emoji' -import { Trans } from 'react-i18next' import { connect } from 'react-redux' import { config, Spring } from 'react-spring' import { compose } from 'redux' @@ -51,6 +50,7 @@ function Distribution({ répartition, cotisationMaximum, total, + cotisations, salaireChargé, salaireNet } = distribution @@ -107,10 +107,8 @@ function Distribution({ + - Cotisations - - {total.partPatronale + total.partSalariale} - + +
= diff --git a/source/components/PaySlip.js b/source/components/PaySlip.js index f581d4324..dd48a38db 100644 --- a/source/components/PaySlip.js +++ b/source/components/PaySlip.js @@ -103,21 +103,13 @@ export default compose( ) })} - - - {/* Total cotisation */}
Total des retenues
diff --git a/source/components/simulationConfigs/assimilé.yaml b/source/components/simulationConfigs/assimilé.yaml index ca789bca7..50bb36b3f 100644 --- a/source/components/simulationConfigs/assimilé.yaml +++ b/source/components/simulationConfigs/assimilé.yaml @@ -14,8 +14,7 @@ objectifs: - entreprise . chiffre d'affaires minimum objectifs secondaires: - - contrat salarié . temps de travail - - contrat salarié . cotisations . patronales . à payer + - contrat salarié . cotisations questions: à l'affiche: diff --git a/source/components/simulationConfigs/salarié.yaml b/source/components/simulationConfigs/salarié.yaml index 01eebd50b..7b6cb3ee0 100644 --- a/source/components/simulationConfigs/salarié.yaml +++ b/source/components/simulationConfigs/salarié.yaml @@ -8,7 +8,7 @@ objectifs: objectifs secondaires: - contrat salarié . temps de travail - - contrat salarié . cotisations . patronales . à payer + - contrat salarié . cotisations questions: à l'affiche: diff --git a/source/engine/grammar.ne b/source/engine/grammar.ne index dd2a90171..3798cc5be 100644 --- a/source/engine/grammar.ne +++ b/source/engine/grammar.ne @@ -5,7 +5,7 @@ @preprocessor esmodule @{% -import {string, filteredVariable, variable, temporalVariable, operation, boolean, number, percentage } from './grammarFunctions' +import {string, filteredVariable, variable, temporalVariable, binaryOperation, unaryOperation, boolean, number, percentage } from './grammarFunctions' const moo = require("moo"); @@ -23,14 +23,14 @@ const lexer = moo.compile({ ')': ')', '[': '[', ']': ']', - comparisonOperator: ['>','<','>=','<=','=','!='], - additionSubstractionOperator: /[\+-]/, - multiplicationDivisionOperator: ['*','/'], + comparison: ['>','<','>=','<=','=','!='], + additionSubstraction: /[\+-]/, + multiplicationDivision: ['*','/'], temporality: ['annuel' , 'mensuel'], words: new RegExp(words), string: /'[ \t\.'a-zA-Z\-\u00C0-\u017F0-9 ]+'/, dot: ' . ', - _: { match: /[\s]/, lineBreaks: true } + space: { match: /[\s]+/, lineBreaks: true } }); %} @@ -40,6 +40,7 @@ main -> AdditionSubstraction {% id %} | Comparison {% id %} | NonNumericTerminal {% id %} + | Negation {% id %} NumericTerminal -> Variable {% id %} @@ -47,11 +48,14 @@ NumericTerminal -> | FilteredVariable {% id %} | number {% id %} +Negation -> + "-" %space Parentheses {% unaryOperation('calculation') %} Parentheses -> "(" AdditionSubstraction ")" {% ([,e]) => e %} + | "(" Negation ")" {% ([,e]) => e %} | NumericTerminal {% id %} -Comparison -> Comparable %_ %comparisonOperator %_ Comparable {% operation('comparison')%} +Comparison -> Comparable %space %comparison %space Comparable {% binaryOperation('comparison')%} Comparable -> ( AdditionSubstraction | NonNumericTerminal) {% ([[e]]) => e %} @@ -65,22 +69,22 @@ Variable -> %words (%dot %words {% ([,words]) => words %}):* {% variable %} Filter -> "[" %words "]" {% ([,filter]) => filter %} -FilteredVariable -> Variable %_ Filter {% filteredVariable %} +FilteredVariable -> Variable %space Filter {% filteredVariable %} TemporalTransform -> "[" %temporality "]" {% ([,temporality]) => temporality %} -TemporalVariable -> Variable %_ TemporalTransform {% temporalVariable %} +TemporalVariable -> Variable %space TemporalTransform {% temporalVariable %} #----- # Addition and subtraction AdditionSubstraction -> - AdditionSubstraction %_ %additionSubstractionOperator %_ MultiplicationDivision {% operation('calculation') %} + AdditionSubstraction %space %additionSubstraction %space MultiplicationDivision {% binaryOperation('calculation') %} | MultiplicationDivision {% id %} # Multiplication and division MultiplicationDivision -> - MultiplicationDivision %_ %multiplicationDivisionOperator %_ Parentheses {% operation('calculation') %} + MultiplicationDivision %space %multiplicationDivision %space Parentheses {% binaryOperation('calculation') %} | Parentheses {% id %} @@ -91,4 +95,5 @@ boolean -> number -> %number {% number %} | %percentage {% percentage %} + string -> %string {% string %} \ No newline at end of file diff --git a/source/engine/grammarFunctions.js b/source/engine/grammarFunctions.js index 6195c8a86..7bbc62707 100644 --- a/source/engine/grammarFunctions.js +++ b/source/engine/grammarFunctions.js @@ -2,13 +2,20 @@ The advantage of putting them here is to get prettier's JS formatting, since Nealrey doesn't support it https://github.com/kach/nearley/issues/310 */ import { parseUnit } from 'Engine/units' -export let operation = operationType => ([A, , operator, , B]) => ({ +export let binaryOperation = operationType => ([A, , operator, , B]) => ({ [operator]: { operationType, explanation: [A, B] } }) +export let unaryOperation = operationType => ([operator, , A]) => ({ + [operator]: { + operationType, + explanation: [number([{ value: '0' }]), A] + } +}) + export let filteredVariable = ( [{ variable }, , { value: filter }], l, diff --git a/source/engine/mecanismViews/Somme.css b/source/engine/mecanismViews/Somme.css index b2f56f841..b10a4bb2f 100644 --- a/source/engine/mecanismViews/Somme.css +++ b/source/engine/mecanismViews/Somme.css @@ -25,17 +25,21 @@ .somme .leaf .name { border: none; } -.somme .operator { - text-align: center; - width: 1em; - padding: 0 0.4em; - font-weight: 600; - border: none !important; - color: black; -} .somme .operator + .element { border-left: none; } + +.mecanism-somme__row > .element > * > .nodeContent + * { + display: none; +} +.mecanism-somme__row > .element > * > .nodeContent { + display: flex; + align-items: center; +} +.mecanism-somme__row > .element > * > .nodeContent > .operator:first-child { + margin-left: 0; +} + #rule-rules .somme table .leaf .situationValue { display: none; } @@ -113,7 +117,6 @@ } .mecanism-somme__row .operator { align-self: center; - display: none; } .mecanism-somme__row .element .variable, @@ -129,7 +132,7 @@ } /* Nested Mecanism */ -.nested .mecanism-somme__row { +.mecanism-somme__row + .nested { padding-left: 2em; border-top: 1px dashed rgba(51, 51, 80, 0.15); } diff --git a/source/engine/mecanismViews/Somme.js b/source/engine/mecanismViews/Somme.js index 75595bd81..821158786 100644 --- a/source/engine/mecanismViews/Somme.js +++ b/source/engine/mecanismViews/Somme.js @@ -34,10 +34,9 @@ function Row({ v, i, unit }) { return [
setFolded(!folded)}> -
{i != 0 && '+'}
{makeJsx(v)} {isSomme && ( diff --git a/source/engine/mecanisms/operation.js b/source/engine/mecanisms/operation.js index 29a49dad1..fe97fed0e 100644 --- a/source/engine/mecanisms/operation.js +++ b/source/engine/mecanisms/operation.js @@ -35,8 +35,12 @@ export default (k, operatorFunction, symbol) => (recurse, k, v) => { unit={unit} child={ - - {makeJsx(explanation[0])} + {(explanation[0].nodeValue !== 0 || symbol !== '−') && ( + <> + + {makeJsx(explanation[0])} + + )} {symbol || k} {makeJsx(explanation[1])} diff --git a/source/règles/base.yaml b/source/règles/base.yaml index 6270e3a4f..1da08b59c 100644 --- a/source/règles/base.yaml +++ b/source/règles/base.yaml @@ -739,8 +739,8 @@ - temps de travail . temps partiel - temps de travail . heures supplémentaires - statut JEI - - régime des impatriés - entreprise . association non lucrative + - régime des impatriés références: Le régime des dirigeants: https://www.urssaf.fr/portail/home/employeur/creer/choisir-une-forme-juridique/le-statut-du-dirigeant/les-dirigeants-rattaches-au-regi.html @@ -1267,7 +1267,7 @@ - CRDS - APEC [salarié] - complémentaire santé [salarié] - - réduction heures supplémentaires + - (- réduction heures supplémentaires) - espace: contrat salarié . cotisations nom: patronales @@ -1299,6 +1299,7 @@ - CDD . majoration chômage - CDD . CIF - forfait social + - (- réductions de cotisations) - espace: contrat salarié nom: rémunération @@ -1322,7 +1323,12 @@ période: flexible formule: allègement: - assiette: base + assiette: + somme: + - rémunération . net de cotisations + - avantages sociaux + - CSG [non déductible] + - CRDS abattement: somme: - indemnité kilométrique vélo @@ -1351,16 +1357,6 @@ références: DSN: https://dsn-info.custhelp.com/app/answers/detail/a_id/2110 -- espace: contrat salarié . rémunération . net imposable - nom: base - période: flexible - formule: - somme: - - rémunération . net de cotisations - - avantages sociaux - - CSG [non déductible] - - CRDS - - espace: contrat salarié nom: prime d'impatriation description: La prime d'impatriation est une partie de la rémunération exonérée d'impôt sur le revenu. @@ -1369,10 +1365,14 @@ unité: € formule: multiplication: - assiette: rémunération . net imposable . base + assiette: + somme: + - rémunération . net + - CSG [non déductible] taux: 30% références: Article 155B du Code général des impôts: https://www.legifrance.gouv.fr/affichCodeArticle.do?cidTexte=LEGITEXT000006069577&idArticle=LEGIARTI000006307476&dateTexte=&categorieLien=cid + Bofip: https://bofip.impots.gouv.fr/bofip/5677-PGP - espace: contrat salarié . rémunération nom: net @@ -1722,15 +1722,16 @@ unité: € période: flexible description: | - C'est la rémunération brute, plus les cotisations patronales, moins les réductions de cotisations sociales. - C'est le total que l'employeur doit verser pour employer un salarié. > C'est donc aussi une mesure de la valeur apportée par le salarié à l'entreprise : l'employeur est prêt à verser cette somme en contrepartie du travail fourni. Des [aides différées](/documentation/aides-employeur) peuvent venir diminuer ce montant. - formule: rémunération . total sans réduction - cotisations . patronales . réductions de cotisations + formule: + somme: + - brut + - cotisations . patronales - espace: contrat salarié . cotisations . patronales nom: réductions de cotisations @@ -1757,12 +1758,6 @@ références: urssaf.fr: https://www.urssaf.fr/portail/home/employeur/beneficier-dune-exoneration/exonerations-generales/la-deduction-forfaitaire-patrona/employeurs-concernes.html -- espace: contrat salarié . cotisations . patronales - nom: à payer - période: flexible - unité: € - formule: patronales - réductions de cotisations - - espace: contrat salarié nom: réduction ACRE applicable si: @@ -1802,7 +1797,7 @@ type: réduction de cotisations unité: € période: flexible - formule: 0 - rémunération . heures supplémentaires * taux des cotisations réduites + formule: rémunération . heures supplémentaires * taux des cotisations réduites références: Code de la sécurité sociale - Article D241-21: https://www.legifrance.gouv.fr/affichCodeArticle.do?idArticle=LEGIARTI000038056813&cidTexte=LEGITEXT000006073189 @@ -1825,15 +1820,6 @@ urssaf.fr: https://www.urssaf.fr/portail/home/employeur/beneficier-dune-exoneration/exonerations-generales/la-reduction-de-cotisations-sala/modalites-de-calcul-et-de-declar.html Circulaire DSS/5B/2019/71: http://circulaire.legifrance.gouv.fr/pdf/2019/04/cir_44492.pdf -- espace: contrat salarié . rémunération - nom: total sans réduction - période: flexible - titre: Rémunération totale sans réduction - type: salaire - formule: - somme: - - brut - - cotisations . patronales - espace: contrat salarié nom: cotisations @@ -3830,11 +3816,6 @@ rend non applicable: - contrat salarié -- espace: indépendant . cotisations et contributions - nom: cotisations à payer - période: flexible - formule: cotisations - réduction ACRE - - espace: indépendant . cotisations et contributions nom: cotisations période: flexible @@ -3853,9 +3834,10 @@ nom: cotisations et contributions formule: somme: - - cotisations à payer + - cotisations - CSG et CRDS - formation professionnelle + - (- réduction ACRE) unité: € période: flexible diff --git a/source/règles/externalized.yaml b/source/règles/externalized.yaml index a081a7a90..a591982e5 100644 --- a/source/règles/externalized.yaml +++ b/source/règles/externalized.yaml @@ -1203,9 +1203,6 @@ contrat salarié . cotisations . patronales . réductions de cotisations: ? contrat salarié . cotisations . patronales . réductions de cotisations . déduction heures supplémentaires : titre.en: flat-rate deduction for overtime titre.fr: déduction forfaitaire pour heures supplémentaires -contrat salarié . cotisations . patronales . à payer: - titre.en: to be paid - titre.fr: à payer contrat salarié . réduction ACRE: titre.en: ACRE reduction titre.fr: réduction ACRE diff --git a/source/selectors/ficheDePaieSelectors.js b/source/selectors/ficheDePaieSelectors.js index 669ff9eb2..5dfc2165b 100644 --- a/source/selectors/ficheDePaieSelectors.js +++ b/source/selectors/ficheDePaieSelectors.js @@ -17,17 +17,6 @@ import { import { createSelector } from 'reselect' import { analysisWithDefaultsSelector } from 'Selectors/analyseSelectors' -import type { Analysis } from 'Types/Analysis' -import type { - VariableWithCotisation, - Cotisation, - Cotisations, - Branche, - FicheDePaie -} from 'Types/ResultViewTypes' - -import type { Règle } from 'Types/RegleTypes' - // These functions help build the payslip. They take the cotisations from the cache, braving all the particularities of the current engine's implementation, handles the part patronale and part salariale, and gives a map by branch. export const COTISATION_BRANCHE_ORDER: Array = [ @@ -95,9 +84,7 @@ const variableToCotisation = (variable: VariableWithCotisation): Cotisation => { } }) } -const groupByBranche = flatRules => ( - cotisations: Array -): Cotisations => { +const groupByBranche = (cotisations: Array): Cotisations => { const cotisationsMap = cotisations.reduce( (acc, cotisation) => ({ ...acc, @@ -121,6 +108,15 @@ export let analysisToCotisations = analysis => { .reduce(concat, []) const cotisations = pipe( + map(rule => + // Following : weird logic to automatically handle negative negated value in sum + + rule.operationType === 'calculation' && + rule.operator === '−' && + rule.explanation[0].nodeValue === 0 + ? { ...rule.explanation[1], nodeValue: rule.nodeValue } + : rule + ), groupBy(prop('dottedName')), values, map( @@ -134,7 +130,7 @@ export let analysisToCotisations = analysis => { cotisation.montant.partPatronale !== 0 || cotisation.montant.partSalariale !== 0 ), - groupByBranche(analysis), + groupByBranche, filter(([, brancheCotisation]) => !!brancheCotisation) )(variables) return cotisations diff --git a/source/selectors/repartitionSelectors.js b/source/selectors/repartitionSelectors.js index 24cb106e0..8a89e63fd 100644 --- a/source/selectors/repartitionSelectors.js +++ b/source/selectors/repartitionSelectors.js @@ -112,6 +112,7 @@ const répartition = (analysis): ?Répartition => { const getRule = getRuleFromAnalysis(analysis), salaireNet = getRule('contrat salarié . rémunération . net'), salaireChargé = getRule('contrat salarié . rémunération . total'), + cotisationsRule = getRule('contrat salarié . cotisations'), réductionsDeCotisations = getRule( 'contrat salarié . cotisations . patronales . réductions de cotisations' ) @@ -146,10 +147,8 @@ const répartition = (analysis): ?Répartition => { ) )(répartitionMap), // $FlowFixMe - total: compose( - reduce(mergeWith(add), 0), - Object.values - )(répartitionMap), + total: cotisationsRule.nodeValue, + cotisations: cotisationsRule, cotisationMaximum: compose( reduce(max, 0), map(montant => montant.partPatronale + montant.partSalariale), diff --git a/test/ficheDePaieSelector.test.js b/test/ficheDePaieSelector.test.js index b3d445c91..5d7a8ea1f 100644 --- a/test/ficheDePaieSelector.test.js +++ b/test/ficheDePaieSelector.test.js @@ -54,7 +54,7 @@ describe('pay slip selector', function() { it('should sum all cotisations', function() { let pat = getRuleFromAnalysis(analysis)( - 'contrat salarié . cotisations . patronales . à payer' + 'contrat salarié . cotisations . patronales' ), sal = getRuleFromAnalysis(analysis)( 'contrat salarié . cotisations . salariales' diff --git a/test/mécanismes/expressions.yaml b/test/mécanismes/expressions.yaml index fbdf41127..916bb571e 100644 --- a/test/mécanismes/expressions.yaml +++ b/test/mécanismes/expressions.yaml @@ -234,3 +234,32 @@ formule: -5 * -10 exemples: - valeur attendue: 50 + +- test: négation de variable + formule: '- salaire de base' + exemples: + - situation: + salaire de base: 3000 + valeur attendue: -3000 + +- test: négation d'expressions + formule: '- (10 * 3 + 5)' + exemples: + - valeur attendue: -35 + +- test: variables négatives dans expression + formule: 10% * (- salaire de base) + exemples: + - situation: + salaire de base: 3000 + valeur attendue: -300 +# TODO +# - test: expression sur plusieurs lignes +# formule: > +# salaire de base +# + 2000 +# = 3000 +# exemples: +# - situation: +# salaire de base: 1000 +# - valeur attendue: true