@socialgouv/modeles-social 4.76.0 → 4.78.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -3,6 +3,33 @@
3
3
  All notable changes to this project will be documented in this file.
4
4
  See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
5
5
 
6
+ # [4.78.0](https://github.com/SocialGouv/code-du-travail-numerique/compare/v4.77.1...v4.78.0) (2022-06-16)
7
+
8
+ **Note:** Version bump only for package @socialgouv/modeles-social
9
+
10
+
11
+
12
+
13
+
14
+ ## [4.77.1](https://github.com/SocialGouv/code-du-travail-numerique/compare/v4.77.0...v4.77.1) (2022-05-18)
15
+
16
+ **Note:** Version bump only for package @socialgouv/modeles-social
17
+
18
+
19
+
20
+
21
+
22
+ # [4.77.0](https://github.com/SocialGouv/code-du-travail-numerique/compare/v4.76.0...v4.77.0) (2022-05-12)
23
+
24
+
25
+ ### Features
26
+
27
+ * **simulator:** amélioration de la gestion du state ([#4383](https://github.com/SocialGouv/code-du-travail-numerique/issues/4383)) ([075ab8a](https://github.com/SocialGouv/code-du-travail-numerique/commit/075ab8abd6595d0bf4935c0185e6ac6b95501b4e)), closes [#4380](https://github.com/SocialGouv/code-du-travail-numerique/issues/4380) [#4382](https://github.com/SocialGouv/code-du-travail-numerique/issues/4382) [#4384](https://github.com/SocialGouv/code-du-travail-numerique/issues/4384)
28
+
29
+
30
+
31
+
32
+
6
33
  # [4.76.0](https://github.com/SocialGouv/code-du-travail-numerique/compare/v4.75.4...v4.76.0) (2022-04-27)
7
34
 
8
35
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@socialgouv/modeles-social",
3
- "version": "4.76.0",
3
+ "version": "4.78.0",
4
4
  "description": "Les règles publicodes des simulateurs de code du travail numérique",
5
5
  "main": "./lib/index.js",
6
6
  "types": "./lib/index.d.ts",
@@ -40,5 +40,5 @@
40
40
  "yarn lint --fix"
41
41
  ]
42
42
  },
43
- "gitHead": "9c3eb4ab865511efef7e232c19bc14f636ff6bc5"
43
+ "gitHead": "40fac67eea470def4c762cbf5d5eb542f4b77889"
44
44
  }
package/src/index.ts CHANGED
@@ -1,2 +1,3 @@
1
1
  export { default as modeles } from "./modeles/modeles.json";
2
+ export * from "./publicodes";
2
3
  export * from "./utils";
@@ -33,6 +33,7 @@ function copyJSFile() {
33
33
  path.resolve(outDir, "index.d.ts")
34
34
  );
35
35
  fse.copySync(`${inDir}/utils`, `${outDir}/utils`);
36
+ fse.copySync(`${inDir}/publicodes`, `${outDir}/publicodes`);
36
37
  }
37
38
 
38
39
  function writeSupportedCCFile() {
@@ -0,0 +1,26 @@
1
+ import type { EvaluatedNode } from "publicodes";
2
+
3
+ import type { Publicodes } from "./Publicodes";
4
+ import { PublicodesBase } from "./PublicodesBase";
5
+ import type { PublicodesPreavisRetraiteResult } from "./types";
6
+ import { PublicodesSimulator } from "./types";
7
+ import { convertDaysIntoBetterUnit } from "./utils";
8
+
9
+ class PreavisRetraitePublicodes
10
+ extends PublicodesBase<PublicodesPreavisRetraiteResult>
11
+ implements Publicodes<PublicodesPreavisRetraiteResult>
12
+ {
13
+ constructor(rules: string) {
14
+ super(rules, PublicodesSimulator.PREAVIS_RETRAITE);
15
+ }
16
+
17
+ protected convertedResult(
18
+ evaluatedNode: EvaluatedNode
19
+ ): PublicodesPreavisRetraiteResult {
20
+ return convertDaysIntoBetterUnit(
21
+ evaluatedNode.nodeValue as unknown as string
22
+ );
23
+ }
24
+ }
25
+
26
+ export default PreavisRetraitePublicodes;
@@ -0,0 +1,10 @@
1
+ import type { Notification, References } from "../utils";
2
+ import type { PublicodesData } from "./types";
3
+
4
+ export interface Publicodes<TResult> {
5
+ readonly data: PublicodesData<TResult>;
6
+ execute: (rule: string) => TResult;
7
+ setSituation: (args: Record<string, string>) => PublicodesData<TResult>;
8
+ getNotifications: () => Notification[];
9
+ getReferences: () => References[];
10
+ }
@@ -0,0 +1,131 @@
1
+ import type { EvaluatedNode } from "publicodes";
2
+ import Engine from "publicodes";
3
+
4
+ import type { Notification, References } from "../utils";
5
+ import { getNotifications, getReferences } from "../utils";
6
+ import type { Publicodes } from "./Publicodes";
7
+ import type { MissingArgs, PublicodesData, SituationElement } from "./types";
8
+
9
+ export abstract class PublicodesBase<TResult> implements Publicodes<TResult> {
10
+ engine: Engine;
11
+
12
+ targetRule: string;
13
+
14
+ data: PublicodesData<TResult> = {
15
+ missingArgs: [],
16
+ result: {} as TResult,
17
+ situation: [],
18
+ };
19
+
20
+ protected constructor(rules: string, targetRule: string) {
21
+ this.engine = new Engine(rules);
22
+ this.targetRule = targetRule;
23
+ }
24
+
25
+ execute(rule: string): TResult {
26
+ const result = this.handleExecute(this.data.situation, rule);
27
+ return this.convertedResult(result);
28
+ }
29
+
30
+ setSituation(args: Record<string, string>): PublicodesData<TResult> {
31
+ const { missingArgs, result, situation } = this.updateSituation(
32
+ this.data.situation,
33
+ args
34
+ );
35
+ this.data = {
36
+ missingArgs,
37
+ result: this.convertedResult(result),
38
+ situation,
39
+ };
40
+ return this.data;
41
+ }
42
+
43
+ getNotifications(): Notification[] {
44
+ return getNotifications(this.engine);
45
+ }
46
+
47
+ getReferences(): References[] {
48
+ return getReferences(this.engine);
49
+ }
50
+
51
+ private buildSituation(map: SituationElement[]): Record<string, string> {
52
+ const situation: Record<string, string> = {};
53
+ map.forEach((arg) => {
54
+ situation[arg.rawNode.nom] = arg.value;
55
+ });
56
+ return situation;
57
+ }
58
+
59
+ private buildMissingArgs(
60
+ engine: Engine,
61
+ missingArgs: Record<string, number>
62
+ ): MissingArgs[] {
63
+ return Object.entries(missingArgs)
64
+ .map(([key, value]) => {
65
+ const detail = engine.getRule(key);
66
+ return {
67
+ indice: value,
68
+ name: key.replace(/ \. /g, " - "),
69
+ rawNode: detail.rawNode,
70
+ };
71
+ })
72
+ .sort((a, b) => b.indice - a.indice);
73
+ }
74
+
75
+ private handleExecute(
76
+ situation: SituationElement[],
77
+ rule: string
78
+ ): EvaluatedNode {
79
+ this.engine.setSituation(this.buildSituation(situation));
80
+ return this.engine.evaluate(rule);
81
+ }
82
+
83
+ private updateSituation(
84
+ situation: SituationElement[],
85
+ args: Record<string, string>
86
+ ): {
87
+ missingArgs: MissingArgs[];
88
+ result: EvaluatedNode;
89
+ situation: SituationElement[];
90
+ } {
91
+ // Situation is an array to keep the order of the answers
92
+ const currentSituation = situation;
93
+ const newSituation: SituationElement[] = [];
94
+
95
+ // Update the current situation with new values
96
+ currentSituation.forEach((element) => {
97
+ // Keep the data only if always here in the form
98
+ if (args[element.name]) {
99
+ newSituation.push({
100
+ name: element.name,
101
+ rawNode: element.rawNode,
102
+ value: args[element.name],
103
+ });
104
+ }
105
+ });
106
+
107
+ // Add the new entries from the form
108
+ Object.entries(args).forEach(([key, value]) => {
109
+ if (!newSituation.find((element) => element.name === key)) {
110
+ const publiKey = key.replace(/ - /g, " . ");
111
+ const detail = this.engine.getRule(publiKey);
112
+ newSituation.push({
113
+ name: key,
114
+ rawNode: detail.rawNode,
115
+ value: value,
116
+ });
117
+ }
118
+ });
119
+
120
+ this.engine.setSituation(this.buildSituation(newSituation));
121
+ const result = this.engine.evaluate(this.targetRule);
122
+
123
+ return {
124
+ missingArgs: this.buildMissingArgs(this.engine, result.missingVariables),
125
+ result,
126
+ situation: newSituation,
127
+ };
128
+ }
129
+
130
+ protected abstract convertedResult(evaluatedNode: EvaluatedNode): TResult;
131
+ }
@@ -0,0 +1,115 @@
1
+ import { mergeModels } from "../../internal/merger";
2
+ import PreavisRetraitePublicodes from "../PreavisRetraitePublicodes";
3
+
4
+ const partialSituation = [
5
+ {
6
+ name: "contrat salarié . convention collective",
7
+ rawNode: {
8
+ nom: "contrat salarié . convention collective",
9
+ "par défaut": "''",
10
+ question: "Quelle est votre convention collective ?",
11
+ titre: "Convention collective",
12
+ },
13
+ value: "''",
14
+ },
15
+ {
16
+ name: "contrat salarié . mise à la retraite",
17
+ rawNode: {
18
+ cdtn: {
19
+ type: "liste",
20
+ valeurs: {
21
+ "Départ à la retraite": "non",
22
+ "Mise à la retraite": "oui",
23
+ },
24
+ },
25
+ nom: "contrat salarié . mise à la retraite",
26
+ question:
27
+ "L’employeur a-t-il décidé de lui-même de mettre à la retraite le salarié par une décision adressée à celui-ci ?",
28
+ titre: "Origine du départ à la retraite",
29
+ },
30
+ value: "oui",
31
+ },
32
+ {
33
+ name: "contrat salarié . travailleur handicapé",
34
+ rawNode: {
35
+ cdtn: { type: "oui-non" },
36
+ nom: "contrat salarié . travailleur handicapé",
37
+ question:
38
+ "Le salarié concerné est-il reconnu en tant que travailleur handicapé ?",
39
+ titre: "Travailleur handicapé",
40
+ },
41
+ value: "non",
42
+ },
43
+ {
44
+ name: "préavis de retraite",
45
+ rawNode: {
46
+ nom: "préavis de retraite",
47
+ "rend non applicable": "contrat salarié . indemnité de licenciement",
48
+ valeur: "non",
49
+ },
50
+ value: "oui",
51
+ },
52
+ ];
53
+
54
+ const ancienneteSituation = {
55
+ name: "contrat salarié . ancienneté",
56
+ rawNode: {
57
+ cdtn: {
58
+ type: "entier",
59
+ },
60
+ nom: "contrat salarié . ancienneté",
61
+ question: "Quel est votre ancienneté en mois ?",
62
+ titre: "Ancienneté du salarié",
63
+ unité: "mois",
64
+ },
65
+ value: "24",
66
+ };
67
+
68
+ describe("PreavisRetraitePublicodes::class", () => {
69
+ const publicodes = new PreavisRetraitePublicodes(mergeModels());
70
+
71
+ it("doit mettre à jour la situation", () => {
72
+ publicodes.setSituation({
73
+ "contrat salarié . convention collective": "''",
74
+ "contrat salarié . mise à la retraite": "oui",
75
+ "contrat salarié . travailleur handicapé": "non",
76
+ "préavis de retraite": "oui",
77
+ });
78
+ expect(publicodes.data.situation).toEqual(partialSituation);
79
+ });
80
+
81
+ it("doit retourner l'information manquante sur l'ancienneté", () => {
82
+ expect(publicodes.data.missingArgs).toHaveLength(1);
83
+ expect(publicodes.data.missingArgs[0].name).toBe(
84
+ "contrat salarié - ancienneté"
85
+ );
86
+ });
87
+ it("ne doit pas retourner de résultat", () => {
88
+ expect(publicodes.data.result.value).toBe(0);
89
+ });
90
+
91
+ it("doit mettre à jour la situation avec l'ancienneté", () => {
92
+ publicodes.setSituation({
93
+ "contrat salarié . ancienneté": "24",
94
+ "contrat salarié . convention collective": "''",
95
+ "contrat salarié . mise à la retraite": "oui",
96
+ "contrat salarié . travailleur handicapé": "non",
97
+ "préavis de retraite": "oui",
98
+ });
99
+ expect(publicodes.data.situation).toEqual(
100
+ partialSituation.concat(ancienneteSituation)
101
+ );
102
+ });
103
+
104
+ it("ne doit retourner aucunes informations manquantes", () => {
105
+ expect(publicodes.data.missingArgs).toHaveLength(0);
106
+ });
107
+
108
+ it("doit retourner le résultat", () => {
109
+ expect(publicodes.data.result).toEqual({
110
+ unit: "mois",
111
+ value: 2,
112
+ valueInDays: 60.833333333333336,
113
+ });
114
+ });
115
+ });
@@ -0,0 +1,3 @@
1
+ export { default as PreavisRetraitePublicodes } from "./PreavisRetraitePublicodes";
2
+ export * from "./Publicodes";
3
+ export * from "./types";
@@ -0,0 +1,87 @@
1
+ import type Engine from "publicodes";
2
+ import type { Evaluation, Unit } from "publicodes";
3
+ import type { Rule as PubliRule } from "publicodes/dist/types/rule";
4
+
5
+ export type OldReference = {
6
+ ref: string;
7
+ refUrl: string;
8
+ };
9
+
10
+ export interface MissingArgs {
11
+ name: string;
12
+ indice: number;
13
+ rawNode: Rule;
14
+ }
15
+
16
+ export enum RuleType {
17
+ Liste = "liste",
18
+ OuiNon = "oui-non",
19
+ SalaireMensuel = "salaire-mensuel",
20
+ }
21
+
22
+ export interface RuleListe {
23
+ type: RuleType;
24
+ valeurs: Record<string, string>;
25
+ }
26
+
27
+ export type RuleCdtn = RuleListe;
28
+
29
+ export interface Rule extends PubliRule {
30
+ cdtn?: RuleCdtn;
31
+ }
32
+
33
+ export interface SituationElement {
34
+ name: string;
35
+ rawNode: Rule;
36
+ value: string;
37
+ }
38
+
39
+ export type PublicodesState = {
40
+ engine: Engine;
41
+ targetRule: string;
42
+ };
43
+
44
+ export type PublicodesData<TResult> = {
45
+ situation: SituationElement[];
46
+ missingArgs: MissingArgs[];
47
+ result: TResult;
48
+ };
49
+
50
+ export type PublicodesProviderRule = {
51
+ children: React.ReactNode;
52
+ rules: any;
53
+ simulator: PublicodesSimulator;
54
+ };
55
+
56
+ export enum PublicodesUnit {
57
+ DAY = "jour",
58
+ MONTH = "mois",
59
+ YEAR = "an",
60
+ EUROS = "€",
61
+ K_EUROS = "k/€",
62
+ }
63
+
64
+ export enum PublicodesSimulator {
65
+ INDEMNITE_LICENCIEMENT = "contrat salarié . indemnité de licenciement",
66
+ PREAVIS_RETRAITE = "contrat salarié . préavis de retraite en jours",
67
+ }
68
+
69
+ export enum PublicodesConvertedUnit {
70
+ DAY = "jour",
71
+ MONTH = "mois",
72
+ YEAR = "an",
73
+ DAYS = "jours",
74
+ WEEK = "semaine",
75
+ WEEKS = "semaines",
76
+ }
77
+
78
+ export type PublicodesPreavisRetraiteResult = {
79
+ value: number;
80
+ unit: PublicodesConvertedUnit;
81
+ valueInDays: number;
82
+ };
83
+
84
+ export type PublicodesIndemniteLicenciementResult = {
85
+ value: Evaluation;
86
+ unit?: Unit;
87
+ };
@@ -0,0 +1,35 @@
1
+ import { PublicodesConvertedUnit } from "../../types";
2
+ import { convertDaysIntoBetterUnit } from "..";
3
+
4
+ describe("Testing the transformation of the unit from publicodes (in days) into a better unit (weeks or months)", () => {
5
+ it.each`
6
+ input | expectedUnit | expectedValue
7
+ ${1} | ${PublicodesConvertedUnit.DAY} | ${1}
8
+ ${"1"} | ${PublicodesConvertedUnit.DAY} | ${1}
9
+ ${2} | ${PublicodesConvertedUnit.DAYS} | ${2}
10
+ ${"2"} | ${PublicodesConvertedUnit.DAYS} | ${2}
11
+ ${10} | ${PublicodesConvertedUnit.DAYS} | ${10}
12
+ ${"12"} | ${PublicodesConvertedUnit.DAYS} | ${12}
13
+ ${7} | ${PublicodesConvertedUnit.WEEK} | ${1}
14
+ ${"14"} | ${PublicodesConvertedUnit.WEEKS} | ${2}
15
+ ${21} | ${PublicodesConvertedUnit.WEEKS} | ${3}
16
+ ${"30.42"} | ${PublicodesConvertedUnit.MONTH} | ${1}
17
+ ${60.83} | ${PublicodesConvertedUnit.MONTH} | ${2}
18
+ ${182.5} | ${PublicodesConvertedUnit.MONTH} | ${6}
19
+ ${365} | ${PublicodesConvertedUnit.MONTH} | ${12}
20
+ ${"730"} | ${PublicodesConvertedUnit.MONTH} | ${24}
21
+ ${28} | ${PublicodesConvertedUnit.WEEKS} | ${4}
22
+ ${30} | ${PublicodesConvertedUnit.DAYS} | ${30}
23
+ ${35} | ${PublicodesConvertedUnit.WEEKS} | ${5}
24
+ ${45} | ${PublicodesConvertedUnit.DAYS} | ${45}
25
+ `(
26
+ "should return $expectedValue $expectedUnit for $input day(s)",
27
+ ({ input, expectedUnit, expectedValue }) => {
28
+ expect(convertDaysIntoBetterUnit(input)).toEqual({
29
+ unit: expectedUnit,
30
+ value: expectedValue,
31
+ valueInDays: parseFloat(input),
32
+ });
33
+ }
34
+ );
35
+ });
@@ -0,0 +1,7 @@
1
+ export function isFloat(n: number): boolean {
2
+ return Number(n) === n && n % 1 !== 0;
3
+ }
4
+
5
+ export function formatNumber(toBeFormmatted: number): string {
6
+ return isNaN(toBeFormmatted) ? "0" : toBeFormmatted.toString();
7
+ }
@@ -0,0 +1,2 @@
1
+ export * from "./common";
2
+ export * from "./preavis-retraite";
@@ -0,0 +1,38 @@
1
+ import type { PublicodesPreavisRetraiteResult } from "../types";
2
+ import { PublicodesConvertedUnit } from "../types";
3
+ import { isFloat } from ".";
4
+
5
+ export const convertDaysIntoBetterUnit = (
6
+ days: number | string
7
+ ): PublicodesPreavisRetraiteResult => {
8
+ const parsedDay = typeof days === "string" ? parseFloat(days) : days;
9
+ const isDay = parsedDay === 1;
10
+ const isConvertibleIntoWeek = parsedDay % 7 === 0;
11
+ if (isConvertibleIntoWeek) {
12
+ const parsedWeek = parsedDay / 7;
13
+ const isWeek = parsedWeek === 1;
14
+ return {
15
+ unit: isWeek
16
+ ? PublicodesConvertedUnit.WEEK
17
+ : PublicodesConvertedUnit.WEEKS,
18
+ value: parsedWeek,
19
+ valueInDays: parsedDay,
20
+ };
21
+ }
22
+ // Hack: publicodes round value when convert months to days.
23
+ // So we loose accuracy, we just look if the value is a float or modulo 365 == 0
24
+ const isConvertibleIntoMonth = isFloat(parsedDay) || parsedDay % 365 === 0;
25
+ if (isConvertibleIntoMonth) {
26
+ const parsedMonth = Math.round(parsedDay / (365 / 12));
27
+ return {
28
+ unit: PublicodesConvertedUnit.MONTH,
29
+ value: parsedMonth,
30
+ valueInDays: parsedDay,
31
+ };
32
+ }
33
+ return {
34
+ unit: isDay ? PublicodesConvertedUnit.DAY : PublicodesConvertedUnit.DAYS,
35
+ value: parsedDay,
36
+ valueInDays: parsedDay,
37
+ };
38
+ };