From 577e7fc60ba3a1ad3afb2a3bdbd3a4a0e2016228 Mon Sep 17 00:00:00 2001 From: Johan Girod Date: Mon, 23 Mar 2020 19:07:44 +0100 Subject: [PATCH] :fountain_pen: ajoute de la documentation pour publicode --- publicode/README.md | 28 ++- publicode/mecanism.md | 263 ++++++++++++++++++++++++++++ source/sites/publi.codes/Landing.js | 4 +- source/sites/publi.codes/Studio.tsx | 63 +++++-- 4 files changed, 339 insertions(+), 19 deletions(-) create mode 100644 publicode/mecanism.md diff --git a/publicode/README.md b/publicode/README.md index 9f3d436e1..1f7ed2f03 100644 --- a/publicode/README.md +++ b/publicode/README.md @@ -32,6 +32,8 @@ prix total: formule: 5 * prix d'un repas ``` +> [Lancer le calcul](https://publi.codes/studio?code=prix%20d'un%20repas%3A%0A%20%20formule%3A%2010%0A%0Aprix%20total%3A%0A%20%20formule%3A%205%20*%20prix%20d'un%20repas) + Il s'agit d'un langage déclaratif : comme dans une formule d'un tableur le `prix total` sera recalculé automatiquement si le prix d'un repas change. L'ordre de définition des règles n'a pas d'importance. @@ -51,6 +53,8 @@ prix total: formule: nombre de repas * prix d'un repas ``` +> [Lancer le calcul](https://publi.codes/studio?code=prix%20d'un%20repas%3A%0A%20%20formule%3A%2010%20%E2%82%AC%2Frepas%0A%0Anombre%20de%20repas%3A%0A%20%20formule%3A%205%20repas%0A%0Aprix%20total%3A%0A%20%20formule%3A%20nombre%20de%20repas%20*%20prix%20d'un%20repas) + Le calcul est inchangé mais on a indiqué que le "prix d'un repas" s'exprime en `€/repas` et que le "nombre de repas" est un nombre de `repas`. L'unité du prix total est inférée automatiquement comme étant en `€`. (`€/repas` \* `repas` = @@ -73,6 +77,8 @@ prix total: # La formule de "prix total" est invalide. ``` +> [Lancer le calcul](https://publi.codes/studio?code=prix%20d'un%20repas%3A%0A%20%20formule%3A%2010%20%E2%82%AC%2Frepas%0A%0Anombre%20de%20repas%3A%0A%20%20formule%3A%205%20repas%0A%0Afrais%20de%20r%C3%A9servation%3A%0A%20%20formule%3A%201%20%E2%82%AC%2Frepas%0A%0Aprix%20total%3A%0A%20%20formule%3A%20nombre%20de%20repas%20*%20prix%20d'un%20repas%20%2B%20frais%20de%20r%C3%A9servation) + Dans l'exemple ci-dessus Publicode détecte une erreur car les termes de l'addition ont des unités incompatibles : d'un côté on a des `€` et de l'autre des `€/repas`. Comme dans les formules de Physique, cette incohérence d'unité @@ -84,6 +90,8 @@ prix total: formule: nombre de repas * (prix d'un repas + frais de réservation) ``` +> [Lancer le calcul]() + > **Attention:** Il ne faut pas insérer d'espace autour de la barre oblique dans > les unités, l'unité ~`€ / mois`~ doit être notée `€/mois` @@ -136,7 +144,9 @@ variable `taux` dans un autre espace de nom. Les règles de calcul élémentaires sont extraites dans des "mécanismes" qui permettent de partager la logique de calcul et de générer une page d'explication -spécifique par mécanisme. Par exemple on a un mécanisme `barème`: +spécifique par mécanisme. + +Par exemple on a un mécanisme `barème`: ```yaml impôt sur le revenu: @@ -173,19 +183,29 @@ prime . taux du bonus: formule: 20% ``` +**[Voir la liste des mécanismes](https://github.com/betagouv/mon-entreprise/blob/master/publicode/mecanism.md)** + ## Applicabilité On peut définir des conditions d'applicabilité des règles : ```yaml -ancienneté: - formule: aujourd'hui - date de début +date de début: + formule: 12/02/2020 + +ancienneté en fin d'année: + formule: + durée: + depuis: date de début + jusqu'à: 31/12/2020 prime de vacances: - applicable si: ancienneté > 1 an + applicable si: ancienneté en fin d'année > 1 an formule: 200€ ``` +> [Lancer le calcul](https://publi.codes/studio?code=date%20de%20d%C3%A9but%3A%20%0A%20%20formule%3A%2012%2F02%2F2020%0A%20%20%0Aanciennet%C3%A9%20en%20fin%20d'ann%C3%A9e%3A%0A%20%20formule%3A%20%0A%20%20%20%20dur%C3%A9e%3A%0A%20%20%20%20%20%20%20depuis%3A%20date%20de%20d%C3%A9but%0A%20%20%20%20%20%20%20jusqu'%C3%A0%3A%2031%2F12%2F2020%0A%0Aprime%20de%20vacances%3A%0A%20%20applicable%20si%3A%20anciennet%C3%A9%20en%20fin%20d'ann%C3%A9e%20%3E%201%20an%0A%20%20formule%3A%20200%E2%82%AC) + Ici si l'ancienneté est inférieure à un an la prime de vacances ne sera pas applicable. Les variables non applicables sont ignorées au niveau des mécanismes (par exemple le mécanisme `somme` comptera une prime non applicable comme valant diff --git a/publicode/mecanism.md b/publicode/mecanism.md new file mode 100644 index 000000000..4fd40f923 --- /dev/null +++ b/publicode/mecanism.md @@ -0,0 +1,263 @@ +## Liste des mécanismes existants + +### `une de ces conditions` + +C'est un `ou` logique. +Contient une liste de conditions. +Renvoie vrai si l'une des conditions est vraie. + +```yaml +age: + formule: 17 ans + +mineur émancipé: + formule: oui + +peut voter: + formule: + une de ces conditions: + - age > 18 ans + - mineur émancipé +``` + +> [Lancer le calcul](http://localhost:8080/publicodes/studio?code=date%20de%20d%C3%A9but%3A%20%0A%20%20formule%3A%2012%2F02%2F2020%0A%20%20%0Aanciennet%C3%A9%20en%20fin%20d%27ann%C3%A9e%3A%0A%20%20formule%3A%20%0A%20%20%20%20dur%C3%A9e%3A%0A%20%20%20%20%20%20%20depuis%3A%20date%20de%20d%C3%A9but%0A%20%20%20%20%20%20%20jusqu%27%C3%A0%3A%2031%2F12%2F2020%0A%0Aprime%20de%20vacances%3A%0A%20%20applicable%20si%3A%20anciennet%C3%A9%20en%20fin%20d%27ann%C3%A9e%20%3E%201%20an%0A%20%20formule%3A%20200%E2%82%AC) + +### `toutes ces conditions` + +C'est un `et` logique. +Contient une liste de conditions. +Renvoie vrai si toutes les conditions vraies. + +### `produit` + +C'est une multiplication un peu améliorée, très utile pour exprimer les cotisations. + +Sa propriété `assiette` est multipliée par un pourcentage, `taux`, ou par un `facteur` quand ce nom est plus approprié. + +La multiplication peut être plafonnée : ce plafond sépare l'assiette en deux, et la partie au-dessus du plafond est tout simplement ignorée. Dans ce cas, elle se comporte comme une barème en taux marginaux à deux tranches, la deuxième au taux nul et allant de `plafond` à l'infini. + +```yaml +plafond sécurité sociale: + formule: 3428 €/mois + +assiette cotisation: + formule: 2300 €/mois + +chômage: + formule: + produit: + assiette: assiette cotisation + plafond: 400% * plafond sécurité sociale + taux: 4.05% +``` + +[Lancer le calcul](https://publi.codes/studio?code=plafond%20s%C3%A9curit%C3%A9%20sociale%3A%0A%20%20formule%3A%203428%20%E2%82%AC%2Fmois%0A%0Aassiette%20cotisation%3A%20%0A%20%20formule%3A%202300%20%E2%82%AC%2Fmois%0A%0Ach%C3%B4mage%3A%20%0A%20%20formule%3A%0A%20%20%20%20produit%3A%20%0A%20%20%20%20%20%20assiette%3A%20assiette%20cotisation%0A%20%20%20%20%20%20plafond%3A%20400%25%20*%20plafond%20s%C3%A9curit%C3%A9%20sociale%0A%20%20%20%20%20%20taux%3A%204.05%25%0A%20%20%20%20%20%20) + +### `variations` + +Contient une liste de conditions (`si`) et leurs conséquences associées (`alors`). +Pour la première condition vraie dans la liste, on retient la valeur qui lui est associée. +Si aucune condition n'est vraie, alors ce mécanisme renvoie implicitement `non applicable` + +```yaml +taux réduit: + formule: oui + +taux allocation familiales: + formule: + variations: + - si: taux réduit + alors: 3.45% + - sinon: 5.25% +``` + +> [Lancer le calcul](https://publi.codes/studio?code=taux%20r%C3%A9duit%3A%0A%20%20formule%3A%20oui%0A%0Ataux%20allocation%20familiales%3A%0A%20%20formule%3A%0A%20%20%20%20variations%3A%0A%20%20%20%20%20%20-%20si%3A%20taux%20r%C3%A9duit%0A%20%20%20%20%20%20%20%20alors%3A%203.45%25%0A%20%20%20%20%20%20-%20sinon%3A%205.25%25) + +Ce mécanisme peut aussi être utilisé au sein d'un mécanisme compatible, tel que la produit ou le barème. + +```yaml +assiette cotisation: + formule: 2300 €/mois + +taux réduit: + formule: oui + +allocation familiales: + formule: + produit: + assiette: assiette cotisation + variations: + - si: taux réduit + alors: + taux: 3.45% + - sinon: + taux: 5.25% +``` + +> [Lancer le calcul](http://localhost:8080/publicodes/studio?code=date%20de%20d%C3%A9but%3A%20%0A%20%20formule%3A%2012%2F02%2F2020%0A%20%20%0Aanciennet%C3%A9%20en%20fin%20d%27ann%C3%A9e%3A%0A%20%20formule%3A%20%0A%20%20%20%20dur%C3%A9e%3A%0A%20%20%20%20%20%20%20depuis%3A%20date%20de%20d%C3%A9but%0A%20%20%20%20%20%20%20jusqu%27%C3%A0%3A%2031%2F12%2F2020%0A%0Aprime%20de%20vacances%3A%0A%20%20applicable%20si%3A%20anciennet%C3%A9%20en%20fin%20d%27ann%C3%A9e%20%3E%201%20an%0A%20%20formule%3A%20200%E2%82%AC) + +### `somme` + +C'est tout simplement la somme de chaque terme de la liste. Si un des terme +n'est pas applicable, il vaut zéro. + +```yaml +a: + formule: 50 € + +b: + applicable si: non + formule: 20 € + +somme: + formule: + somme: + - a + - b + - 40 € +``` + +> [Lancer le calcul](https://publi.codes/studio?code=a%3A%20%0A%20%20formule%3A%2050%20%E2%82%AC%0A%0Ab%3A%20%0A%20%20applicable%20si%3A%20non%0A%20%20formule%3A%2020%20%E2%82%AC%0A%0Asomme%3A%0A%20%20formule%3A%0A%20%20%20%20somme%3A%0A%20%20%20%20%20%20-%20a%0A%20%20%20%20%20%20-%20b%0A%20%20%20%20%20%20-%2040%20%E2%82%AC) + +### `le maximum de` + +Renvoie la valeur numérique de la liste de propositions fournie qui est la plus grande. + +Il est conseillé de renseigner une description de chaque proposition par exemple quand elles représentent des méthodes de calcul alternatives. + +### `le minimum de` + +Renvoie l'élément de la liste de propositions fournie qui a la plus petite valeur. + +Ces propositions doivent avoir un mécanisme de calcul ou être une valeur numérique. + +Il est conseillé de renseigner une description de chaque proposition par exemple quand elles représentent des méthodes de calcul alternatives parmi lesquelles il faut en choisir une. + +### `arrondi` + +Arrondi à l'entier le plus proche, ou à une précision donnée. + +```yaml +a: + formule: 12.45 + +arrondi: + formule: + arrondi: + valeur: a + décimales: 1 +``` + +### `régularisation` + +Permet de régulariser progressivement un calcul de cotisation par rapport à une +variable temporelle. + +```yaml +brut: + formule: + somme: + - 2000 €/mois | du 01/01/2020 | au 31/05/2020 + - 4000 €/mois | du 01/06/2020 | au 31/12/2020 + +cotisation: + formule: + régularisation: + règle: + produit: + assiette: brut + plafond: 3000€/mois + taux: 10% + valeurs cumulées: + - brut + +cotisation en 2020: + formule: cotisation | du 01/01/2020 | au 31/12/2020 +``` + +[Lancer le calcul](https://publi.codes/studio?code=brut%3A%0A%20%20formule%3A%0A%20%20%20%20somme%3A%0A%20%20%20%20%20%20-%202000%20%E2%82%AC%2Fmois%20%7C%20du%2001%2F01%2F2020%20%7C%20au%2031%2F05%2F2020%0A%20%20%20%20%20%20-%204000%20%E2%82%AC%2Fmois%20%7C%20du%2001%2F06%2F2020%20%7C%20au%2031%2F12%2F2020%0A%0Acotisation%3A%0A%20%20formule%3A%20%0A%20%20%20%20r%C3%A9gularisation%3A%0A%20%20%20%20%20%20r%C3%A8gle%3A%0A%20%20%20%20%20%20%20%20produit%3A%0A%20%20%20%20%20%20%20%20%20%20assiette%3A%20brut%0A%20%20%20%20%20%20%20%20%20%20plafond%3A%203000%E2%82%AC%2Fmois%0A%20%20%20%20%20%20%20%20%20%20taux%3A%2010%25%0A%20%20%20%20%20%20valeurs%20cumul%C3%A9es%3A%0A%20%20%20%20%20%20%20%20-%20brut%0A%0Acotisation%20en%202020%3A%0A%20%20formule%3A%0A%20%20%20%20cotisation%20%7C%20du%2001%2F01%2F2020%20%7C%20au%2031%2F12%2F2020%0A) + +### `recalcul` + +Relance le calcul d'une règle dans une situation différente de la situation +courante. Permet par exemple de calculer le montant des cotisations au +niveau du SMIC, même si le salaire est plus élevé dans la situation +actuelle. + +### `barème` + +C'est un barème en taux marginaux, mécanisme de calcul connu son utilisation +dans le calcul de l'impôt sur le revenu. + +L'assiette est décomposée en plusieurs tranches, qui sont multipliées par un +taux spécifique. + +Les tranches sont souvent exprimées sous forme de facteurs d'une variable +que l'on appelle `multiplicateur`, par exemple `1 x le plafond de la sécurité sociale`. + +### `grille` + +C'est un barème sous la forme d'une grille de correspondance. C'est le +mécanisme de calcul de l'impôt neutre, aussi appelé impôt non personnalisé. + +Il est composé de tranches qui se suivent. Il suffit de trouver l'assiette +qui correspond à la tranche, et de selectionner le montant associé à +l'assiette. + +### `taux progressif` + +Ce mécanisme permet de calculer un taux progressif. On spécifie pour chaque +tranche le plafond et le taux associé. Le taux effectif renvoyé est calculé +en lissant la différence de taux entre la borne inférieure et supérieure de +l'assiette + +> Par exemple, si nous nous avons les tranches suivantes : + +- taux: 50% / plafond: 0 +- taux: 100% / plafond: 1000 + +> Pour une assiette de 500, le taux retourné sera 75%, car il correspond au +> taux situé à la moitié de la tranche correspondante. + +### `composantes` + +Beaucoup de cotisations sont composées de deux parties qui partagent la méthode de calcul mais diffèrent par des paramètres différents. + +Pour ne pas définir deux variables presque redondantes, on utilise le mécanisme de composante. Il se comportera comme une somme dans les calculs, mais son affichage sur les pages /règle sera adapté. + +Il est même possible, pour les mécanismes `barème` et `produit` de garder en commun un paramètre comme l'assiette, puis de déclarer des composantes pour le taux. + +> L'exemple le plus courant de composantes, c'est la distinction part employeur, part salarié (ex. retraite AGIRC). + +### `allègement` + +Permet de réduire le montant d'une variable. +Très utilisé dans le contexte des impôts. + +### `encadrement` + +Permet d'ajouter un plafond et/ou un plancher à une valeur. + +### `durée` + +Permet d'obtenir le nombre de jours entre deux dates + +### `synchronisation` + +Pour éviter trop de saisies à l'utilisateur, certaines informations sont +récupérées à partir de ce que l'on appelle des API. Ce sont des services +auxquels ont fait appel pour obtenir des informations sur un sujet précis. +Par exemple, l'État français fournit gratuitement l'API géo, qui permet à +partir du nom d'une ville, d'obtenir son code postal, son département, la +population etc. + +Ce mécanismes `synchronisation` permet de faire le lien entre les règles de +notre système et les réponses de ces API. + +### `inversion numérique` + +La formule de calcul de cette variable n'est pas connue, souvent elle n'a même pas de sens. Mais le mécanisme `inversion` indique qu'elle peut être _estimée_ à partir de l'un des _objectifs_ listés sous l'attribut `avec`. Il faut alors renseigner une valeur cible pour cet objectif. + +Voilà comment ça marche : on va donner à la variable une valeur au hasard, calculer _l'objectif_, puis grâce à des calculs savants améliorer notre choix jusqu'à ce que l'écart entre le calcul et la valeur cible devienne satisfaisant. + +Concrètement, si l'on demande au moteur (même indirectement) la valeur d'une variable qui a pour formule une inversion, il va vérifier qu'une des possibilités d'inversion a bien une valeur calculée ou saisie, et procéder à l'inversion décrite plus haut à partir de celle-ci. Sinon, ces possibilités d'inversions seront listées comme manquantes. diff --git a/source/sites/publi.codes/Landing.js b/source/sites/publi.codes/Landing.js index 982fa93bb..323d414dd 100644 --- a/source/sites/publi.codes/Landing.js +++ b/source/sites/publi.codes/Landing.js @@ -46,11 +46,11 @@ export default function Landing() {

Projets phares

diff --git a/source/sites/publi.codes/Studio.tsx b/source/sites/publi.codes/Studio.tsx index bde859a40..98ed20238 100644 --- a/source/sites/publi.codes/Studio.tsx +++ b/source/sites/publi.codes/Studio.tsx @@ -3,7 +3,8 @@ import douche from '!!raw-loader!./exemples/douche.yaml' import { ControlledEditor } from '@monaco-editor/react' import Engine from 'Engine/react' import { safeLoad } from 'js-yaml' -import React, { useEffect, useState } from 'react' +import { last } from 'ramda' +import React, { useCallback, useEffect, useState } from 'react' import emoji from 'react-easy-emoji' import { useLocation } from 'react-router' import styled from 'styled-components' @@ -28,15 +29,23 @@ d: ` export default function Studio() { - const { search } = useLocation() - const currentExample = new URLSearchParams(search ?? '').get('exemple') + const search = new URLSearchParams(useLocation().search ?? '') + const currentExample = search.get('exemple') + const code = search.get('code') const [editorValue, setEditorValue] = useState( - currentExample && Object.keys(examples).includes(currentExample) + code + ? code + : currentExample && Object.keys(examples).includes(currentExample) ? examples[currentExample] : initialInput ) const [targets, setTargets] = useState([]) const [rules, setRules] = useState(editorValue) + const handleShare = useCallback(() => { + navigator.clipboard.writeText( + `https://publi.codes/studio?code=${encodeURIComponent(editorValue)}` + ) + }, [editorValue]) useEffect(() => { try { @@ -76,6 +85,7 @@ export default function Studio() { setRules(editorValue)} + onClickShare={handleShare} /> @@ -84,11 +94,18 @@ export default function Studio() { ) } -export const Results = ({ targets, onClickUpdate }) => { - const [currentTarget, setCurrentTarget] = useState('') - const rule = targets.includes(currentTarget) ? currentTarget : targets[0] +export const Results = ({ targets, onClickUpdate, onClickShare }) => { + const [rule, setCurrentTarget] = useState() + const currentTarget = rule ?? last(targets) const error = Engine.useError() - const analysis = Engine.useEvaluation(rule) + // EN ATTENDANT d'AVOIR une meilleure gestion d'erreur, on va mocker + // console.warn + const warnings: string[] = [] + const originalWarn = console.warn + console.warn = warning => warnings.push(warning) + const analysis = Engine.useEvaluation(currentTarget) + console.warn = originalWarn + return error !== null ? (
{ `} > {targets.map(target => ( - ))}

- +
+ + +
+ {warnings.map(warning => ( +
+ {nl2br(warning)} +
+ ))} {analysis ? (

Résultats

{analysis.isApplicable === false ? ( <>{emoji('❌')} Cette règle n'est pas applicable ) : ( - + )}
) : null}