✨ transforme applicable si et non applicable si en mécanisme chainée
Par la même occasion, uniformise l'écriture des mécanismes chainéespull/1167/head
parent
8b7bb6353c
commit
fcb44fc317
|
@ -7,7 +7,6 @@ export default function LinkToForm() {
|
|||
document.referrer ||
|
||||
document.location.origin
|
||||
).hostname.replace(/^www\.|^m\./, '')
|
||||
console.log(hostname)
|
||||
return (
|
||||
<div
|
||||
style={{ display: 'flex', justifyContent: 'center', marginTop: '1rem' }}
|
||||
|
|
|
@ -36,7 +36,6 @@ export default function Conversation({ customEndMessages }: ConversationProps) {
|
|||
dispatch(goToQuestion(currentQuestion))
|
||||
}
|
||||
}, [dispatch, currentQuestion])
|
||||
|
||||
const setDefault = () =>
|
||||
dispatch(
|
||||
validateStepWithValue(
|
||||
|
|
|
@ -718,9 +718,8 @@ dirigeant . indépendant . conjoint collaborateur . cotisations . indemnités jo
|
|||
unité: €/an
|
||||
formule:
|
||||
produit:
|
||||
assiette: plafond sécurité sociale temps plein
|
||||
facteur: 40%
|
||||
taux: 0.85%
|
||||
assiette: 40% * plafond sécurité sociale temps plein
|
||||
taux: cotisations et contributions . indemnités journalières maladie . taux
|
||||
arrondi: oui
|
||||
|
||||
dirigeant . indépendant . cotisations et contributions . cotisations:
|
||||
|
@ -904,7 +903,7 @@ dirigeant . indépendant . cotisations et contributions . indemnités journaliè
|
|||
formule:
|
||||
produit:
|
||||
assiette: maladie . assiette
|
||||
taux: 0.85%
|
||||
taux [ref]: 0.85%
|
||||
plafond: 5 * plafond sécurité sociale temps plein
|
||||
arrondi: oui
|
||||
|
||||
|
@ -912,9 +911,8 @@ dirigeant . indépendant . cotisations et contributions . maladie:
|
|||
formule:
|
||||
barème:
|
||||
assiette [ref]:
|
||||
encadrement:
|
||||
valeur: assiette des cotisations
|
||||
plancher [ref]: 40% * plafond sécurité sociale temps plein
|
||||
valeur: assiette des cotisations
|
||||
plancher [ref]: 40% * plafond sécurité sociale temps plein
|
||||
multiplicateur: plafond sécurité sociale temps plein
|
||||
tranches:
|
||||
- taux: taux variable
|
||||
|
@ -940,9 +938,8 @@ dirigeant . indépendant . cotisations et contributions . maladie . taux variabl
|
|||
|
||||
dirigeant . indépendant . cotisations et contributions . maladie . taux RSA:
|
||||
formule:
|
||||
encadrement:
|
||||
valeur: taux RSA part variable + 1.35%
|
||||
plafond: 6.35%
|
||||
valeur: taux RSA part variable + 1.35%
|
||||
plafond: 6.35%
|
||||
note: |
|
||||
Pour les indépendants au RSA, seule la réduction simple définie dans le décret de calcul de la cotisation maladie est prise en compte. La réduction renforcée en-dessous de 40% du plafond de la sécurité sociale ne l'est pas, car il n'y a pas d'assiette minimale.
|
||||
|
||||
|
@ -977,9 +974,8 @@ dirigeant . indépendant . cotisations et contributions . retraite de base:
|
|||
formule:
|
||||
barème:
|
||||
assiette [ref]:
|
||||
encadrement:
|
||||
valeur: assiette des cotisations
|
||||
plancher [ref]: 11.5% * plafond sécurité sociale temps plein
|
||||
valeur: assiette des cotisations
|
||||
plancher [ref]: 11.5% * plafond sécurité sociale temps plein
|
||||
multiplicateur: plafond sécurité sociale temps plein
|
||||
tranches:
|
||||
- taux: 17.75%
|
||||
|
@ -1009,9 +1005,8 @@ dirigeant . indépendant . cotisations et contributions . invalidité et décès
|
|||
formule:
|
||||
produit:
|
||||
assiette [ref]:
|
||||
encadrement:
|
||||
valeur: assiette des cotisations
|
||||
plancher [ref]: 11.5% * plafond sécurité sociale temps plein
|
||||
valeur: assiette des cotisations
|
||||
plancher [ref]: 11.5% * plafond sécurité sociale temps plein
|
||||
taux: 1.3%
|
||||
plafond: plafond sécurité sociale temps plein
|
||||
arrondi: oui
|
||||
|
|
|
@ -89,7 +89,8 @@ impôt . revenu imposable . abattement contrat court:
|
|||
- contrat salarié . CDD
|
||||
- contrat salarié . CDD . durée contrat <= 2 mois
|
||||
formule:
|
||||
arrondi: 50% * SMIC temps plein . net imposable * 1 mois
|
||||
valeur: 50% * SMIC temps plein . net imposable * 1 mois
|
||||
arrondi: oui
|
||||
note: Cet abattement s'applique aussi pour les conventions de stage ou les contrats de mission (intérim) de moins de 2 mois.
|
||||
références:
|
||||
Bofip - dispositions spécifiques aux contrats courts: https://bofip.impots.gouv.fr/bofip/11252-PGP.html?identifiant=BOI-IR-PAS-20-20-30-10-20180515
|
||||
|
|
|
@ -196,7 +196,7 @@ exports[`calculate simulations-professions-libérales: auxiliaire médical 1`] =
|
|||
|
||||
exports[`calculate simulations-professions-libérales: auxiliaire médical 2`] = `"[30000,0,8077,21923,932,20991]"`;
|
||||
|
||||
exports[`calculate simulations-professions-libérales: auxiliaire médical 3`] = `"[300000,0,61784,238216,81297,156919]"`;
|
||||
exports[`calculate simulations-professions-libérales: auxiliaire médical 3`] = `"[300000,0,61784,238217,81297,156920]"`;
|
||||
|
||||
exports[`calculate simulations-professions-libérales: avocat 1`] = `"[50000,0,11410,38590,4753,33837]"`;
|
||||
|
||||
|
@ -210,7 +210,7 @@ exports[`calculate simulations-professions-libérales: médecin 1`] = `"[50000,0
|
|||
|
||||
exports[`calculate simulations-professions-libérales: médecin 2`] = `"[50000,0,20229,29771,2334,27437]"`;
|
||||
|
||||
exports[`calculate simulations-professions-libérales: médecin 3`] = `"[300000,0,86481,213519,73147,140372]"`;
|
||||
exports[`calculate simulations-professions-libérales: médecin 3`] = `"[300000,0,86481,213520,73147,140373]"`;
|
||||
|
||||
exports[`calculate simulations-professions-libérales: médecin 4`] = `"[400000,0,106201,293799,115768,178031]"`;
|
||||
|
||||
|
|
|
@ -5,7 +5,6 @@ import { Trans } from 'react-i18next'
|
|||
import { Mecanism } from './common'
|
||||
|
||||
export default function Recalcul({ nodeValue, explanation, unit }) {
|
||||
console.log(nodeValue, explanation)
|
||||
return (
|
||||
<Mecanism name="recalcul" value={nodeValue} unit={unit}>
|
||||
<>
|
||||
|
|
|
@ -102,10 +102,12 @@ export function Mecanism({
|
|||
|
||||
export const InfixMecanism = ({
|
||||
value,
|
||||
prefixed,
|
||||
children
|
||||
}: {
|
||||
value: EvaluatedNode
|
||||
children: React.ReactNode
|
||||
prefixed?: boolean
|
||||
}) => {
|
||||
return (
|
||||
<div
|
||||
|
@ -120,8 +122,9 @@ export const InfixMecanism = ({
|
|||
}
|
||||
`}
|
||||
>
|
||||
{prefixed && children}
|
||||
<div className="value">{makeJsx(value)}</div>
|
||||
{children}
|
||||
{!prefixed && children}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
|
|
@ -167,26 +167,78 @@ export function isRecalculMech<Names extends string>(
|
|||
)
|
||||
}
|
||||
|
||||
export type EncadrementMech = AbstractMechanism & {
|
||||
name: 'encadrement'
|
||||
export type PlafondMech = AbstractMechanism & {
|
||||
name: 'plafond'
|
||||
explanation: {
|
||||
valeur: ASTNode
|
||||
plafond: ASTNode
|
||||
}
|
||||
}
|
||||
export function isPlafondMech(node: ASTNode): node is PlafondMech {
|
||||
const encadrementMech = node as PlafondMech
|
||||
return (
|
||||
isAbstractMechanism(encadrementMech) &&
|
||||
encadrementMech.name == 'plafond' &&
|
||||
typeof encadrementMech.explanation === 'object' &&
|
||||
encadrementMech.explanation.valeur !== undefined &&
|
||||
encadrementMech.explanation.plafond !== undefined
|
||||
)
|
||||
}
|
||||
|
||||
export type PlancherMech = AbstractMechanism & {
|
||||
name: 'plancher'
|
||||
explanation: {
|
||||
valeur: ASTNode
|
||||
plancher: ASTNode
|
||||
}
|
||||
}
|
||||
export function isEncadrementMech(node: ASTNode): node is EncadrementMech {
|
||||
const encadrementMech = node as EncadrementMech
|
||||
export function isPlancherMech(node: ASTNode): node is PlancherMech {
|
||||
const encadrementMech = node as PlancherMech
|
||||
return (
|
||||
isAbstractMechanism(encadrementMech) &&
|
||||
encadrementMech.name == 'encadrement' &&
|
||||
encadrementMech.name == 'plancher' &&
|
||||
typeof encadrementMech.explanation === 'object' &&
|
||||
encadrementMech.explanation.valeur !== undefined &&
|
||||
encadrementMech.explanation.plafond !== undefined &&
|
||||
encadrementMech.explanation.plancher !== undefined
|
||||
)
|
||||
}
|
||||
|
||||
export type ApplicableMech = AbstractMechanism & {
|
||||
name: 'applicable si'
|
||||
explanation: {
|
||||
valeur: ASTNode
|
||||
condition: ASTNode
|
||||
}
|
||||
}
|
||||
export function isApplicableMech(node: ASTNode): node is ApplicableMech {
|
||||
const mech = node as ApplicableMech
|
||||
return (
|
||||
isAbstractMechanism(mech) &&
|
||||
mech.name == 'applicable si' &&
|
||||
typeof mech.explanation === 'object' &&
|
||||
mech.explanation.valeur !== undefined &&
|
||||
mech.explanation.condition !== undefined
|
||||
)
|
||||
}
|
||||
|
||||
export type NonApplicableMech = AbstractMechanism & {
|
||||
name: 'non applicable si'
|
||||
explanation: {
|
||||
valeur: ASTNode
|
||||
condition: ASTNode
|
||||
}
|
||||
}
|
||||
export function isNonApplicableMech(node: ASTNode): node is NonApplicableMech {
|
||||
const mech = node as NonApplicableMech
|
||||
return (
|
||||
isAbstractMechanism(mech) &&
|
||||
mech.name == 'non applicable si' &&
|
||||
typeof mech.explanation === 'object' &&
|
||||
mech.explanation.valeur !== undefined &&
|
||||
mech.explanation.condition !== undefined
|
||||
)
|
||||
}
|
||||
|
||||
export type SommeMech = AbstractMechanism & {
|
||||
name: 'somme'
|
||||
explanation: Array<ASTNode>
|
||||
|
@ -325,8 +377,8 @@ export function isArrondiMech(node: ASTNode): node is ArrondiMech {
|
|||
isAbstractMechanism(arrondiMech) &&
|
||||
arrondiMech.name === 'arrondi' &&
|
||||
typeof arrondiMech.explanation === 'object' &&
|
||||
arrondiMech.explanation.decimals !== undefined &&
|
||||
arrondiMech.explanation.value !== undefined
|
||||
arrondiMech.explanation.arrondi !== undefined &&
|
||||
arrondiMech.explanation.valeur !== undefined
|
||||
)
|
||||
}
|
||||
|
||||
|
@ -484,7 +536,10 @@ export function isDureeMech(node: ASTNode): node is DureeMech {
|
|||
|
||||
export type AnyMechanism<Names extends string> =
|
||||
| RecalculMech<Names>
|
||||
| EncadrementMech
|
||||
| PlancherMech
|
||||
| PlafondMech
|
||||
| ApplicableMech
|
||||
| NonApplicableMech
|
||||
| SommeMech
|
||||
| ProduitMech
|
||||
| VariationsMech
|
||||
|
@ -506,7 +561,10 @@ export function isAnyMechanism<Names extends string>(
|
|||
): node is AnyMechanism<Names> {
|
||||
return (
|
||||
isRecalculMech<Names>(node) ||
|
||||
isEncadrementMech(node) ||
|
||||
isPlafondMech(node) ||
|
||||
isPlancherMech(node) ||
|
||||
isApplicableMech(node) ||
|
||||
isNonApplicableMech(node) ||
|
||||
isSommeMech(node) ||
|
||||
isProduitMech(node) ||
|
||||
isVariationsMech(node) ||
|
||||
|
|
|
@ -56,12 +56,11 @@ export function ruleDepsOfNode<Names extends string>(
|
|||
return ruleReference === ruleName ? [] : [ruleReference]
|
||||
}
|
||||
|
||||
function ruleDepsOfEncadrementMech(
|
||||
encadrementMech: ASTTypes.EncadrementMech
|
||||
function ruleDepsOfPlafondMech(
|
||||
encadrementMech: ASTTypes.PlafondMech
|
||||
): RuleDependencies<Names> {
|
||||
const result = [
|
||||
encadrementMech.explanation.plafond,
|
||||
encadrementMech.explanation.plancher,
|
||||
encadrementMech.explanation.valeur
|
||||
].flatMap(
|
||||
R.partial<Names, ASTTypes.ASTNode, RuleDependencies<Names>>(
|
||||
|
@ -72,6 +71,33 @@ export function ruleDepsOfNode<Names extends string>(
|
|||
return result
|
||||
}
|
||||
|
||||
function ruleDepsOfPlancherMech(
|
||||
mech: ASTTypes.PlancherMech
|
||||
): RuleDependencies<Names> {
|
||||
const result = [mech.explanation.plancher, mech.explanation.valeur].flatMap(
|
||||
R.partial<Names, ASTTypes.ASTNode, RuleDependencies<Names>>(
|
||||
ruleDepsOfNode,
|
||||
[ruleName]
|
||||
)
|
||||
)
|
||||
return result
|
||||
}
|
||||
|
||||
function ruleDepsOfApplicableMech(
|
||||
mech: ASTTypes.ApplicableMech | ASTTypes.NonApplicableMech
|
||||
): RuleDependencies<Names> {
|
||||
const result = [
|
||||
mech.explanation.condition,
|
||||
mech.explanation.valeur
|
||||
].flatMap(
|
||||
R.partial<Names, ASTTypes.ASTNode, RuleDependencies<Names>>(
|
||||
ruleDepsOfNode,
|
||||
[ruleName]
|
||||
)
|
||||
)
|
||||
return result
|
||||
}
|
||||
|
||||
function ruleDepsOfSommeMech(
|
||||
sommeMech: ASTTypes.SommeMech
|
||||
): RuleDependencies<Names> {
|
||||
|
@ -168,8 +194,8 @@ export function ruleDepsOfNode<Names extends string>(
|
|||
arrondiMech: ASTTypes.ArrondiMech
|
||||
): RuleDependencies<Names> {
|
||||
const result = [
|
||||
arrondiMech.explanation.decimals,
|
||||
arrondiMech.explanation.value
|
||||
arrondiMech.explanation.arrondi,
|
||||
arrondiMech.explanation.valeur
|
||||
].flatMap(
|
||||
R.partial<Names, ASTTypes.ASTNode, RuleDependencies<Names>>(
|
||||
ruleDepsOfNode,
|
||||
|
@ -312,8 +338,14 @@ export function ruleDepsOfNode<Names extends string>(
|
|||
result = ruleDepsOfPossibilities2(node)
|
||||
} else if (ASTTypes.isRecalculMech<Names>(node)) {
|
||||
result = ruleDepsOfRecalculMech(node)
|
||||
} else if (ASTTypes.isEncadrementMech(node)) {
|
||||
result = ruleDepsOfEncadrementMech(node)
|
||||
} else if (ASTTypes.isApplicableMech(node)) {
|
||||
result = ruleDepsOfApplicableMech(node)
|
||||
} else if (ASTTypes.isNonApplicableMech(node)) {
|
||||
result = ruleDepsOfApplicableMech(node)
|
||||
} else if (ASTTypes.isPlafondMech(node)) {
|
||||
result = ruleDepsOfPlafondMech(node)
|
||||
} else if (ASTTypes.isPlancherMech(node)) {
|
||||
result = ruleDepsOfPlancherMech(node)
|
||||
} else if (ASTTypes.isSommeMech(node)) {
|
||||
result = ruleDepsOfSommeMech(node)
|
||||
} else if (ASTTypes.isProduitMech(node)) {
|
||||
|
|
|
@ -33,7 +33,7 @@ export const collectNodeMissing = node => node.missingVariables || {}
|
|||
export const bonus = (missings, hasCondition = true) =>
|
||||
hasCondition ? map(x => x + 0.0001, missings || {}) : missings
|
||||
export const mergeAllMissing = missings =>
|
||||
reduce(mergeWith(add), {}, map(collectNodeMissing, missings))
|
||||
missings.map(collectNodeMissing).reduce(mergeMissing, {})
|
||||
export const mergeMissing = (left, right) =>
|
||||
mergeWith(add, left || {}, right || {})
|
||||
|
||||
|
|
|
@ -0,0 +1,58 @@
|
|||
import React from 'react'
|
||||
import { InfixMecanism } from '../components/mecanisms/common'
|
||||
import { bonus, evaluateNode, makeJsx, mergeMissing } from '../evaluation'
|
||||
|
||||
function MecanismApplicable({ explanation }) {
|
||||
return (
|
||||
<InfixMecanism prefixed value={explanation.valeur}>
|
||||
<p>
|
||||
<strong>Applicable si : </strong>
|
||||
{makeJsx(explanation.applicable)}
|
||||
</p>
|
||||
</InfixMecanism>
|
||||
)
|
||||
}
|
||||
|
||||
const evaluate = (cache, situation, parsedRules, node) => {
|
||||
const evaluateAttribute = evaluateNode.bind(
|
||||
null,
|
||||
cache,
|
||||
situation,
|
||||
parsedRules
|
||||
)
|
||||
const condition = evaluateAttribute(node.explanation.condition)
|
||||
let valeur = node.explanation.valeur
|
||||
if (condition.nodeValue !== false) {
|
||||
valeur = evaluateAttribute(valeur)
|
||||
}
|
||||
return {
|
||||
...node,
|
||||
nodeValue:
|
||||
condition.nodeValue == null || condition.nodeValue === false
|
||||
? condition.nodeValue
|
||||
: valeur.nodeValue,
|
||||
explanation: { valeur, condition },
|
||||
missingVariables: mergeMissing(
|
||||
valeur.missingVariables,
|
||||
bonus(condition.missingVariables)
|
||||
),
|
||||
unit: valeur.unit
|
||||
}
|
||||
}
|
||||
|
||||
export default function Applicable(recurse, v) {
|
||||
const explanation = {
|
||||
valeur: recurse(v.valeur),
|
||||
condition: recurse(v['applicable si'])
|
||||
}
|
||||
return {
|
||||
evaluate,
|
||||
jsx: MecanismApplicable,
|
||||
explanation,
|
||||
category: 'mecanism',
|
||||
name: Applicable.name,
|
||||
unit: explanation.valeur.unit
|
||||
}
|
||||
}
|
||||
|
||||
Applicable.nom = 'applicable si'
|
|
@ -1,37 +1,20 @@
|
|||
import { has } from 'ramda'
|
||||
import React from 'react'
|
||||
import { Trans } from 'react-i18next'
|
||||
import { InfixMecanism } from '../components/mecanisms/common'
|
||||
import { defaultNode, evaluateNode, mergeAllMissing } from '../evaluation'
|
||||
import { simplifyNodeUnit } from '../nodeUnits'
|
||||
import { mapTemporal, pureTemporal, temporalAverage } from '../temporal'
|
||||
import { EvaluatedNode, EvaluatedRule } from '../types'
|
||||
import { serializeUnit } from '../units'
|
||||
|
||||
type MecanismRoundProps = {
|
||||
explanation: ArrondiExplanation
|
||||
}
|
||||
import { evaluateNode, makeJsx, mergeAllMissing } from '../evaluation'
|
||||
import { EvaluatedNode } from '../types'
|
||||
|
||||
export type ArrondiExplanation = {
|
||||
value: EvaluatedNode<string, number>
|
||||
decimals: EvaluatedNode<string, number>
|
||||
valeur: EvaluatedNode<string, number>
|
||||
arrondi: EvaluatedNode<string, number>
|
||||
}
|
||||
|
||||
function MecanismRound({ explanation }: MecanismRoundProps) {
|
||||
function MecanismArrondi({ explanation }) {
|
||||
return (
|
||||
<InfixMecanism value={explanation.value}>
|
||||
{explanation.decimals.nodeValue !== false &&
|
||||
explanation.decimals.isDefault != false && (
|
||||
<p>
|
||||
<Trans
|
||||
i18nKey="arrondi-to-decimals"
|
||||
count={explanation.decimals.nodeValue ?? undefined}
|
||||
>
|
||||
<strong>Arrondi à : </strong>
|
||||
{{ count: explanation.decimals.nodeValue }} décimales
|
||||
</Trans>
|
||||
</p>
|
||||
)}
|
||||
<InfixMecanism value={explanation.valeur}>
|
||||
<p>
|
||||
<strong>Arrondi : </strong>
|
||||
{makeJsx(explanation.arrondi)}
|
||||
</p>
|
||||
</InfixMecanism>
|
||||
)
|
||||
}
|
||||
|
@ -40,66 +23,52 @@ function roundWithPrecision(n: number, fractionDigits: number) {
|
|||
return +n.toFixed(fractionDigits)
|
||||
}
|
||||
|
||||
function evaluate<Names extends string>(
|
||||
cache,
|
||||
situation,
|
||||
parsedRules,
|
||||
node: EvaluatedRule<Names, ArrondiExplanation>
|
||||
) {
|
||||
const evaluate = (cache, situation, parsedRules, node) => {
|
||||
const evaluateAttribute = evaluateNode.bind(
|
||||
null,
|
||||
cache,
|
||||
situation,
|
||||
parsedRules
|
||||
)
|
||||
const value = simplifyNodeUnit(evaluateAttribute(node.explanation.value))
|
||||
const decimals = evaluateAttribute(node.explanation.decimals)
|
||||
const valeur = evaluateAttribute(node.explanation.valeur)
|
||||
const nodeValue = valeur.nodeValue
|
||||
let arrondi = node.explanation.arrondi
|
||||
if (nodeValue !== false) {
|
||||
arrondi = evaluateAttribute(arrondi)
|
||||
}
|
||||
|
||||
const temporalValue = mapTemporal(
|
||||
(val: number | false | null) =>
|
||||
typeof val === 'number'
|
||||
? roundWithPrecision(val, decimals.nodeValue)
|
||||
: val,
|
||||
value.temporalValue ?? pureTemporal(value.nodeValue)
|
||||
)
|
||||
|
||||
const nodeValue = temporalAverage(temporalValue, value.unit)
|
||||
return {
|
||||
...node,
|
||||
unit: value.unit,
|
||||
nodeValue,
|
||||
...(temporalValue.length > 1 && { temporalValue }),
|
||||
missingVariables: mergeAllMissing([value, decimals]),
|
||||
explanation: { value, decimals }
|
||||
nodeValue:
|
||||
typeof valeur.nodeValue !== 'number'
|
||||
? valeur.nodeValue
|
||||
: typeof arrondi.nodeValue === 'number'
|
||||
? roundWithPrecision(valeur.nodeValue, arrondi.nodeValue)
|
||||
: arrondi.nodeValue === true
|
||||
? roundWithPrecision(valeur.nodeValue, 0)
|
||||
: arrondi.nodeValue === null
|
||||
? null
|
||||
: valeur.nodeValue,
|
||||
explanation: { valeur, arrondi },
|
||||
missingVariables: mergeAllMissing([valeur, arrondi]),
|
||||
unit: valeur.unit
|
||||
}
|
||||
}
|
||||
|
||||
export default (recurse, v) => {
|
||||
export default function Arrondi(recurse, v) {
|
||||
const explanation = {
|
||||
value: has('valeur', v) ? recurse(v['valeur']) : recurse(v),
|
||||
decimals: has('décimales', v) ? recurse(v['décimales']) : defaultNode(0)
|
||||
} as ArrondiExplanation
|
||||
|
||||
valeur: recurse(v.valeur),
|
||||
arrondi: recurse(v.arrondi)
|
||||
}
|
||||
return {
|
||||
explanation,
|
||||
evaluate,
|
||||
jsx: MecanismRound,
|
||||
jsx: MecanismArrondi,
|
||||
explanation,
|
||||
category: 'mecanism',
|
||||
name: 'arrondi',
|
||||
type: 'numeric',
|
||||
unit: explanation.value.unit
|
||||
unit: explanation.valeur.unit
|
||||
}
|
||||
}
|
||||
|
||||
export function unchainRoundMecanism(recurse, rawNode) {
|
||||
const { arrondi, ...valeur } = rawNode
|
||||
const arrondiValue = recurse(arrondi)
|
||||
|
||||
if (serializeUnit(arrondiValue.unit) === 'décimales') {
|
||||
return { arrondi: { valeur, décimales: arrondiValue.nodeValue } }
|
||||
} else if (arrondiValue.nodeValue === true) {
|
||||
return { arrondi: { valeur } }
|
||||
} else {
|
||||
return valeur
|
||||
}
|
||||
}
|
||||
Arrondi.nom = 'arrondi'
|
||||
|
|
|
@ -3,7 +3,7 @@ import {
|
|||
defaultNode,
|
||||
evaluateNode,
|
||||
makeJsx,
|
||||
mergeMissing,
|
||||
mergeAllMissing,
|
||||
parseObject
|
||||
} from '../evaluation'
|
||||
import { Mecanism } from '../components/mecanisms/common'
|
||||
|
@ -54,10 +54,7 @@ const evaluate = (cache, situation, parsedRules, node) => {
|
|||
)
|
||||
)
|
||||
}
|
||||
const missingVariables = mergeMissing(
|
||||
from.missingVariables,
|
||||
to.missingVariables
|
||||
)
|
||||
const missingVariables = mergeAllMissing([from, to])
|
||||
return {
|
||||
...node,
|
||||
missingVariables,
|
||||
|
|
|
@ -1,97 +0,0 @@
|
|||
import React from 'react'
|
||||
import { InfixMecanism } from '../components/mecanisms/common'
|
||||
import { typeWarning } from '../error'
|
||||
import {
|
||||
defaultNode,
|
||||
evaluateObject,
|
||||
makeJsx,
|
||||
parseObject
|
||||
} from '../evaluation'
|
||||
import { convertNodeToUnit } from '../nodeUnits'
|
||||
|
||||
function MecanismEncadrement({ nodeValue, explanation }) {
|
||||
return (
|
||||
<InfixMecanism value={explanation.valeur}>
|
||||
{!explanation.plancher.isDefault && (
|
||||
<>
|
||||
<p
|
||||
style={
|
||||
nodeValue && nodeValue === explanation.plancher.nodeValue
|
||||
? { background: 'var(--lighterColor)', fontWeight: 'bold' }
|
||||
: {}
|
||||
}
|
||||
>
|
||||
<strong>Minimum : </strong>
|
||||
{makeJsx(explanation.plancher)}
|
||||
</p>
|
||||
</>
|
||||
)}
|
||||
{!explanation.plafond.isDefault && (
|
||||
<>
|
||||
<p
|
||||
style={
|
||||
nodeValue && nodeValue === explanation.plancher.nodeValue
|
||||
? { background: 'var(--lighterColor)', fontWeight: 'bold' }
|
||||
: {}
|
||||
}
|
||||
>
|
||||
<strong>Plafonné à : </strong>
|
||||
{makeJsx(explanation.plafond)}
|
||||
</p>
|
||||
</>
|
||||
)}
|
||||
</InfixMecanism>
|
||||
)
|
||||
}
|
||||
|
||||
const objectShape = {
|
||||
valeur: false,
|
||||
plafond: defaultNode(Infinity),
|
||||
plancher: defaultNode(-Infinity)
|
||||
}
|
||||
|
||||
const evaluate = evaluateObject(
|
||||
objectShape,
|
||||
({ valeur, plafond, plancher }, cache) => {
|
||||
if (plafond.nodeValue === false || plafond.nodeValue === null) {
|
||||
plafond = objectShape.plafond
|
||||
}
|
||||
|
||||
if (valeur.unit) {
|
||||
try {
|
||||
plafond = convertNodeToUnit(valeur.unit, plafond)
|
||||
plancher = convertNodeToUnit(valeur.unit, plancher)
|
||||
} catch (e) {
|
||||
typeWarning(
|
||||
cache._meta.contextRule,
|
||||
"Le plafond / plancher de l'encadrement a une unité incompatible avec celle de la valeur à encadrer",
|
||||
e
|
||||
)
|
||||
}
|
||||
}
|
||||
return {
|
||||
nodeValue:
|
||||
typeof valeur.nodeValue !== 'number'
|
||||
? valeur.nodeValue
|
||||
: Math.max(
|
||||
plancher.nodeValue,
|
||||
Math.min(plafond.nodeValue, valeur.nodeValue)
|
||||
),
|
||||
unit: valeur.unit
|
||||
}
|
||||
}
|
||||
)
|
||||
|
||||
export default (recurse, v) => {
|
||||
const explanation = parseObject(recurse, objectShape, v)
|
||||
|
||||
return {
|
||||
evaluate,
|
||||
jsx: MecanismEncadrement,
|
||||
explanation,
|
||||
category: 'mecanism',
|
||||
name: 'encadrement',
|
||||
type: 'numeric',
|
||||
unit: explanation.valeur.unit
|
||||
}
|
||||
}
|
|
@ -1,66 +0,0 @@
|
|||
import { map } from 'ramda'
|
||||
import { evaluateNode, mergeMissing } from '../evaluation'
|
||||
|
||||
const evaluate = (cache, situation, parsedRules, node) => {
|
||||
const explanation = map(
|
||||
node => evaluateNode(cache, situation, parsedRules, node),
|
||||
node.explanation
|
||||
)
|
||||
const evaluation = Object.entries(explanation).reduce(
|
||||
({ missingVariables, nodeValue }, [name, evaluation]) => {
|
||||
const mergedMissingVariables = mergeMissing(
|
||||
missingVariables,
|
||||
evaluation.missingVariables
|
||||
)
|
||||
if (evaluation.explanation.isApplicable === false) {
|
||||
return { missingVariables: mergedMissingVariables, nodeValue }
|
||||
}
|
||||
return {
|
||||
missingVariables: mergedMissingVariables,
|
||||
nodeValue: {
|
||||
[name]: evaluation.nodeValue,
|
||||
...nodeValue
|
||||
}
|
||||
}
|
||||
},
|
||||
{ missingVariables: {}, nodeValue: {} }
|
||||
)
|
||||
|
||||
return { ...evaluation, explanation, ...node }
|
||||
}
|
||||
|
||||
export const mecanismGroup = (rules: Array<string>, dottedName: string) => (
|
||||
parse,
|
||||
k,
|
||||
v
|
||||
) => {
|
||||
let références: Array<string>
|
||||
if (v === 'tous') {
|
||||
références = rules
|
||||
.filter(
|
||||
name =>
|
||||
name.startsWith(dottedName) &&
|
||||
name.split(' . ').length === dottedName.split(' . ').length + 1
|
||||
)
|
||||
.map(name => name.split(' . ').slice(-1)[0])
|
||||
} else {
|
||||
références = v
|
||||
}
|
||||
const parsedRéférences = références.reduce(
|
||||
(acc, name) => ({
|
||||
...acc,
|
||||
[name]: parse(name)
|
||||
}),
|
||||
{}
|
||||
)
|
||||
return {
|
||||
explanation: parsedRéférences,
|
||||
evaluate,
|
||||
jsx: function Groupe({ explanation }) {
|
||||
return null
|
||||
},
|
||||
category: 'mecanism',
|
||||
name: 'groupe',
|
||||
type: 'groupe'
|
||||
}
|
||||
}
|
|
@ -0,0 +1,66 @@
|
|||
import React from 'react'
|
||||
import { InfixMecanism } from '../components/mecanisms/common'
|
||||
import {
|
||||
bonus,
|
||||
evaluateNode,
|
||||
makeJsx,
|
||||
mergeAllMissing,
|
||||
mergeMissing
|
||||
} from '../evaluation'
|
||||
|
||||
function MecanismNonApplicable({ explanation }) {
|
||||
return (
|
||||
<InfixMecanism prefixed value={explanation.valeur}>
|
||||
<p>
|
||||
<strong>Non applicable si : </strong>
|
||||
{makeJsx(explanation.applicable)}
|
||||
</p>
|
||||
</InfixMecanism>
|
||||
)
|
||||
}
|
||||
|
||||
const evaluate = (cache, situation, parsedRules, node) => {
|
||||
const evaluateAttribute = evaluateNode.bind(
|
||||
null,
|
||||
cache,
|
||||
situation,
|
||||
parsedRules
|
||||
)
|
||||
const condition = evaluateAttribute(node.explanation.condition)
|
||||
let valeur = node.explanation.valeur
|
||||
if (condition.nodeValue !== true) {
|
||||
valeur = evaluateAttribute(valeur)
|
||||
}
|
||||
return {
|
||||
...node,
|
||||
nodeValue:
|
||||
condition.nodeValue == null
|
||||
? condition.nodeValue
|
||||
: condition.nodeValue === true
|
||||
? false
|
||||
: valeur.nodeValue,
|
||||
explanation: { valeur, condition },
|
||||
missingVariables: mergeMissing(
|
||||
valeur.missingVariables,
|
||||
bonus(condition.missingVariables)
|
||||
),
|
||||
unit: valeur.unit
|
||||
}
|
||||
}
|
||||
|
||||
export default function NonApplicable(recurse, v) {
|
||||
const explanation = {
|
||||
valeur: recurse(v.valeur),
|
||||
condition: recurse(v['non applicable si'])
|
||||
}
|
||||
return {
|
||||
evaluate,
|
||||
jsx: MecanismNonApplicable,
|
||||
explanation,
|
||||
category: 'mecanism',
|
||||
name: 'non applicable',
|
||||
unit: explanation.valeur.unit
|
||||
}
|
||||
}
|
||||
|
||||
NonApplicable.nom = 'non applicable si'
|
|
@ -3,7 +3,7 @@ import React from 'react'
|
|||
import { Operation } from '../components/mecanisms/common'
|
||||
import { convertToDate } from '../date'
|
||||
import { typeWarning } from '../error'
|
||||
import { evaluateNode, makeJsx, mergeMissing } from '../evaluation'
|
||||
import { evaluateNode, makeJsx, mergeAllMissing } from '../evaluation'
|
||||
import { convertNodeToUnit } from '../nodeUnits'
|
||||
import { liftTemporal2, pureTemporal, temporalAverage } from '../temporal'
|
||||
import { inferUnit, serializeUnit } from '../units'
|
||||
|
@ -15,10 +15,7 @@ export default (k, operatorFunction, symbol) => (recurse, v) => {
|
|||
node.explanation
|
||||
)
|
||||
let [node1, node2] = explanation
|
||||
const missingVariables = mergeMissing(
|
||||
node1.missingVariables,
|
||||
node2.missingVariables
|
||||
)
|
||||
const missingVariables = mergeAllMissing([node1, node2])
|
||||
|
||||
if (node1.nodeValue == null || node2.nodeValue == null) {
|
||||
return { ...node, nodeValue: null, explanation, missingVariables }
|
||||
|
|
|
@ -0,0 +1,82 @@
|
|||
import React from 'react'
|
||||
import { InfixMecanism } from '../components/mecanisms/common'
|
||||
import { typeWarning } from '../error'
|
||||
import { evaluateNode, makeJsx, mergeAllMissing } from '../evaluation'
|
||||
import { convertNodeToUnit } from '../nodeUnits'
|
||||
|
||||
function MecanismPlafond({ explanation }) {
|
||||
return (
|
||||
<InfixMecanism value={explanation.valeur}>
|
||||
<p
|
||||
style={
|
||||
explanation.plafond.isActive
|
||||
? { background: 'var(--lighterColor)', fontWeight: 'bold' }
|
||||
: {}
|
||||
}
|
||||
>
|
||||
<strong>Plafonné à : </strong>
|
||||
{makeJsx(explanation.plafond)}
|
||||
</p>
|
||||
</InfixMecanism>
|
||||
)
|
||||
}
|
||||
|
||||
const evaluate = (cache, situation, parsedRules, node) => {
|
||||
const evaluateAttribute = evaluateNode.bind(
|
||||
null,
|
||||
cache,
|
||||
situation,
|
||||
parsedRules
|
||||
)
|
||||
const valeur = evaluateAttribute(node.explanation.valeur)
|
||||
|
||||
let nodeValue = valeur.nodeValue
|
||||
let plafond = node.explanation.plafond
|
||||
if (nodeValue !== false) {
|
||||
plafond = evaluateAttribute(plafond)
|
||||
if (valeur.unit) {
|
||||
try {
|
||||
plafond = convertNodeToUnit(valeur.unit, plafond)
|
||||
} catch (e) {
|
||||
typeWarning(
|
||||
cache._meta.contextRule,
|
||||
"L'unité du plafond n'est pas compatible avec celle de la valeur à encadrer",
|
||||
e
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
if (
|
||||
typeof nodeValue === 'number' &&
|
||||
typeof plafond.nodeValue === 'number' &&
|
||||
nodeValue > plafond.nodeValue
|
||||
) {
|
||||
nodeValue = plafond.nodeValue
|
||||
plafond.isActive = true
|
||||
}
|
||||
return {
|
||||
...node,
|
||||
nodeValue,
|
||||
unit: valeur.unit,
|
||||
explanation: { valeur, plafond },
|
||||
missingVariables: mergeAllMissing([valeur, plafond])
|
||||
}
|
||||
}
|
||||
|
||||
export default function Plafond(recurse, v) {
|
||||
const explanation = {
|
||||
valeur: recurse(v.valeur),
|
||||
plafond: recurse(v.plafond)
|
||||
}
|
||||
return {
|
||||
evaluate,
|
||||
jsx: MecanismPlafond,
|
||||
explanation,
|
||||
category: 'mecanism',
|
||||
name: 'plafond',
|
||||
type: 'numeric',
|
||||
unit: explanation.valeur.unit
|
||||
}
|
||||
}
|
||||
|
||||
Plafond.nom = 'plafond'
|
|
@ -0,0 +1,81 @@
|
|||
import React from 'react'
|
||||
import { InfixMecanism } from '../components/mecanisms/common'
|
||||
import { typeWarning } from '../error'
|
||||
import { evaluateNode, makeJsx, mergeAllMissing } from '../evaluation'
|
||||
import { convertNodeToUnit } from '../nodeUnits'
|
||||
|
||||
function MecanismPlancher({ explanation }) {
|
||||
return (
|
||||
<InfixMecanism value={explanation.valeur}>
|
||||
<p
|
||||
style={
|
||||
explanation.plancher.isActive
|
||||
? { background: 'var(--lighterColor)', fontWeight: 'bold' }
|
||||
: {}
|
||||
}
|
||||
>
|
||||
<strong>Minimum : </strong>
|
||||
{makeJsx(explanation.plancher)}
|
||||
</p>
|
||||
</InfixMecanism>
|
||||
)
|
||||
}
|
||||
|
||||
const evaluate = (cache, situation, parsedRules, node) => {
|
||||
const evaluateAttribute = evaluateNode.bind(
|
||||
null,
|
||||
cache,
|
||||
situation,
|
||||
parsedRules
|
||||
)
|
||||
const valeur = evaluateAttribute(node.explanation.valeur)
|
||||
let nodeValue = valeur.nodeValue
|
||||
let plancher = node.explanation.plancher
|
||||
if (nodeValue !== false) {
|
||||
plancher = evaluateAttribute(plancher)
|
||||
if (valeur.unit) {
|
||||
try {
|
||||
plancher = convertNodeToUnit(valeur.unit, plancher)
|
||||
} catch (e) {
|
||||
typeWarning(
|
||||
cache._meta.contextRule,
|
||||
"L'unité du plancher n'est pas compatible avec celle de la valeur à encadrer",
|
||||
e
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
if (
|
||||
typeof nodeValue === 'number' &&
|
||||
typeof plancher.nodeValue === 'number' &&
|
||||
nodeValue < plancher.nodeValue
|
||||
) {
|
||||
nodeValue = plancher.nodeValue
|
||||
plancher.isActive = true
|
||||
}
|
||||
return {
|
||||
...node,
|
||||
nodeValue,
|
||||
explanation: { valeur, plancher },
|
||||
missingVariables: mergeAllMissing([valeur, plancher]),
|
||||
unit: valeur.unit
|
||||
}
|
||||
}
|
||||
|
||||
export default function Plancher(recurse, v) {
|
||||
const explanation = {
|
||||
valeur: recurse(v.valeur),
|
||||
plancher: recurse(v.plancher)
|
||||
}
|
||||
return {
|
||||
evaluate,
|
||||
jsx: MecanismPlancher,
|
||||
explanation,
|
||||
category: 'mecanism',
|
||||
name: 'plancher',
|
||||
type: 'numeric',
|
||||
unit: explanation.valeur.unit
|
||||
}
|
||||
}
|
||||
|
||||
Plancher.nom = 'plancher'
|
|
@ -1,7 +1,7 @@
|
|||
import Product from '../components/mecanisms/Product'
|
||||
import { typeWarning } from '../error'
|
||||
import { defaultNode, evaluateObject, parseObject } from '../evaluation'
|
||||
import { convertNodeToUnit } from '../nodeUnits'
|
||||
import { convertNodeToUnit, simplifyNodeUnit } from '../nodeUnits'
|
||||
import { areUnitConvertible, convertUnit, inferUnit } from '../units'
|
||||
|
||||
export const mecanismProduct = (recurse, v) => {
|
||||
|
@ -45,13 +45,13 @@ export const mecanismProduct = (recurse, v) => {
|
|||
nodeValue = convertUnit(unit, assiette.unit, nodeValue)
|
||||
unit = assiette.unit
|
||||
}
|
||||
return {
|
||||
return simplifyNodeUnit({
|
||||
nodeValue,
|
||||
unit,
|
||||
explanation: {
|
||||
plafondActif: assiette.nodeValue > plafond.nodeValue
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
const explanation = parseObject(recurse, objectShape, v),
|
||||
evaluate = evaluateObject(objectShape, effect)
|
||||
|
|
|
@ -13,18 +13,22 @@ import {
|
|||
lt,
|
||||
lte,
|
||||
multiply,
|
||||
omit,
|
||||
subtract
|
||||
} from 'ramda'
|
||||
import React from 'react'
|
||||
import { EngineError, syntaxError } from './error'
|
||||
import { formatValue } from './format'
|
||||
import grammar from './grammar.ne'
|
||||
import mecanismRound, { unchainRoundMecanism } from './mecanisms/arrondi'
|
||||
import arrondi from './mecanisms/arrondi'
|
||||
import barème from './mecanisms/barème'
|
||||
import { mecanismAllOf } from './mecanisms/condition-allof'
|
||||
import { mecanismOneOf } from './mecanisms/condition-oneof'
|
||||
import durée from './mecanisms/durée'
|
||||
import encadrement from './mecanisms/encadrement'
|
||||
import plafond from './mecanisms/plafond'
|
||||
import plancher from './mecanisms/plancher'
|
||||
import applicable from './mecanisms/applicable'
|
||||
import nonApplicable from './mecanisms/nonApplicable'
|
||||
import grille from './mecanisms/grille'
|
||||
import { mecanismInversion } from './mecanisms/inversion'
|
||||
import { mecanismMax } from './mecanisms/max'
|
||||
|
@ -98,22 +102,19 @@ Les mécanisme possibles sont : 'somme', 'le maximum de', 'le minimum de', 'tout
|
|||
`
|
||||
)
|
||||
}
|
||||
const keys = Object.keys(rawNode)
|
||||
const unchainableMecanisms = difference(keys, chainableMecanisms)
|
||||
if (keys.length > 1) {
|
||||
if (unchainableMecanisms.length > 1) {
|
||||
syntaxError(
|
||||
rule.dottedName,
|
||||
`
|
||||
Les mécanismes suivants se situent au même niveau : ${unchainableMecanisms
|
||||
.map(x => `'${x}'`)
|
||||
.join(', ')}
|
||||
Cela vient probablement d'une erreur dans l'indentation
|
||||
`
|
||||
)
|
||||
}
|
||||
|
||||
return parseChainedMecanisms(rules, rule, parsedRules, rawNode)
|
||||
rawNode = unfoldChainedMecanisms(rawNode)
|
||||
const keys = Object.keys(rawNode)
|
||||
if (keys.length > 1) {
|
||||
syntaxError(
|
||||
rule.dottedName,
|
||||
`
|
||||
Les mécanismes suivants se situent au même niveau : ${keys
|
||||
.map(x => `'${x}'`)
|
||||
.join(', ')}
|
||||
Cela vient probablement d'une erreur dans l'indentation
|
||||
`
|
||||
)
|
||||
}
|
||||
const mecanismName = Object.keys(rawNode)[0]
|
||||
const values = rawNode[mecanismName]
|
||||
|
@ -173,32 +174,34 @@ Vérifiez qu'il n'y ait pas d'erreur dans l'orthographe du nom.`
|
|||
}
|
||||
}
|
||||
|
||||
const chainableMecanisms = ['arrondi', 'plancher', 'plafond']
|
||||
|
||||
function parseChainedMecanisms(rules, rule, parsedRules, rawNode) {
|
||||
const keys = Object.keys(rawNode)
|
||||
const recurse = parseMecanism(rules, rule, parsedRules)
|
||||
if (keys.includes('arrondi')) {
|
||||
return recurse(
|
||||
unchainRoundMecanism(parse(rules, rule, parsedRules), rawNode)
|
||||
)
|
||||
} else if (keys.includes('plancher')) {
|
||||
const { plancher, ...valeur } = rawNode
|
||||
return recurse({
|
||||
encadrement: {
|
||||
valeur,
|
||||
plancher
|
||||
}
|
||||
})
|
||||
} else if (keys.includes('plafond')) {
|
||||
const { plafond, ...valeur } = rawNode
|
||||
return recurse({
|
||||
encadrement: {
|
||||
valeur,
|
||||
plafond
|
||||
}
|
||||
})
|
||||
const chainableMecanisms = [
|
||||
applicable,
|
||||
nonApplicable,
|
||||
plancher,
|
||||
plafond,
|
||||
arrondi
|
||||
]
|
||||
function unfoldChainedMecanisms(rawNode) {
|
||||
if (Object.keys(rawNode).length === 1) {
|
||||
return rawNode
|
||||
}
|
||||
return chainableMecanisms.reduceRight(
|
||||
(node, parseFn) => {
|
||||
if (!(parseFn.nom in rawNode)) {
|
||||
return node
|
||||
}
|
||||
return {
|
||||
[parseFn.nom]: {
|
||||
[parseFn.nom]: rawNode[parseFn.nom],
|
||||
valeur: node
|
||||
}
|
||||
}
|
||||
},
|
||||
omit(
|
||||
chainableMecanisms.map(fn => fn.nom),
|
||||
rawNode
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
const knownOperations = {
|
||||
|
@ -223,6 +226,7 @@ const operationDispatch = fromPairs(
|
|||
|
||||
const statelessParseFunction = {
|
||||
...operationDispatch,
|
||||
...chainableMecanisms.reduce((acc, fn) => ({ [fn.nom]: fn, ...acc }), {}),
|
||||
'une de ces conditions': mecanismOneOf,
|
||||
'toutes ces conditions': mecanismAllOf,
|
||||
somme: mecanismSum,
|
||||
|
@ -230,11 +234,9 @@ const statelessParseFunction = {
|
|||
multiplication: mecanismProduct,
|
||||
produit: mecanismProduct,
|
||||
temporalValue: variableTemporelle,
|
||||
arrondi: mecanismRound,
|
||||
barème,
|
||||
grille,
|
||||
'taux progressif': tauxProgressif,
|
||||
encadrement,
|
||||
durée,
|
||||
'le maximum de': mecanismMax,
|
||||
'le minimum de': mecanismMin,
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
import parseRule from './parseRule'
|
||||
import yaml from 'yaml'
|
||||
import { lensPath, set } from 'ramda'
|
||||
import { compose, dissoc, lensPath, over, set } from 'ramda'
|
||||
import { compilationError } from './error'
|
||||
import { parseReference } from './parseReference'
|
||||
import { ParsedRules, Rules } from './types'
|
||||
|
@ -63,7 +63,6 @@ export default function parseRules<Names extends string>(
|
|||
...other
|
||||
}))
|
||||
})
|
||||
|
||||
return parsedRules as ParsedRules<Names>
|
||||
}
|
||||
|
||||
|
@ -82,6 +81,7 @@ function extractInlinedNames(rules: Record<string, Record<string, any>>) {
|
|||
context: Array<string | number> = []
|
||||
) => ([key, value]: [string, Record<string, any>]) => {
|
||||
const match = /\[ref( (.+))?\]$/.exec(key)
|
||||
|
||||
if (match) {
|
||||
const argumentType = key.replace(match[0], '').trim()
|
||||
const argumentName = match[2]?.trim() || argumentType
|
||||
|
@ -93,18 +93,17 @@ function extractInlinedNames(rules: Record<string, Record<string, any>>) {
|
|||
`Le paramètre [ref] ${argumentName} entre en conflit avec la règle déjà existante ${extractedReferenceName}`
|
||||
)
|
||||
}
|
||||
|
||||
rules[extractedReferenceName] = {
|
||||
formule: value,
|
||||
// The `virtualRule` parameter is used to avoid creating a
|
||||
// dedicated documentation page.
|
||||
virtualRule: true
|
||||
}
|
||||
rules[dottedName] = set(
|
||||
lensPath([...context, argumentType]),
|
||||
extractedReferenceName,
|
||||
rules[dottedName]
|
||||
)
|
||||
|
||||
rules[dottedName] = compose(
|
||||
over(lensPath(context), dissoc(key)) as any,
|
||||
set(lensPath([...context, argumentType]), extractedReferenceName)
|
||||
)(rules[dottedName]) as any
|
||||
extractNamesInRule(extractedReferenceName)
|
||||
} else if (Array.isArray(value)) {
|
||||
value.forEach((content: Record<string, any>, i) =>
|
||||
|
|
|
@ -12,6 +12,7 @@ import { coerceArray } from '../source/utils'
|
|||
import testSuites from './load-mecanism-tests'
|
||||
testSuites.forEach(([suiteName, suite]) => {
|
||||
const engine = new Engine(suite)
|
||||
|
||||
describe(`Mécanisme ${suiteName}`, () => {
|
||||
Object.entries(suite)
|
||||
.filter(([, rule]) => rule?.exemples)
|
||||
|
|
|
@ -15,3 +15,16 @@ prévoyance obligatoire cadre:
|
|||
situation:
|
||||
statut cadre: non
|
||||
valeur attendue: false
|
||||
|
||||
|
||||
variable:
|
||||
par défaut: oui
|
||||
applicable comme mécanisme chainé:
|
||||
formule:
|
||||
applicable si: variable
|
||||
valeur: 5
|
||||
exemples:
|
||||
- valeur attendue: 5
|
||||
- situation:
|
||||
variable: non
|
||||
valeur attendue: false
|
||||
|
|
|
@ -2,13 +2,15 @@ cotisation retraite:
|
|||
|
||||
demie part:
|
||||
formule:
|
||||
arrondi: 50% * 100.2€
|
||||
valeur: 0.5 * 100.2€
|
||||
arrondi: oui
|
||||
exemples:
|
||||
- valeur attendue: 50
|
||||
|
||||
Arrondi:
|
||||
formule:
|
||||
arrondi: cotisation retraite
|
||||
valeur: cotisation retraite
|
||||
arrondi: oui
|
||||
|
||||
exemples:
|
||||
- nom: arrondi en dessous
|
||||
|
@ -24,9 +26,8 @@ nombre de décimales:
|
|||
|
||||
Arrondi avec precision:
|
||||
formule:
|
||||
arrondi:
|
||||
valeur: cotisation retraite
|
||||
décimales: nombre de décimales
|
||||
valeur: cotisation retraite
|
||||
arrondi: nombre de décimales
|
||||
exemples:
|
||||
- nom: pas de décimales
|
||||
situation:
|
||||
|
|
|
@ -1,8 +1,7 @@
|
|||
plafonnement:
|
||||
formule:
|
||||
encadrement:
|
||||
valeur: 1000 €
|
||||
plafond: 250 €
|
||||
valeur: 1000 €
|
||||
plafond: 250 €
|
||||
|
||||
exemples:
|
||||
- valeur attendue: 250
|
||||
|
@ -25,18 +24,16 @@ plancher nouvelle ecriture:
|
|||
|
||||
plafonnement inactif:
|
||||
formule:
|
||||
encadrement:
|
||||
valeur: 1000 €
|
||||
plafond: non
|
||||
valeur: 1000 €
|
||||
plafond: non
|
||||
|
||||
exemples:
|
||||
- valeur attendue: 1000
|
||||
|
||||
plafonnement reference inactive:
|
||||
formule:
|
||||
encadrement:
|
||||
valeur: 1000 €
|
||||
plafond: plafond
|
||||
valeur: 1000 €
|
||||
plafond: plafond
|
||||
|
||||
exemples:
|
||||
- valeur attendue: 1000
|
||||
|
@ -44,9 +41,19 @@ plafonnement reference inactive:
|
|||
plafonnement reference inactive . plafond: non
|
||||
plancher:
|
||||
formule:
|
||||
encadrement:
|
||||
valeur: 1000 €
|
||||
plancher: 2500 €
|
||||
valeur: 1000 €
|
||||
plancher: 2500 €
|
||||
|
||||
exemples:
|
||||
- valeur attendue: 2500
|
||||
|
||||
|
||||
encadrement inférieur et supérieur:
|
||||
formule:
|
||||
somme:
|
||||
- 500
|
||||
- 400
|
||||
plafond: 800
|
||||
plancher: 200
|
||||
exemples:
|
||||
- valeur attendue: 800
|
||||
|
|
|
@ -19,13 +19,11 @@ paramètre nommés imbriqués:
|
|||
formule:
|
||||
multiplication:
|
||||
assiette [ref]:
|
||||
encadrement:
|
||||
valeur: 1000€
|
||||
plafond [ref]: 100€
|
||||
valeur: 1000€
|
||||
plafond [ref]: 100€
|
||||
taux: 5%
|
||||
exemples:
|
||||
- valeur attendue: 5
|
||||
|
||||
- situation:
|
||||
paramètre nommés imbriqués . assiette . plafond: 200
|
||||
valeur attendue: 10
|
||||
|
|
|
@ -15,11 +15,10 @@ SMIC net:
|
|||
|
||||
Recalcule règle courante:
|
||||
formule:
|
||||
encadrement:
|
||||
valeur: 10% * salaire brut
|
||||
plafond:
|
||||
recalcul:
|
||||
avec:
|
||||
salaire brut: 100€
|
||||
valeur: 10% * salaire brut
|
||||
plafond:
|
||||
recalcul:
|
||||
avec:
|
||||
salaire brut: 100€
|
||||
exemples:
|
||||
- valeur attendue: 10
|
||||
|
|
Loading…
Reference in New Issue