diff --git a/modele-social/tsconfig.json b/modele-social/tsconfig.json new file mode 100644 index 000000000..41aef4043 --- /dev/null +++ b/modele-social/tsconfig.json @@ -0,0 +1,8 @@ +{ + "extends": "../tsconfig.json", + "compilerOptions": { + "noEmit": true, + "strict": true + }, + "include": ["build.js"] +} diff --git a/mon-entreprise/source/components/Notifications.tsx b/mon-entreprise/source/components/Notifications.tsx index f0a623326..98d371c54 100644 --- a/mon-entreprise/source/components/Notifications.tsx +++ b/mon-entreprise/source/components/Notifications.tsx @@ -1,7 +1,7 @@ import { hideNotification } from 'Actions/actions' import animate from 'Components/ui/animate' import { useEngine, useInversionFail } from 'Components/utils/EngineContext' -import Engine, { EvaluatedRule } from 'publicodes' +import Engine, { RuleNode } from 'publicodes' import emoji from 'react-easy-emoji' import { useTranslation } from 'react-i18next' import { useDispatch, useSelector } from 'react-redux' @@ -14,8 +14,9 @@ import { ScrollToElement } from './utils/Scroll' // with the "type: notification" attribute. The display can be customized with // the "sévérité" attribute. The notification will only be displayed if the // publicode rule is applicable. -type Notification = Pick & { - type: 'notification' +type Notification = { + dottedName: RuleNode['dottedName'] + description: RuleNode['rawNode']['description'] sévérité: 'avertissement' | 'information' } @@ -26,12 +27,15 @@ export function getNotifications(engine: Engine) { rule.rawNode['type'] === 'notification' && !!engine.evaluate(rule.dottedName).nodeValue ) - .map((node) => node.dottedName) + .map(({ dottedName, rawNode: { sévérité, description } }) => ({ + dottedName, + sévérité, + description, + })) } export default function Notifications() { const { t } = useTranslation() const engine = useEngine() - const inversionFail = useInversionFail() const hiddenNotifications = useSelector( (state: RootState) => state.simulation?.hiddenNotifications @@ -46,11 +50,10 @@ export default function Notifications() { 'simulateurs.inversionFail', 'Le montant saisi abouti à un résultat impossible. Cela est dû à un effet de seuil dans le calcul des cotisations.\n\nNous vous invitons à réessayer en modifiant légèrement le montant renseigné (quelques euros de plus par exemple).' ), - type: 'notification', sévérité: 'avertissement', }, ] - : ((getNotifications(engine) as any) as Array) + : (getNotifications(engine) as Array) if (!messages?.length) return null return ( diff --git a/mon-entreprise/source/components/PaySlip.tsx b/mon-entreprise/source/components/PaySlip.tsx index 4813f3550..09b18449c 100644 --- a/mon-entreprise/source/components/PaySlip.tsx +++ b/mon-entreprise/source/components/PaySlip.tsx @@ -13,6 +13,7 @@ import { Trans, useTranslation } from 'react-i18next' import { DottedName } from 'modele-social' import './PaySlip.css' import { Line, SalaireBrutSection, SalaireNetSection } from './PaySlipSections' +import { RuleNode } from 'publicodes/dist/types/rule' export const SECTION_ORDER = [ 'protection sociale . santé', @@ -27,7 +28,7 @@ export const SECTION_ORDER = [ type Section = typeof SECTION_ORDER[number] -function getSection(rule: ASTNode & { nodeKind: 'rule' }): Section { +function getSection(rule: RuleNode): Section { const section = ('protection sociale . ' + rule.rawNode.cotisation?.branche) as Section if (SECTION_ORDER.includes(section)) { diff --git a/mon-entreprise/source/components/StackedBarChart.tsx b/mon-entreprise/source/components/StackedBarChart.tsx index 1844a8ecc..0e232ef16 100644 --- a/mon-entreprise/source/components/StackedBarChart.tsx +++ b/mon-entreprise/source/components/StackedBarChart.tsx @@ -1,14 +1,13 @@ import RuleLink from 'Components/RuleLink' import useDisplayOnIntersecting from 'Components/utils/useDisplayOnIntersecting' -import { EvaluatedNode, EvaluatedRule } from 'publicodes' -import React, { useContext } from 'react' -import { animated, useSpring } from 'react-spring' -import { DottedName } from 'modele-social' -import styled from 'styled-components' -import { EngineContext, useEngine } from './utils/EngineContext' -import { useSelector } from 'react-redux' -import { targetUnitSelector } from 'Selectors/simulationSelectors' import { Names } from 'modele-social/dist/names' +import { EvaluatedNode } from 'publicodes' +import React from 'react' +import { useSelector } from 'react-redux' +import { animated, useSpring } from 'react-spring' +import { targetUnitSelector } from 'Selectors/simulationSelectors' +import styled from 'styled-components' +import { useEngine } from './utils/EngineContext' const BarStack = styled.div` display: flex; @@ -150,7 +149,7 @@ export default function StackedRulesChart({ data }: StackedRulesChartProps) { key: dottedName, value: engine.evaluate({ valeur: dottedName, unité: targetUnit }) .nodeValue, - legend: , + legend: {title}, color, }))} /> diff --git a/mon-entreprise/source/components/TargetSelection.tsx b/mon-entreprise/source/components/TargetSelection.tsx index f01e4abbe..dad7330f8 100644 --- a/mon-entreprise/source/components/TargetSelection.tsx +++ b/mon-entreprise/source/components/TargetSelection.tsx @@ -14,7 +14,13 @@ import { import { SitePathsContext } from 'Components/utils/SitePathsContext' import { DottedName } from 'modele-social' import { Names } from 'modele-social/dist/names' -import { ASTNode, EvaluatedRule, formatValue, reduceAST } from 'publicodes' +import { + ASTNode, + EvaluatedNode, + formatValue, + reduceAST, + RuleNode, +} from 'publicodes' import { isNil } from 'ramda' import { Fragment, useCallback, useContext } from 'react' import emoji from 'react-easy-emoji' @@ -80,6 +86,10 @@ export default function TargetSelection({ showPeriodSwitch = true }) { type TargetProps = { dottedName: DottedName } +type TargetType = EvaluatedNode & + RuleNode['rawNode'] & + RuleNode & { dottedName: DottedName } + const Target = ({ dottedName }: TargetProps) => { const activeInput = useSelector((state: RootState) => state.activeTargetInput) const engine = useEngine() @@ -89,7 +99,7 @@ const Target = ({ dottedName }: TargetProps) => { unité: useSelector(targetUnitSelector), arrondi: 'oui', }) - const target = { ...evaluation, ...rule.rawNode, ...rule } + const target: TargetType = { ...evaluation, ...rule.rawNode, ...rule } const dispatch = useDispatch() const onSuggestionClick = useCallback( (value) => { @@ -155,7 +165,7 @@ const Target = ({ dottedName }: TargetProps) => { ) } -const Header = ({ target }: { target: EvaluatedRule }) => { +const Header = ({ target }: { target: TargetType }) => { const sitePaths = useContext(SitePathsContext) const { t } = useTranslation() const { pathname } = useLocation() @@ -179,7 +189,7 @@ const Header = ({ target }: { target: EvaluatedRule }) => { } type TargetInputOrValueProps = { - target: EvaluatedRule + target: TargetType isActiveInput: boolean isSmallTarget: boolean } @@ -290,7 +300,6 @@ function TitreRestaurant() { } function AidesGlimpse() { const targetUnit = useSelector(targetUnitSelector) - const { language } = useTranslation().i18n const dottedName = 'contrat salarié . aides employeur' as Names const engine = useEngine() const aides = engine.getRule(dottedName) diff --git a/mon-entreprise/source/components/conversation/DateInput.tsx b/mon-entreprise/source/components/conversation/DateInput.tsx index dba88783f..15e994f52 100644 --- a/mon-entreprise/source/components/conversation/DateInput.tsx +++ b/mon-entreprise/source/components/conversation/DateInput.tsx @@ -2,7 +2,7 @@ import { InputCommonProps, RuleInputProps, } from 'Components/conversation/RuleInput' -import { EvaluatedRule } from 'publicodes' +import { RuleNode } from 'publicodes' import { useCallback, useMemo } from 'react' import styled from 'styled-components' import InputSuggestions from './InputSuggestions' @@ -12,7 +12,7 @@ type DateInputProps = { id: InputCommonProps['id'] onSubmit: RuleInputProps['onSubmit'] value: InputCommonProps['value'] - suggestions: EvaluatedRule['suggestions'] + suggestions: RuleNode['suggestions'] } export default function DateInput({ diff --git a/mon-entreprise/source/components/conversation/Question.tsx b/mon-entreprise/source/components/conversation/Question.tsx index f24d17961..060c57738 100644 --- a/mon-entreprise/source/components/conversation/Question.tsx +++ b/mon-entreprise/source/components/conversation/Question.tsx @@ -1,6 +1,6 @@ import classnames from 'classnames' import { Markdown } from 'Components/utils/markdown' -import { ASTNode } from 'publicodes' +import { RuleNode } from 'publicodes' import { References } from 'publicodes-react' import { Rule } from 'publicodes/dist/types/rule' import { useCallback, useEffect, useState } from 'react' @@ -25,7 +25,7 @@ import { binaryQuestion, InputCommonProps, RuleInputProps } from './RuleInput' */ -export type Choice = ASTNode & { nodeKind: 'rule' } & { +export type Choice = RuleNode & { canGiveUp?: boolean children: Array } diff --git a/mon-entreprise/source/components/conversation/RuleInput.tsx b/mon-entreprise/source/components/conversation/RuleInput.tsx index d6406847b..d48730ff3 100644 --- a/mon-entreprise/source/components/conversation/RuleInput.tsx +++ b/mon-entreprise/source/components/conversation/RuleInput.tsx @@ -7,13 +7,9 @@ import PercentageField from 'Components/PercentageField' import ToggleSwitch from 'Components/ui/ToggleSwitch' import { EngineContext } from 'Components/utils/EngineContext' import { DottedName } from 'modele-social' -import Engine, { - ASTNode, - EvaluatedRule, - formatValue, - reduceAST, -} from 'publicodes' +import Engine, { ASTNode, formatValue, reduceAST } from 'publicodes' import { Evaluation } from 'publicodes/dist/types/AST/types' +import { RuleNode } from 'publicodes/dist/types/rule' import React, { useContext } from 'react' import { useTranslation } from 'react-i18next' import DateInput from './DateInput' @@ -37,7 +33,8 @@ export type InputCommonProps = Pick< RuleInputProps, 'dottedName' | 'onChange' | 'autoFocus' | 'className' > & - Pick, 'title' | 'question' | 'suggestions'> & { + Pick & { + question: RuleNode['rawNode']['question'] key: string id: string value: any //TODO EvaluatedRule['nodeValue'] @@ -181,7 +178,7 @@ export default function RuleInput({ ) } -const getVariant = (node: ASTNode & { nodeKind: 'rule' }) => +const getVariant = (node: RuleNode) => reduceAST( (_, node) => { if (node.nodeKind === 'une possibilité') { diff --git a/mon-entreprise/source/components/conversation/select/SelectEuropeCountry.tsx b/mon-entreprise/source/components/conversation/select/SelectEuropeCountry.tsx index 140cc2976..bc37362f5 100644 --- a/mon-entreprise/source/components/conversation/select/SelectEuropeCountry.tsx +++ b/mon-entreprise/source/components/conversation/select/SelectEuropeCountry.tsx @@ -46,7 +46,7 @@ export default function SelectEuropeCountry({ name="country" id={id} className="ui__" - defaultValue={value?.slice(1, -1)} + defaultValue={value ? value.slice(1, -1) : undefined} onChange={(e) => onChange(`'${e.target.value}'`)} > diff --git a/mon-entreprise/source/site/pages/Gérer/AideDéclarationIndépendant/index.tsx b/mon-entreprise/source/site/pages/Gérer/AideDéclarationIndépendant/index.tsx index 1317a4e35..b06b980ca 100644 --- a/mon-entreprise/source/site/pages/Gérer/AideDéclarationIndépendant/index.tsx +++ b/mon-entreprise/source/site/pages/Gérer/AideDéclarationIndépendant/index.tsx @@ -11,13 +11,13 @@ import { EngineContext, useEngine } from 'Components/utils/EngineContext' import { ScrollToTop } from 'Components/utils/Scroll' import useDisplayOnIntersecting from 'Components/utils/useDisplayOnIntersecting' import { useNextQuestions } from 'Components/utils/useNextQuestion' -import { EvaluatedRule, UNSAFE_evaluateRule, formatValue } from 'publicodes' -import { Fragment, useCallback, useContext, useEffect, useState } from 'react' +import { DottedName } from 'modele-social' +import { RuleNode } from 'publicodes' +import { Fragment, useCallback, useContext, useEffect } from 'react' import emoji from 'react-easy-emoji' import { Trans } from 'react-i18next' import { useDispatch, useSelector } from 'react-redux' import { RootState } from 'Reducers/rootReducer' -import { DottedName } from 'modele-social' import { situationSelector } from 'Selectors/simulationSelectors' import styled from 'styled-components' import { CompanySection } from '../Home' @@ -338,8 +338,8 @@ function SubSection({ type SimpleFieldProps = { dottedName: DottedName - summary?: EvaluatedRule['résumé'] - question?: EvaluatedRule['question'] + summary?: RuleNode['rawNode']['résumé'] + question?: RuleNode['rawNode']['question'] } function SimpleField({ dottedName, question, summary }: SimpleFieldProps) { const dispatch = useDispatch() diff --git a/mon-entreprise/source/site/pages/Gérer/DemandeMobilite/EndBlock.tsx b/mon-entreprise/source/site/pages/Gérer/DemandeMobilite/EndBlock.tsx index 54be65e1e..15d279df2 100644 --- a/mon-entreprise/source/site/pages/Gérer/DemandeMobilite/EndBlock.tsx +++ b/mon-entreprise/source/site/pages/Gérer/DemandeMobilite/EndBlock.tsx @@ -2,11 +2,13 @@ import { BlobProvider } from '@react-pdf/renderer' import Overlay from 'Components/Overlay' import Checkbox from 'Components/ui/Checkbox' import { ThemeColorsContext } from 'Components/utils/colors' +import { EngineContext, EngineProvider } from 'Components/utils/EngineContext' import { TrackerContext } from 'Components/utils/withTracker' +import { RuleNode } from 'publicodes/dist/types/rule' import { lazy, Suspense, useContext, useRef, useState } from 'react' import emoji from 'react-easy-emoji' import SignaturePad from 'react-signature-pad-wrapper' -import PDFDocument, { PDFDocumentProps } from './PDFDocument' +import PDFDocument from './PDFDocument' const IS_TOUCH_DEVICE = isOnTouchDevice() type SignaturePadInstance = { @@ -15,7 +17,7 @@ type SignaturePadInstance = { } type EndBlockProps = { - fields: PDFDocumentProps['fields'] + fields: Array isMissingValues: boolean } @@ -23,7 +25,7 @@ export default function EndBlock({ fields, isMissingValues }: EndBlockProps) { const [isCertified, setCertified] = useState(false) const [place, setPlace] = useState() const [showDownloadLink, toggleDownloadLink] = useState(false) - + const engine = useContext(EngineContext) const { darkColor } = useContext(ThemeColorsContext) const signatureRef = useRef() const tracker = useContext(TrackerContext) @@ -131,13 +133,15 @@ export default function EndBlock({ fields, isMissingValues }: EndBlockProps) { > + + + } > {({ url, loading, error }) => @@ -175,7 +179,7 @@ export default function EndBlock({ fields, isMissingValues }: EndBlockProps) { ]) } className="ui__ cta plain button" - download="demande-mobilité-europe.pdf" + download="demande-mobilité-internationale.pdf" > Télécharger le fichier diff --git a/mon-entreprise/source/site/pages/Gérer/DemandeMobilite/FieldsPDF.tsx b/mon-entreprise/source/site/pages/Gérer/DemandeMobilite/FieldsPDF.tsx index c8313b031..2337ded79 100644 --- a/mon-entreprise/source/site/pages/Gérer/DemandeMobilite/FieldsPDF.tsx +++ b/mon-entreprise/source/site/pages/Gérer/DemandeMobilite/FieldsPDF.tsx @@ -1,47 +1,51 @@ import { StyleSheet, Text, View } from '@react-pdf/renderer' -import { formatValue, EvaluatedRule } from 'publicodes' +import Value from 'Components/EngineValue' +import { EngineContext } from 'Components/utils/EngineContext' +import { formatValue } from 'publicodes' +import { RuleNode } from 'publicodes/dist/types/rule' +import { useContext } from 'react' -export type FieldsPDFProps = { - fields: Array> +type FieldsPDFProps = { + fields: Array } export default function FieldsPDF({ fields }: FieldsPDFProps) { + const engine = useContext(EngineContext) return ( <> - {fields.map((field) => ( - - {field.type === 'groupe' ? ( - <> - - {field.title}{' '} - {field.note && ( - ({field.note}) - )} - - - ) : ( - <> - - {field.question ?? field.title}{' '} - {field.note && ( - ({field.note}) - )} - - {field.nodeValue != null && ( - - {formatValue(field) + - (field.API === 'commune' - ? ` (${ - (field.nodeValue as Record) - .codePostal as string - })` - : '')} + {fields.map( + ({ rawNode: { type, question, note, API }, title, dottedName }) => ( + + {type === 'groupe' ? ( + <> + + {title}{' '} + {note && ({note})} - )} - - )} - - ))} + + ) : ( + <> + + {question ?? title}{' '} + {note && ({note})} + + + + {formatValue(engine.evaluate(dottedName)) + + (API === 'commune' + ? ` (${ + (engine.evaluate(dottedName).nodeValue as Record< + string, + unknown + >)?.codePostal as string + })` + : '')}{' '} + + + )} + + ) + )} ) } diff --git a/mon-entreprise/source/site/pages/Gérer/DemandeMobilite/PDFDocument.tsx b/mon-entreprise/source/site/pages/Gérer/DemandeMobilite/PDFDocument.tsx index 8f460f962..2d1fc4bac 100644 --- a/mon-entreprise/source/site/pages/Gérer/DemandeMobilite/PDFDocument.tsx +++ b/mon-entreprise/source/site/pages/Gérer/DemandeMobilite/PDFDocument.tsx @@ -2,19 +2,20 @@ import ReactPDF, { Document, Font, Image, + Link, Page, StyleSheet, Text, View, - Link, } from '@react-pdf/renderer' import urssafPng from 'Images/destinataires/URSSAF.png' -import FieldsPDF, { FieldsPDFProps, styles as fieldStyles } from './FieldsPDF' +import { RuleNode } from 'publicodes' +import FieldsPDF, { styles as fieldStyles } from './FieldsPDF' import montserratUrl from './Montserrat-SemiBold.ttf' import robotoUrl from './Roboto-Regular.ttf' export type PDFDocumentProps = { - fields: FieldsPDFProps['fields'] + fields: Array signatureURL?: ReactPDF.SourceObject | false place?: string } @@ -33,8 +34,8 @@ export default function PDFDocument({ {fields.find(({ dottedName }) => dottedName === 'détachement') - ? 'Demande de détachement en Europe' - : "Demande d'activité transfrontalière simultanée en Europe"} + ? 'Demande de détachement' + : "Demande d'activité transfrontalière"} Afin d’examiner votre situation au regard des règlements diff --git a/mon-entreprise/source/site/pages/Gérer/DemandeMobilite/formulaire-détachement.yaml b/mon-entreprise/source/site/pages/Gérer/DemandeMobilite/formulaire-détachement.yaml index 64bfcb7c9..485b5e7a5 100644 --- a/mon-entreprise/source/site/pages/Gérer/DemandeMobilite/formulaire-détachement.yaml +++ b/mon-entreprise/source/site/pages/Gérer/DemandeMobilite/formulaire-détachement.yaml @@ -139,9 +139,9 @@ demande . détachement possible: détachement: note: 3.3 + applicable si: demande . détachement possible titre: Demande de détachement - formule: demande . détachement possible - par défaut: non + formule: oui type: groupe détachement . pays: @@ -195,10 +195,11 @@ détachement . activité . code postal: type: texte activité transfrontalière simultanée: - formule: demande . détachement possible = non - par défaut: non + non applicable si: demande . détachement possible + formule: oui titre: Demande d'activité transfrontalière simultanée type: groupe + activité transfrontalière simultanée . salarié hors France: question: > Travaillez-vous en tant que salarié dans un autre pays ? diff --git a/mon-entreprise/source/site/pages/Gérer/DemandeMobilite/index.tsx b/mon-entreprise/source/site/pages/Gérer/DemandeMobilite/index.tsx index 46c458022..1b82767f3 100644 --- a/mon-entreprise/source/site/pages/Gérer/DemandeMobilite/index.tsx +++ b/mon-entreprise/source/site/pages/Gérer/DemandeMobilite/index.tsx @@ -1,19 +1,20 @@ import { Explicable } from 'Components/conversation/Explicable' import RuleInput from 'Components/conversation/RuleInput' +import { Condition } from 'Components/EngineValue' import * as Animate from 'Components/ui/animate' import Emoji from 'Components/utils/Emoji' import { EngineContext, EngineProvider } from 'Components/utils/EngineContext' import { Markdown } from 'Components/utils/markdown' import { usePersistingState } from 'Components/utils/persistState' -import Engine, { UNSAFE_evaluateRule, EvaluatedRule } from 'publicodes' -import { equals } from 'ramda' +import Engine, { UNSAFE_isNotApplicable } from 'publicodes' +import { equals, isEmpty } from 'ramda' import { - lazy, createElement, + lazy, Suspense, useCallback, - useState, useContext, + useState, } from 'react' import emoji from 'react-easy-emoji' import { hash } from '../../../../utils' @@ -91,21 +92,24 @@ export default function FormulaireMobilitéIndépendant() { const useFields = ( engine: Engine, - fieldNames: Array, - situation: Record -): Array => { - const fields = fieldNames - .map((name) => UNSAFE_evaluateRule(engine, name)) - .filter( - (node: EvaluatedRule) => - node.isNotApplicable !== true && - // TODO change this when not applicable value can be differenciated from false value - (equals(node.missingVariables, { [node.dottedName]: 1 }) || - node.dottedName in situation || - (node.nodeValue !== false && node.nodeValue !== null)) && - (node.question || ((node.type || node.API) && node.nodeValue !== false)) - ) - return fields + fieldNames: Array +): Array> => { + return fieldNames + .filter((dottedName) => { + const isNotApplicable = UNSAFE_isNotApplicable(engine, dottedName) + const evaluation = engine.evaluate(dottedName) + const rule = engine.getRule(dottedName) + return ( + isNotApplicable === false && + (equals(evaluation.missingVariables, { [dottedName]: 1 }) || + isEmpty(evaluation.missingVariables)) && + (rule.rawNode.question || + rule.rawNode.API || + rule.rawNode.type || + rule.rawNode.description) + ) + }) + .map((dottedName) => engine.getRule(dottedName)) } const VERSION = hash(JSON.stringify(formulaire)) @@ -133,64 +137,66 @@ function FormulairePublicodes() { }, [clearFieldsKey, setSituation]) engine.setSituation(situation) - const fields = useFields(engine, Object.keys(formulaire), situation) - const missingValues = fields.filter( - ({ dottedName, type }) => - type !== 'groupe' && - (situation[dottedName] == null || situation[dottedName] === '') + const fields = useFields(engine, Object.keys(formulaire)) + + const isMissingValues = fields.some( + ({ dottedName }) => !isEmpty(engine.evaluate(dottedName).missingVariables) ) - const isMissingValues = !!missingValues.length return ( - {fields.map((field) => ( - - {field.type === 'groupe' ? ( - <> - {createElement( - `h${Math.min(field.dottedName.split(' . ').length + 1, 6)}`, - {}, - field.title - )} - {field.description && } - - ) : field.type === 'notification' && field.nodeValue === true ? ( - - {field.description} - - ) : ( - <> - - {field.description && ( - -

{field.title}

- -
- )} - onChange(field.dottedName, value)} - /> - - )} -
- ))} + {fields.map( + ({ rawNode: { description, type, question }, title, dottedName }) => ( + + {type === 'groupe' ? ( + <> + {createElement( + `h${Math.min(dottedName.split(' . ').length + 1, 6)}`, + {}, + title + )} + {description && } + + ) : type === 'notification' ? ( + + + {description} + + + ) : ( + <> + + {description && ( + +

{title}

+ +
+ )} + onChange(dottedName, value)} + /> + + )} +
+ ) + )} diff --git a/mon-entreprise/test/regressions/simulations.jest.js b/mon-entreprise/test/regressions/simulations.jest.js index 25ced65ba..e02cb850a 100644 --- a/mon-entreprise/test/regressions/simulations.jest.js +++ b/mon-entreprise/test/regressions/simulations.jest.js @@ -7,6 +7,7 @@ /* eslint-disable no-undef */ import rules from 'modele-social' +import Engine from 'publicodes' import artisteAuteurConfig from '../../source/site/pages/Simulateurs/configs/artiste-auteur.yaml' import autoentrepreneurConfig from '../../source/site/pages/Simulateurs/configs/auto-entrepreneur.yaml' import independantConfig from '../../source/site/pages/Simulateurs/configs/indépendant.yaml' @@ -20,6 +21,7 @@ import independentSituations from './simulations-indépendant.yaml' import professionsLibéralesSituations from './simulations-professions-libérales.yaml' import remunerationDirigeantSituations from './simulations-rémunération-dirigeant.yaml' import employeeSituations from './simulations-salarié.yaml' +import aideDéclarationIndépendantsSituations from './aide-déclaration-indépendants.yaml' const roundResult = (arr) => arr.map((x) => Math.round(x)) const engine = new Engine(rules) diff --git a/package.json b/package.json index b416cc1b9..e2b593f5e 100644 --- a/package.json +++ b/package.json @@ -115,9 +115,9 @@ "prepare": "if [ -z \"$NETLIFY\" ]; then yarn workspaces run prepare; fi", "lint": "yarn lint:eslintrc && yarn lint:eslint && yarn lint:prettier", "test": "yarn workspaces run test", - "test:type": "yarn workspace publicodes run tsc && yarn workspace publicodes-react run tsc && yarn workspace mon-entreprise run tsc", + "test:type": "yarn workspaces run tsc", "test:regressions": "jest", - "clean": "yarn workspaces run clean", + "clean": "yarn workspaces run clean && rimraf node_modules", "start": "yarn workspace publicodes build --watch & yarn workspace publicodes-react build --watch & yarn workspace mon-entreprise start", "moso:up": "yarn workspace modele-social run up && yarn workspace mon-entreprise upgrade modele-social", "publicodes:up": "yarn workspace publicodes-react upgrade publicodes && yarn workspace mon-entreprise upgrade publicodes publicodes-react" diff --git a/publicodes/core/esm/index.js b/publicodes/core/esm/index.js index cf4cc7dba..d7eb057a2 100644 --- a/publicodes/core/esm/index.js +++ b/publicodes/core/esm/index.js @@ -10,3 +10,4 @@ export const formatValue = publicodes.formatValue export const utils = publicodes.utils export const translateRules = publicodes.translateRules export const UNSAFE_isNotApplicable = publicodes.UNSAFE_isNotApplicable +export const mecanismsDoc = publicodes.mecanismsDoc diff --git a/publicodes/core/source/error.ts b/publicodes/core/source/error.ts index c96698687..32abe8825 100644 --- a/publicodes/core/source/error.ts +++ b/publicodes/core/source/error.ts @@ -1,9 +1,4 @@ -<<<<<<< HEAD:publicodes/core/source/error.ts -const coerceArray = (x) => (Array.isArray(x) ? x : [x]) -======= import { Logger } from '.' -import { Context } from './parsePublicodes' ->>>>>>> 2c06fb45 (:fire: Ajoute la possibilité de définir un logger pour l'engine):publicodes/source/error.ts export class EngineError extends Error {} export function syntaxError( diff --git a/publicodes/core/source/index.ts b/publicodes/core/source/index.ts index 318962f52..875b0793b 100644 --- a/publicodes/core/source/index.ts +++ b/publicodes/core/source/index.ts @@ -3,7 +3,6 @@ import { compose, mapObjIndexed } from 'ramda' import { reduceAST } from './AST' import { ASTNode, EvaluatedNode, NodeKind } from './AST/types' import { evaluationFunctions } from './evaluationFunctions' -import { simplifyNodeUnit } from './nodeUnits' import parse from './parse' import parsePublicodes, { disambiguateReference } from './parsePublicodes' import { @@ -13,11 +12,6 @@ import { } from './replacement' import { Rule, RuleNode } from './rule' import * as utils from './ruleUtils' -<<<<<<< HEAD:publicodes/core/source/index.ts -import { reduceAST } from './AST' -import mecanismsDoc from '../../docs/mecanisms.yaml' -======= ->>>>>>> 2c06fb45 (:fire: Ajoute la possibilité de définir un logger pour l'engine):publicodes/source/index.ts const emptyCache = () => ({ _meta: { ruleStack: [] }, @@ -47,16 +41,13 @@ export type EvaluationOptions = Partial<{ export * as cyclesLib from './AST/graph' export { reduceAST, transformAST } from './AST/index' export { Evaluation, Unit } from './AST/types' -export { formatValue, capitalise0 } from './format' -export { serializeUnit } from './units' +export { capitalise0, formatValue } from './format' export { simplifyNodeUnit } from './nodeUnits' export { default as translateRules } from './translateRules' -export { ASTNode, EvaluatedNode } -export { parsePublicodes } -export { mecanismsDoc } -export { utils } -export { Rule } - +export { serializeUnit } from './units' +export { parsePublicodes, utils } +export { Rule, RuleNode, ASTNode, EvaluatedNode } +export { default as mecanismsDoc } from '../../docs/mecanisms.yaml' type PublicodesExpression = string | Record | number export type Logger = { @@ -166,7 +157,7 @@ export default class Engine { disambiguateReference(this.parsedRules) )( parse(value, { - dottedName: `evaluation`, + dottedName: 'evaluation', parsedRules: {}, logger: this.logger, }) @@ -189,7 +180,7 @@ export default class Engine { } /** - This function allows to mimic the old 'isApplicable' property on evaluatedRules + This function allows to mimic the former 'isApplicable' property on evaluatedRules It will be deprecated when applicability will be encoded as a Literal type */ @@ -216,7 +207,10 @@ export function UNSAFE_isNotApplicable( return fn(engine.evaluate(rule)) } if (node.nodeKind === 'applicable si') { - return (node.explanation.condition as any).nodeValue === false + return ( + (node.explanation.condition as any).nodeValue === false || + fn(node.explanation.valeur) + ) } if (node.nodeKind === 'non applicable si') { return ( @@ -224,45 +218,14 @@ export function UNSAFE_isNotApplicable( (node.explanation.condition as any).nodeValue !== null ) } + if (node.nodeKind === 'rule') { + return ( + (node.explanation.parent as any).nodeValue === false || + fn(node.explanation.valeur) + ) + } }, false, engine.evaluate(dottedName) ) } - -/** - This function allows smother migration to the new Engine API - - It will be deprecated when applicability will be encoded as a Literal type - Prefer the use of `engine.evaluate(engine.getRule(dottedName))` -*/ -export function UNSAFE_evaluateRule( - engine: Engine, - dottedName: DottedName, - modifiers: Object = {} -): EvaluatedRule { - const evaluation = simplifyNodeUnit( - engine.evaluate({ valeur: dottedName, ...modifiers }) - ) - const rule = engine.getRule(dottedName) as RuleNode & { - dottedName: DottedName - } - - return { - isNotApplicable: UNSAFE_isNotApplicable(engine, dottedName), - ...rule.rawNode, - ...rule, - ...evaluation, - } as EvaluatedRule -} - -export type EvaluatedRule = EvaluatedNode & - Omit< - (ASTNode & { - nodeKind: 'rule' - }) & - (ASTNode & { - nodeKind: 'rule' - })['rawNode'] & { dottedName: Name; isNotApplicable: boolean }, - 'nodeKind' - > diff --git a/publicodes/core/source/rule.ts b/publicodes/core/source/rule.ts index 149d1da49..a43b75da3 100644 --- a/publicodes/core/source/rule.ts +++ b/publicodes/core/source/rule.ts @@ -24,6 +24,7 @@ export type Rule = { résumé?: string icônes?: string titre?: string + sévérité?: string cotisation?: { branche: string } diff --git a/publicodes/react/package.json b/publicodes/react/package.json index 3588f0bf6..4d2be8387 100644 --- a/publicodes/react/package.json +++ b/publicodes/react/package.json @@ -6,6 +6,7 @@ "types": "dist/index.d.ts", "scripts": { "build": "tsc", + "clean": "rimraf dist node_modules", "prepare": "yarn run build", "test": "echo \"Error: no test specified\"" }, diff --git a/publicodes/react/source/mecanisms/common.tsx b/publicodes/react/source/mecanisms/common.tsx index 59a1e4924..bf1a8ae46 100644 --- a/publicodes/react/source/mecanisms/common.tsx +++ b/publicodes/react/source/mecanisms/common.tsx @@ -278,7 +278,7 @@ export function Leaf( const { dottedName, nodeValue, unit } = node const rule = engine?.getRule(node.dottedName) if (!rule) { - throw new Error(`Unknown node`) + throw new Error('Unknown node') } const [folded, setFolded] = useState(true) diff --git a/publicodes/site/package.json b/publicodes/site/package.json index c05baba1d..03b2a78d3 100644 --- a/publicodes/site/package.json +++ b/publicodes/site/package.json @@ -6,7 +6,8 @@ "license": "MIT", "scripts": { "prepare": "echo 1", - "test": "echo 1" + "test": "echo 1", + "clean": "rimraf node_modules" }, "devDependencies": { "core-js": "^3.8.1"