commit
593febd811
|
@ -8,7 +8,7 @@ import type {
|
|||
SetSituationBranchAction
|
||||
} from 'Types/ActionsTypes'
|
||||
// $FlowFixMe
|
||||
import { clearFields, reset } from 'redux-form'
|
||||
import { change, reset } from 'redux-form'
|
||||
import { deletePersistedSimulation } from '../storage/persistSimulation'
|
||||
import type { Thunk } from 'Types/ActionsTypes'
|
||||
|
||||
|
@ -25,14 +25,15 @@ export const goToQuestion = (question: string): StepAction => ({
|
|||
name: 'unfold',
|
||||
step: question
|
||||
})
|
||||
export const skipQuestion = (
|
||||
question: string
|
||||
export const validateStepWithValue = (
|
||||
dottedName,
|
||||
value: any
|
||||
): Thunk<StepAction> => dispatch => {
|
||||
dispatch(clearFields('conversation', false, false, question))
|
||||
dispatch(change('conversation', dottedName, value))
|
||||
dispatch({
|
||||
type: 'STEP_ACTION',
|
||||
name: 'fold',
|
||||
step: question
|
||||
step: dottedName
|
||||
})
|
||||
}
|
||||
|
||||
|
|
|
@ -113,17 +113,17 @@ class Distribution extends Component<Props, State> {
|
|||
<div className="distribution-chart__total">
|
||||
<span />
|
||||
<RuleLink {...salaireNet} />
|
||||
<Value {...salaireNet} unit="€" numFractionDigits={0} />
|
||||
<Value {...salaireNet} unit="€" maximumFractionDigits={0} />
|
||||
<span>+</span>
|
||||
<Trans>Cotisations</Trans>
|
||||
<Value numFractionDigits={0} unit="€">
|
||||
<Value maximumFractionDigits={0} unit="€">
|
||||
{total.partPatronale + total.partSalariale}
|
||||
</Value>
|
||||
<span />
|
||||
<div className="distribution-chart__total-border" />
|
||||
<span>=</span>
|
||||
<RuleLink {...salaireChargé} />
|
||||
<Value {...salaireChargé} unit="€" numFractionDigits={0} />
|
||||
<Value {...salaireChargé} unit="€" maximumFractionDigits={0} />
|
||||
</div>
|
||||
</>
|
||||
)
|
||||
|
@ -154,7 +154,7 @@ let ChartItemBar = ({ styles, colour, montant, total }) => (
|
|||
margin-left: 1em;
|
||||
color: var(--textColourOnWhite);
|
||||
`}>
|
||||
<Value numFractionDigits={0} unit="€">
|
||||
<Value maximumFractionDigits={0} unit="€">
|
||||
{montant}
|
||||
</Value>
|
||||
</div>
|
||||
|
|
|
@ -52,8 +52,8 @@ export default compose(
|
|||
return
|
||||
})
|
||||
return (
|
||||
<div id="PeriodSwitch">
|
||||
<div className="base ui__ small toggle">
|
||||
<span id="PeriodSwitch">
|
||||
<span className="base ui__ small toggle">
|
||||
<label>
|
||||
<Field
|
||||
name="période"
|
||||
|
@ -82,8 +82,8 @@ export default compose(
|
|||
<Trans>mois</Trans>
|
||||
</span>
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
</span>
|
||||
</span>
|
||||
)
|
||||
})
|
||||
|
||||
|
|
|
@ -626,7 +626,7 @@ const RuleValueLink = compose(
|
|||
sitePaths.documentation.index + '/' + encodeRuleName(rule.dottedName)
|
||||
}>
|
||||
<Value
|
||||
numFractionDigits={0}
|
||||
maximumFractionDigits={0}
|
||||
{...rule}
|
||||
unit={
|
||||
/* //TODO the unit should be integrated in the leaf rules of base.yaml and infered by mecanisms. Will be done in a future release*/
|
||||
|
|
|
@ -8,12 +8,17 @@ const NumberFormat = memoizeWith(
|
|||
Intl.NumberFormat
|
||||
)
|
||||
|
||||
let numberFormatter = (style, numFractionDigits = 2) => (value, language) =>
|
||||
export let numberFormatter = ({
|
||||
style,
|
||||
maximumFractionDigits,
|
||||
minimumFractionDigits = 0,
|
||||
language
|
||||
}) => value =>
|
||||
NumberFormat(language, {
|
||||
style,
|
||||
currency: 'EUR',
|
||||
maximumFractionDigits: numFractionDigits,
|
||||
minimumFractionDigits: numFractionDigits
|
||||
maximumFractionDigits,
|
||||
minimumFractionDigits
|
||||
}).format(value)
|
||||
|
||||
// let booleanTranslations = { true: '✅', false: '❌' }
|
||||
|
@ -34,7 +39,8 @@ export default withLanguage(
|
|||
nodeValue: value,
|
||||
unit,
|
||||
nilValueSymbol,
|
||||
numFractionDigits,
|
||||
maximumFractionDigits,
|
||||
minimumFractionDigits,
|
||||
children,
|
||||
negative,
|
||||
language,
|
||||
|
@ -63,11 +69,24 @@ export default withLanguage(
|
|||
nodeValue.nom
|
||||
) : valueType === 'boolean' ? (
|
||||
booleanTranslations[language][nodeValue]
|
||||
) : unit === '€' ? (
|
||||
numberFormatter('currency', numFractionDigits)(nodeValue, language)
|
||||
) : unitText === '€' ? (
|
||||
numberFormatter({
|
||||
style: 'currency',
|
||||
maximumFractionDigits,
|
||||
minimumFractionDigits,
|
||||
language
|
||||
})(nodeValue)
|
||||
) : unitText === '%' ? (
|
||||
numberFormatter({ style: 'percent', maximumFractionDigits: 3 })(
|
||||
nodeValue
|
||||
)
|
||||
) : (
|
||||
<>
|
||||
{numberFormatter('decimal', numFractionDigits)(nodeValue)}
|
||||
{numberFormatter({
|
||||
style: 'decimal',
|
||||
minimumFractionDigits,
|
||||
maximumFractionDigits
|
||||
})(nodeValue)}
|
||||
|
||||
{unitText}
|
||||
</>
|
||||
|
|
|
@ -1,4 +1,8 @@
|
|||
import { goToQuestion, resetSimulation, skipQuestion } from 'Actions/actions'
|
||||
import {
|
||||
goToQuestion,
|
||||
resetSimulation,
|
||||
validateStepWithValue
|
||||
} from 'Actions/actions'
|
||||
import { T } from 'Components'
|
||||
import QuickLinks from 'Components/QuickLinks'
|
||||
import { getInputComponent } from 'Engine/generateQuestions'
|
||||
|
@ -15,6 +19,7 @@ import {
|
|||
import * as Animate from 'Ui/animate'
|
||||
import Aide from './Aide'
|
||||
import './conversation.css'
|
||||
import { findRuleByDottedName } from 'Engine/rules'
|
||||
|
||||
export default compose(
|
||||
reduxForm({
|
||||
|
@ -28,7 +33,7 @@ export default compose(
|
|||
previousAnswers: state.conversationSteps.foldedSteps,
|
||||
nextSteps: nextStepsSelector(state)
|
||||
}),
|
||||
{ resetSimulation, skipQuestion, goToQuestion }
|
||||
{ resetSimulation, validateStepWithValue, goToQuestion }
|
||||
)
|
||||
)(function Conversation({
|
||||
nextSteps,
|
||||
|
@ -37,14 +42,18 @@ export default compose(
|
|||
customEndMessages,
|
||||
flatRules,
|
||||
resetSimulation,
|
||||
skipQuestion,
|
||||
goToQuestion
|
||||
goToQuestion,
|
||||
validateStepWithValue
|
||||
}) {
|
||||
const goToNext = () => skipQuestion(nextSteps[0])
|
||||
const setDefault = () =>
|
||||
validateStepWithValue(
|
||||
currentQuestion,
|
||||
findRuleByDottedName(flatRules, currentQuestion).defaultValue
|
||||
)
|
||||
const goToPrevious = () => goToQuestion(previousAnswers.slice(-1)[0])
|
||||
const handleKeyDown = ({ key }) => {
|
||||
if (['Escape'].includes(key)) {
|
||||
goToNext()
|
||||
setDefault()
|
||||
}
|
||||
}
|
||||
return nextSteps.length ? (
|
||||
|
@ -67,7 +76,7 @@ export default compose(
|
|||
</>
|
||||
)}
|
||||
<button
|
||||
onClick={goToNext}
|
||||
onClick={setDefault}
|
||||
className="ui__ simple small skip button right">
|
||||
Passer →
|
||||
</button>
|
||||
|
|
|
@ -35,12 +35,25 @@ export var FormDecorator = formType => RenderField =>
|
|||
helpVisible: false
|
||||
}
|
||||
render() {
|
||||
let { stepAction, fieldName, inversion, setFormValue } = this.props
|
||||
let {
|
||||
stepAction,
|
||||
fieldName,
|
||||
inversion,
|
||||
setFormValue,
|
||||
unit
|
||||
} = this.props
|
||||
let submit = cause => stepAction('fold', fieldName, cause),
|
||||
stepProps = {
|
||||
...this.props,
|
||||
submit,
|
||||
setFormValue: (value, name = fieldName) => setFormValue(name, value)
|
||||
setFormValue: (value, name = fieldName) =>
|
||||
setFormValue(name, value),
|
||||
...(unit === '%'
|
||||
? {
|
||||
format: x => (x == null ? null : x * 100),
|
||||
normalize: x => (x == null ? null : x / 100)
|
||||
}
|
||||
: {})
|
||||
}
|
||||
|
||||
return (
|
||||
|
|
|
@ -55,7 +55,6 @@ export default compose(
|
|||
/>
|
||||
{suffixed && (
|
||||
<label className="suffix" htmlFor={'step-' + dottedName}>
|
||||
{unit}
|
||||
{rulePeriod && (
|
||||
<span>
|
||||
{' '}
|
||||
|
|
|
@ -23,24 +23,22 @@
|
|||
display: inline-block;
|
||||
}
|
||||
|
||||
.node.inlineExpression:not(.comparison):not(.negation) {
|
||||
.node.inlineExpression:not(.comparison) {
|
||||
padding-left: 0;
|
||||
display: flex;
|
||||
align-items: baseline;
|
||||
flex-direction: row;
|
||||
flex-wrap: wrap;
|
||||
justify-content: space-between
|
||||
}
|
||||
.nodeContent {
|
||||
margin-right: 0.3em;
|
||||
}
|
||||
#rule-rules
|
||||
.inlineExpression:not(.comparison):not(.negation)
|
||||
> .situationValue {
|
||||
#rule-rules .inlineExpression:not(.comparison) > .situationValue {
|
||||
margin-left: 0.3em;
|
||||
margin-bottom: 0.6em;
|
||||
margin-top: 0.3em;
|
||||
align-items: flex-end;
|
||||
flex: 1;
|
||||
text-align: right;
|
||||
}
|
||||
|
||||
|
@ -114,13 +112,6 @@
|
|||
margin-right: 0.6em;
|
||||
}
|
||||
|
||||
.maximum .description,
|
||||
.composanteName {
|
||||
font-weight: 500;
|
||||
margin-bottom: 0.4em;
|
||||
margin-left: 1em;
|
||||
}
|
||||
|
||||
.leaf .situationValue {
|
||||
text-align: center;
|
||||
}
|
||||
|
@ -147,10 +138,6 @@
|
|||
margin-top: 0.6em;
|
||||
}
|
||||
|
||||
.composantes .composanteName::first-letter {
|
||||
text-transform: capitalize;
|
||||
}
|
||||
|
||||
.mecanism {
|
||||
border: 1px solid;
|
||||
max-width: 100%;
|
||||
|
@ -166,13 +153,8 @@
|
|||
.mecanism-result {
|
||||
position: absolute;
|
||||
display: none;
|
||||
bottom: 0;
|
||||
bottom: 0px;
|
||||
right: 0;
|
||||
border-top: 1px solid;
|
||||
border-left: 1px solid;
|
||||
border-top-left-radius: 0.3rem;
|
||||
border-color: inherit;
|
||||
padding: 0.1rem 0.6rem;
|
||||
}
|
||||
#rule-rules.showValues .mecanism-result {
|
||||
display: initial;
|
||||
|
@ -198,7 +180,6 @@
|
|||
}
|
||||
|
||||
.variable,
|
||||
.nodeHead,
|
||||
.operator {
|
||||
display: inline-block;
|
||||
}
|
||||
|
@ -215,8 +196,7 @@
|
|||
|
||||
.mecanism.cond *:not(.nodeContent) > .variable .name,
|
||||
.mecanism.variations *:not(.nodeContent) > .variable .name,
|
||||
.inlineExpression.comparison,
|
||||
.inlineExpression.negation {
|
||||
.inlineExpression.comparison {
|
||||
display: flex;
|
||||
align-items: baseline;
|
||||
}
|
||||
|
@ -228,8 +208,7 @@
|
|||
> .variable
|
||||
.name
|
||||
.situationValue,
|
||||
.inlineExpression.comparison > .situationValue,
|
||||
.inlineExpression.negation > .situationValue {
|
||||
.inlineExpression.comparison > .situationValue {
|
||||
order: -1;
|
||||
}
|
||||
|
||||
|
|
|
@ -50,9 +50,6 @@
|
|||
padding: 1em 2em;
|
||||
}
|
||||
|
||||
#ruleHeader #PeriodSwitch {
|
||||
margin: 0.3em 1em;
|
||||
}
|
||||
#ruleHeader #PeriodSwitch img {
|
||||
display: none;
|
||||
}
|
||||
|
|
|
@ -1,9 +1,7 @@
|
|||
import PeriodSwitch from 'Components/PeriodSwitch'
|
||||
import withColours from 'Components/utils/withColours'
|
||||
import { path } from 'ramda'
|
||||
import React from 'react'
|
||||
import emoji from 'react-easy-emoji'
|
||||
import { Trans } from 'react-i18next'
|
||||
import { capitalise0 } from '../../utils'
|
||||
import { Markdown } from '../utils/markdown'
|
||||
import Destinataire from './Destinataire'
|
||||
|
@ -21,8 +19,7 @@ let RuleHeader = withColours(
|
|||
name,
|
||||
title,
|
||||
icon,
|
||||
colours,
|
||||
valuesToShow
|
||||
colours
|
||||
}) => (
|
||||
<section id="ruleHeader">
|
||||
<header className="ui__ plain card">
|
||||
|
@ -40,39 +37,14 @@ let RuleHeader = withColours(
|
|||
<div id="ruleHeader__description">
|
||||
<Markdown source={description || question} />
|
||||
</div>
|
||||
{(type || flatRule['période']) && (
|
||||
<div id="ruleHeader__infobox">
|
||||
{type && (
|
||||
<div className="infobox__item">
|
||||
<h4>Type :</h4>
|
||||
<Trans>{capitalise0(type)}</Trans>
|
||||
</div>
|
||||
)}
|
||||
{do {
|
||||
let period = flatRule['période']
|
||||
period && (
|
||||
<div className="infobox__item">
|
||||
<h4>Période :</h4>
|
||||
{valuesToShow && period === 'flexible' ? (
|
||||
<PeriodSwitch />
|
||||
) : (
|
||||
<div className="inlineMecanism">
|
||||
<span
|
||||
className="name"
|
||||
data-term-definition="période"
|
||||
style={{ background: '#8e44ad' }}>
|
||||
{period}
|
||||
</span>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
)
|
||||
}}
|
||||
<Destinataire
|
||||
destinataire={path([type, 'destinataire'])(flatRule)}
|
||||
/>
|
||||
</div>
|
||||
)}
|
||||
{do {
|
||||
let destinataire = path([type, 'destinataire'])(flatRule)
|
||||
destinataire && (
|
||||
<div id="ruleHeader__infobox">
|
||||
<Destinataire destinataire={destinataire} />
|
||||
</div>
|
||||
)
|
||||
}}
|
||||
</div>
|
||||
</section>
|
||||
)
|
||||
|
|
|
@ -20,12 +20,6 @@ h2 small {
|
|||
font-size: 75%;
|
||||
}
|
||||
|
||||
#rule #ruleValue {
|
||||
text-align: center;
|
||||
font-size: 200%;
|
||||
margin-bottom: 0.6em;
|
||||
margin-top: 0.4em;
|
||||
}
|
||||
|
||||
#rule #ruleDefault {
|
||||
text-align: center;
|
||||
|
|
|
@ -30,6 +30,7 @@ import Examples from './Examples'
|
|||
import RuleHeader from './Header'
|
||||
import References from './References'
|
||||
import './Rule.css'
|
||||
import PeriodSwitch from 'Components/PeriodSwitch'
|
||||
|
||||
let LazySource = React.lazy(() => import('./RuleSource'))
|
||||
|
||||
|
@ -63,6 +64,7 @@ export default compose(
|
|||
namespaceRules = findRuleByNamespace(flatRules, dottedName)
|
||||
|
||||
let displayedRule = analysedExample || analysedRule
|
||||
|
||||
return (
|
||||
<>
|
||||
{this.state.viewSource ? (
|
||||
|
@ -100,7 +102,23 @@ export default compose(
|
|||
/>
|
||||
|
||||
<section id="rule-content">
|
||||
<div id="ruleValue">
|
||||
<div
|
||||
id="ruleValue"
|
||||
css={`
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
flex-wrap: wrap;
|
||||
align-items: center;
|
||||
|
||||
> .value {
|
||||
font-size: 220%;
|
||||
}
|
||||
|
||||
margin: 0.6em 0;
|
||||
> * {
|
||||
margin: 0 0.6em;
|
||||
}
|
||||
`}>
|
||||
<Value
|
||||
{...displayedRule}
|
||||
nilValueSymbol={
|
||||
|
@ -109,6 +127,10 @@ export default compose(
|
|||
: null
|
||||
}
|
||||
/>
|
||||
<Period
|
||||
period={flatRule['période']}
|
||||
valuesToShow={valuesToShow}
|
||||
/>
|
||||
</div>
|
||||
{displayedRule.defaultValue != null && (
|
||||
<div id="ruleDefault">
|
||||
|
@ -201,7 +223,7 @@ let NamespaceRulesList = compose(
|
|||
return (
|
||||
<section>
|
||||
<h2>
|
||||
<Trans>Règles associées</Trans>
|
||||
<Trans>Pages associées</Trans>
|
||||
</h2>
|
||||
<ul>
|
||||
{namespaceRules.map(r => (
|
||||
|
@ -224,3 +246,19 @@ let NamespaceRulesList = compose(
|
|||
</section>
|
||||
)
|
||||
})
|
||||
|
||||
let Period = ({ period, valuesToShow }) =>
|
||||
period ? (
|
||||
valuesToShow && period === 'flexible' ? (
|
||||
<PeriodSwitch />
|
||||
) : (
|
||||
<span className="inlineMecanism">
|
||||
<span
|
||||
className="name"
|
||||
data-term-definition="période"
|
||||
style={{ background: '#8e44ad' }}>
|
||||
{period}
|
||||
</span>
|
||||
</span>
|
||||
)
|
||||
) : null
|
||||
|
|
|
@ -16,7 +16,7 @@ import {
|
|||
|
||||
export let makeJsx = node =>
|
||||
typeof node.jsx == 'function'
|
||||
? node.jsx(node.nodeValue, node.explanation, node.lazyEval)
|
||||
? node.jsx(node.nodeValue, node.explanation, node.lazyEval, node.unit)
|
||||
: node.jsx
|
||||
|
||||
export let collectNodeMissing = node => node.missingVariables || {}
|
||||
|
|
|
@ -32,11 +32,8 @@ Comparable -> ( AdditionSubstraction | NonNumericTerminal) {% ([[e]]) => e %}
|
|||
NonNumericTerminal ->
|
||||
Boolean {% id %}
|
||||
| String {% id %}
|
||||
| NegatedVariable {% id %}
|
||||
|
||||
|
||||
NegatedVariable -> "≠" _ Variable {% ([,,{variable}]) => ({'≠': {explanation: variable} }) %}
|
||||
|
||||
FilteredVariable -> Variable _ Filter {% filteredVariable %}
|
||||
|
||||
Filter -> "[" VariableFragment "]" {% ([,filter]) => filter %}
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
/* Those are postprocessor functions for the Nearley grammar.ne.
|
||||
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]) => ({
|
||||
[operator]: {
|
||||
|
@ -41,6 +42,7 @@ export let percentage = d => ({
|
|||
constant: {
|
||||
rawNode: d,
|
||||
type: 'percentage',
|
||||
unit: parseUnit('%'),
|
||||
nodeValue:
|
||||
parseFloat(d[0].join('') + (d[1] ? d[1][0] + d[1][1].join('') : '')) / 100
|
||||
}
|
||||
|
|
|
@ -7,7 +7,8 @@ import React from 'react'
|
|||
import { Trans } from 'react-i18next'
|
||||
import { makeJsx } from '../evaluation'
|
||||
import './Barème.css'
|
||||
import { formatNumber, Node, NodeValuePointer } from './common'
|
||||
import { Node, NodeValuePointer } from './common'
|
||||
import { numberFormatter } from 'Components/Value'
|
||||
|
||||
export let BarèmeAttributes = ({ explanation, lazyEval = identity }) => (
|
||||
<>
|
||||
|
@ -36,7 +37,8 @@ let Component = withLanguage(function Barème({
|
|||
nodeValue,
|
||||
explanation,
|
||||
barèmeType,
|
||||
lazyEval
|
||||
lazyEval,
|
||||
unit
|
||||
}) {
|
||||
return (
|
||||
<ShowValuesConsumer>
|
||||
|
@ -45,6 +47,7 @@ let Component = withLanguage(function Barème({
|
|||
classes="mecanism barème"
|
||||
name={barèmeType === 'marginal' ? 'barème' : 'barème linéaire'}
|
||||
value={nodeValue}
|
||||
unit={unit}
|
||||
child={
|
||||
<ul className="properties">
|
||||
<BarèmeAttributes explanation={explanation} lazyEval={lazyEval} />
|
||||
|
@ -96,12 +99,13 @@ let Component = withLanguage(function Barème({
|
|||
<b>
|
||||
<Trans>Taux final</Trans> :{' '}
|
||||
</b>
|
||||
{formatNumber(
|
||||
(nodeValue /
|
||||
lazyEval(explanation['assiette']).nodeValue) *
|
||||
100,
|
||||
language
|
||||
)}{' '}
|
||||
<NodeValuePointer
|
||||
data={
|
||||
(nodeValue /
|
||||
lazyEval(explanation['assiette']).nodeValue) *
|
||||
100
|
||||
}
|
||||
/>
|
||||
%
|
||||
</>
|
||||
)}
|
||||
|
@ -132,16 +136,17 @@ let Tranche = ({
|
|||
<td key="tranche">
|
||||
{maxOnly ? (
|
||||
<>
|
||||
<Trans>En-dessous de</Trans> {formatNumber(maxOnly, language)}
|
||||
<Trans>En-dessous de</Trans>{' '}
|
||||
{numberFormatter({ language })(maxOnly)}
|
||||
</>
|
||||
) : minOnly ? (
|
||||
<>
|
||||
<Trans>Au-dessus de</Trans> {formatNumber(minOnly, language)}
|
||||
<Trans>Au-dessus de</Trans> {numberFormatter({ language })(minOnly)}
|
||||
</>
|
||||
) : (
|
||||
<>
|
||||
<Trans>De</Trans> {formatNumber(min, language)} <Trans>à</Trans>{' '}
|
||||
{formatNumber(max, language)}
|
||||
<Trans>De</Trans> {numberFormatter({ language })(min)}{' '}
|
||||
<Trans>à</Trans> {numberFormatter({ language })(max)}
|
||||
</>
|
||||
)}
|
||||
</td>
|
||||
|
@ -156,6 +161,9 @@ let Tranche = ({
|
|||
}
|
||||
|
||||
//eslint-disable-next-line
|
||||
export default barèmeType => (nodeValue, explanation, lazyEval = identity) => (
|
||||
<Component {...{ nodeValue, explanation, barèmeType, lazyEval }} />
|
||||
)
|
||||
export default barèmeType => (
|
||||
nodeValue,
|
||||
explanation,
|
||||
lazyEval = identity,
|
||||
unit
|
||||
) => <Component {...{ nodeValue, explanation, barèmeType, lazyEval, unit }} />
|
||||
|
|
|
@ -7,7 +7,7 @@ import withLanguage from 'Components/utils/withLanguage'
|
|||
import { BarèmeAttributes } from './Barème'
|
||||
import { sortObjectByKeys } from 'Engine/mecanismViews/common'
|
||||
|
||||
let Comp = withLanguage(function Barème({ nodeValue, explanation }) {
|
||||
let Comp = withLanguage(function Barème({ nodeValue, explanation, unit }) {
|
||||
return (
|
||||
<ShowValuesConsumer>
|
||||
{showValues => (
|
||||
|
@ -15,6 +15,7 @@ let Comp = withLanguage(function Barème({ nodeValue, explanation }) {
|
|||
classes="mecanism barème"
|
||||
name="barème continu"
|
||||
value={nodeValue}
|
||||
unit={unit}
|
||||
child={
|
||||
<ul className="properties">
|
||||
<BarèmeAttributes explanation={explanation} />
|
||||
|
@ -60,6 +61,6 @@ let Comp = withLanguage(function Barème({ nodeValue, explanation }) {
|
|||
})
|
||||
|
||||
//eslint-disable-next-line
|
||||
export default (nodeValue, explanation) => (
|
||||
<Comp {...{ nodeValue, explanation }} />
|
||||
export default (nodeValue, explanation, _, unit) => (
|
||||
<Comp {...{ nodeValue, explanation, unit }} />
|
||||
)
|
||||
|
|
|
@ -3,16 +3,8 @@
|
|||
counter-reset: li;
|
||||
padding-left: 1em;
|
||||
}
|
||||
|
||||
.composantes > ol > li.composante::before {
|
||||
counter-increment: li;
|
||||
}
|
||||
.composantes > ol > li.composante::before {
|
||||
content: counter(li) ')';
|
||||
color: grey;
|
||||
display: inline-block;
|
||||
width: 1em;
|
||||
margin-left: -1em;
|
||||
.composantes > ol > li > ul > li {
|
||||
list-style-type: none;
|
||||
}
|
||||
|
||||
.composantes .composanteAttributes {
|
||||
|
|
|
@ -6,11 +6,13 @@ import { Trans } from 'react-i18next'
|
|||
import { toPairs } from 'ramda'
|
||||
import writtenNumbers from '../../locales/writtenNumbers.yaml'
|
||||
import withLanguage from 'Components/utils/withLanguage'
|
||||
import colours from 'Engine/mecanismViews/colours'
|
||||
|
||||
let Comp = withLanguage(function Composantes({
|
||||
language,
|
||||
nodeValue,
|
||||
explanation
|
||||
explanation,
|
||||
unit
|
||||
}) {
|
||||
return (
|
||||
<Node
|
||||
|
@ -18,20 +20,31 @@ let Comp = withLanguage(function Composantes({
|
|||
name="composantes"
|
||||
inline
|
||||
value={nodeValue}
|
||||
unit={unit}
|
||||
child={
|
||||
<>
|
||||
<p>
|
||||
<Trans>Cette règle est la somme de</Trans>{' '}
|
||||
<p css="margin-bottom: 1em">
|
||||
<Trans>La somme de</Trans>{' '}
|
||||
{writtenNumbers[language][explanation.length]}{' '}
|
||||
<InlineMecanism name="composantes" /> :
|
||||
</p>
|
||||
<ol>
|
||||
{explanation.map(c => [
|
||||
<li className="composante" key={JSON.stringify(c.composante)}>
|
||||
<ul className="composanteAttributes">
|
||||
{explanation.map((c, i) => [
|
||||
<li
|
||||
className="composante"
|
||||
css={``}
|
||||
key={JSON.stringify(c.composante)}>
|
||||
<ul
|
||||
className="composanteAttributes"
|
||||
css={`
|
||||
border-left: 4px solid ${colours('composantes')};
|
||||
`}>
|
||||
{toPairs(c.composante).map(([k, v]) => (
|
||||
<li key={k} className="composanteName">
|
||||
<span>
|
||||
<span
|
||||
css={`
|
||||
color: ${colours('composantes')};
|
||||
`}>
|
||||
<Trans>{k}</Trans>:{' '}
|
||||
</span>
|
||||
<span>
|
||||
|
@ -41,6 +54,15 @@ let Comp = withLanguage(function Composantes({
|
|||
))}
|
||||
</ul>
|
||||
<div className="content">{makeJsx(c)}</div>
|
||||
<div
|
||||
css={`
|
||||
text-align: center;
|
||||
width: 100%;
|
||||
font-size: 2.6rem;
|
||||
margin: 0.4em 0 0.2em;
|
||||
`}>
|
||||
{i === explanation.length - 1 ? null : '+'}
|
||||
</div>
|
||||
</li>
|
||||
])}
|
||||
</ol>
|
||||
|
@ -51,6 +73,6 @@ let Comp = withLanguage(function Composantes({
|
|||
})
|
||||
|
||||
// eslint-disable-next-line
|
||||
export default (nodeValue, explanation) => (
|
||||
<Comp {...{ nodeValue, explanation }} />
|
||||
export default (nodeValue, explanation, _, unit) => (
|
||||
<Comp {...{ nodeValue, explanation, unit }} />
|
||||
)
|
||||
|
|
|
@ -4,13 +4,14 @@ import { Trans } from 'react-i18next'
|
|||
import { Node } from './common'
|
||||
import './InversionNumérique.css'
|
||||
|
||||
export default function ProductView(nodeValue, explanation) {
|
||||
export default function ProductView(nodeValue, explanation, _, unit) {
|
||||
return (
|
||||
// The rate and factor and threshold are given defaut neutral values. If there is nothing to explain, don't display them at all
|
||||
<Node
|
||||
classes="mecanism multiplication"
|
||||
name="multiplication"
|
||||
value={nodeValue}
|
||||
unit={unit}
|
||||
child={
|
||||
<div
|
||||
style={{
|
||||
|
|
|
@ -4,21 +4,22 @@ import { makeJsx } from '../evaluation'
|
|||
import './Somme.css'
|
||||
import { Node, NodeValuePointer } from './common'
|
||||
|
||||
const SommeNode = ({ explanation, nodeValue }) => (
|
||||
const SommeNode = ({ explanation, nodeValue, unit }) => (
|
||||
<Node
|
||||
classes="mecanism somme"
|
||||
name="somme"
|
||||
value={nodeValue}
|
||||
child={<Table explanation={explanation} />}
|
||||
unit={unit}
|
||||
child={<Table explanation={explanation} unit={unit} />}
|
||||
/>
|
||||
)
|
||||
export default SommeNode
|
||||
|
||||
let Table = ({ explanation }) => (
|
||||
let Table = ({ explanation, unit }) => (
|
||||
<div className="mecanism-somme__table">
|
||||
<div>
|
||||
{explanation.map((v, i) => (
|
||||
<Row key={i} {...{ v, i }} />
|
||||
<Row key={i} {...{ v, i }} unit={unit} />
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
|
@ -30,7 +31,7 @@ class Row extends Component {
|
|||
folded: true
|
||||
}
|
||||
render() {
|
||||
let { v, i } = this.props,
|
||||
let { v, i, unit } = this.props,
|
||||
rowFormula = path(['explanation', 'formule', 'explanation'], v),
|
||||
isSomme = rowFormula && rowFormula.name == 'somme'
|
||||
|
||||
|
@ -50,13 +51,13 @@ class Row extends Component {
|
|||
)}
|
||||
</div>
|
||||
<div className="situationValue value">
|
||||
<NodeValuePointer data={v.nodeValue} />
|
||||
<NodeValuePointer data={v.nodeValue} unit={unit} />
|
||||
</div>
|
||||
</div>,
|
||||
...(isSomme && !this.state.folded
|
||||
? [
|
||||
<div className="nested" key={v.name + '-nest'}>
|
||||
<Table explanation={rowFormula.explanation} />
|
||||
<Table explanation={rowFormula.explanation} unit={unit} />
|
||||
</div>
|
||||
]
|
||||
: [])
|
||||
|
|
|
@ -12,7 +12,8 @@ import './Variations.css'
|
|||
let Comp = withLanguage(function Variations({
|
||||
language,
|
||||
nodeValue,
|
||||
explanation
|
||||
explanation,
|
||||
unit
|
||||
}) {
|
||||
let [expandedVariation, toggleVariation] = useState(null)
|
||||
|
||||
|
@ -23,12 +24,12 @@ let Comp = withLanguage(function Variations({
|
|||
classes="mecanism variations"
|
||||
name="variations"
|
||||
inline
|
||||
unit={unit}
|
||||
value={nodeValue}
|
||||
child={
|
||||
<>
|
||||
<p>
|
||||
<Trans>Cette règle présente</Trans>{' '}
|
||||
{writtenNumbers[language][explanation.length]}{' '}
|
||||
<p css="text-transform: capitalize">
|
||||
<Trans >{writtenNumbers[language][explanation.length]}{' '}</Trans>
|
||||
<InlineMecanism name="variations" /> :
|
||||
</p>
|
||||
<ol>
|
||||
|
@ -44,7 +45,7 @@ let Comp = withLanguage(function Variations({
|
|||
}}>
|
||||
{!satisfied && showValues && (
|
||||
<>
|
||||
non applicable{' '}
|
||||
<em>non applicable </em>
|
||||
{expandedVariation !== i ? (
|
||||
<button
|
||||
className="ui__ link-button"
|
||||
|
@ -111,6 +112,6 @@ let Comp = withLanguage(function Variations({
|
|||
)
|
||||
})
|
||||
// eslint-disable-next-line
|
||||
export default (nodeValue, explanation) => (
|
||||
<Comp {...{ nodeValue, explanation }} />
|
||||
export default (nodeValue, explanation, _, unit) => (
|
||||
<Comp {...{ nodeValue, explanation, unit }} />
|
||||
)
|
||||
|
|
|
@ -13,17 +13,21 @@ import mecanismColours from './colours'
|
|||
import classnames from 'classnames'
|
||||
import Value from 'Components/Value'
|
||||
|
||||
//TODO remove this one, it should reside in 'Value.js'
|
||||
export let formatNumber = (data, language) =>
|
||||
!isNaN(data)
|
||||
? Intl.NumberFormat(language, { maximumFractionDigits: 4 }).format(data)
|
||||
: data
|
||||
|
||||
export let NodeValuePointer = ({ data, unit }) => (
|
||||
<span
|
||||
className={classnames('situationValue', {
|
||||
boolean: typeof data == 'boolean'
|
||||
})}>
|
||||
})}
|
||||
css={`
|
||||
background: white;
|
||||
border-bottom: 0 !important;
|
||||
padding: 0 0.2rem;
|
||||
text-decoration: none !important;
|
||||
font-size: 80%;
|
||||
box-shadow: 2px 2px 4px 1px #d9d9d9, 0 0 0 1px #d9d9d9;
|
||||
line-height: 1.6em;
|
||||
border-radius: 0.2rem;
|
||||
`}>
|
||||
<Value nodeValue={data} unit={unit} />
|
||||
</span>
|
||||
)
|
||||
|
@ -31,7 +35,7 @@ export let NodeValuePointer = ({ data, unit }) => (
|
|||
// Un élément du graphe de calcul qui a une valeur interprétée (à afficher)
|
||||
export class Node extends Component {
|
||||
render() {
|
||||
let { classes, name, value, child, inline } = this.props,
|
||||
let { classes, name, value, child, inline, unit } = this.props,
|
||||
termDefinition = contains('mecanism', classes) && name
|
||||
|
||||
return (
|
||||
|
@ -39,7 +43,7 @@ export class Node extends Component {
|
|||
className={classNames(classes, 'node', { inline })}
|
||||
style={termDefinition ? { borderColor: mecanismColours(name) } : {}}>
|
||||
{name && !inline && (
|
||||
<span className="nodeHead">
|
||||
<div className="nodeHead" css="margin-bottom: 1em">
|
||||
<LinkButton
|
||||
className="name"
|
||||
style={
|
||||
|
@ -48,22 +52,29 @@ export class Node extends Component {
|
|||
data-term-definition={termDefinition}>
|
||||
<Trans>{name}</Trans>
|
||||
</LinkButton>
|
||||
</span>
|
||||
</div>
|
||||
)}
|
||||
{child}{' '}
|
||||
{name ? (
|
||||
!isNil(value) && (
|
||||
<div className="mecanism-result">
|
||||
<NodeValuePointer data={value} />
|
||||
<span css="font-size: 90%; margin: 0 .6em">=</span>
|
||||
<NodeValuePointer data={value} unit={unit} />
|
||||
</div>
|
||||
)
|
||||
) : (
|
||||
<>
|
||||
<span
|
||||
css={`
|
||||
@media (max-width: 1200px) {
|
||||
width: 100%;
|
||||
text-align: right;
|
||||
}
|
||||
`}>
|
||||
{value !== true && value !== false && !isNil(value) && (
|
||||
<span className="operator"> = </span>
|
||||
<span className="operator"> = </span>
|
||||
)}
|
||||
<NodeValuePointer data={value} />
|
||||
</>
|
||||
<NodeValuePointer data={value} unit={unit} />
|
||||
</span>
|
||||
)}
|
||||
</div>
|
||||
)
|
||||
|
@ -94,7 +105,7 @@ export const Leaf = compose(
|
|||
classes,
|
||||
dottedName,
|
||||
name,
|
||||
value,
|
||||
nodeValue,
|
||||
flatRules,
|
||||
filter,
|
||||
sitePaths,
|
||||
|
@ -114,11 +125,16 @@ export const Leaf = compose(
|
|||
}>
|
||||
<span className="name">
|
||||
{rule.title || capitalise0(name)} {filter}
|
||||
{!isNil(value) && (
|
||||
<NodeValuePointer data={value} unit={unit} />
|
||||
)}
|
||||
</span>
|
||||
</Link>
|
||||
{!isNil(nodeValue) && (
|
||||
<span
|
||||
css={`
|
||||
margin: 0 0.3rem;
|
||||
`}>
|
||||
<NodeValuePointer data={nodeValue} unit={unit} />
|
||||
</span>
|
||||
)}
|
||||
</span>
|
||||
)}
|
||||
</span>
|
||||
|
|
|
@ -1,9 +1,7 @@
|
|||
import { desugarScale } from 'Engine/mecanisms/barème'
|
||||
import { decompose, devariateExplanation } from 'Engine/mecanisms/utils'
|
||||
import { decompose } from 'Engine/mecanisms/utils'
|
||||
import {
|
||||
add,
|
||||
any,
|
||||
aperture,
|
||||
curry,
|
||||
equals,
|
||||
evolve,
|
||||
|
@ -12,9 +10,7 @@ import {
|
|||
head,
|
||||
is,
|
||||
isEmpty,
|
||||
isNil,
|
||||
keys,
|
||||
last,
|
||||
map,
|
||||
max,
|
||||
mergeWith,
|
||||
|
@ -23,11 +19,7 @@ import {
|
|||
pipe,
|
||||
pluck,
|
||||
prop,
|
||||
propEq,
|
||||
reduce,
|
||||
reduced,
|
||||
reject,
|
||||
sort,
|
||||
subtract,
|
||||
toPairs
|
||||
} from 'ramda'
|
||||
|
@ -48,98 +40,15 @@ import {
|
|||
rewriteNode
|
||||
} from './evaluation'
|
||||
import Allègement from './mecanismViews/Allègement'
|
||||
import Barème from './mecanismViews/Barème'
|
||||
import BarèmeContinu from './mecanismViews/BarèmeContinu'
|
||||
import { Node, SimpleRuleLink } from './mecanismViews/common'
|
||||
import InversionNumérique from './mecanismViews/InversionNumérique'
|
||||
import Product from './mecanismViews/Product'
|
||||
import Somme from './mecanismViews/Somme'
|
||||
import Variations from './mecanismViews/Variations'
|
||||
import { disambiguateRuleReference, findRuleByDottedName } from './rules'
|
||||
import { anyNull, val } from './traverse-common-functions'
|
||||
import uniroot from './uniroot'
|
||||
|
||||
/* @devariate = true => This function will produce variations of a same mecanism (e.g. product) that share some common properties */
|
||||
export let mecanismVariations = (recurse, k, v, devariate) => {
|
||||
let explanation = devariate
|
||||
? devariateExplanation(recurse, k, v)
|
||||
: v.map(({ si, alors, sinon }) =>
|
||||
sinon !== undefined
|
||||
? { consequence: recurse(sinon), condition: undefined }
|
||||
: { consequence: recurse(alors), condition: recurse(si) }
|
||||
)
|
||||
|
||||
let evaluate = (cache, situationGate, parsedRules, node) => {
|
||||
let evaluateVariationProp = prop =>
|
||||
prop === undefined
|
||||
? undefined
|
||||
: evaluateNode(cache, situationGate, parsedRules, prop),
|
||||
// mark the satisfied variation if any in the explanation
|
||||
[, resolvedExplanation] = reduce(
|
||||
([resolved, result], variation) => {
|
||||
if (resolved) return [true, [...result, variation]]
|
||||
|
||||
// evaluate the condition
|
||||
let evaluatedCondition = evaluateVariationProp(variation.condition)
|
||||
|
||||
if (evaluatedCondition == undefined) {
|
||||
// We've reached the eventual defaut case
|
||||
let evaluatedVariation = {
|
||||
consequence: evaluateVariationProp(variation.consequence),
|
||||
satisfied: true
|
||||
}
|
||||
return [true, [...result, evaluatedVariation]]
|
||||
}
|
||||
|
||||
if (evaluatedCondition.nodeValue === null)
|
||||
// one case has missing variables => we can't go further
|
||||
return [true, [...result, { condition: evaluatedCondition }]]
|
||||
|
||||
if (evaluatedCondition.nodeValue === true) {
|
||||
let evaluatedVariation = {
|
||||
condition: evaluatedCondition,
|
||||
consequence: evaluateVariationProp(variation.consequence),
|
||||
satisfied: true
|
||||
}
|
||||
return [true, [...result, evaluatedVariation]]
|
||||
}
|
||||
return [false, [...result, variation]]
|
||||
},
|
||||
[false, []]
|
||||
)(node.explanation),
|
||||
satisfiedVariation = resolvedExplanation.find(v => v.satisfied),
|
||||
nodeValue = satisfiedVariation
|
||||
? satisfiedVariation.consequence.nodeValue
|
||||
: null
|
||||
|
||||
let leftMissing = mergeAllMissing(
|
||||
reject(isNil, pluck('condition', resolvedExplanation))
|
||||
),
|
||||
candidateVariations = filter(
|
||||
node => !node.condition || node.condition.nodeValue !== false,
|
||||
resolvedExplanation
|
||||
),
|
||||
rightMissing = mergeAllMissing(
|
||||
reject(isNil, pluck('consequence', candidateVariations))
|
||||
),
|
||||
missingVariables = satisfiedVariation
|
||||
? collectNodeMissing(satisfiedVariation.consequence)
|
||||
: mergeMissing(bonus(leftMissing), rightMissing)
|
||||
|
||||
return rewriteNode(node, nodeValue, resolvedExplanation, missingVariables)
|
||||
}
|
||||
|
||||
// TODO - find an appropriate representation
|
||||
|
||||
return {
|
||||
explanation,
|
||||
evaluate,
|
||||
jsx: Variations,
|
||||
category: 'mecanism',
|
||||
name: 'variations',
|
||||
type: 'numeric'
|
||||
}
|
||||
}
|
||||
import { inferUnit } from 'Engine/units'
|
||||
import variations from 'Engine/mecanisms/variations'
|
||||
|
||||
export let mecanismOneOf = (recurse, k, v) => {
|
||||
if (!is(Array, v)) throw new Error('should be array')
|
||||
|
@ -253,7 +162,7 @@ export let mecanismNumericalSwitch = (recurse, k, v) => {
|
|||
|
||||
// la conséquence peut être un 'string' ou un autre aiguillage numérique
|
||||
let parseCondition = ([condition, consequence]) => {
|
||||
let conditionNode = recurse(condition), // can be a 'comparison', a 'variable', TODO a 'negation'
|
||||
let conditionNode = recurse(condition), // can be a 'comparison', a 'variable'
|
||||
consequenceNode = mecanismNumericalSwitch(recurse, condition, consequence)
|
||||
|
||||
let evaluate = (cache, situationGate, parsedRules, node) => {
|
||||
|
@ -496,13 +405,14 @@ export let mecanismSum = (recurse, k, v) => {
|
|||
return {
|
||||
evaluate,
|
||||
// eslint-disable-next-line
|
||||
jsx: (nodeValue, explanation) => (
|
||||
<Somme nodeValue={nodeValue} explanation={explanation} />
|
||||
jsx: (nodeValue, explanation, _, unit) => (
|
||||
<Somme nodeValue={nodeValue} explanation={explanation} unit={unit} />
|
||||
),
|
||||
explanation,
|
||||
category: 'mecanism',
|
||||
name: 'somme',
|
||||
type: 'numeric'
|
||||
type: 'numeric',
|
||||
unit: inferUnit('+', explanation.map(r => r.unit))
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -573,7 +483,7 @@ export let mecanismProduct = (recurse, k, v) => {
|
|||
return decompose(recurse, k, v)
|
||||
}
|
||||
if (v.variations) {
|
||||
return mecanismVariations(recurse, k, v, true)
|
||||
return variations(recurse, k, v, true)
|
||||
}
|
||||
|
||||
let objectShape = {
|
||||
|
@ -608,120 +518,13 @@ export let mecanismProduct = (recurse, k, v) => {
|
|||
explanation,
|
||||
category: 'mecanism',
|
||||
name: 'multiplication',
|
||||
type: 'numeric'
|
||||
}
|
||||
}
|
||||
|
||||
/* on réécrit en une syntaxe plus bas niveau mais plus régulière les tranches :
|
||||
`en-dessous de: 1`
|
||||
devient
|
||||
```
|
||||
de: 0
|
||||
à: 1
|
||||
```
|
||||
*/
|
||||
|
||||
export let mecanismLinearScale = (recurse, k, v) => {
|
||||
if (v.composantes) {
|
||||
//mécanisme de composantes. Voir known-mecanisms.md/composantes
|
||||
return decompose(recurse, k, v)
|
||||
}
|
||||
if (v.variations) {
|
||||
return mecanismVariations(recurse, k, v, true)
|
||||
}
|
||||
let tranches = desugarScale(recurse)(v['tranches']),
|
||||
objectShape = {
|
||||
assiette: false,
|
||||
multiplicateur: defaultNode(1)
|
||||
}
|
||||
|
||||
let effect = ({ assiette, multiplicateur, tranches }) => {
|
||||
if (val(assiette) === null) return null
|
||||
|
||||
let roundedAssiette = Math.round(val(assiette))
|
||||
|
||||
let matchedTranche = tranches.find(
|
||||
({ de: min, à: max }) =>
|
||||
roundedAssiette >= val(multiplicateur) * min &&
|
||||
roundedAssiette <= max * val(multiplicateur)
|
||||
type: 'numeric',
|
||||
unit: inferUnit(
|
||||
'*',
|
||||
[explanation.assiette, explanation.taux, explanation.facteur].map(
|
||||
el => el.unit
|
||||
)
|
||||
)
|
||||
|
||||
if (!matchedTranche) return 0
|
||||
if (matchedTranche.taux)
|
||||
return matchedTranche.taux.nodeValue * val(assiette)
|
||||
return matchedTranche.montant
|
||||
}
|
||||
|
||||
let explanation = {
|
||||
...parseObject(recurse, objectShape, v),
|
||||
tranches
|
||||
},
|
||||
evaluate = evaluateObject(objectShape, effect)
|
||||
|
||||
return {
|
||||
evaluate,
|
||||
jsx: Barème('linéaire'),
|
||||
explanation,
|
||||
category: 'mecanism',
|
||||
name: 'barème linéaire',
|
||||
barème: 'en taux',
|
||||
type: 'numeric'
|
||||
}
|
||||
}
|
||||
|
||||
export let mecanismContinuousScale = (recurse, k, v) => {
|
||||
let objectShape = {
|
||||
assiette: false,
|
||||
multiplicateur: defaultNode(1)
|
||||
}
|
||||
|
||||
let returnRate = v['retourne seulement le taux'] === 'oui'
|
||||
let effect = ({ assiette, multiplicateur, points }) => {
|
||||
if (anyNull([assiette, multiplicateur])) return null
|
||||
//We'll build a linear function given the two constraints that must be respected
|
||||
let result = pipe(
|
||||
toPairs,
|
||||
// we don't rely on the sorting of objects
|
||||
sort(([k1], [k2]) => k1 - k2),
|
||||
points => [...points, [Infinity, last(points)[1]]],
|
||||
aperture(2),
|
||||
reduce((_, [[lowerLimit, lowerRate], [upperLimit, upperRate]]) => {
|
||||
let x1 = val(multiplicateur) * lowerLimit,
|
||||
x2 = val(multiplicateur) * upperLimit,
|
||||
y1 = val(assiette) * val(recurse(lowerRate)),
|
||||
y2 = val(assiette) * val(recurse(upperRate))
|
||||
if (val(assiette) > x1 && val(assiette) <= x2) {
|
||||
// Outside of these 2 limits, it's a linear function a * x + b
|
||||
let a = (y2 - y1) / (x2 - x1),
|
||||
b = y1 - x1 * a,
|
||||
nodeValue = a * val(assiette) + b,
|
||||
taux = nodeValue / val(assiette)
|
||||
return reduced({
|
||||
nodeValue: returnRate ? taux : nodeValue,
|
||||
additionalExplanation: {
|
||||
seuil: val(assiette) / val(multiplicateur),
|
||||
taux
|
||||
}
|
||||
})
|
||||
}
|
||||
}, 0)
|
||||
)(points)
|
||||
|
||||
return result
|
||||
}
|
||||
let explanation = {
|
||||
...parseObject(recurse, objectShape, v),
|
||||
points: v.points,
|
||||
returnRate
|
||||
},
|
||||
evaluate = evaluateObject(objectShape, effect)
|
||||
return {
|
||||
evaluate,
|
||||
jsx: BarèmeContinu,
|
||||
explanation,
|
||||
category: 'mecanism',
|
||||
name: 'barème continu',
|
||||
type: 'numeric'
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -0,0 +1,63 @@
|
|||
import { defaultNode, evaluateObject } from 'Engine/evaluation'
|
||||
import BarèmeContinu from 'Engine/mecanismViews/BarèmeContinu'
|
||||
import { val, anyNull } from 'Engine/traverse-common-functions'
|
||||
import { parseUnit } from 'Engine/units'
|
||||
import { parseObject } from 'Engine/evaluation'
|
||||
import { reduce, toPairs, sort, aperture, pipe, reduced, last } from 'ramda'
|
||||
|
||||
export default (recurse, k, v) => {
|
||||
let objectShape = {
|
||||
assiette: false,
|
||||
multiplicateur: defaultNode(1)
|
||||
}
|
||||
|
||||
let returnRate = v['retourne seulement le taux'] === 'oui'
|
||||
let effect = ({ assiette, multiplicateur, points }) => {
|
||||
if (anyNull([assiette, multiplicateur])) return null
|
||||
//We'll build a linear function given the two constraints that must be respected
|
||||
let result = pipe(
|
||||
toPairs,
|
||||
// we don't rely on the sorting of objects
|
||||
sort(([k1], [k2]) => k1 - k2),
|
||||
points => [...points, [Infinity, last(points)[1]]],
|
||||
aperture(2),
|
||||
reduce((_, [[lowerLimit, lowerRate], [upperLimit, upperRate]]) => {
|
||||
let x1 = val(multiplicateur) * lowerLimit,
|
||||
x2 = val(multiplicateur) * upperLimit,
|
||||
y1 = val(assiette) * val(recurse(lowerRate)),
|
||||
y2 = val(assiette) * val(recurse(upperRate))
|
||||
if (val(assiette) > x1 && val(assiette) <= x2) {
|
||||
// Outside of these 2 limits, it's a linear function a * x + b
|
||||
let a = (y2 - y1) / (x2 - x1),
|
||||
b = y1 - x1 * a,
|
||||
nodeValue = a * val(assiette) + b,
|
||||
taux = nodeValue / val(assiette)
|
||||
return reduced({
|
||||
nodeValue: returnRate ? taux : nodeValue,
|
||||
additionalExplanation: {
|
||||
seuil: val(assiette) / val(multiplicateur),
|
||||
taux
|
||||
}
|
||||
})
|
||||
}
|
||||
}, 0)
|
||||
)(points)
|
||||
|
||||
return result
|
||||
}
|
||||
let explanation = {
|
||||
...parseObject(recurse, objectShape, v),
|
||||
points: v.points,
|
||||
returnRate
|
||||
},
|
||||
evaluate = evaluateObject(objectShape, effect)
|
||||
return {
|
||||
evaluate,
|
||||
jsx: BarèmeContinu,
|
||||
explanation,
|
||||
category: 'mecanism',
|
||||
name: 'barème continu',
|
||||
type: 'numeric',
|
||||
unit: returnRate ? parseUnit('%') : explanation.assiette.unit
|
||||
}
|
||||
}
|
|
@ -0,0 +1,66 @@
|
|||
import { defaultNode, evaluateObject } from 'Engine/evaluation'
|
||||
import Barème from 'Engine/mecanismViews/Barème'
|
||||
import variations from 'Engine/mecanisms/variations'
|
||||
import { decompose } from 'Engine/mecanisms/utils'
|
||||
import { val } from 'Engine/traverse-common-functions'
|
||||
import { inferUnit, parseUnit } from 'Engine/units'
|
||||
import { parseObject } from 'Engine/evaluation'
|
||||
import { desugarScale } from './barème'
|
||||
/* on réécrit en une syntaxe plus bas niveau mais plus régulière les tranches :
|
||||
`en-dessous de: 1`
|
||||
devient
|
||||
```
|
||||
de: 0
|
||||
à: 1
|
||||
```
|
||||
*/
|
||||
|
||||
export default (recurse, k, v) => {
|
||||
if (v.composantes) {
|
||||
//mécanisme de composantes. Voir known-mecanisms.md/composantes
|
||||
return decompose(recurse, k, v)
|
||||
}
|
||||
if (v.variations) {
|
||||
return variations(recurse, k, v, true)
|
||||
}
|
||||
let tranches = desugarScale(recurse)(v['tranches']),
|
||||
objectShape = {
|
||||
assiette: false,
|
||||
multiplicateur: defaultNode(1)
|
||||
}
|
||||
|
||||
let effect = ({ assiette, multiplicateur, tranches }) => {
|
||||
if (val(assiette) === null) return null
|
||||
|
||||
let roundedAssiette = Math.round(val(assiette))
|
||||
|
||||
let matchedTranche = tranches.find(
|
||||
({ de: min, à: max }) =>
|
||||
roundedAssiette >= val(multiplicateur) * min &&
|
||||
roundedAssiette <= max * val(multiplicateur)
|
||||
)
|
||||
|
||||
if (!matchedTranche) return 0
|
||||
if (matchedTranche.taux)
|
||||
return matchedTranche.taux.nodeValue * val(assiette)
|
||||
return matchedTranche.montant
|
||||
}
|
||||
|
||||
let explanation = {
|
||||
...parseObject(recurse, objectShape, v),
|
||||
tranches
|
||||
},
|
||||
evaluate = evaluateObject(objectShape, effect)
|
||||
|
||||
return {
|
||||
evaluate,
|
||||
jsx: Barème('linéaire'),
|
||||
explanation,
|
||||
category: 'mecanism',
|
||||
name: 'barème linéaire',
|
||||
barème: 'en taux',
|
||||
type: 'numeric',
|
||||
|
||||
unit: explanation.assiette.unit
|
||||
}
|
||||
}
|
|
@ -1,9 +1,10 @@
|
|||
import { defaultNode, E, rewriteNode } from 'Engine/evaluation'
|
||||
import { mecanismVariations } from 'Engine/mecanisms'
|
||||
import variations from 'Engine/mecanisms/variations'
|
||||
import { decompose } from 'Engine/mecanisms/utils'
|
||||
import Barème from 'Engine/mecanismViews/Barème'
|
||||
import { val } from 'Engine/traverse-common-functions'
|
||||
import { evolve, has, pluck, sum } from 'ramda'
|
||||
import { inferUnit, parseUnit } from 'Engine/units'
|
||||
|
||||
export let desugarScale = recurse => tranches =>
|
||||
tranches
|
||||
|
@ -38,7 +39,7 @@ export default (recurse, k, v) => {
|
|||
return decompose(recurse, k, v)
|
||||
}
|
||||
if (v.variations) {
|
||||
return mecanismVariations(recurse, k, v, true)
|
||||
return variations(recurse, k, v, true)
|
||||
}
|
||||
|
||||
let { assiette, multiplicateur } = v,
|
||||
|
@ -85,6 +86,7 @@ export default (recurse, k, v) => {
|
|||
jsx: Barème('marginal'),
|
||||
category: 'mecanism',
|
||||
name: 'barème',
|
||||
barème: 'marginal'
|
||||
barème: 'marginal',
|
||||
unit: inferUnit('*', [explanation.assiette.unit, parseUnit('%')])
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,62 @@
|
|||
import React from 'react'
|
||||
import { curry, map } from 'ramda'
|
||||
import { inferUnit } from 'Engine/units'
|
||||
import {
|
||||
evaluateNode,
|
||||
makeJsx,
|
||||
mergeMissing,
|
||||
rewriteNode
|
||||
} from 'Engine/evaluation'
|
||||
import { Node } from 'Engine/mecanismViews/common'
|
||||
|
||||
export default (k, operatorFunction, symbol) => (recurse, k, v) => {
|
||||
let evaluate = (cache, situation, parsedRules, node) => {
|
||||
let explanation = map(
|
||||
curry(evaluateNode)(cache, situation, parsedRules),
|
||||
node.explanation
|
||||
),
|
||||
value1 = explanation[0].nodeValue,
|
||||
value2 = explanation[1].nodeValue,
|
||||
nodeValue =
|
||||
value1 == null || value2 == null
|
||||
? null
|
||||
: operatorFunction(value1, value2),
|
||||
missingVariables = mergeMissing(
|
||||
explanation[0].missingVariables,
|
||||
explanation[1].missingVariables
|
||||
)
|
||||
|
||||
return rewriteNode(node, nodeValue, explanation, missingVariables)
|
||||
}
|
||||
|
||||
let explanation = v.explanation.map(recurse)
|
||||
|
||||
let unit = inferUnit(k, [explanation[0].unit, explanation[1].unit])
|
||||
|
||||
let jsx = (nodeValue, explanation) => (
|
||||
<Node
|
||||
classes={'inlineExpression ' + k}
|
||||
value={nodeValue}
|
||||
unit={unit}
|
||||
child={
|
||||
<span className="nodeContent">
|
||||
<span className="fa fa" />
|
||||
{makeJsx(explanation[0])}
|
||||
<span className="operator">{symbol || k}</span>
|
||||
|
||||
{makeJsx(explanation[1])}
|
||||
</span>
|
||||
}
|
||||
/>
|
||||
)
|
||||
|
||||
return {
|
||||
...v,
|
||||
evaluate,
|
||||
jsx,
|
||||
operator: symbol || k,
|
||||
// is this useful ? text: rawNode,
|
||||
explanation,
|
||||
unit
|
||||
}
|
||||
}
|
|
@ -1,6 +1,7 @@
|
|||
import Composantes from 'Engine/mecanismViews/Composantes'
|
||||
import { add, dissoc, objOf } from 'ramda'
|
||||
import { evaluateArrayWithFilter } from 'Engine/evaluation'
|
||||
import { inferUnit } from 'Engine/units'
|
||||
|
||||
export let decompose = (recurse, k, v) => {
|
||||
let subProps = dissoc('composantes')(v),
|
||||
|
@ -28,21 +29,8 @@ export let decompose = (recurse, k, v) => {
|
|||
evaluate: evaluateArrayWithFilter(filter, add, 0),
|
||||
category: 'mecanism',
|
||||
name: 'composantes',
|
||||
type: 'numeric'
|
||||
type: 'numeric',
|
||||
unit: inferUnit('+', explanation.map(e => e.unit))
|
||||
}
|
||||
}
|
||||
|
||||
export let devariateExplanation = (recurse, mecanismKey, v) => {
|
||||
let fixedProps = dissoc('variations')(v),
|
||||
explanation = v.variations.map(({ si, alors, sinon }) => ({
|
||||
consequence: recurse({
|
||||
[mecanismKey]: {
|
||||
...fixedProps,
|
||||
...(sinon || alors)
|
||||
}
|
||||
}),
|
||||
condition: sinon ? undefined : recurse(si)
|
||||
}))
|
||||
|
||||
return explanation
|
||||
}
|
||||
|
|
|
@ -0,0 +1,110 @@
|
|||
import { inferUnit } from 'Engine/units'
|
||||
import {
|
||||
bonus,
|
||||
collectNodeMissing,
|
||||
evaluateNode,
|
||||
mergeAllMissing,
|
||||
mergeMissing,
|
||||
rewriteNode
|
||||
} from 'Engine/evaluation'
|
||||
import { reject, pluck, isNil, filter, dissoc, reduce } from 'ramda'
|
||||
import Variations from 'Engine/mecanismViews/Variations'
|
||||
|
||||
/* @devariate = true => This function will produce variations of a same mecanism (e.g. product) that share some common properties */
|
||||
export default (recurse, k, v, devariate) => {
|
||||
let explanation = devariate
|
||||
? devariateExplanation(recurse, k, v)
|
||||
: v.map(({ si, alors, sinon }) =>
|
||||
sinon !== undefined
|
||||
? { consequence: recurse(sinon), condition: undefined }
|
||||
: { consequence: recurse(alors), condition: recurse(si) }
|
||||
)
|
||||
|
||||
let evaluate = (cache, situationGate, parsedRules, node) => {
|
||||
let evaluateVariationProp = prop =>
|
||||
prop && evaluateNode(cache, situationGate, parsedRules, prop),
|
||||
// mark the satisfied variation if any in the explanation
|
||||
[, resolvedExplanation] = reduce(
|
||||
([resolved, result], variation) => {
|
||||
if (resolved) return [true, [...result, variation]]
|
||||
|
||||
// evaluate the condition
|
||||
let evaluatedCondition = evaluateVariationProp(variation.condition)
|
||||
|
||||
if (evaluatedCondition == undefined) {
|
||||
// No condition : we've reached the eventual defaut case
|
||||
let evaluatedVariation = {
|
||||
consequence: evaluateVariationProp(variation.consequence),
|
||||
satisfied: true
|
||||
}
|
||||
return [true, [...result, evaluatedVariation]]
|
||||
}
|
||||
|
||||
if (evaluatedCondition.nodeValue === null)
|
||||
// the current variation case has missing variables => we can't go further
|
||||
return [
|
||||
true,
|
||||
[...result, { ...variation, condition: evaluatedCondition }]
|
||||
]
|
||||
|
||||
if (evaluatedCondition.nodeValue === true) {
|
||||
let evaluatedVariation = {
|
||||
condition: evaluatedCondition,
|
||||
consequence: evaluateVariationProp(variation.consequence),
|
||||
satisfied: true
|
||||
}
|
||||
return [true, [...result, evaluatedVariation]]
|
||||
}
|
||||
return [false, [...result, variation]]
|
||||
},
|
||||
[false, []]
|
||||
)(node.explanation),
|
||||
satisfiedVariation = resolvedExplanation.find(v => v.satisfied),
|
||||
nodeValue = satisfiedVariation
|
||||
? satisfiedVariation.consequence.nodeValue
|
||||
: null
|
||||
|
||||
let leftMissing = mergeAllMissing(
|
||||
reject(isNil, pluck('condition', resolvedExplanation))
|
||||
),
|
||||
candidateVariations = filter(
|
||||
node => !node.condition || node.condition.nodeValue !== false,
|
||||
resolvedExplanation
|
||||
),
|
||||
rightMissing = mergeAllMissing(
|
||||
reject(isNil, pluck('consequence', candidateVariations))
|
||||
),
|
||||
missingVariables = satisfiedVariation
|
||||
? collectNodeMissing(satisfiedVariation.consequence)
|
||||
: mergeMissing(bonus(leftMissing), rightMissing)
|
||||
|
||||
return rewriteNode(node, nodeValue, resolvedExplanation, missingVariables)
|
||||
}
|
||||
|
||||
// TODO - find an appropriate representation
|
||||
|
||||
return {
|
||||
explanation,
|
||||
evaluate,
|
||||
jsx: Variations,
|
||||
category: 'mecanism',
|
||||
name: 'variations',
|
||||
type: 'numeric',
|
||||
unit: inferUnit('+', explanation.map(r => r.consequence.unit))
|
||||
}
|
||||
}
|
||||
|
||||
export let devariateExplanation = (recurse, mecanismKey, v) => {
|
||||
let fixedProps = dissoc('variations')(v),
|
||||
explanation = v.variations.map(({ si, alors, sinon }) => ({
|
||||
consequence: recurse({
|
||||
[mecanismKey]: {
|
||||
...fixedProps,
|
||||
...(sinon || alors)
|
||||
}
|
||||
}),
|
||||
condition: sinon ? undefined : recurse(si)
|
||||
}))
|
||||
|
||||
return explanation
|
||||
}
|
|
@ -2,11 +2,15 @@
|
|||
// In a specific file
|
||||
// TODO import them automatically
|
||||
// TODO convert the legacy functions to new files
|
||||
import barème from 'Engine/mecanisms/barème.js'
|
||||
import barème from 'Engine/mecanisms/barème'
|
||||
import barèmeContinu from 'Engine/mecanisms/barème-continu'
|
||||
import barèmeLinéaire from 'Engine/mecanisms/barème-linéaire'
|
||||
import variations from 'Engine/mecanisms/variations'
|
||||
import operation from 'Engine/mecanisms/operation'
|
||||
import { Parser } from 'nearley'
|
||||
|
||||
import {
|
||||
add,
|
||||
curry,
|
||||
divide,
|
||||
equals,
|
||||
gt,
|
||||
|
@ -15,7 +19,6 @@ import {
|
|||
without,
|
||||
lt,
|
||||
lte,
|
||||
map,
|
||||
multiply,
|
||||
propOr,
|
||||
subtract,
|
||||
|
@ -25,15 +28,12 @@ import {
|
|||
T
|
||||
} from 'ramda'
|
||||
import React from 'react'
|
||||
import { evaluateNode, makeJsx, mergeMissing, rewriteNode } from './evaluation'
|
||||
import Grammar from './grammar.ne'
|
||||
import {
|
||||
mecanismAllOf,
|
||||
mecanismComplement,
|
||||
mecanismContinuousScale,
|
||||
mecanismError,
|
||||
mecanismInversion,
|
||||
mecanismLinearScale,
|
||||
mecanismMax,
|
||||
mecanismMin,
|
||||
mecanismNumericalSwitch,
|
||||
|
@ -42,16 +42,9 @@ import {
|
|||
mecanismReduction,
|
||||
mecanismSum,
|
||||
mecanismSynchronisation,
|
||||
mecanismVariations,
|
||||
mecanismOnePossibility
|
||||
} from './mecanisms'
|
||||
import { Node } from './mecanismViews/common'
|
||||
import {
|
||||
parseNegatedReference,
|
||||
parseReference,
|
||||
parseReferenceTransforms
|
||||
} from './parseReference'
|
||||
import { inferUnit } from 'Engine/units'
|
||||
import { parseReferenceTransforms } from './parseReference'
|
||||
|
||||
export let parse = (rules, rule, parsedRules) => rawNode => {
|
||||
let onNodeType = cond([
|
||||
|
@ -130,7 +123,7 @@ export let parseObject = (rules, rule, parsedRules) => rawNode => {
|
|||
operationDispatch = fromPairs(
|
||||
Object.entries(knownOperations).map(([k, [f, symbol]]) => [
|
||||
k,
|
||||
mecanismOperation(k, f, symbol)
|
||||
operation(k, f, symbol)
|
||||
])
|
||||
)
|
||||
|
||||
|
@ -141,21 +134,17 @@ export let parseObject = (rules, rule, parsedRules) => rawNode => {
|
|||
somme: mecanismSum,
|
||||
multiplication: mecanismProduct,
|
||||
barème,
|
||||
'barème linéaire': mecanismLinearScale,
|
||||
'barème continu': mecanismContinuousScale,
|
||||
'barème linéaire': barèmeLinéaire,
|
||||
'barème continu': barèmeContinu,
|
||||
'le maximum de': mecanismMax,
|
||||
'le minimum de': mecanismMin,
|
||||
complément: mecanismComplement,
|
||||
'une possibilité': mecanismOnePossibility(rule.dottedName),
|
||||
'inversion numérique': mecanismInversion(rule.dottedName),
|
||||
allègement: mecanismReduction,
|
||||
variations: mecanismVariations,
|
||||
variations,
|
||||
synchronisation: mecanismSynchronisation,
|
||||
...operationDispatch,
|
||||
'≠': () =>
|
||||
parseNegatedReference(
|
||||
parseReference(rules, rule, parsedRules)(v.explanation)
|
||||
),
|
||||
filter: () =>
|
||||
parseReferenceTransforms(rules, rule, parsedRules)({
|
||||
filter: v.filter,
|
||||
|
@ -171,6 +160,7 @@ export let parseObject = (rules, rule, parsedRules) => rawNode => {
|
|||
constant: () => ({
|
||||
type: v.type,
|
||||
nodeValue: v.nodeValue,
|
||||
unit: v.unit,
|
||||
// eslint-disable-next-line
|
||||
jsx: () => <span className={v.type}>{v.rawNode}</span>
|
||||
})
|
||||
|
@ -179,58 +169,3 @@ export let parseObject = (rules, rule, parsedRules) => rawNode => {
|
|||
|
||||
return action(parse(rules, rule, parsedRules), k, v)
|
||||
}
|
||||
|
||||
let mecanismOperation = (k, operatorFunction, symbol) => (recurse, k, v) => {
|
||||
let evaluate = (cache, situation, parsedRules, node) => {
|
||||
let explanation = map(
|
||||
curry(evaluateNode)(cache, situation, parsedRules),
|
||||
node.explanation
|
||||
),
|
||||
value1 = explanation[0].nodeValue,
|
||||
value2 = explanation[1].nodeValue,
|
||||
nodeValue =
|
||||
value1 == null || value2 == null
|
||||
? null
|
||||
: operatorFunction(value1, value2),
|
||||
missingVariables = mergeMissing(
|
||||
explanation[0].missingVariables,
|
||||
explanation[1].missingVariables
|
||||
)
|
||||
|
||||
return rewriteNode(node, nodeValue, explanation, missingVariables)
|
||||
}
|
||||
|
||||
let explanation = v.explanation.map(recurse)
|
||||
|
||||
let unit = inferUnit(
|
||||
k,
|
||||
explanation[0].unit || undefined,
|
||||
explanation[1].unit || undefined
|
||||
)
|
||||
|
||||
let jsx = (nodeValue, explanation) => (
|
||||
<Node
|
||||
classes={'inlineExpression ' + k}
|
||||
value={nodeValue}
|
||||
child={
|
||||
<span className="nodeContent">
|
||||
<span className="fa fa" />
|
||||
{makeJsx(explanation[0])}
|
||||
<span className="operator">{symbol || k}</span>
|
||||
|
||||
{makeJsx(explanation[1])}
|
||||
</span>
|
||||
}
|
||||
/>
|
||||
)
|
||||
|
||||
return {
|
||||
...v,
|
||||
evaluate,
|
||||
jsx,
|
||||
operator: symbol || k,
|
||||
// is this useful ? text: rawNode,
|
||||
explanation,
|
||||
unit
|
||||
}
|
||||
}
|
||||
|
|
|
@ -17,9 +17,13 @@ export let parseReference = (rules, rule, parsedRules, filter) => ({
|
|||
let partialReference = fragments.join(' . '),
|
||||
dottedName = disambiguateRuleReference(rules, rule, partialReference)
|
||||
|
||||
let inInversionFormula = rule.formule?.['inversion numérique']
|
||||
|
||||
let parsedRule =
|
||||
parsedRules[dottedName] ||
|
||||
parseRule(rules, findRuleByDottedName(rules, dottedName), parsedRules)
|
||||
// the 'inversion numérique' formula should not exist. The instructions to the evaluation should be enough to infer that an inversion is necessary (assuming it is possible, the client decides this)
|
||||
(!inInversionFormula &&
|
||||
parseRule(rules, findRuleByDottedName(rules, dottedName), parsedRules))
|
||||
|
||||
let evaluate = (cache, situation, parsedRules, node) => {
|
||||
let dottedName = node.dottedName,
|
||||
|
@ -93,14 +97,16 @@ export let parseReference = (rules, rule, parsedRules, filter) => ({
|
|||
evaluate,
|
||||
//eslint-disable-next-line react/display-name
|
||||
jsx: nodeValue => (
|
||||
<Leaf
|
||||
classes="variable filtered"
|
||||
filter={filter}
|
||||
name={fragments.join(' . ')}
|
||||
dottedName={dottedName}
|
||||
value={nodeValue}
|
||||
unit={parsedRule.unit}
|
||||
/>
|
||||
<>
|
||||
<Leaf
|
||||
classes="variable filtered"
|
||||
filter={filter}
|
||||
name={fragments.join(' . ')}
|
||||
dottedName={dottedName}
|
||||
nodeValue={nodeValue}
|
||||
unit={parsedRule.unit}
|
||||
/>
|
||||
</>
|
||||
),
|
||||
|
||||
name: partialReference,
|
||||
|
@ -155,11 +161,7 @@ export let parseReferenceTransforms = (
|
|||
if (!rule.période && !inlinePeriodTransform) {
|
||||
if (supportedPeriods.includes(ruleToTransform.période))
|
||||
throw new Error(
|
||||
`Attention, une variable sans période, ${
|
||||
rule.dottedName
|
||||
}, qui appelle une variable à période, ${
|
||||
ruleToTransform.dottedName
|
||||
}, c'est suspect !
|
||||
`Attention, une variable sans période, ${rule.dottedName}, qui appelle une variable à période, ${ruleToTransform.dottedName}, c'est suspect !
|
||||
|
||||
Si la période de la variable appelée est neutralisée dans la formule de calcul, par exemple un montant mensuel divisé par 30 (comprendre 30 jours), utilisez "période: aucune" pour taire cette erreur et rassurer tout le monde.
|
||||
`
|
||||
|
@ -217,40 +219,3 @@ export let parseReferenceTransforms = (
|
|||
evaluate: evaluateTransforms(node.evaluate)
|
||||
}
|
||||
}
|
||||
|
||||
export let parseNegatedReference = variable => {
|
||||
let evaluate = (cache, situation, parsedRules, node) => {
|
||||
let explanation = evaluateNode(
|
||||
cache,
|
||||
situation,
|
||||
parsedRules,
|
||||
node.explanation
|
||||
),
|
||||
nodeValue = explanation.nodeValue == null ? null : !explanation.nodeValue,
|
||||
missingVariables = explanation.missingVariables
|
||||
|
||||
return rewriteNode(node, nodeValue, explanation, missingVariables)
|
||||
}
|
||||
|
||||
let jsx = (nodeValue, explanation) => (
|
||||
<Node
|
||||
classes="inlineExpression negation"
|
||||
value={nodeValue}
|
||||
child={
|
||||
<span className="nodeContent">
|
||||
<Trans i18nKey="inlineExpressionNegation">Non</Trans>{' '}
|
||||
{makeJsx(explanation)}
|
||||
</span>
|
||||
}
|
||||
/>
|
||||
)
|
||||
|
||||
return {
|
||||
evaluate,
|
||||
jsx,
|
||||
category: 'mecanism',
|
||||
name: 'négation',
|
||||
type: 'boolean',
|
||||
explanation: variable
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
import { remove, isEmpty } from 'ramda'
|
||||
import { remove, isEmpty, unnest } from 'ramda'
|
||||
|
||||
//TODO this function does not handle complex units like passenger-kilometer/flight
|
||||
export let parseUnit = string => {
|
||||
let [a, b = ''] = string.split('/'),
|
||||
result = {
|
||||
|
@ -9,32 +10,56 @@ export let parseUnit = string => {
|
|||
return result
|
||||
}
|
||||
|
||||
export let serialiseUnit = ({ numerators, denominators }) => {
|
||||
let printUnits = units => units.filter(unit => unit !== '%').join('-')
|
||||
|
||||
export let serialiseUnit = rawUnit => {
|
||||
let unit = simplify(rawUnit),
|
||||
{ numerators = [], denominators = [] } = unit
|
||||
|
||||
// the unit '%' is only displayed when it is the only unit
|
||||
let merge = [...numerators, ...denominators]
|
||||
if (merge.length === 1 && merge[0] === '%') return '%'
|
||||
|
||||
let n = !isEmpty(numerators)
|
||||
let d = !isEmpty(denominators)
|
||||
return !n && !d
|
||||
? ''
|
||||
: n && !d
|
||||
? numerators.join('')
|
||||
: !n && d
|
||||
? `/${denominators.join('')}`
|
||||
: `${numerators.join('')} / ${denominators.join('')}`
|
||||
let string =
|
||||
!n && !d
|
||||
? ''
|
||||
: n && !d
|
||||
? printUnits(numerators)
|
||||
: !n && d
|
||||
? `/${printUnits(denominators)}`
|
||||
: `${printUnits(numerators)} / ${printUnits(denominators)}`
|
||||
|
||||
return string
|
||||
}
|
||||
|
||||
let noUnit = { numerators: [], denominators: [] }
|
||||
export let inferUnit = (operator, unit1 = noUnit, unit2 = noUnit) =>
|
||||
operator === '*'
|
||||
? simplify({
|
||||
numerators: [...unit1.numerators, ...unit2.numerators],
|
||||
denominators: [...unit1.denominators, ...unit2.denominators]
|
||||
})
|
||||
: operator === '/'
|
||||
? inferUnit('*', unit1, {
|
||||
numerators: unit2.denominators,
|
||||
denominators: unit2.numerators
|
||||
})
|
||||
: null
|
||||
export let inferUnit = (operator, rawUnits) => {
|
||||
let units = rawUnits.map(u => u || noUnit)
|
||||
if (operator === '*')
|
||||
return simplify({
|
||||
numerators: unnest(units.map(u => u.numerators)),
|
||||
denominators: unnest(units.map(u => u.denominators))
|
||||
})
|
||||
if (operator === '/') {
|
||||
if (units.length !== 2)
|
||||
throw new Error('Infer units of a division with units.length !== 2)')
|
||||
return inferUnit('*', [
|
||||
units[0],
|
||||
{
|
||||
numerators: units[1].denominators,
|
||||
denominators: units[1].numerators
|
||||
}
|
||||
])
|
||||
}
|
||||
|
||||
if (operator === '-' || operator === '+') {
|
||||
return rawUnits.find(u => u)
|
||||
}
|
||||
|
||||
return null
|
||||
}
|
||||
export let removeOnce = element => list => {
|
||||
let index = list.indexOf(element)
|
||||
if (index > -1) return remove(index, 1)(list)
|
||||
|
|
|
@ -28,7 +28,6 @@ radio_Non: No
|
|||
radio_Aucun: None
|
||||
oui: yes
|
||||
non: no
|
||||
inlineExpressionNegation: Not
|
||||
déplier: show more
|
||||
replier: show less
|
||||
Salaire brut: Gross salary
|
||||
|
@ -42,7 +41,7 @@ Quel est le salaire mensuel ?: What is the monthly salary?
|
|||
back: Back to your simulation
|
||||
ambiguous: More than one rule with this name found. Which one are you looking for ?
|
||||
Références: Relevant legal information (fr)
|
||||
Règles du groupe: Rules of the group
|
||||
Pages associées: Related pages
|
||||
Pas de sources officielles: No official information
|
||||
Votre avis nous intéresse !: Your opinion matters!
|
||||
afficher les sources complémentaires: display additional information sources
|
||||
|
@ -63,7 +62,6 @@ satisfaction-mailto: click here to directly write an email
|
|||
Votre adresse e-mail: Your email address
|
||||
envoyer: send
|
||||
Rechercher: Search
|
||||
Règle non applicable: Not applicable
|
||||
Situation incomplète: More information needed
|
||||
Destinataire: Levied by
|
||||
cotisation: contribution
|
||||
|
@ -178,8 +176,7 @@ legalNotice:
|
|||
contact@mon-entreprise.beta.gouv.fr
|
||||
</0>
|
||||
|
||||
Cette règle est la somme de: This rule is the sum of
|
||||
Cette règle présente: This rule has
|
||||
La somme de: This rule is the sum of
|
||||
Si: If
|
||||
Sinon: Else
|
||||
Alors: Then
|
||||
|
|
|
@ -753,7 +753,7 @@
|
|||
- si:
|
||||
toutes ces conditions:
|
||||
- brut de base [mensuel] < SMIC [mensuel]
|
||||
- ≠ assimilé salarié
|
||||
- assimilé salarié != oui
|
||||
niveau: avertissement
|
||||
message: |
|
||||
Le salaire saisi est inférieur au SMIC.
|
||||
|
@ -990,7 +990,6 @@
|
|||
- espace: contrat salarié
|
||||
nom: indemnités salarié
|
||||
période: flexible
|
||||
unité: €
|
||||
formule:
|
||||
somme:
|
||||
- CDD . indemnités salarié CDD
|
||||
|
@ -1014,6 +1013,7 @@
|
|||
description: Le plafond de Sécurité sociale est le montant maximum des rémunérations à prendre en compte pour le calcul de certaines cotisations.
|
||||
période: mois
|
||||
formule: 3377
|
||||
unité: €
|
||||
références:
|
||||
2019: https://www.urssaf.fr/portail/home/actualites/toute-lactualite-employeur/plafond-de-la-securite-social-1.html
|
||||
arrêté: https://www.legifrance.gouv.fr/affichTexte.do?cidTexte=JORFTEXT000036171732
|
||||
|
@ -1649,7 +1649,7 @@
|
|||
question: Quel est l'effectif de l'entreprise ?
|
||||
description: |
|
||||
De nombreuses cotisations patronales varient selon l'effectif de l'entreprise.
|
||||
unité: _
|
||||
unité: employés
|
||||
suggestions:
|
||||
1: 1
|
||||
20: 20
|
||||
|
@ -1657,20 +1657,17 @@
|
|||
1000: 1000
|
||||
par défaut: 1
|
||||
|
||||
- espace: entreprise
|
||||
nom: ratio alternants
|
||||
formule: ratio alternants saisi / 100
|
||||
|
||||
- espace: entreprise
|
||||
nom: ratio alternants saisi
|
||||
nom: ratio alternants
|
||||
question: Quelle est la fraction de contrats d'alternance dans l'effectif moyen de l'entreprise ?
|
||||
titre: Fraction d'alternants
|
||||
description: |
|
||||
Cette fraction détermine la contribution supplémentaire pour l'apprentissage pour les entreprises concernées.
|
||||
unité: '%'
|
||||
suggestions:
|
||||
1: 1
|
||||
5: 5
|
||||
1%: 0.1
|
||||
5%: 0.5
|
||||
par défaut: 0
|
||||
|
||||
- espace: entreprise
|
||||
|
@ -2039,8 +2036,8 @@
|
|||
- si:
|
||||
toutes ces conditions:
|
||||
- cotisations . assiette < plafond de réduction
|
||||
- ≠ statut JEI
|
||||
- ≠ assimilé salarié
|
||||
- statut JEI != oui
|
||||
- assimilé salarié != oui
|
||||
alors: 3.45%
|
||||
- sinon: 5.25%
|
||||
références:
|
||||
|
@ -2101,11 +2098,11 @@
|
|||
|
||||
composantes:
|
||||
- attributs:
|
||||
composante: base
|
||||
nom: base
|
||||
taux: 4%
|
||||
|
||||
- attributs:
|
||||
composante: contribution exceptionnelle temporaire
|
||||
nom: contribution exceptionnelle temporaire
|
||||
description: |
|
||||
Instaurée le 1er octobre 2017, applicable jusqu’au 30 septembre 2020 au plus tard.
|
||||
taux: 0.05%
|
||||
|
@ -2140,10 +2137,10 @@
|
|||
# Répartition arbitraire, en sachant que l'employeur doit prendre en charge au minimum 50%
|
||||
- attributs:
|
||||
dû par: employeur
|
||||
taux: part employeur / 100
|
||||
taux: part employeur
|
||||
- attributs:
|
||||
dû par: salarié
|
||||
taux: part salarié / 100
|
||||
taux: part salarié
|
||||
exemples:
|
||||
- nom: forfait à 40€
|
||||
situation:
|
||||
|
@ -2152,7 +2149,7 @@
|
|||
- nom: forfait à 100€ payé par l'employeur
|
||||
situation:
|
||||
forfait: 100
|
||||
part employeur: 100
|
||||
part employeur: 1
|
||||
valeur attendue: 100
|
||||
|
||||
- espace: contrat salarié . complémentaire santé
|
||||
|
@ -2161,11 +2158,11 @@
|
|||
question: Quel est la part de la complémentaire santé payée par l'employeur ?
|
||||
unité: '%'
|
||||
suggestions:
|
||||
50%: 50
|
||||
100%: 100
|
||||
par défaut: 50
|
||||
50%: 0.50
|
||||
100%: 1
|
||||
par défaut: 0.50
|
||||
contrôles:
|
||||
- si: part employeur < 50
|
||||
- si: part employeur < 50%
|
||||
niveau: avertissement
|
||||
message: La part employeur de la complémentaire santé doit être de 50% au minimum
|
||||
|
||||
|
@ -2173,7 +2170,7 @@
|
|||
nom: part salarié
|
||||
description: Part de la complémentaire santé payée par l'employé. Ne peut pas être supérieure à 50%
|
||||
unité: '%'
|
||||
formule: 100 - part employeur
|
||||
formule: 100% - part employeur
|
||||
|
||||
- espace: contrat salarié . complémentaire santé
|
||||
nom: forfait
|
||||
|
@ -2397,27 +2394,15 @@
|
|||
assiette: cotisations . assiette
|
||||
composantes:
|
||||
- attributs:
|
||||
composante: maladie, maternité, invalidité, décès
|
||||
nom: maladie, maternité, invalidité, décès
|
||||
dû par: employeur
|
||||
taux:
|
||||
variations:
|
||||
- si:
|
||||
toutes ces conditions:
|
||||
- cotisations . assiette < plafond de réduction employeur
|
||||
- ≠ statut JEI
|
||||
- ≠ assimilé salarié
|
||||
alors: 7%
|
||||
- sinon: 13%
|
||||
taux: taux employeur
|
||||
- attributs:
|
||||
composante: maladie, maternité, invalidité, décès
|
||||
nom: maladie, maternité, invalidité, décès
|
||||
dû par: salarié
|
||||
taux:
|
||||
variations:
|
||||
- si: régime alsace moselle
|
||||
alors: 1.5%
|
||||
- sinon: 0%
|
||||
taux: taux salarié
|
||||
- attributs:
|
||||
composante: Contribution Solidarité Autonomie
|
||||
nom: Contribution Solidarité Autonomie
|
||||
abbréviation: CSA
|
||||
dû par: employeur
|
||||
références:
|
||||
|
@ -2425,6 +2410,30 @@
|
|||
- https://www.service-public.fr/professionnels-entreprises/vosdroits/F32872
|
||||
taux: 0.3%
|
||||
|
||||
|
||||
- espace: contrat salarié . maladie
|
||||
nom: taux employeur
|
||||
période: aucune
|
||||
formule:
|
||||
variations:
|
||||
- si:
|
||||
toutes ces conditions:
|
||||
- cotisations . assiette < plafond de réduction employeur
|
||||
- statut JEI != oui
|
||||
- assimilé salarié != oui
|
||||
alors: 7%
|
||||
- sinon: 13%
|
||||
|
||||
- espace: contrat salarié . maladie
|
||||
nom: taux salarié
|
||||
période: aucune
|
||||
formule:
|
||||
variations:
|
||||
- si: régime alsace moselle
|
||||
alors: 1.5%
|
||||
- sinon: 0%
|
||||
|
||||
|
||||
- espace: contrat salarié . maladie
|
||||
nom: plafond de réduction employeur
|
||||
période: flexible
|
||||
|
@ -3328,7 +3337,7 @@
|
|||
formule:
|
||||
toutes ces conditions:
|
||||
- entreprise . catégorie d'activité . libérale règlementée
|
||||
- ≠ rattachée à la CIPAV
|
||||
- rattachée à la CIPAV != oui
|
||||
note: D'autres conditions d'exclusions existent, il faudra les compléter, mais la question de la catégorie d'activité doit avant être complétée.
|
||||
|
||||
- nom: indépendant
|
||||
|
@ -3373,7 +3382,7 @@
|
|||
formule:
|
||||
une de ces conditions:
|
||||
- toutes ces conditions:
|
||||
- ≠ contrat salarié
|
||||
- contrat salarié != oui
|
||||
- entreprise . catégorie d'activité . libérale règlementée
|
||||
- toutes ces conditions:
|
||||
- entreprise . année d'activité
|
||||
|
@ -3548,7 +3557,7 @@
|
|||
titre: assiette retraite de base
|
||||
formule:
|
||||
variations:
|
||||
- si: ≠ situation personnelle . RSA
|
||||
- si: situation personnelle . RSA != oui
|
||||
alors:
|
||||
le maximum de:
|
||||
- 11.5% * plafond sécurité sociale temps plein
|
||||
|
@ -3620,7 +3629,7 @@
|
|||
titre: assiette invalidité et décès
|
||||
formule:
|
||||
variations:
|
||||
- si: ≠ situation personnelle . RSA
|
||||
- si: situation personnelle . RSA != oui
|
||||
alors:
|
||||
le maximum de:
|
||||
- 11.5% * plafond sécurité sociale temps plein
|
||||
|
@ -3648,7 +3657,6 @@
|
|||
|
||||
- espace: indépendant . cotisations et contributions . CSG et CRDS
|
||||
nom: assiette
|
||||
note: Il faut vérifier que l'assiette de la CSG et CRDS est correcte. Pourquoi les cotisations sont-elles dans l'assiette ? La retraite complémentaire doit l'être aussi ?
|
||||
période: flexible
|
||||
formule:
|
||||
somme:
|
||||
|
@ -4146,9 +4154,9 @@
|
|||
|
||||
- espace: protection sociale . retraite
|
||||
nom: mois cotisés
|
||||
unité: mois
|
||||
formule: 172 * 3
|
||||
notes: On prends l'hypotèse d'une retraite à taux plein pour un travailleur né en 1973 ou après
|
||||
unité: €
|
||||
|
||||
- espace: protection sociale . retraite
|
||||
nom: complémentaire salarié
|
||||
|
@ -4170,12 +4178,14 @@
|
|||
nom: points acquis
|
||||
formule: points acquis par mois * mois cotisés
|
||||
période: aucune
|
||||
unité: points
|
||||
références:
|
||||
service-public.fr: https://www.service-public.fr/particuliers/vosdroits/F15396
|
||||
|
||||
- espace: protection sociale . retraite . complémentaire salarié
|
||||
nom: points acquis par mois
|
||||
période: mois
|
||||
unité: points/mois
|
||||
formule: contrat salarié . retraite complémentaire / prix d'achat du point
|
||||
|
||||
- espace: protection sociale . retraite . complémentaire salarié
|
||||
|
|
|
@ -580,7 +580,7 @@ contrat salarié . salaire . brut de base:
|
|||
- si:
|
||||
toutes ces conditions:
|
||||
- 'brut de base [mensuel] < SMIC [mensuel]'
|
||||
- ≠ assimilé salarié
|
||||
- assimilé salarié != oui
|
||||
niveau: avertissement
|
||||
message: |
|
||||
The wage entered is lower than the minimum wage.
|
||||
|
@ -599,7 +599,7 @@ contrat salarié . salaire . brut de base:
|
|||
- si:
|
||||
toutes ces conditions:
|
||||
- 'brut de base [mensuel] < SMIC [mensuel]'
|
||||
- ≠ assimilé salarié
|
||||
- assimilé salarié != oui
|
||||
niveau: avertissement
|
||||
message: |
|
||||
Le salaire saisi est inférieur au SMIC.
|
||||
|
|
|
@ -145,7 +145,7 @@ describe('inversions', () => {
|
|||
- si: cadre
|
||||
alors:
|
||||
taux: 80%
|
||||
- si: ≠ cadre
|
||||
- si: cadre != oui
|
||||
alors:
|
||||
taux: 70%
|
||||
|
||||
|
|
|
@ -11,11 +11,12 @@ import { collectMissingVariables } from '../source/engine/generateQuestions'
|
|||
import testSuites from './load-mecanism-tests'
|
||||
import * as R from 'ramda'
|
||||
import { isNumeric } from '../source/utils'
|
||||
import { serialiseUnit } from 'Engine/units'
|
||||
|
||||
describe('Mécanismes', () =>
|
||||
testSuites.map(([suiteName, suite]) =>
|
||||
suite.map(
|
||||
({ exemples, test }) =>
|
||||
({ exemples, test, 'unité attendue': unit }) =>
|
||||
exemples &&
|
||||
describe(`Suite ${suiteName}, test : ${test ||
|
||||
'Nom de test (propriété "test") manquant dans la variable contenant ces "exemples"'}`, () =>
|
||||
|
@ -51,6 +52,11 @@ describe('Mécanismes', () =>
|
|||
if (expectedMissing) {
|
||||
expect(missing).to.eql(expectedMissing)
|
||||
}
|
||||
|
||||
if (unit) {
|
||||
expect(target.unit).not.to.be.equal(undefined)
|
||||
expect(serialiseUnit(target.unit)).to.eql(unit)
|
||||
}
|
||||
})
|
||||
))
|
||||
)
|
||||
|
|
|
@ -1,9 +1,9 @@
|
|||
- nom: base
|
||||
unité: _
|
||||
unité: £
|
||||
formule: 300
|
||||
|
||||
- nom: assiette
|
||||
unité: _
|
||||
unité: £
|
||||
|
||||
- test: Simple
|
||||
formule:
|
||||
|
@ -14,7 +14,7 @@
|
|||
0: 0%
|
||||
0.4: 3.16%
|
||||
1.1: 6.35%
|
||||
|
||||
unité attendue: £
|
||||
exemples:
|
||||
- nom: Premier point
|
||||
situation:
|
||||
|
@ -39,11 +39,12 @@
|
|||
|
||||
|
||||
- nom: base deux
|
||||
unité: _
|
||||
unité: µ
|
||||
formule: 300
|
||||
|
||||
- nom: assiette deux
|
||||
unité: _
|
||||
unité: µ
|
||||
|
||||
- test: Retour de taux, pas d'assiette
|
||||
formule:
|
||||
barème continu:
|
||||
|
@ -54,6 +55,7 @@
|
|||
0.75: 100%
|
||||
1: 0%
|
||||
retourne seulement le taux: oui
|
||||
unité attendue: '%'
|
||||
exemples:
|
||||
- nom: Premier point
|
||||
situation:
|
||||
|
|
|
@ -14,6 +14,7 @@
|
|||
taux: 10%
|
||||
- au-dessus de: 2000
|
||||
taux: 15%
|
||||
unité attendue: €
|
||||
|
||||
exemples:
|
||||
- nom: "petite assiette"
|
||||
|
@ -51,6 +52,7 @@
|
|||
- au-dessus de: 2000
|
||||
montant: 400
|
||||
|
||||
unité attendue: €
|
||||
exemples:
|
||||
- nom: "petite assiette"
|
||||
situation:
|
||||
|
|
|
@ -17,7 +17,7 @@
|
|||
taux: 3%
|
||||
- au-dessus de: 3
|
||||
taux: 1%
|
||||
|
||||
unité attendue: €
|
||||
exemples:
|
||||
- nom: 'petite assiette'
|
||||
situation:
|
||||
|
@ -51,6 +51,7 @@
|
|||
taux: 9%
|
||||
- au-dessus de: 2
|
||||
taux: 29%
|
||||
unité attendue: €
|
||||
|
||||
exemples:
|
||||
- nom:
|
||||
|
@ -61,12 +62,14 @@
|
|||
|
||||
- nom: ma condition
|
||||
|
||||
- nom: taux variable
|
||||
- test: taux variable
|
||||
formule:
|
||||
variations:
|
||||
- si: ma condition
|
||||
alors: 29%
|
||||
- sinon: 56%
|
||||
unité attendue: '%'
|
||||
exemples: []
|
||||
|
||||
- nom: deuxième barème
|
||||
test: Barème à taux variable
|
||||
|
@ -79,6 +82,7 @@
|
|||
taux: taux variable
|
||||
- au-dessus de: 1
|
||||
taux: 90%
|
||||
unité attendue: €
|
||||
|
||||
exemples:
|
||||
- nom: taux faible
|
||||
|
|
|
@ -1,12 +1,17 @@
|
|||
- test: Composantes
|
||||
formule:
|
||||
multiplication:
|
||||
assiette: 100
|
||||
assiette: richesse
|
||||
composantes:
|
||||
- taux: 8%
|
||||
- taux: 2%
|
||||
|
||||
unité attendue: crédits
|
||||
exemples:
|
||||
- nom:
|
||||
situation:
|
||||
valeur attendue: 10
|
||||
|
||||
- nom: richesse
|
||||
unité: crédits
|
||||
formule: 100
|
||||
|
|
|
@ -29,10 +29,13 @@
|
|||
- valeur attendue: 29
|
||||
|
||||
- nom: salaire de base
|
||||
unité: $
|
||||
|
||||
- nom: contrat . salaire de base
|
||||
|
||||
- test: multiplication
|
||||
formule: salaire de base * 3
|
||||
unité attendue: $
|
||||
exemples:
|
||||
- situation:
|
||||
salaire de base: 1000
|
||||
|
@ -46,9 +49,11 @@
|
|||
valeur attendue: 3000
|
||||
|
||||
- nom: taux
|
||||
unité: '%'
|
||||
|
||||
- test: soustraction
|
||||
formule: 1 - taux
|
||||
unité attendue: '%'
|
||||
exemples:
|
||||
- situation:
|
||||
taux: 0.89
|
||||
|
@ -56,6 +61,7 @@
|
|||
|
||||
- test: addition
|
||||
formule: salaire de base + 2000
|
||||
unité attendue: $
|
||||
exemples:
|
||||
- situation:
|
||||
salaire de base: 3000
|
||||
|
@ -78,6 +84,27 @@
|
|||
salaire de base: 3000
|
||||
valeur attendue: 1000
|
||||
|
||||
- test: division deux
|
||||
formule: 2000 / salaire de base
|
||||
unité attendue: /$
|
||||
exemples:
|
||||
- situation:
|
||||
salaire de base: 3000
|
||||
valeur attendue: 0.66667
|
||||
|
||||
- nom: nombre de personnes
|
||||
unité: personne
|
||||
|
||||
- test: division trois
|
||||
formule: salaire de base / nombre de personnes
|
||||
unité attendue: $ / personne
|
||||
exemples:
|
||||
- situation:
|
||||
salaire de base: 3000
|
||||
nombre de personnes: 10
|
||||
valeur attendue: 300
|
||||
|
||||
|
||||
- test: comparaison stricte
|
||||
formule: salaire de base < 3001
|
||||
exemples:
|
||||
|
@ -133,7 +160,7 @@
|
|||
- valeur attendue: true
|
||||
|
||||
- test: négation
|
||||
formule: ≠ CDD . poursuivi en CDI
|
||||
formule: CDD . poursuivi en CDI != oui
|
||||
exemples:
|
||||
- situation:
|
||||
CDD . poursuivi en CDI: oui
|
||||
|
@ -150,6 +177,7 @@
|
|||
|
||||
- test: multiplication et pourcentage
|
||||
formule: 38.1% * salaire de base
|
||||
unité attendue: $
|
||||
exemples:
|
||||
- situation:
|
||||
salaire de base: 1000
|
||||
|
|
|
@ -6,7 +6,7 @@
|
|||
multiplication:
|
||||
assiette: mon assiette
|
||||
taux: 3%
|
||||
|
||||
unité attendue: €
|
||||
exemples:
|
||||
- nom: entier
|
||||
situation:
|
||||
|
@ -51,9 +51,10 @@
|
|||
valeur attendue: 1.5
|
||||
|
||||
- nom: mon facteur
|
||||
unité: _
|
||||
unité: patates
|
||||
|
||||
- test: Multiplication à facteur
|
||||
unité attendue: patates
|
||||
formule:
|
||||
multiplication:
|
||||
assiette: 100
|
||||
|
@ -75,6 +76,7 @@
|
|||
plafond: mon plafond
|
||||
taux: 0.5%
|
||||
|
||||
unité attendue: €-patates
|
||||
exemples:
|
||||
- nom:
|
||||
situation:
|
||||
|
|
|
@ -21,7 +21,7 @@
|
|||
variations:
|
||||
- si: statut cadre
|
||||
alors: 2300
|
||||
- si: ≠ statut cadre
|
||||
- si: statut cadre != oui
|
||||
alors: 2100
|
||||
|
||||
exemples:
|
||||
|
@ -143,7 +143,7 @@
|
|||
- si: statut cadre
|
||||
alors:
|
||||
taux: 8%
|
||||
- si: ≠ statut cadre
|
||||
- si: statut cadre != oui
|
||||
alors:
|
||||
taux: 5%
|
||||
|
||||
|
|
|
@ -19,7 +19,7 @@ describe('Units', () => {
|
|||
it('should work with simple use case *', () => {
|
||||
let unit1 = { numerators: ['m'], denominators: ['s'] }
|
||||
let unit2 = { numerators: ['s'], denominators: [] }
|
||||
let unit = inferUnit('*', unit1, unit2)
|
||||
let unit = inferUnit('*', [unit1, unit2])
|
||||
|
||||
expect(unit).to.deep.equal({
|
||||
numerators: ['m'],
|
||||
|
@ -29,7 +29,7 @@ describe('Units', () => {
|
|||
it('should work with simple use case / ', () => {
|
||||
let unit1 = { numerators: ['m'], denominators: ['s'] }
|
||||
let unit2 = { numerators: ['m'], denominators: [] }
|
||||
let unit = inferUnit('/', unit1, unit2)
|
||||
let unit = inferUnit('/', [unit1, unit2])
|
||||
|
||||
expect(unit).to.deep.equal({
|
||||
numerators: [],
|
||||
|
@ -39,7 +39,7 @@ describe('Units', () => {
|
|||
it('should work with advanced use case /', () => {
|
||||
let unit1 = { numerators: ['a', 'b', 'a', 'z'], denominators: ['c'] }
|
||||
let unit2 = { numerators: ['a', 'e', 'f'], denominators: ['z', 'c'] }
|
||||
let unit = inferUnit('/', unit1, unit2)
|
||||
let unit = inferUnit('/', [unit1, unit2])
|
||||
|
||||
expect(unit).to.deep.equal({
|
||||
numerators: ['b', 'a', 'z', 'z'],
|
||||
|
|
Loading…
Reference in New Issue