From c0acf1e143dcdda0d1ef2c17b2491e76489794a7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micha=C3=ABl=20Bitard?= <bitard.michael@gmail.com> Date: Tue, 12 Mar 2024 10:50:37 +0100 Subject: [PATCH] =?UTF-8?q?feat(pxg):=20ajoute=20la=20possibilit=C3=A9=20d?= =?UTF-8?q?e=20d=C3=A9poser=20des=20d=C3=A9marches=20simplifi=C3=A9es=20(#?= =?UTF-8?q?1069)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: vmaubert <v.maubert@code-troopers.com> --- package-lock.json | 16 +- packages/api/package.json | 2 +- .../titres.test.integration.ts.snap | 4 +- .../src/api/rest/etapes.test.integration.ts | 156 +++++++++--------- packages/api/src/api/rest/etapes.test.ts | 6 +- .../__snapshots__/oct.machine.test.ts.snap | 14 +- .../rules-demarches/arm/oct.machine.test.ts | 17 +- .../rules-demarches/arm/oct.machine.ts | 116 ++++++------- .../arm/ren-pro.machine.test.ts | 4 +- .../rules-demarches/arm/ren-pro.machine.ts | 30 ++-- .../__snapshots__/oct.machine.test.ts.snap | 2 +- .../rules-demarches/axm/oct.machine.ts | 109 ++++++------ .../rules-demarches/axm/pro.machine.ts | 102 +++++++----- .../rules-demarches/machine-helper.test.ts | 136 +++++++-------- .../rules-demarches/machine-helper.ts | 107 ++++++------ .../rules-demarches/machine-test-helper.ts | 36 ++-- .../rules-demarches/prm/oct.machine.test.ts | 8 +- .../rules-demarches/prm/oct.machine.ts | 76 ++++----- .../business/rules-demarches/pxg/oct.cas.json | 2 +- .../rules-demarches/pxg/oct.machine.test.ts | 82 ++++++++- .../rules-demarches/pxg/oct.machine.ts | 98 ++++++++--- 21 files changed, 635 insertions(+), 488 deletions(-) diff --git a/package-lock.json b/package-lock.json index 0fdb36217..2bd7125c2 100644 --- a/package-lock.json +++ b/package-lock.json @@ -29713,9 +29713,9 @@ "dev": true }, "node_modules/xstate": { - "version": "4.37.2", - "resolved": "https://registry.npmjs.org/xstate/-/xstate-4.37.2.tgz", - "integrity": "sha512-Qm337O49CRTZ3PRyRuK6b+kvI+D3JGxXIZCTul+xEsyFCVkTFDt5jixaL1nBWcUBcaTQ9um/5CRGVItPi7fveg==", + "version": "5.9.1", + "resolved": "https://registry.npmjs.org/xstate/-/xstate-5.9.1.tgz", + "integrity": "sha512-85edx7iMqRJSRlEPevDwc98EWDYUlT5zEQ54AXuRVR+G76gFbcVTAUdtAeqOVxy8zYnUr9FBB5114iK6enljjw==", "funding": { "type": "opencollective", "url": "https://opencollective.com/xstate" @@ -29919,7 +29919,7 @@ "stream-json": "^1.8.0", "ts-node": "^10.9.1", "xlsx": "^0.18.5", - "xstate": "^4.37.2", + "xstate": "^5.9.1", "zod": "^3.22.4" }, "devDependencies": { @@ -42627,7 +42627,7 @@ "typescript": "^5.3.2", "vitest": "^0.34.6", "xlsx": "^0.18.5", - "xstate": "^4.37.2", + "xstate": "^5.9.1", "zod": "^3.22.4" }, "dependencies": { @@ -57531,9 +57531,9 @@ "dev": true }, "xstate": { - "version": "4.37.2", - "resolved": "https://registry.npmjs.org/xstate/-/xstate-4.37.2.tgz", - "integrity": "sha512-Qm337O49CRTZ3PRyRuK6b+kvI+D3JGxXIZCTul+xEsyFCVkTFDt5jixaL1nBWcUBcaTQ9um/5CRGVItPi7fveg==" + "version": "5.9.1", + "resolved": "https://registry.npmjs.org/xstate/-/xstate-5.9.1.tgz", + "integrity": "sha512-85edx7iMqRJSRlEPevDwc98EWDYUlT5zEQ54AXuRVR+G76gFbcVTAUdtAeqOVxy8zYnUr9FBB5114iK6enljjw==" }, "xtend": { "version": "4.0.2", diff --git a/packages/api/package.json b/packages/api/package.json index 4e9ecfee5..0cd4623af 100644 --- a/packages/api/package.json +++ b/packages/api/package.json @@ -90,7 +90,7 @@ "stream-json": "^1.8.0", "ts-node": "^10.9.1", "xlsx": "^0.18.5", - "xstate": "^4.37.2", + "xstate": "^5.9.1", "zod": "^3.22.4" }, "devDependencies": { diff --git a/packages/api/src/api/rest/__snapshots__/titres.test.integration.ts.snap b/packages/api/src/api/rest/__snapshots__/titres.test.integration.ts.snap index 5e6a3edf9..a42b230a0 100644 --- a/packages/api/src/api/rest/__snapshots__/titres.test.integration.ts.snap +++ b/packages/api/src/api/rest/__snapshots__/titres.test.integration.ts.snap @@ -11,8 +11,8 @@ exports[`titresAdministration > teste la récupération des données pour les Ad "id": Any<String>, "nom": "titre1", "prochainesEtapes": [ - "vfd", "mod", + "vfd", ], "references": [ { @@ -58,8 +58,8 @@ exports[`titresAdministration > teste la récupération des données pour les Ad "id": Any<String>, "nom": "titre2", "prochainesEtapes": [ - "mcp", "mod", + "mcp", ], "references": [ { diff --git a/packages/api/src/api/rest/etapes.test.integration.ts b/packages/api/src/api/rest/etapes.test.integration.ts index e62f09348..b140e5da7 100644 --- a/packages/api/src/api/rest/etapes.test.integration.ts +++ b/packages/api/src/api/rest/etapes.test.integration.ts @@ -41,82 +41,82 @@ test('getEtapesTypesEtapesStatusWithMainStep', async () => { expect(tested.statusCode).toBe(HTTP_STATUS.HTTP_STATUS_OK) expect(tested.body).toMatchInlineSnapshot(` - [ - { - "etapeStatutId": "def", - "etapeTypeId": "rde", - "mainStep": false, - }, - { - "etapeStatutId": "fav", - "etapeTypeId": "rde", - "mainStep": true, - }, - { - "etapeStatutId": "aco", - "etapeTypeId": "mfr", - "mainStep": true, - }, - { - "etapeStatutId": "fai", - "etapeTypeId": "mfr", - "mainStep": true, - }, - { - "etapeStatutId": "aco", - "etapeTypeId": "mfr", - "mainStep": true, - }, - { - "etapeStatutId": "fai", - "etapeTypeId": "mfr", - "mainStep": true, - }, - { - "etapeStatutId": "aco", - "etapeTypeId": "mfr", - "mainStep": true, - }, - { - "etapeStatutId": "fai", - "etapeTypeId": "mfr", - "mainStep": true, - }, - { - "etapeStatutId": "aco", - "etapeTypeId": "mfr", - "mainStep": true, - }, - { - "etapeStatutId": "fai", - "etapeTypeId": "mfr", - "mainStep": true, - }, - { - "etapeStatutId": "aco", - "etapeTypeId": "mfr", - "mainStep": true, - }, - { - "etapeStatutId": "fai", - "etapeTypeId": "mfr", - "mainStep": true, - }, - { - "etapeStatutId": "fai", - "etapeTypeId": "pfd", - "mainStep": true, - }, - { - "etapeStatutId": "req", - "etapeTypeId": "dae", - "mainStep": false, - }, - { - "etapeStatutId": "exe", - "etapeTypeId": "dae", - "mainStep": true, - }, - ] - `) + [ + { + "etapeStatutId": "aco", + "etapeTypeId": "mfr", + "mainStep": true, + }, + { + "etapeStatutId": "fai", + "etapeTypeId": "mfr", + "mainStep": true, + }, + { + "etapeStatutId": "aco", + "etapeTypeId": "mfr", + "mainStep": true, + }, + { + "etapeStatutId": "fai", + "etapeTypeId": "mfr", + "mainStep": true, + }, + { + "etapeStatutId": "aco", + "etapeTypeId": "mfr", + "mainStep": true, + }, + { + "etapeStatutId": "fai", + "etapeTypeId": "mfr", + "mainStep": true, + }, + { + "etapeStatutId": "aco", + "etapeTypeId": "mfr", + "mainStep": true, + }, + { + "etapeStatutId": "fai", + "etapeTypeId": "mfr", + "mainStep": true, + }, + { + "etapeStatutId": "aco", + "etapeTypeId": "mfr", + "mainStep": true, + }, + { + "etapeStatutId": "fai", + "etapeTypeId": "mfr", + "mainStep": true, + }, + { + "etapeStatutId": "fai", + "etapeTypeId": "pfd", + "mainStep": true, + }, + { + "etapeStatutId": "req", + "etapeTypeId": "dae", + "mainStep": false, + }, + { + "etapeStatutId": "exe", + "etapeTypeId": "dae", + "mainStep": true, + }, + { + "etapeStatutId": "def", + "etapeTypeId": "rde", + "mainStep": false, + }, + { + "etapeStatutId": "fav", + "etapeTypeId": "rde", + "mainStep": true, + }, + ] + `) }) diff --git a/packages/api/src/api/rest/etapes.test.ts b/packages/api/src/api/rest/etapes.test.ts index 380f0783d..941251bdd 100644 --- a/packages/api/src/api/rest/etapes.test.ts +++ b/packages/api/src/api/rest/etapes.test.ts @@ -240,7 +240,7 @@ describe('etapesTypesPossibleACetteDateOuALaPlaceDeLEtape', function () { const tested = etapesTypesPossibleACetteDateOuALaPlaceDeLEtape(machine, etapes, null, toCaminoDate('2019-12-04')) .map(({ etapeTypeId }) => etapeTypeId) .filter(onlyUnique) - expect(tested).toStrictEqual(['rde', 'pfd', 'dae']) + expect(tested).toStrictEqual(['pfd', 'dae', 'rde']) }) test('peut faire que une pfd AVANT la mfr non mecanisee', () => { @@ -423,7 +423,7 @@ describe('etapesTypesPossibleACetteDateOuALaPlaceDeLEtape', function () { const tested = etapesTypesPossibleACetteDateOuALaPlaceDeLEtape(machine, etapes, null, toCaminoDate('2022-07-01')) .map(({ etapeTypeId }) => etapeTypeId) .filter(onlyUnique) - expect(tested).toStrictEqual(['rcb', 'rde', 'mcb', 'mod', 'des', 'css', 'aof', 'mia', 'ede']) + expect(tested).toStrictEqual(['mod', 'des', 'css', 'aof', 'ede', 'mia', 'rcb', 'rde', 'mcb']) vi.resetAllMocks() }) test('peut faire une completude (mcp) le même jour que le dépôt (mdp) de la demande', () => { @@ -480,6 +480,6 @@ describe('etapesTypesPossibleACetteDateOuALaPlaceDeLEtape', function () { const tested = etapesTypesPossibleACetteDateOuALaPlaceDeLEtape(machine, etapes, null, toCaminoDate('2022-07-01')) .map(({ etapeTypeId }) => etapeTypeId) .filter(onlyUnique) - expect(tested).toStrictEqual(['mcp', 'mod', 'des', 'css', 'rde', 'mcb']) + expect(tested).toStrictEqual(['mod', 'des', 'css', 'rde', 'mcb', 'mcp']) }) }) diff --git a/packages/api/src/business/rules-demarches/arm/__snapshots__/oct.machine.test.ts.snap b/packages/api/src/business/rules-demarches/arm/__snapshots__/oct.machine.test.ts.snap index 2e04a2364..1aaba16d2 100644 --- a/packages/api/src/business/rules-demarches/arm/__snapshots__/oct.machine.test.ts.snap +++ b/packages/api/src/business/rules-demarches/arm/__snapshots__/oct.machine.test.ts.snap @@ -1,21 +1,21 @@ // Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html -exports[`vérifie l’arbre d’octroi d’ARM > ne peut pas créer 2 "mfr" 1`] = `"Error: cannot execute step: '{\\"etapeTypeId\\":\\"mfr\\",\\"etapeStatutId\\":\\"fai\\",\\"date\\":\\"2020-01-03\\"}' after '[\\"mfr_fai\\",\\"mdp_fai\\"]'. The event {\\"type\\":\\"FAIRE_DEMANDE\\",\\"mecanise\\":false,\\"franchissements\\":null} should be one of 'MODIFIER_DEMANDE,DESISTER_PAR_LE_DEMANDEUR,CLASSER_SANS_SUITE,PAYER_FRAIS_DE_DOSSIER'"`; +exports[`vérifie l’arbre d’octroi d’ARM > ne peut pas créer 2 "mfr" 1`] = `"Error: cannot execute step: '{\\"etapeTypeId\\":\\"mfr\\",\\"etapeStatutId\\":\\"fai\\",\\"date\\":\\"2020-01-03\\"}' after '[\\"mfr_fai\\",\\"mdp_fai\\"]'. The event {\\"type\\":\\"FAIRE_DEMANDE\\",\\"mecanise\\":false,\\"franchissements\\":null} should be one of 'CLASSER_SANS_SUITE,DESISTER_PAR_LE_DEMANDEUR,MODIFIER_DEMANDE,PAYER_FRAIS_DE_DOSSIER'"`; exports[`vérifie l’arbre d’octroi d’ARM > ne peut pas créer deux "des" 1`] = `"Error: cannot execute step: '{\\"etapeTypeId\\":\\"des\\",\\"etapeStatutId\\":\\"fai\\",\\"date\\":\\"2020-01-04\\"}' after '[\\"mfr_fai\\",\\"mdp_fai\\",\\"des_fai\\"]'. The event {\\"type\\":\\"DESISTER_PAR_LE_DEMANDEUR\\"} should be one of 'VALIDER_FRAIS_DE_DOSSIER'"`; exports[`vérifie l’arbre d’octroi d’ARM > ne peut pas créer une "css" après une "des" 1`] = `"Error: cannot execute step: '{\\"etapeTypeId\\":\\"css\\",\\"etapeStatutId\\":\\"fai\\",\\"date\\":\\"2020-01-05\\"}' after '[\\"mfr_fai\\",\\"mdp_fai\\",\\"des_fai\\"]'. The event {\\"type\\":\\"CLASSER_SANS_SUITE\\"} should be one of 'VALIDER_FRAIS_DE_DOSSIER'"`; -exports[`vérifie l’arbre d’octroi d’ARM > ne peut pas créer une "mno" après la "aca" si le titre n’est pas mécanisé 1`] = `"Error: cannot execute step: '{\\"etapeTypeId\\":\\"mnb\\",\\"etapeStatutId\\":\\"fai\\",\\"date\\":\\"2020-01-04\\"}' after '[\\"mfr_fai\\",\\"mdp_fai\\",\\"pfd_fai\\",\\"mcp_com\\",\\"vfd_fai\\",\\"mcr_fav\\",\\"eof_fai\\",\\"aof_fav\\",\\"sca_fai\\",\\"aca_fav\\"]'. The event {\\"type\\":\\"NOTIFIER_DEMANDEUR_AVIS_FAVORABLE_CARM\\"} should be one of 'SIGNER_AUTORISATION_DE_RECHERCHE_MINIERE,DESISTER_PAR_LE_DEMANDEUR,CLASSER_SANS_SUITE'"`; +exports[`vérifie l’arbre d’octroi d’ARM > ne peut pas créer une "mno" après la "aca" si le titre n’est pas mécanisé 1`] = `"Error: cannot execute step: '{\\"etapeTypeId\\":\\"mnb\\",\\"etapeStatutId\\":\\"fai\\",\\"date\\":\\"2020-01-04\\"}' after '[\\"mfr_fai\\",\\"mdp_fai\\",\\"pfd_fai\\",\\"mcp_com\\",\\"vfd_fai\\",\\"mcr_fav\\",\\"eof_fai\\",\\"aof_fav\\",\\"sca_fai\\",\\"aca_fav\\"]'. The event {\\"type\\":\\"NOTIFIER_DEMANDEUR_AVIS_FAVORABLE_CARM\\"} should be one of 'CLASSER_SANS_SUITE,DESISTER_PAR_LE_DEMANDEUR,SIGNER_AUTORISATION_DE_RECHERCHE_MINIERE'"`; -exports[`vérifie l’arbre d’octroi d’ARM > ne peut pas créer une étape "{ etapeTypeId: 'mcb', etapeStatutId: 'fai', date: '2020-01-01' }" si il n’existe pas d’autres étapes 1`] = `"Error: cannot execute step: '{\\"etapeTypeId\\":\\"mcb\\",\\"etapeStatutId\\":\\"fai\\",\\"date\\":\\"2020-01-01\\"}' after '[]'. The event {\\"type\\":\\"DEMANDER_COMPLEMENTS_RDE\\"} should be one of 'REFUSER_RDE,ACCEPTER_RDE,FAIRE_DEMANDE,PAYER_FRAIS_DE_DOSSIER,DEMANDER_MODIFICATION_DE_LA_DEMANDE,EXEMPTER_DAE'"`; +exports[`vérifie l’arbre d’octroi d’ARM > ne peut pas créer une étape "{ etapeTypeId: 'mcb', etapeStatutId: 'fai', date: '2020-01-01' }" si il n’existe pas d’autres étapes 1`] = `"Error: cannot execute step: '{\\"etapeTypeId\\":\\"mcb\\",\\"etapeStatutId\\":\\"fai\\",\\"date\\":\\"2020-01-01\\"}' after '[]'. The event {\\"type\\":\\"DEMANDER_COMPLEMENTS_RDE\\"} should be one of 'ACCEPTER_RDE,DEMANDER_MODIFICATION_DE_LA_DEMANDE,EXEMPTER_DAE,FAIRE_DEMANDE,PAYER_FRAIS_DE_DOSSIER,REFUSER_RDE'"`; -exports[`vérifie l’arbre d’octroi d’ARM > ne peut pas créer une étape "{ etapeTypeId: 'mcd', etapeStatutId: 'fai', date: '2020-01-01' }" si il n’existe pas d’autres étapes 1`] = `"Error: cannot execute step: '{\\"etapeTypeId\\":\\"mcd\\",\\"etapeStatutId\\":\\"fai\\",\\"date\\":\\"2020-01-01\\"}' after '[]'. The event {\\"type\\":\\"DEMANDER_COMPLEMENTS_DAE\\"} should be one of 'REFUSER_RDE,ACCEPTER_RDE,FAIRE_DEMANDE,PAYER_FRAIS_DE_DOSSIER,DEMANDER_MODIFICATION_DE_LA_DEMANDE,EXEMPTER_DAE'"`; +exports[`vérifie l’arbre d’octroi d’ARM > ne peut pas créer une étape "{ etapeTypeId: 'mcd', etapeStatutId: 'fai', date: '2020-01-01' }" si il n’existe pas d’autres étapes 1`] = `"Error: cannot execute step: '{\\"etapeTypeId\\":\\"mcd\\",\\"etapeStatutId\\":\\"fai\\",\\"date\\":\\"2020-01-01\\"}' after '[]'. The event {\\"type\\":\\"DEMANDER_COMPLEMENTS_DAE\\"} should be one of 'ACCEPTER_RDE,DEMANDER_MODIFICATION_DE_LA_DEMANDE,EXEMPTER_DAE,FAIRE_DEMANDE,PAYER_FRAIS_DE_DOSSIER,REFUSER_RDE'"`; exports[`vérifie l’arbre d’octroi d’ARM > ne peut pas créer une étape "mcp" sans "mdp" 1`] = `"Error: cannot execute step: '{\\"etapeTypeId\\":\\"mcp\\",\\"etapeStatutId\\":\\"com\\",\\"date\\":\\"2022-04-16\\"}' after '[\\"mfr_fai\\"]'. The event {\\"type\\":\\"ACCEPTER_COMPLETUDE\\"} should be one of 'DEPOSER_DEMANDE,PAYER_FRAIS_DE_DOSSIER'"`; -exports[`vérifie l’arbre d’octroi d’ARM > ne peut pas déplacer une étape "mdp" sans "mfr" 1`] = `"Error: cannot execute step: '{\\"etapeTypeId\\":\\"mdp\\",\\"etapeStatutId\\":\\"fai\\",\\"date\\":\\"2020-02-02\\"}' after '[]'. The event {\\"type\\":\\"DEPOSER_DEMANDE\\"} should be one of 'REFUSER_RDE,ACCEPTER_RDE,FAIRE_DEMANDE,PAYER_FRAIS_DE_DOSSIER,DEMANDER_MODIFICATION_DE_LA_DEMANDE,EXEMPTER_DAE'"`; +exports[`vérifie l’arbre d’octroi d’ARM > ne peut pas déplacer une étape "mdp" sans "mfr" 1`] = `"Error: cannot execute step: '{\\"etapeTypeId\\":\\"mdp\\",\\"etapeStatutId\\":\\"fai\\",\\"date\\":\\"2020-02-02\\"}' after '[]'. The event {\\"type\\":\\"DEPOSER_DEMANDE\\"} should be one of 'ACCEPTER_RDE,DEMANDER_MODIFICATION_DE_LA_DEMANDE,EXEMPTER_DAE,FAIRE_DEMANDE,PAYER_FRAIS_DE_DOSSIER,REFUSER_RDE'"`; -exports[`vérifie l’arbre d’octroi d’ARM > ne peut pas faire de mfr non mécanisée après une dae 1`] = `"Error: cannot execute step: '{\\"etapeTypeId\\":\\"mfr\\",\\"etapeStatutId\\":\\"fai\\",\\"date\\":\\"2021-02-25\\",\\"contenu\\":{\\"arm\\":{\\"mecanise\\":false}}}' after '[\\"dae_exe\\"]'. The event {\\"type\\":\\"FAIRE_DEMANDE\\",\\"mecanise\\":false,\\"franchissements\\":null} should be one of 'FAIRE_DEMANDE,PAYER_FRAIS_DE_DOSSIER,REFUSER_RDE,ACCEPTER_RDE'"`; +exports[`vérifie l’arbre d’octroi d’ARM > ne peut pas faire de mfr non mécanisée après une dae 1`] = `"Error: cannot execute step: '{\\"etapeTypeId\\":\\"mfr\\",\\"etapeStatutId\\":\\"fai\\",\\"date\\":\\"2021-02-25\\",\\"contenu\\":{\\"arm\\":{\\"mecanise\\":false}}}' after '[\\"dae_exe\\"]'. The event {\\"type\\":\\"FAIRE_DEMANDE\\",\\"mecanise\\":false,\\"franchissements\\":null} should be one of 'ACCEPTER_RDE,FAIRE_DEMANDE,PAYER_FRAIS_DE_DOSSIER,REFUSER_RDE'"`; -exports[`vérifie l’arbre d’octroi d’ARM > ne peut pas faire de mfr non mécanisée après une rde 1`] = `"Error: cannot execute step: '{\\"etapeTypeId\\":\\"mfr\\",\\"etapeStatutId\\":\\"fai\\",\\"date\\":\\"2021-02-25\\",\\"contenu\\":{\\"arm\\":{\\"mecanise\\":false}}}' after '[\\"rde_fav\\"]'. The event {\\"type\\":\\"FAIRE_DEMANDE\\",\\"mecanise\\":false,\\"franchissements\\":null} should be one of 'FAIRE_DEMANDE,PAYER_FRAIS_DE_DOSSIER,DEMANDER_MODIFICATION_DE_LA_DEMANDE,EXEMPTER_DAE'"`; +exports[`vérifie l’arbre d’octroi d’ARM > ne peut pas faire de mfr non mécanisée après une rde 1`] = `"Error: cannot execute step: '{\\"etapeTypeId\\":\\"mfr\\",\\"etapeStatutId\\":\\"fai\\",\\"date\\":\\"2021-02-25\\",\\"contenu\\":{\\"arm\\":{\\"mecanise\\":false}}}' after '[\\"rde_fav\\"]'. The event {\\"type\\":\\"FAIRE_DEMANDE\\",\\"mecanise\\":false,\\"franchissements\\":null} should be one of 'DEMANDER_MODIFICATION_DE_LA_DEMANDE,EXEMPTER_DAE,FAIRE_DEMANDE,PAYER_FRAIS_DE_DOSSIER'"`; diff --git a/packages/api/src/business/rules-demarches/arm/oct.machine.test.ts b/packages/api/src/business/rules-demarches/arm/oct.machine.test.ts index 9b156ae9d..afb4dbd0d 100644 --- a/packages/api/src/business/rules-demarches/arm/oct.machine.test.ts +++ b/packages/api/src/business/rules-demarches/arm/oct.machine.test.ts @@ -1,5 +1,4 @@ import { ArmOctMachine } from './oct.machine.js' -import { interpret } from 'xstate' import { interpretMachine, orderAndInterpretMachine as commonOrderAndInterpretMachine } from '../machine-test-helper.js' import { IContenu } from '../../../types.js' import { EtapeStatutId, ETAPES_STATUTS } from 'camino-common/src/static/etapesStatuts.js' @@ -15,14 +14,16 @@ const orderAndInterpretMachine = (etapes: readonly Etape[]) => { describe('vérifie l’arbre d’octroi d’ARM', () => { const armOctMachine = new ArmOctMachine() test('ne peut pas désister', () => { - const service = interpret(armOctMachine.machine) - const interpreter = service.start() + const service = orderAndInterpretMachine([]) - const state = interpreter.state - - // DESISTER_PAR_LE_DEMANDEUR est un événement potentiel mais pas faisable, dû à une condition - expect(state.nextEvents).toContain('DESISTER_PAR_LE_DEMANDEUR') - expect(state.can('DESISTER_PAR_LE_DEMANDEUR')).toBe(false) + expect(service).canOnlyTransitionTo({ machine: armOctMachine, date: toCaminoDate('2020-01-01') }, [ + 'ACCEPTER_RDE', + 'DEMANDER_MODIFICATION_DE_LA_DEMANDE', + 'EXEMPTER_DAE', + 'FAIRE_DEMANDE', + 'PAYER_FRAIS_DE_DOSSIER', + 'REFUSER_RDE', + ]) }) test('quelles sont mes prochaines étapes sur un titre mécanisé', () => { diff --git a/packages/api/src/business/rules-demarches/arm/oct.machine.ts b/packages/api/src/business/rules-demarches/arm/oct.machine.ts index bb0bccdd0..f2c695404 100644 --- a/packages/api/src/business/rules-demarches/arm/oct.machine.ts +++ b/packages/api/src/business/rules-demarches/arm/oct.machine.ts @@ -4,6 +4,7 @@ import { EtapesTypesEtapesStatuts } from 'camino-common/src/static/etapesTypesEt import { CaminoMachine } from '../machine-helper.js' import { CaminoCommonContext, DBEtat, Etape, tags } from '../machine-common.js' import { DemarchesStatutsIds } from 'camino-common/src/static/demarchesStatuts.js' +import { CaminoDate } from 'camino-common/src/date.js' type FaireDemandeEvent = { mecanise: boolean @@ -265,7 +266,7 @@ export class ArmOctMachine extends CaminoMachine<OctARMContext, XStateEvent> { throw new Error(`no event from ${JSON.stringify(etape)}`) } - toPotentialCaminoXStateEvent(event: XStateEvent['type']): XStateEvent[] { + toPotentialCaminoXStateEvent(event: XStateEvent['type'], _date: CaminoDate): XStateEvent[] { switch (event) { case 'FAIRE_DEMANDE': { return [ @@ -334,26 +335,26 @@ const fraisDeDossierComplementairesPayeOuExempte = (mecanisation: MecanisationIn const validationFraisApresDesistementOuClassementSansSuite = [ { target: 'demandeEnConstructionOuDeposeeOuEnInstructionMachine.pasRdeMachine.validationDesFraisDossierAFaire', - cond: (context: OctARMContext) => { + guard: ({ context }: { context: OctARMContext }) => { return !context.paiementFraisDossierValide }, }, { target: 'validationDuPaiementDesFraisDeDossierComplementairesAFaire', - cond: (context: OctARMContext) => { + guard: ({ context }: { context: OctARMContext }) => { return context.paiementFraisDossierValide && mustPayerFraisDossierComplementaire(context.mecanisation) }, }, { target: 'fini', - cond: (context: OctARMContext) => { + guard: ({ context }: { context: OctARMContext }) => { return context.paiementFraisDossierValide && fraisDeDossierComplementairesPayeOuExempte(context.mecanisation) }, }, ] -const actionMecanisation = assign<OctARMContext>({ - mecanisation: context => { +const actionMecanisation = assign<OctARMContext, any, any, any, any>({ + mecanisation: ({ context }) => { if (isMecanise(context.mecanisation)) { return context.mecanisation } @@ -366,8 +367,8 @@ const actionMecanisation = assign<OctARMContext>({ }, }) -const actionAccepterOuRefuserRDE = assign<OctARMContext, AccepterRDE | RefuserRDE>({ - mecanisation: (context, event) => { +const actionAccepterOuRefuserRDE = assign<OctARMContext, AccepterRDE | RefuserRDE, any, any, any>({ + mecanisation: ({ context, event }) => { if (event.franchissements === null || event.franchissements < 1) { throw new Error('cas impossible') } @@ -386,8 +387,8 @@ const actionAccepterOuRefuserRDE = assign<OctARMContext, AccepterRDE | RefuserRD }, }) -const actionRecevoirComplementsRde = assign<OctARMContext, RecevoirComplementsRde>({ - mecanisation: (context, event) => { +const actionRecevoirComplementsRde = assign<OctARMContext, RecevoirComplementsRde, any, any, any>({ + mecanisation: ({ context, event }) => { if (event.franchissements === null) { throw new Error('cas impossible') } @@ -406,8 +407,8 @@ const actionRecevoirComplementsRde = assign<OctARMContext, RecevoirComplementsRd }, }) -const armOctMachine = createMachine<OctARMContext, XStateEvent>({ - predictableActionArguments: true, +const armOctMachine = createMachine({ + types: {} as { context: OctARMContext; events: XStateEvent }, id: 'oct', initial: 'demandeEnConstructionOuDeposeeOuEnInstructionMachine', context: { @@ -420,20 +421,20 @@ const armOctMachine = createMachine<OctARMContext, XStateEvent>({ on: { MODIFIER_DEMANDE: { actions: () => ({}), - cond: context => context.demarcheStatut === DemarchesStatutsIds.EnInstruction && context.visibilite === 'confidentielle', + guard: ({ context }) => context.demarcheStatut === DemarchesStatutsIds.EnInstruction && context.visibilite === 'confidentielle', }, DESISTER_PAR_LE_DEMANDEUR: { - target: 'desistementDuDemandeurFait', - cond: context => context.demarcheStatut === DemarchesStatutsIds.EnInstruction, - actions: assign<OctARMContext, { type: 'DESISTER_PAR_LE_DEMANDEUR' }>({ + target: '.desistementDuDemandeurFait', + guard: ({ context }) => context.demarcheStatut === DemarchesStatutsIds.EnInstruction, + actions: assign({ demarcheStatut: DemarchesStatutsIds.Desiste, visibilite: 'publique', }), }, CLASSER_SANS_SUITE: { - target: 'decisionDeClassementSansSuiteFait', - cond: context => context.demarcheStatut === DemarchesStatutsIds.EnInstruction, - actions: assign<OctARMContext, { type: 'CLASSER_SANS_SUITE' }>({ + target: '.decisionDeClassementSansSuiteFait', + guard: ({ context }) => context.demarcheStatut === DemarchesStatutsIds.EnInstruction, + actions: assign({ demarcheStatut: DemarchesStatutsIds.ClasseSansSuite, visibilite: 'publique', }), @@ -459,8 +460,8 @@ const armOctMachine = createMachine<OctARMContext, XStateEvent>({ on: { FAIRE_DEMANDE: { target: 'demandeADeposer', - actions: assign<OctARMContext, FaireDemandeEvent>({ - mecanisation: (context, event) => { + actions: assign({ + mecanisation: ({ context, event }): MecanisationInconnu => { if (isMecanise(context.mecanisation) && context.mecanisation.franchissementCoursEau > 0 && (event.franchissements === null || event.franchissements === 0)) { throw new Error("pas le droit d'avoir pas de franchissements alors qu'une rde a été faite") } else { @@ -474,7 +475,7 @@ const armOctMachine = createMachine<OctARMContext, XStateEvent>({ } }, }), - cond: (context, event) => { + guard: ({ context, event }) => { if (isMecanise(context.mecanisation) && !event.mecanise) { return false } @@ -492,7 +493,7 @@ const armOctMachine = createMachine<OctARMContext, XStateEvent>({ on: { DEPOSER_DEMANDE: { target: 'demandeDeposee', - actions: assign<OctARMContext, { type: 'DEPOSER_DEMANDE' }>({ + actions: assign({ demarcheStatut: DemarchesStatutsIds.EnInstruction, }), }, @@ -522,7 +523,7 @@ const armOctMachine = createMachine<OctARMContext, XStateEvent>({ decisionAutoriteEnvironnementaleAFaire: { always: { target: 'decisionAutoriteEnvironnementaleExemptee', - cond: context => isNonMecanise(context.mecanisation), + guard: ({ context }) => isNonMecanise(context.mecanisation), }, on: { DEMANDER_MODIFICATION_DE_LA_DEMANDE: { @@ -535,7 +536,7 @@ const armOctMachine = createMachine<OctARMContext, XStateEvent>({ }, DEMANDER_COMPLEMENTS_DAE: { target: 'recevoirComplementDAEAFaire', - cond: context => context.demarcheStatut !== DemarchesStatutsIds.EnConstruction, + guard: ({ context }) => context.demarcheStatut !== DemarchesStatutsIds.EnConstruction, }, }, }, @@ -590,18 +591,19 @@ const armOctMachine = createMachine<OctARMContext, XStateEvent>({ VALIDER_FRAIS_DE_DOSSIER: [ { target: 'recevabiliteDeLaDemandeAFaire', - actions: assign<OctARMContext, { type: 'VALIDER_FRAIS_DE_DOSSIER' }>({ + actions: assign({ paiementFraisDossierValide: true, }), - cond: context => context.demarcheStatut !== DemarchesStatutsIds.Desiste && context.demarcheStatut !== DemarchesStatutsIds.ClasseSansSuite, + guard: ({ context }) => context.demarcheStatut !== DemarchesStatutsIds.Desiste && context.demarcheStatut !== DemarchesStatutsIds.ClasseSansSuite, }, { target: '#fini', - cond: context => (context.demarcheStatut === DemarchesStatutsIds.Desiste || context.demarcheStatut === DemarchesStatutsIds.ClasseSansSuite) && isNonMecanise(context.mecanisation), + guard: ({ context }) => + (context.demarcheStatut === DemarchesStatutsIds.Desiste || context.demarcheStatut === DemarchesStatutsIds.ClasseSansSuite) && isNonMecanise(context.mecanisation), }, { target: '#validationDuPaiementDesFraisDeDossierComplementairesAFaire', - cond: context => + guard: ({ context }) => (context.demarcheStatut === DemarchesStatutsIds.Desiste || context.demarcheStatut === DemarchesStatutsIds.ClasseSansSuite) && mustPayerFraisDossierComplementaire(context.mecanisation), }, @@ -653,7 +655,7 @@ const armOctMachine = createMachine<OctARMContext, XStateEvent>({ on: { DEMANDER_INFORMATION_AVIS_ONF: 'receptionInformationAvisONFAFaire', }, - entry: assign<OctARMContext>({ expertiseONFFaite: true }), + entry: assign({ expertiseONFFaite: true }), }, receptionInformationAvisONFAFaire: { on: { @@ -690,7 +692,7 @@ const armOctMachine = createMachine<OctARMContext, XStateEvent>({ on: { RENDRE_AVIS_ONF: { target: 'avisONFRendu', - cond: context => context.expertiseONFFaite, + guard: ({ context }) => context.expertiseONFFaite, }, }, }, @@ -708,17 +710,17 @@ const armOctMachine = createMachine<OctARMContext, XStateEvent>({ declarationLoiSurLEauAFaire: { always: { target: 'declarationLoiSurLEauExemptee', - cond: context => isNonMecanise(context.mecanisation) || (isMecanise(context.mecanisation) && !context.mecanisation.franchissementCoursEau) || isInconnu(context.mecanisation), + guard: ({ context }) => isNonMecanise(context.mecanisation) || (isMecanise(context.mecanisation) && !context.mecanisation.franchissementCoursEau) || isInconnu(context.mecanisation), }, on: { REFUSER_RDE: { target: 'declarationLoiSurLEauFaite', - cond: (_context, event) => (event.franchissements ?? 0) > 0, + guard: ({ event }) => (event.franchissements ?? 0) > 0, actions: actionAccepterOuRefuserRDE, }, ACCEPTER_RDE: { target: 'declarationLoiSurLEauFaite', - cond: (_context, event) => (event.franchissements ?? 0) > 0, + guard: ({ event }) => (event.franchissements ?? 0) > 0, actions: actionAccepterOuRefuserRDE, }, DEMANDER_COMPLEMENTS_RDE: 'receptionDeComplementsAFaire', @@ -732,12 +734,12 @@ const armOctMachine = createMachine<OctARMContext, XStateEvent>({ }, REFUSER_RDE: { target: 'declarationLoiSurLEauFaite', - cond: context => isMecanise(context.mecanisation) && context.mecanisation.franchissementCoursEau > 0, + guard: ({ context }) => isMecanise(context.mecanisation) && context.mecanisation.franchissementCoursEau > 0, actions: actionAccepterOuRefuserRDE, }, ACCEPTER_RDE: { target: 'declarationLoiSurLEauFaite', - cond: context => isMecanise(context.mecanisation) && context.mecanisation.franchissementCoursEau > 0, + guard: ({ context }) => isMecanise(context.mecanisation) && context.mecanisation.franchissementCoursEau > 0, actions: actionAccepterOuRefuserRDE, }, DEMANDER_COMPLEMENTS_RDE: 'receptionDeComplementsAFaire', @@ -746,21 +748,21 @@ const armOctMachine = createMachine<OctARMContext, XStateEvent>({ declarationLoiSurLEauExemptee: { always: { target: 'declarationLoiSurLEauAFaire', - cond: context => isMecanise(context.mecanisation) && context.mecanisation.franchissementCoursEau > 0, + guard: ({ context }) => isMecanise(context.mecanisation) && context.mecanisation.franchissementCoursEau > 0, }, on: { DEMANDER_COMPLEMENTS_RDE: { target: 'receptionDeComplementsAFaire', - cond: context => (context.demarcheStatut === DemarchesStatutsIds.Depose || context.demarcheStatut === DemarchesStatutsIds.EnInstruction) && isMecanise(context.mecanisation), + guard: ({ context }) => (context.demarcheStatut === DemarchesStatutsIds.Depose || context.demarcheStatut === DemarchesStatutsIds.EnInstruction) && isMecanise(context.mecanisation), }, REFUSER_RDE: { target: 'declarationLoiSurLEauFaite', - cond: (context, event) => (isInconnu(context.mecanisation) || isMecanise(context.mecanisation)) && (event.franchissements ?? 0) > 0, + guard: ({ context, event }) => (isInconnu(context.mecanisation) || isMecanise(context.mecanisation)) && (event.franchissements ?? 0) > 0, actions: actionAccepterOuRefuserRDE, }, ACCEPTER_RDE: { target: 'declarationLoiSurLEauFaite', - cond: (context, event) => (isInconnu(context.mecanisation) || isMecanise(context.mecanisation)) && (event.franchissements ?? 0) > 0, + guard: ({ context, event }) => (isInconnu(context.mecanisation) || isMecanise(context.mecanisation)) && (event.franchissements ?? 0) > 0, actions: actionAccepterOuRefuserRDE, }, }, @@ -775,7 +777,7 @@ const armOctMachine = createMachine<OctARMContext, XStateEvent>({ on: { FAIRE_SAISINE_CARM: { target: 'avisCommissionAutorisationDeRecherchesMinieresAFaire', - actions: assign<OctARMContext, { type: 'FAIRE_SAISINE_CARM' }>({ + actions: assign({ visibilite: 'publique', }), }, @@ -784,16 +786,16 @@ const armOctMachine = createMachine<OctARMContext, XStateEvent>({ 'demandeEnConstructionOuDeposeeOuEnInstructionMachine.declarationLoiSurLEauMachine.receptionDeComplementsAFaire', 'demandeEnConstructionOuDeposeeOuEnInstructionMachine.pasRdeMachine.avisONFRendu', ], - cond: context => isMecanise(context.mecanisation) && !context.mecanisation.franchissementCoursEau, + guard: ({ context }) => isMecanise(context.mecanisation) && !context.mecanisation.franchissementCoursEau, }, REFUSER_RDE: { target: 'saisineCommissionAutorisationsDeRecherchesMinieresAFaire', - cond: (context, event) => isMecanise(context.mecanisation) && !context.mecanisation.franchissementCoursEau && (event.franchissements ?? 0) > 0, + guard: ({ context, event }) => isMecanise(context.mecanisation) && !context.mecanisation.franchissementCoursEau && (event.franchissements ?? 0) > 0, actions: actionAccepterOuRefuserRDE, }, ACCEPTER_RDE: { target: 'saisineCommissionAutorisationsDeRecherchesMinieresAFaire', - cond: (context, event) => isMecanise(context.mecanisation) && !context.mecanisation.franchissementCoursEau && (event.franchissements ?? 0) > 0, + guard: ({ context, event }) => isMecanise(context.mecanisation) && !context.mecanisation.franchissementCoursEau && (event.franchissements ?? 0) > 0, actions: actionAccepterOuRefuserRDE, }, }, @@ -803,16 +805,16 @@ const armOctMachine = createMachine<OctARMContext, XStateEvent>({ RENDRE_AVIS_FAVORABLE_CARM: [ { target: 'signatureAutorisationDeRechercheMiniereAFaire', - cond: context => isNonMecanise(context.mecanisation), + guard: ({ context }) => isNonMecanise(context.mecanisation), }, { target: 'notificationDuDemandeurFraisDeDossierComplementairesAFaire', - cond: context => isMecanise(context.mecanisation), + guard: ({ context }) => isMecanise(context.mecanisation), }, ], RENDRE_AVIS_DEFAVORABLE_CARM: { target: 'notificationDuDemandeurAvisDefavorableCARMAFaire', - actions: assign<OctARMContext, { type: 'RENDRE_AVIS_DEFAVORABLE_CARM' }>({ + actions: assign({ demarcheStatut: DemarchesStatutsIds.Rejete, }), }, @@ -858,8 +860,8 @@ const armOctMachine = createMachine<OctARMContext, XStateEvent>({ VALIDER_PAIEMENT_FRAIS_DE_DOSSIER_COMPLEMENTAIRES: [ { target: 'signatureAutorisationDeRechercheMiniereAFaire', - actions: assign<OctARMContext, { type: 'VALIDER_PAIEMENT_FRAIS_DE_DOSSIER_COMPLEMENTAIRES' }>({ - mecanisation: context => { + actions: assign({ + mecanisation: ({ context }) => { if (!isMecanise(context.mecanisation)) { throw new Error('cas impossible') } @@ -870,12 +872,12 @@ const armOctMachine = createMachine<OctARMContext, XStateEvent>({ } }, }), - cond: context => context.demarcheStatut !== DemarchesStatutsIds.Desiste && context.demarcheStatut !== DemarchesStatutsIds.ClasseSansSuite, + guard: ({ context }) => context.demarcheStatut !== DemarchesStatutsIds.Desiste && context.demarcheStatut !== DemarchesStatutsIds.ClasseSansSuite, }, { target: '#fini', - actions: assign<OctARMContext, { type: 'VALIDER_PAIEMENT_FRAIS_DE_DOSSIER_COMPLEMENTAIRES' }>({ - mecanisation: context => { + actions: assign({ + mecanisation: ({ context }) => { if (!isMecanise(context.mecanisation)) { throw new Error('cas impossible') } @@ -886,7 +888,7 @@ const armOctMachine = createMachine<OctARMContext, XStateEvent>({ } }, }), - cond: context => context.demarcheStatut === DemarchesStatutsIds.Desiste || context.demarcheStatut === DemarchesStatutsIds.ClasseSansSuite, + guard: ({ context }) => context.demarcheStatut === DemarchesStatutsIds.Desiste || context.demarcheStatut === DemarchesStatutsIds.ClasseSansSuite, }, ], }, @@ -896,17 +898,17 @@ const armOctMachine = createMachine<OctARMContext, XStateEvent>({ SIGNER_AUTORISATION_DE_RECHERCHE_MINIERE: [ { target: 'avenantARMAFaire', - actions: assign<OctARMContext, { type: 'SIGNER_AUTORISATION_DE_RECHERCHE_MINIERE' }>({ + actions: assign({ demarcheStatut: DemarchesStatutsIds.Accepte, }), - cond: context => isMecanise(context.mecanisation), + guard: ({ context }) => isMecanise(context.mecanisation), }, { target: 'notificationSignatureARMAFaire', - actions: assign<OctARMContext, { type: 'SIGNER_AUTORISATION_DE_RECHERCHE_MINIERE' }>({ + actions: assign({ demarcheStatut: DemarchesStatutsIds.Accepte, }), - cond: context => isNonMecanise(context.mecanisation), + guard: ({ context }) => isNonMecanise(context.mecanisation), }, ], }, diff --git a/packages/api/src/business/rules-demarches/arm/ren-pro.machine.test.ts b/packages/api/src/business/rules-demarches/arm/ren-pro.machine.test.ts index 81302a931..8c6f1a57e 100644 --- a/packages/api/src/business/rules-demarches/arm/ren-pro.machine.test.ts +++ b/packages/api/src/business/rules-demarches/arm/ren-pro.machine.test.ts @@ -26,7 +26,7 @@ describe('vérifie l’arbre de renonciation et de prolongation d’ARM', () => { ...ETES.modificationDeLaDemande.FAIT, date: toCaminoDate('2020-06-30') }, ]) ).toThrowErrorMatchingInlineSnapshot( - '"Error: cannot execute step: \'{\\"etapeTypeId\\":\\"mod\\",\\"etapeStatutId\\":\\"fai\\",\\"date\\":\\"2020-06-30\\"}\' after \'[\\"mfr_fai\\",\\"mdp_fai\\",\\"mcr_fav\\"]\'. The event {\\"type\\":\\"RECEVOIR_MODIFICATION_DE_LA_DEMANDE\\"} should be one of \'DEMANDER_INFORMATION_EXPERTISE_ONF,FAIRE_EXPERTISE_ONF,DESISTER_PAR_LE_DEMANDEUR,CLASSER_SANS_SUITE\'"' + '"Error: cannot execute step: \'{\\"etapeTypeId\\":\\"mod\\",\\"etapeStatutId\\":\\"fai\\",\\"date\\":\\"2020-06-30\\"}\' after \'[\\"mfr_fai\\",\\"mdp_fai\\",\\"mcr_fav\\"]\'. The event {\\"type\\":\\"RECEVOIR_MODIFICATION_DE_LA_DEMANDE\\"} should be one of \'CLASSER_SANS_SUITE,DEMANDER_INFORMATION_EXPERTISE_ONF,DESISTER_PAR_LE_DEMANDEUR,FAIRE_EXPERTISE_ONF\'"' ) }) @@ -40,7 +40,7 @@ describe('vérifie l’arbre de renonciation et de prolongation d’ARM', () => // {...ETES.recevabiliteDeLaDemande.DEFAVORABLE, date: toCaminoDate('2020-05-30') }, ]) ).toThrowErrorMatchingInlineSnapshot( - '"Error: cannot execute step: \'{\\"etapeTypeId\\":\\"mca\\",\\"etapeStatutId\\":\\"fai\\",\\"date\\":\\"2020-06-30\\"}\' after \'[\\"mfr_fai\\",\\"mdp_fai\\",\\"mca_fai\\"]\'. The event {\\"type\\":\\"DEMANDER_COMPLEMENTS_POUR_RECEVABILITE\\"} should be one of \'RECEVOIR_COMPLEMENTS_POUR_RECEVABILITE,FAIRE_RECEVABILITE_DEMANDE_FAVORABLE,FAIRE_RECEVABILITE_DEMANDE_DEFAVORABLE,DESISTER_PAR_LE_DEMANDEUR\'"' + '"Error: cannot execute step: \'{\\"etapeTypeId\\":\\"mca\\",\\"etapeStatutId\\":\\"fai\\",\\"date\\":\\"2020-06-30\\"}\' after \'[\\"mfr_fai\\",\\"mdp_fai\\",\\"mca_fai\\"]\'. The event {\\"type\\":\\"DEMANDER_COMPLEMENTS_POUR_RECEVABILITE\\"} should be one of \'DESISTER_PAR_LE_DEMANDEUR,FAIRE_RECEVABILITE_DEMANDE_DEFAVORABLE,FAIRE_RECEVABILITE_DEMANDE_FAVORABLE,RECEVOIR_COMPLEMENTS_POUR_RECEVABILITE\'"' ) }) diff --git a/packages/api/src/business/rules-demarches/arm/ren-pro.machine.ts b/packages/api/src/business/rules-demarches/arm/ren-pro.machine.ts index af2698890..44cce6caf 100644 --- a/packages/api/src/business/rules-demarches/arm/ren-pro.machine.ts +++ b/packages/api/src/business/rules-demarches/arm/ren-pro.machine.ts @@ -88,8 +88,8 @@ export class ArmRenProMachine extends CaminoMachine<CaminoCommonContext, XStateE } } -const armRenProMachine = createMachine<CaminoCommonContext, XStateEvent>({ - predictableActionArguments: true, +const armRenProMachine = createMachine({ + types: {} as { context: CaminoCommonContext; events: XStateEvent }, id: 'renpro', initial: 'demandeAFaire', context: { @@ -98,17 +98,17 @@ const armRenProMachine = createMachine<CaminoCommonContext, XStateEvent>({ }, on: { DESISTER_PAR_LE_DEMANDEUR: { - target: 'done', - cond: context => context.demarcheStatut === DemarchesStatutsIds.EnInstruction || context.demarcheStatut === DemarchesStatutsIds.Depose, - actions: assign<CaminoCommonContext, { type: 'DESISTER_PAR_LE_DEMANDEUR' }>({ + target: '.done', + guard: ({ context }) => context.demarcheStatut === DemarchesStatutsIds.EnInstruction || context.demarcheStatut === DemarchesStatutsIds.Depose, + actions: assign({ demarcheStatut: DemarchesStatutsIds.Desiste, visibilite: 'publique', }), }, CLASSER_SANS_SUITE: { - target: 'notificationDuDemandeurApresClassementSansSuiteAFaire', - cond: context => context.demarcheStatut === DemarchesStatutsIds.EnInstruction, - actions: assign<CaminoCommonContext, { type: 'CLASSER_SANS_SUITE' }>({ + target: '.notificationDuDemandeurApresClassementSansSuiteAFaire', + guard: ({ context }) => context.demarcheStatut === DemarchesStatutsIds.EnInstruction, + actions: assign({ demarcheStatut: DemarchesStatutsIds.ClasseSansSuite, visibilite: 'publique', }), @@ -124,7 +124,7 @@ const armRenProMachine = createMachine<CaminoCommonContext, XStateEvent>({ on: { DEPOSER_DEMANDE: { target: 'recevabiliteDeLaDemandeAFaire', - actions: assign<CaminoCommonContext, { type: 'DEPOSER_DEMANDE' }>({ + actions: assign({ demarcheStatut: DemarchesStatutsIds.Depose, }), }, @@ -135,7 +135,7 @@ const armRenProMachine = createMachine<CaminoCommonContext, XStateEvent>({ DEMANDER_COMPLEMENTS_POUR_RECEVABILITE: 'complementsPourRecevabiliteAFaire', FAIRE_RECEVABILITE_DEMANDE_FAVORABLE: { target: 'expertiseONFAFaire', - actions: assign<CaminoCommonContext, { type: 'FAIRE_RECEVABILITE_DEMANDE_FAVORABLE' }>({ + actions: assign({ demarcheStatut: DemarchesStatutsIds.EnInstruction, visibilite: 'publique', }), @@ -148,7 +148,7 @@ const armRenProMachine = createMachine<CaminoCommonContext, XStateEvent>({ RECEVOIR_COMPLEMENTS_POUR_RECEVABILITE: 'recevabiliteDeLaDemandeAFaire', FAIRE_RECEVABILITE_DEMANDE_FAVORABLE: { target: 'expertiseONFAFaire', - actions: assign<CaminoCommonContext, { type: 'FAIRE_RECEVABILITE_DEMANDE_FAVORABLE' }>({ + actions: assign({ demarcheStatut: DemarchesStatutsIds.EnInstruction, visibilite: 'publique', }), @@ -178,13 +178,13 @@ const armRenProMachine = createMachine<CaminoCommonContext, XStateEvent>({ DEMANDER_INFORMATION_AVIS_ONF: 'receptionInformationAvisONFAFaire', RENDRE_AVIS_ONF_FAVORABLE: { target: 'avenantARMAFaire', - actions: assign<CaminoCommonContext, { type: 'RENDRE_AVIS_ONF_FAVORABLE' }>({ + actions: assign({ demarcheStatut: DemarchesStatutsIds.Accepte, }), }, RENDRE_AVIS_ONF_DEFAVORABLE: { target: 'notificationDuDemandeurApresAvisONFDefavorableAFaire', - actions: assign<CaminoCommonContext, { type: 'RENDRE_AVIS_ONF_DEFAVORABLE' }>({ + actions: assign({ demarcheStatut: DemarchesStatutsIds.Rejete, }), }, @@ -195,13 +195,13 @@ const armRenProMachine = createMachine<CaminoCommonContext, XStateEvent>({ RECEVOIR_INFORMATION_AVIS_ONF: 'demandeAvisONFAFaire', RENDRE_AVIS_ONF_FAVORABLE: { target: 'avenantARMAFaire', - actions: assign<CaminoCommonContext, { type: 'RENDRE_AVIS_ONF_FAVORABLE' }>({ + actions: assign({ demarcheStatut: DemarchesStatutsIds.Accepte, }), }, RENDRE_AVIS_ONF_DEFAVORABLE: { target: 'notificationDuDemandeurApresAvisONFDefavorableAFaire', - actions: assign<CaminoCommonContext, { type: 'RENDRE_AVIS_ONF_DEFAVORABLE' }>({ + actions: assign({ demarcheStatut: DemarchesStatutsIds.Rejete, }), }, diff --git a/packages/api/src/business/rules-demarches/axm/__snapshots__/oct.machine.test.ts.snap b/packages/api/src/business/rules-demarches/axm/__snapshots__/oct.machine.test.ts.snap index 1ac066b9c..f437dbfbf 100644 --- a/packages/api/src/business/rules-demarches/axm/__snapshots__/oct.machine.test.ts.snap +++ b/packages/api/src/business/rules-demarches/axm/__snapshots__/oct.machine.test.ts.snap @@ -1,3 +1,3 @@ // Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html -exports[`vérifie l’arbre d’octroi d’AXM > ne peut pas faire deux fois la même étape à la même date 1`] = `"Error: cannot execute step: '{\\"etapeTypeId\\":\\"mfr\\",\\"etapeStatutId\\":\\"fai\\",\\"date\\":\\"2022-04-01\\"}' after '[\\"mfr_fai\\"]'. The event {\\"type\\":\\"FAIRE_DEMANDE\\"} should be one of 'FAIRE_NOTE_INTERNE_SIGNALEE,FAIRE_DESISTEMENT_DEMANDEUR,RENDRE_DAE_REQUISE,RENDRE_DAE_EXEMPTEE,RENDRE_DECISION_DU_PROPRIETAIRE_DU_SOL_FAVORABLE,RENDRE_DECISION_DU_PROPRIETAIRE_DU_SOL_FAVORABLE_AVEC_RESERVE,RENDRE_DECISION_DU_PROPRIETAIRE_DU_SOL_DEFAVORABLE'"`; +exports[`vérifie l’arbre d’octroi d’AXM > ne peut pas faire deux fois la même étape à la même date 1`] = `"Error: cannot execute step: '{\\"etapeTypeId\\":\\"mfr\\",\\"etapeStatutId\\":\\"fai\\",\\"date\\":\\"2022-04-01\\"}' after '[\\"mfr_fai\\"]'. The event {\\"type\\":\\"FAIRE_DEMANDE\\"} should be one of 'FAIRE_DESISTEMENT_DEMANDEUR,FAIRE_NOTE_INTERNE_SIGNALEE,RENDRE_DAE_EXEMPTEE,RENDRE_DAE_REQUISE,RENDRE_DECISION_DU_PROPRIETAIRE_DU_SOL_DEFAVORABLE,RENDRE_DECISION_DU_PROPRIETAIRE_DU_SOL_FAVORABLE,RENDRE_DECISION_DU_PROPRIETAIRE_DU_SOL_FAVORABLE_AVEC_RESERVE'"`; diff --git a/packages/api/src/business/rules-demarches/axm/oct.machine.ts b/packages/api/src/business/rules-demarches/axm/oct.machine.ts index 491a1cc06..3dc310cd4 100644 --- a/packages/api/src/business/rules-demarches/axm/oct.machine.ts +++ b/packages/api/src/business/rules-demarches/axm/oct.machine.ts @@ -228,23 +228,32 @@ export class AxmOctMachine extends CaminoMachine<AxmContext, AXMOctXStateEvent> type SaisineDesServices = { faite: false } | { faite: true; date: CaminoDate } interface AxmContext extends CaminoCommonContext { demandeFaite: boolean + notificationDuDemandeurFaite: boolean + notificationDesCollectivitesLocalesFaite: boolean + publicationDecisionsRecueilActesAdministratifsFaite: boolean + publicationDansUnJournalLocalOuNationalFaite: boolean daeRequiseOuDemandeDeposee: boolean decisionDuProprietaireDuSolFavorableSansReserve: boolean saisineDesCollectivitesLocalesFaite: boolean saisineDesServices: SaisineDesServices } -const peutRendreAvisDREAL = (context: AxmContext, event: RendreAvisDreal): boolean => { +const peutRendreAvisDREAL = ({ context, event }: { context: AxmContext; event: RendreAvisDreal }): boolean => { return context.saisineDesServices.faite && daysBetween(dateAddMonths(context.saisineDesServices.date, 1), event.date) >= 0 } -const axmOctMachine = createMachine<AxmContext, AXMOctXStateEvent>({ - predictableActionArguments: true, +const axmOctMachine = createMachine({ + types: {} as { context: AxmContext; events: AXMOctXStateEvent }, + id: 'AXMOct', initial: 'demandeAFaireEtDecisionsARendre', context: { demarcheStatut: DemarchesStatutsIds.EnConstruction, demandeFaite: false, + notificationDuDemandeurFaite: false, + notificationDesCollectivitesLocalesFaite: false, + publicationDecisionsRecueilActesAdministratifsFaite: false, + publicationDansUnJournalLocalOuNationalFaite: false, decisionDuProprietaireDuSolFavorableSansReserve: false, saisineDesCollectivitesLocalesFaite: false, saisineDesServices: { faite: false }, @@ -253,16 +262,17 @@ const axmOctMachine = createMachine<AxmContext, AXMOctXStateEvent>({ }, on: { FAIRE_NOTE_INTERNE_SIGNALEE: { - actions: assign<AxmContext, { type: 'FAIRE_NOTE_INTERNE_SIGNALEE' }>({}), - cond: context => context.demandeFaite, + actions: assign({}), + guard: ({ context }) => context.demandeFaite, }, FAIRE_DESISTEMENT_DEMANDEUR: { - cond: context => context.demandeFaite && [DemarchesStatutsIds.EnConstruction, DemarchesStatutsIds.Depose, DemarchesStatutsIds.EnInstruction].includes(context.demarcheStatut), - target: 'desistementDuDemandeurRendu', + guard: ({ context }) => context.demandeFaite && [DemarchesStatutsIds.EnConstruction, DemarchesStatutsIds.Depose, DemarchesStatutsIds.EnInstruction].includes(context.demarcheStatut), + target: '.desistementDuDemandeurRendu', }, FAIRE_CLASSEMENT_SANS_SUITE: { - cond: context => context.daeRequiseOuDemandeDeposee && [DemarchesStatutsIds.EnConstruction, DemarchesStatutsIds.Depose, DemarchesStatutsIds.EnInstruction].includes(context.demarcheStatut), - target: 'classementSansSuiteRendu', + guard: ({ context }) => + context.daeRequiseOuDemandeDeposee && [DemarchesStatutsIds.EnConstruction, DemarchesStatutsIds.Depose, DemarchesStatutsIds.EnInstruction].includes(context.demarcheStatut), + target: '.classementSansSuiteRendu', }, }, states: { @@ -279,7 +289,7 @@ const axmOctMachine = createMachine<AxmContext, AXMOctXStateEvent>({ }, demandeFaite: { type: 'final', - entry: assign<AxmContext>({ demandeFaite: true }), + entry: assign({ demandeFaite: true }), }, }, }, @@ -290,7 +300,7 @@ const axmOctMachine = createMachine<AxmContext, AXMOctXStateEvent>({ on: { RENDRE_DAE_REQUISE: { target: 'demandeAModifier', - actions: assign<AxmContext, { type: 'RENDRE_DAE_REQUISE' }>({ + actions: assign({ daeRequiseOuDemandeDeposee: true, }), }, @@ -311,7 +321,7 @@ const axmOctMachine = createMachine<AxmContext, AXMOctXStateEvent>({ on: { RENDRE_DECISION_DU_PROPRIETAIRE_DU_SOL_FAVORABLE: { target: 'decisionRendue', - actions: assign<AxmContext, { type: 'RENDRE_DECISION_DU_PROPRIETAIRE_DU_SOL_FAVORABLE' }>({ + actions: assign({ decisionDuProprietaireDuSolFavorableSansReserve: true, }), }, @@ -333,7 +343,7 @@ const axmOctMachine = createMachine<AxmContext, AXMOctXStateEvent>({ on: { DEPOSER_DEMANDE: { target: 'recevabiliteDeLaDemandeAFaire', - actions: assign<AxmContext, { type: 'DEPOSER_DEMANDE' }>({ + actions: assign({ demarcheStatut: DemarchesStatutsIds.Depose, daeRequiseOuDemandeDeposee: true, }), @@ -346,7 +356,7 @@ const axmOctMachine = createMachine<AxmContext, AXMOctXStateEvent>({ DEMANDER_COMPLEMENTS_POUR_RECEVABILITE: 'complementsPourRecevabiliteAFaire', FAIRE_RECEVABILITE_DEMANDE_FAVORABLE: { target: 'saisinesAFairePuisRendreAvisDREAL', - actions: assign<AxmContext, { type: 'FAIRE_RECEVABILITE_DEMANDE_FAVORABLE' }>({ + actions: assign({ demarcheStatut: DemarchesStatutsIds.EnInstruction, visibilite: 'publique', }), @@ -354,7 +364,7 @@ const axmOctMachine = createMachine<AxmContext, AXMOctXStateEvent>({ FAIRE_RECEVABILITE_DEMANDE_DEFAVORABLE: 'modificationDeLaDemandeAFaire', RENDRE_DECISION_IMPLICITE_REJET: { target: 'decisionAnnulationParJugeAdministratifAFaire', - actions: assign<AxmContext, { type: 'RENDRE_DECISION_IMPLICITE_REJET' }>({ + actions: assign({ demarcheStatut: DemarchesStatutsIds.Rejete, visibilite: 'publique', }), @@ -366,7 +376,7 @@ const axmOctMachine = createMachine<AxmContext, AXMOctXStateEvent>({ RECEVOIR_COMPLEMENTS_POUR_RECEVABILITE: 'recevabiliteDeLaDemandeAFaire', FAIRE_RECEVABILITE_DEMANDE_FAVORABLE: { target: 'saisinesAFairePuisRendreAvisDREAL', - actions: assign<AxmContext, { type: 'FAIRE_RECEVABILITE_DEMANDE_FAVORABLE' }>({ + actions: assign({ demarcheStatut: DemarchesStatutsIds.EnInstruction, visibilite: 'publique', }), @@ -388,14 +398,14 @@ const axmOctMachine = createMachine<AxmContext, AXMOctXStateEvent>({ rendreAvisDrealPasEncorePossible: { always: { target: 'rendreAvisDrealAFaire', - cond: (context: AxmContext) => context.saisineDesServices.faite && context.saisineDesCollectivitesLocalesFaite && context.decisionDuProprietaireDuSolFavorableSansReserve, + guard: ({ context }) => context.saisineDesServices.faite && context.saisineDesCollectivitesLocalesFaite && context.decisionDuProprietaireDuSolFavorableSansReserve, }, }, rendreAvisDrealAFaire: { tags: [tags.responsable[ADMINISTRATION_IDS['DGTM - GUYANE']]], on: { RENDRE_AVIS_DREAL: { - cond: peutRendreAvisDREAL, + guard: peutRendreAvisDREAL, target: '#saisineOuAvisCommissionDepartementaleDesMinesARendre', }, }, @@ -424,8 +434,8 @@ const axmOctMachine = createMachine<AxmContext, AXMOctXStateEvent>({ on: { FAIRE_SAISINE_COLLECTIVITES_LOCALES: { target: 'avisDunMaireARendre', - cond: context => !context.saisineDesCollectivitesLocalesFaite, - actions: assign<AxmContext, { type: 'FAIRE_SAISINE_COLLECTIVITES_LOCALES' }>({ + guard: ({ context }) => !context.saisineDesCollectivitesLocalesFaite, + actions: assign({ saisineDesCollectivitesLocalesFaite: true, }), }, @@ -444,9 +454,9 @@ const axmOctMachine = createMachine<AxmContext, AXMOctXStateEvent>({ on: { FAIRE_SAISINE_DES_SERVICES: { target: 'avisDesServicesARendre', - cond: context => !context.saisineDesServices.faite, - actions: assign<AxmContext, FaireSaisineDesServices>({ - saisineDesServices: (context, event) => { + guard: ({ context }) => !context.saisineDesServices.faite, + actions: assign({ + saisineDesServices: ({ event }) => { return { faite: true, date: event.date, @@ -470,7 +480,7 @@ const axmOctMachine = createMachine<AxmContext, AXMOctXStateEvent>({ }, confirmationAccordProprietaireDuSolFait: { type: 'final', - entry: assign<AxmContext>({ + entry: assign({ decisionDuProprietaireDuSolFavorableSansReserve: true, }), }, @@ -613,7 +623,7 @@ const axmOctMachine = createMachine<AxmContext, AXMOctXStateEvent>({ tags: [tags.responsable[ADMINISTRATION_IDS['DGTM - GUYANE']]], on: { RENDRE_AVIS_DREAL: { - cond: peutRendreAvisDREAL, + guard: peutRendreAvisDREAL, target: 'saisineOuAvisCommissionDepartementaleDesMinesARendre', }, }, @@ -637,13 +647,13 @@ const axmOctMachine = createMachine<AxmContext, AXMOctXStateEvent>({ FAIRE_SAISINE_AUTORITE_SIGNATAIRE: 'decisionAdministrationARendre', RENDRE_DECISION_ADMINISTRATION_ACCEPTE: { target: 'decisionAdministrationRendue', - actions: assign<AxmContext, { type: 'RENDRE_DECISION_ADMINISTRATION_ACCEPTE' }>({ + actions: assign({ demarcheStatut: DemarchesStatutsIds.Accepte, }), }, RENDRE_DECISION_ADMINISTRATION_REJETE: { target: 'decisionAdministrationRendue', - actions: assign<AxmContext, { type: 'RENDRE_DECISION_ADMINISTRATION_REJETE' }>({ + actions: assign({ demarcheStatut: DemarchesStatutsIds.Rejete, }), }, @@ -653,13 +663,13 @@ const axmOctMachine = createMachine<AxmContext, AXMOctXStateEvent>({ on: { RENDRE_DECISION_ADMINISTRATION_ACCEPTE: { target: 'decisionAdministrationRendue', - actions: assign<AxmContext, { type: 'RENDRE_DECISION_ADMINISTRATION_ACCEPTE' }>({ + actions: assign({ demarcheStatut: DemarchesStatutsIds.Accepte, }), }, RENDRE_DECISION_ADMINISTRATION_REJETE: { target: 'decisionAdministrationRendue', - actions: assign<AxmContext, { type: 'RENDRE_DECISION_ADMINISTRATION_REJETE' }>({ + actions: assign({ demarcheStatut: DemarchesStatutsIds.Rejete, }), }, @@ -671,10 +681,10 @@ const axmOctMachine = createMachine<AxmContext, AXMOctXStateEvent>({ RENDRE_DECISION_ANNULATION_PAR_JUGE_ADMINISTRATIF: { target: 'decisionAnnulationParJugeAdministratifRendu', }, - NOTIFIER_DEMANDEUR: { target: 'publicationsEtNotificationsMachine' }, - PUBLIER_DECISIONS_RECUEIL_ACTES_ADMINISTRATIFS: 'publicationsEtNotificationsMachine', - PUBLIER_DANS_UN_JOURNAL_LOCAL_OU_NATIONAL: 'publicationsEtNotificationsMachine', - NOTIFIER_COLLECTIVITES_LOCALES: 'publicationsEtNotificationsMachine', + NOTIFIER_DEMANDEUR: { target: 'publicationsEtNotificationsMachine', actions: assign({ notificationDuDemandeurFaite: true }) }, + PUBLIER_DECISIONS_RECUEIL_ACTES_ADMINISTRATIFS: { target: 'publicationsEtNotificationsMachine', actions: assign({ publicationDecisionsRecueilActesAdministratifsFaite: true }) }, + PUBLIER_DANS_UN_JOURNAL_LOCAL_OU_NATIONAL: { target: 'publicationsEtNotificationsMachine', actions: assign({ publicationDansUnJournalLocalOuNationalFaite: true }) }, + NOTIFIER_COLLECTIVITES_LOCALES: { target: 'publicationsEtNotificationsMachine', actions: assign({ notificationDesCollectivitesLocalesFaite: true }) }, }, }, publicationsEtNotificationsMachine: { @@ -686,11 +696,11 @@ const axmOctMachine = createMachine<AxmContext, AXMOctXStateEvent>({ notificationDuDemandeurAFaire: { always: { target: 'notificationDuDemandeurFaite', - cond: (_context, _event, meta) => { - return meta.state.history?.event.type === 'NOTIFIER_DEMANDEUR' + guard: ({ context }) => { + return context.notificationDuDemandeurFaite }, }, - on: { NOTIFIER_DEMANDEUR: 'notificationDuDemandeurFaite' }, + on: { NOTIFIER_DEMANDEUR: { target: 'notificationDuDemandeurFaite', actions: assign({ notificationDuDemandeurFaite: true }) } }, }, notificationDuDemandeurFaite: { type: 'final' }, }, @@ -701,12 +711,15 @@ const axmOctMachine = createMachine<AxmContext, AXMOctXStateEvent>({ publicationDecisionsRecueilActesAdministratifsAFaire: { always: { target: 'publicationDecisionsRecueilActesAdministratifsFaite', - cond: (_context, _event, meta) => { - return meta.state.history?.event.type === 'PUBLIER_DECISIONS_RECUEIL_ACTES_ADMINISTRATIFS' + guard: ({ context }) => { + return context.publicationDecisionsRecueilActesAdministratifsFaite }, }, on: { - PUBLIER_DECISIONS_RECUEIL_ACTES_ADMINISTRATIFS: 'publicationDecisionsRecueilActesAdministratifsFaite', + PUBLIER_DECISIONS_RECUEIL_ACTES_ADMINISTRATIFS: { + target: 'publicationDecisionsRecueilActesAdministratifsFaite', + actions: assign({ publicationDecisionsRecueilActesAdministratifsFaite: true }), + }, }, }, publicationDecisionsRecueilActesAdministratifsFaite: { @@ -720,12 +733,12 @@ const axmOctMachine = createMachine<AxmContext, AXMOctXStateEvent>({ publicationDansUnJournalLocalOuNationalAFaire: { always: { target: 'publicationDansUnJournalLocalOuNationalFaite', - cond: (_context, _event, meta) => { - return meta.state.history?.event.type === 'PUBLIER_DANS_UN_JOURNAL_LOCAL_OU_NATIONAL' + guard: ({ context }) => { + return context.publicationDansUnJournalLocalOuNationalFaite }, }, on: { - PUBLIER_DANS_UN_JOURNAL_LOCAL_OU_NATIONAL: 'publicationDansUnJournalLocalOuNationalFaite', + PUBLIER_DANS_UN_JOURNAL_LOCAL_OU_NATIONAL: { target: 'publicationDansUnJournalLocalOuNationalFaite', actions: assign({ publicationDansUnJournalLocalOuNationalFaite: true }) }, }, }, publicationDansUnJournalLocalOuNationalFaite: { type: 'final' }, @@ -737,12 +750,12 @@ const axmOctMachine = createMachine<AxmContext, AXMOctXStateEvent>({ notificationDesCollectivitesLocalesAFaire: { always: { target: 'notificationDesCollectivitesLocalesFaite', - cond: (_context, _event, meta) => { - return meta.state.history?.event.type === 'NOTIFIER_COLLECTIVITES_LOCALES' + guard: ({ context }) => { + return context.notificationDesCollectivitesLocalesFaite }, }, on: { - NOTIFIER_COLLECTIVITES_LOCALES: 'notificationDesCollectivitesLocalesFaite', + NOTIFIER_COLLECTIVITES_LOCALES: { target: 'notificationDesCollectivitesLocalesFaite', actions: assign({ notificationDesCollectivitesLocalesFaite: true }) }, }, }, notificationDesCollectivitesLocalesFaite: { type: 'final' }, @@ -765,18 +778,18 @@ const axmOctMachine = createMachine<AxmContext, AXMOctXStateEvent>({ decisionAbrogationFaite: { type: 'final' }, decisionAnnulationParJugeAdministratifRendu: { type: 'final', - entry: assign<AxmContext>({ demarcheStatut: DemarchesStatutsIds.Rejete }), + entry: assign({ demarcheStatut: DemarchesStatutsIds.Rejete }), }, desistementDuDemandeurRendu: { type: 'final', - entry: assign<AxmContext>({ + entry: assign({ demarcheStatut: DemarchesStatutsIds.Desiste, visibilite: 'publique', }), }, classementSansSuiteRendu: { type: 'final', - entry: assign<AxmContext>({ + entry: assign({ demarcheStatut: DemarchesStatutsIds.ClasseSansSuite, visibilite: 'publique', }), diff --git a/packages/api/src/business/rules-demarches/axm/pro.machine.ts b/packages/api/src/business/rules-demarches/axm/pro.machine.ts index 6ea205b87..e890c7232 100644 --- a/packages/api/src/business/rules-demarches/axm/pro.machine.ts +++ b/packages/api/src/business/rules-demarches/axm/pro.machine.ts @@ -196,35 +196,44 @@ type SaisineDesServices = { faite: false } | { faite: true; date: CaminoDate } interface AxmProContext extends CaminoCommonContext { saisineDesCollectivitesLocalesFaite: boolean saisineDesServices: SaisineDesServices + notificationDuDemandeurFaite: boolean + notificationDesCollectivitesLocalesFaite: boolean + publicationDecisionsRecueilActesAdministratifsFaite: boolean + publicationDansUnJournalLocalOuNationalFaite: boolean } -const peutRendreAvisDREAL = (context: AxmProContext, event: RendreAvisDreal): boolean => { +const peutRendreAvisDREAL = ({ context, event }: { context: AxmProContext; event: RendreAvisDreal }): boolean => { return context.saisineDesServices.faite && daysBetween(dateAddMonths(context.saisineDesServices.date, 1), event.date) >= 0 } -const axmProMachine = createMachine<AxmProContext, AXMProXStateEvent>({ - predictableActionArguments: true, +const axmProMachine = createMachine({ + types: {} as { context: AxmProContext; events: AXMProXStateEvent }, + id: 'AXMPro', initial: 'demandeAFaire', context: { demarcheStatut: DemarchesStatutsIds.EnConstruction, + notificationDuDemandeurFaite: false, + notificationDesCollectivitesLocalesFaite: false, + publicationDecisionsRecueilActesAdministratifsFaite: false, + publicationDansUnJournalLocalOuNationalFaite: false, saisineDesCollectivitesLocalesFaite: false, saisineDesServices: { faite: false }, visibilite: 'confidentielle', }, on: { FAIRE_DESISTEMENT_DEMANDEUR: { - cond: context => [DemarchesStatutsIds.Depose, DemarchesStatutsIds.EnInstruction].includes(context.demarcheStatut), - target: 'desistementDuDemandeurRendu', + guard: ({ context }) => [DemarchesStatutsIds.Depose, DemarchesStatutsIds.EnInstruction].includes(context.demarcheStatut), + target: '.desistementDuDemandeurRendu', }, FAIRE_CLASSEMENT_SANS_SUITE: { - cond: context => [DemarchesStatutsIds.Depose, DemarchesStatutsIds.EnInstruction].includes(context.demarcheStatut), - target: 'classementSansSuiteRendu', + guard: ({ context }) => [DemarchesStatutsIds.Depose, DemarchesStatutsIds.EnInstruction].includes(context.demarcheStatut), + target: '.classementSansSuiteRendu', }, RENDRE_DECISION_IMPLICITE_REJET: { - cond: context => [DemarchesStatutsIds.Depose, DemarchesStatutsIds.EnInstruction].includes(context.demarcheStatut), - target: 'decisionAnnulationParJugeAdministratifAFaire', - actions: assign<AxmProContext, { type: 'RENDRE_DECISION_IMPLICITE_REJET' }>({ + guard: ({ context }) => [DemarchesStatutsIds.Depose, DemarchesStatutsIds.EnInstruction].includes(context.demarcheStatut), + target: '.decisionAnnulationParJugeAdministratifAFaire', + actions: assign({ demarcheStatut: DemarchesStatutsIds.Rejete, visibilite: 'publique', }), @@ -241,7 +250,7 @@ const axmProMachine = createMachine<AxmProContext, AXMProXStateEvent>({ on: { DEPOSER_DEMANDE: { target: 'recevabiliteDeLaDemandeAFaire', - actions: assign<AxmProContext, { type: 'DEPOSER_DEMANDE' }>({ + actions: assign({ demarcheStatut: DemarchesStatutsIds.Depose, }), }, @@ -253,7 +262,7 @@ const axmProMachine = createMachine<AxmProContext, AXMProXStateEvent>({ DEMANDER_COMPLEMENTS_POUR_RECEVABILITE: 'complementsPourRecevabiliteAFaire', FAIRE_RECEVABILITE_DEMANDE_FAVORABLE: { target: 'saisinesAFairePuisRendreAvisDREAL', - actions: assign<AxmProContext, { type: 'FAIRE_RECEVABILITE_DEMANDE_FAVORABLE' }>({ + actions: assign({ demarcheStatut: DemarchesStatutsIds.EnInstruction, visibilite: 'publique', }), @@ -266,7 +275,7 @@ const axmProMachine = createMachine<AxmProContext, AXMProXStateEvent>({ RECEVOIR_COMPLEMENTS_POUR_RECEVABILITE: 'recevabiliteDeLaDemandeAFaire', FAIRE_RECEVABILITE_DEMANDE_FAVORABLE: { target: 'saisinesAFairePuisRendreAvisDREAL', - actions: assign<AxmProContext, { type: 'FAIRE_RECEVABILITE_DEMANDE_FAVORABLE' }>({ + actions: assign({ demarcheStatut: DemarchesStatutsIds.EnInstruction, visibilite: 'publique', }), @@ -288,14 +297,14 @@ const axmProMachine = createMachine<AxmProContext, AXMProXStateEvent>({ rendreAvisDrealPasEncorePossible: { always: { target: 'rendreAvisDrealAFaire', - cond: (context: AxmProContext) => context.saisineDesServices.faite && context.saisineDesCollectivitesLocalesFaite, + guard: ({ context }) => context.saisineDesServices.faite && context.saisineDesCollectivitesLocalesFaite, }, }, rendreAvisDrealAFaire: { tags: [tags.responsable[ADMINISTRATION_IDS['DGTM - GUYANE']]], on: { RENDRE_AVIS_DREAL: { - cond: peutRendreAvisDREAL, + guard: peutRendreAvisDREAL, target: '#saisineOuAvisCommissionDepartementaleDesMinesARendre', }, }, @@ -324,8 +333,8 @@ const axmProMachine = createMachine<AxmProContext, AXMProXStateEvent>({ on: { FAIRE_SAISINE_COLLECTIVITES_LOCALES: { target: 'avisDunMaireARendre', - cond: context => !context.saisineDesCollectivitesLocalesFaite, - actions: assign<AxmProContext, { type: 'FAIRE_SAISINE_COLLECTIVITES_LOCALES' }>({ + guard: ({ context }) => !context.saisineDesCollectivitesLocalesFaite, + actions: assign({ saisineDesCollectivitesLocalesFaite: true, }), }, @@ -344,9 +353,9 @@ const axmProMachine = createMachine<AxmProContext, AXMProXStateEvent>({ on: { FAIRE_SAISINE_DES_SERVICES: { target: 'avisDesServicesARendre', - cond: context => !context.saisineDesServices.faite, - actions: assign<AxmProContext, FaireSaisineDesServices>({ - saisineDesServices: (context, event) => { + guard: ({ context }) => !context.saisineDesServices.faite, + actions: assign({ + saisineDesServices: ({ event }) => { return { faite: true, date: event.date, @@ -497,7 +506,7 @@ const axmProMachine = createMachine<AxmProContext, AXMProXStateEvent>({ tags: [tags.responsable[ADMINISTRATION_IDS['DGTM - GUYANE']]], on: { RENDRE_AVIS_DREAL: { - cond: peutRendreAvisDREAL, + guard: peutRendreAvisDREAL, target: 'saisineOuAvisCommissionDepartementaleDesMinesARendre', }, }, @@ -521,13 +530,13 @@ const axmProMachine = createMachine<AxmProContext, AXMProXStateEvent>({ FAIRE_SAISINE_AUTORITE_SIGNATAIRE: 'decisionAdministrationARendre', RENDRE_DECISION_ADMINISTRATION_ACCEPTE: { target: 'decisionAdministrationRendue', - actions: assign<AxmProContext, { type: 'RENDRE_DECISION_ADMINISTRATION_ACCEPTE' }>({ + actions: assign({ demarcheStatut: DemarchesStatutsIds.Accepte, }), }, RENDRE_DECISION_ADMINISTRATION_REJETE: { target: 'decisionAdministrationRendue', - actions: assign<AxmProContext, { type: 'RENDRE_DECISION_ADMINISTRATION_REJETE' }>({ + actions: assign({ demarcheStatut: DemarchesStatutsIds.Rejete, }), }, @@ -537,13 +546,13 @@ const axmProMachine = createMachine<AxmProContext, AXMProXStateEvent>({ on: { RENDRE_DECISION_ADMINISTRATION_ACCEPTE: { target: 'decisionAdministrationRendue', - actions: assign<AxmProContext, { type: 'RENDRE_DECISION_ADMINISTRATION_ACCEPTE' }>({ + actions: assign({ demarcheStatut: DemarchesStatutsIds.Accepte, }), }, RENDRE_DECISION_ADMINISTRATION_REJETE: { target: 'decisionAdministrationRendue', - actions: assign<AxmProContext, { type: 'RENDRE_DECISION_ADMINISTRATION_REJETE' }>({ + actions: assign({ demarcheStatut: DemarchesStatutsIds.Rejete, }), }, @@ -555,26 +564,26 @@ const axmProMachine = createMachine<AxmProContext, AXMProXStateEvent>({ RENDRE_DECISION_ANNULATION_PAR_JUGE_ADMINISTRATIF: { target: 'decisionAnnulationParJugeAdministratifRendu', }, - NOTIFIER_DEMANDEUR: { target: 'publicationsEtNotificationsMachine' }, - PUBLIER_DECISIONS_RECUEIL_ACTES_ADMINISTRATIFS: 'publicationsEtNotificationsMachine', - PUBLIER_DANS_UN_JOURNAL_LOCAL_OU_NATIONAL: 'publicationsEtNotificationsMachine', - NOTIFIER_COLLECTIVITES_LOCALES: 'publicationsEtNotificationsMachine', + NOTIFIER_DEMANDEUR: { target: 'publicationsEtNotificationsMachine', actions: assign({ notificationDuDemandeurFaite: true }) }, + PUBLIER_DECISIONS_RECUEIL_ACTES_ADMINISTRATIFS: { target: 'publicationsEtNotificationsMachine', actions: assign({ publicationDecisionsRecueilActesAdministratifsFaite: true }) }, + PUBLIER_DANS_UN_JOURNAL_LOCAL_OU_NATIONAL: { target: 'publicationsEtNotificationsMachine', actions: assign({ publicationDansUnJournalLocalOuNationalFaite: true }) }, + NOTIFIER_COLLECTIVITES_LOCALES: { target: 'publicationsEtNotificationsMachine', actions: assign({ notificationDesCollectivitesLocalesFaite: true }) }, }, }, publicationsEtNotificationsMachine: { type: 'parallel', states: { - notificationDuDemadeurMachine: { + notificationDuDemandeurMachine: { initial: 'notificationDuDemandeurAFaire', states: { notificationDuDemandeurAFaire: { always: { target: 'notificationDuDemandeurFaite', - cond: (_context, _event, meta) => { - return meta.state.history?.event.type === 'NOTIFIER_DEMANDEUR' + guard: ({ context }) => { + return context.notificationDuDemandeurFaite }, }, - on: { NOTIFIER_DEMANDEUR: 'notificationDuDemandeurFaite' }, + on: { NOTIFIER_DEMANDEUR: { target: 'notificationDuDemandeurFaite', actions: assign({ notificationDuDemandeurFaite: true }) } }, }, notificationDuDemandeurFaite: { type: 'final' }, }, @@ -585,12 +594,15 @@ const axmProMachine = createMachine<AxmProContext, AXMProXStateEvent>({ publicationDecisionsRecueilActesAdministratifsAFaire: { always: { target: 'publicationDecisionsRecueilActesAdministratifsFaite', - cond: (_context, _event, meta) => { - return meta.state.history?.event.type === 'PUBLIER_DECISIONS_RECUEIL_ACTES_ADMINISTRATIFS' + guard: ({ context }) => { + return context.publicationDecisionsRecueilActesAdministratifsFaite }, }, on: { - PUBLIER_DECISIONS_RECUEIL_ACTES_ADMINISTRATIFS: 'publicationDecisionsRecueilActesAdministratifsFaite', + PUBLIER_DECISIONS_RECUEIL_ACTES_ADMINISTRATIFS: { + target: 'publicationDecisionsRecueilActesAdministratifsFaite', + actions: assign({ publicationDecisionsRecueilActesAdministratifsFaite: true }), + }, }, }, publicationDecisionsRecueilActesAdministratifsFaite: { @@ -604,12 +616,12 @@ const axmProMachine = createMachine<AxmProContext, AXMProXStateEvent>({ publicationDansUnJournalLocalOuNationalAFaire: { always: { target: 'publicationDansUnJournalLocalOuNationalFaite', - cond: (_context, _event, meta) => { - return meta.state.history?.event.type === 'PUBLIER_DANS_UN_JOURNAL_LOCAL_OU_NATIONAL' + guard: ({ context }) => { + return context.publicationDansUnJournalLocalOuNationalFaite }, }, on: { - PUBLIER_DANS_UN_JOURNAL_LOCAL_OU_NATIONAL: 'publicationDansUnJournalLocalOuNationalFaite', + PUBLIER_DANS_UN_JOURNAL_LOCAL_OU_NATIONAL: { target: 'publicationDansUnJournalLocalOuNationalFaite', actions: assign({ publicationDansUnJournalLocalOuNationalFaite: true }) }, }, }, publicationDansUnJournalLocalOuNationalFaite: { type: 'final' }, @@ -621,12 +633,12 @@ const axmProMachine = createMachine<AxmProContext, AXMProXStateEvent>({ notificationDesCollectivitesLocalesAFaire: { always: { target: 'notificationDesCollectivitesLocalesFaite', - cond: (_context, _event, meta) => { - return meta.state.history?.event.type === 'NOTIFIER_COLLECTIVITES_LOCALES' + guard: ({ context }) => { + return context.notificationDesCollectivitesLocalesFaite }, }, on: { - NOTIFIER_COLLECTIVITES_LOCALES: 'notificationDesCollectivitesLocalesFaite', + NOTIFIER_COLLECTIVITES_LOCALES: { target: 'notificationDesCollectivitesLocalesFaite', actions: assign({ notificationDesCollectivitesLocalesFaite: true }) }, }, }, notificationDesCollectivitesLocalesFaite: { type: 'final' }, @@ -649,18 +661,18 @@ const axmProMachine = createMachine<AxmProContext, AXMProXStateEvent>({ decisionAbrogationFaite: { type: 'final' }, decisionAnnulationParJugeAdministratifRendu: { type: 'final', - entry: assign<AxmProContext>({ demarcheStatut: DemarchesStatutsIds.Rejete }), + entry: assign({ demarcheStatut: DemarchesStatutsIds.Rejete }), }, desistementDuDemandeurRendu: { type: 'final', - entry: assign<AxmProContext>({ + entry: assign({ demarcheStatut: DemarchesStatutsIds.Desiste, visibilite: 'publique', }), }, classementSansSuiteRendu: { type: 'final', - entry: assign<AxmProContext>({ + entry: assign({ demarcheStatut: DemarchesStatutsIds.ClasseSansSuite, visibilite: 'publique', }), diff --git a/packages/api/src/business/rules-demarches/machine-helper.test.ts b/packages/api/src/business/rules-demarches/machine-helper.test.ts index f72e0d86c..156777195 100644 --- a/packages/api/src/business/rules-demarches/machine-helper.test.ts +++ b/packages/api/src/business/rules-demarches/machine-helper.test.ts @@ -449,55 +449,55 @@ describe('mainStep', () => { { "contenu": undefined, "etapeStatutId": "fai", - "etapeTypeId": "mio", + "etapeTypeId": "mod", "mainStep": false, }, { "contenu": undefined, "etapeStatutId": "fai", - "etapeTypeId": "eof", - "mainStep": true, + "etapeTypeId": "des", + "mainStep": false, }, { "contenu": undefined, - "etapeStatutId": "fav", - "etapeTypeId": "ede", + "etapeStatutId": "fai", + "etapeTypeId": "css", "mainStep": false, }, { "contenu": undefined, - "etapeStatutId": "def", - "etapeTypeId": "ede", + "etapeStatutId": "fai", + "etapeTypeId": "mio", "mainStep": false, }, { "contenu": undefined, - "etapeStatutId": "fav", - "etapeTypeId": "edm", - "mainStep": false, + "etapeStatutId": "fai", + "etapeTypeId": "eof", + "mainStep": true, }, { "contenu": undefined, - "etapeStatutId": "def", - "etapeTypeId": "edm", + "etapeStatutId": "fav", + "etapeTypeId": "ede", "mainStep": false, }, { "contenu": undefined, - "etapeStatutId": "fai", - "etapeTypeId": "mod", + "etapeStatutId": "def", + "etapeTypeId": "ede", "mainStep": false, }, { "contenu": undefined, - "etapeStatutId": "fai", - "etapeTypeId": "des", + "etapeStatutId": "fav", + "etapeTypeId": "edm", "mainStep": false, }, { "contenu": undefined, - "etapeStatutId": "fai", - "etapeTypeId": "css", + "etapeStatutId": "def", + "etapeTypeId": "edm", "mainStep": false, }, ] @@ -522,7 +522,19 @@ describe('mainStep', () => { { "contenu": undefined, "etapeStatutId": "fai", - "etapeTypeId": "mia", + "etapeTypeId": "mod", + "mainStep": false, + }, + { + "contenu": undefined, + "etapeStatutId": "fai", + "etapeTypeId": "des", + "mainStep": false, + }, + { + "contenu": undefined, + "etapeStatutId": "fai", + "etapeTypeId": "css", "mainStep": false, }, { @@ -549,24 +561,6 @@ describe('mainStep', () => { "etapeTypeId": "aof", "mainStep": true, }, - { - "contenu": undefined, - "etapeStatutId": "fai", - "etapeTypeId": "mod", - "mainStep": false, - }, - { - "contenu": undefined, - "etapeStatutId": "fai", - "etapeTypeId": "des", - "mainStep": false, - }, - { - "contenu": undefined, - "etapeStatutId": "fai", - "etapeTypeId": "css", - "mainStep": false, - }, { "contenu": undefined, "etapeStatutId": "fav", @@ -591,6 +585,12 @@ describe('mainStep', () => { "etapeTypeId": "edm", "mainStep": false, }, + { + "contenu": undefined, + "etapeStatutId": "fai", + "etapeTypeId": "mia", + "mainStep": false, + }, ] `) }) @@ -611,43 +611,43 @@ describe('mainStep', () => { { "contenu": undefined, "etapeStatutId": "fai", - "etapeTypeId": "mim", + "etapeTypeId": "mod", "mainStep": false, }, { "contenu": undefined, "etapeStatutId": "fai", - "etapeTypeId": "mca", + "etapeTypeId": "des", "mainStep": false, }, { "contenu": undefined, - "etapeStatutId": "fav", - "etapeTypeId": "mcr", - "mainStep": true, + "etapeStatutId": "fai", + "etapeTypeId": "css", + "mainStep": false, }, { "contenu": undefined, - "etapeStatutId": "def", - "etapeTypeId": "mcr", + "etapeStatutId": "fai", + "etapeTypeId": "mim", "mainStep": false, }, { "contenu": undefined, "etapeStatutId": "fai", - "etapeTypeId": "mod", + "etapeTypeId": "mca", "mainStep": false, }, { "contenu": undefined, - "etapeStatutId": "fai", - "etapeTypeId": "des", - "mainStep": false, + "etapeStatutId": "fav", + "etapeTypeId": "mcr", + "mainStep": true, }, { "contenu": undefined, - "etapeStatutId": "fai", - "etapeTypeId": "css", + "etapeStatutId": "def", + "etapeTypeId": "mcr", "mainStep": false, }, ] @@ -668,6 +668,24 @@ describe('mainStep', () => { ) ).toMatchInlineSnapshot(` [ + { + "contenu": undefined, + "etapeStatutId": "fai", + "etapeTypeId": "mod", + "mainStep": false, + }, + { + "contenu": undefined, + "etapeStatutId": "fai", + "etapeTypeId": "des", + "mainStep": false, + }, + { + "contenu": undefined, + "etapeStatutId": "fai", + "etapeTypeId": "css", + "mainStep": false, + }, { "contenu": undefined, "etapeStatutId": "fav", @@ -692,24 +710,6 @@ describe('mainStep', () => { "etapeTypeId": "aof", "mainStep": true, }, - { - "contenu": undefined, - "etapeStatutId": "fai", - "etapeTypeId": "mod", - "mainStep": false, - }, - { - "contenu": undefined, - "etapeStatutId": "fai", - "etapeTypeId": "des", - "mainStep": false, - }, - { - "contenu": undefined, - "etapeStatutId": "fai", - "etapeTypeId": "css", - "mainStep": false, - }, ] `) }) diff --git a/packages/api/src/business/rules-demarches/machine-helper.ts b/packages/api/src/business/rules-demarches/machine-helper.ts index c83f750aa..454aef8f1 100644 --- a/packages/api/src/business/rules-demarches/machine-helper.ts +++ b/packages/api/src/business/rules-demarches/machine-helper.ts @@ -1,40 +1,18 @@ -import { BaseActionObject, interpret, ResolveTypegenMeta, ServiceMap, State, StateMachine, TypegenDisabled } from 'xstate' -import { EventObject } from 'xstate/lib/types.js' +import { AnyMachineSnapshot, createActor, EventObject, MachineSnapshot, StateMachine } from 'xstate' import { CaminoCommonContext, DBEtat, Etape, Intervenant, intervenants, tags } from './machine-common.js' import { DemarchesStatutsIds, DemarcheStatutId } from 'camino-common/src/static/demarchesStatuts.js' import { CaminoDate } from 'camino-common/src/date.js' -type CaminoState<CaminoContext extends CaminoCommonContext, CaminoEvent extends EventObject> = State< - CaminoContext, - CaminoEvent, - any, - { value: any; context: CaminoContext }, - ResolveTypegenMeta<TypegenDisabled, CaminoEvent, BaseActionObject, ServiceMap> -> +type CaminoState<CaminoContext extends CaminoCommonContext, CaminoEvent extends EventObject> = MachineSnapshot<CaminoContext, CaminoEvent, any, any, any, any> + export abstract class CaminoMachine<CaminoContext extends CaminoCommonContext, CaminoEvent extends EventObject> { - public readonly machine: StateMachine< - CaminoContext, - any, - CaminoEvent, - { value: any; context: CaminoContext }, - BaseActionObject, - ServiceMap, - ResolveTypegenMeta<TypegenDisabled, CaminoEvent, BaseActionObject, ServiceMap> - > + public readonly machine: StateMachine<CaminoContext, CaminoEvent, any, any, any, any, any, any, any, any, any, EventObject, any> private readonly trad: { [key in CaminoEvent['type']]: { db: DBEtat; mainStep: boolean } } private readonly events: Array<CaminoEvent['type']> protected constructor( - machine: StateMachine< - CaminoContext, - any, - CaminoEvent, - { value: any; context: CaminoContext }, - BaseActionObject, - ServiceMap, - ResolveTypegenMeta<TypegenDisabled, CaminoEvent, BaseActionObject, ServiceMap> - >, + machine: StateMachine<CaminoContext, CaminoEvent, any, any, any, any, any, any, any, any, any, EventObject, any>, trad: { [key in CaminoEvent['type']]: { db: DBEtat; mainStep: boolean } } ) { this.machine = machine @@ -122,7 +100,7 @@ export abstract class CaminoMachine<CaminoContext extends CaminoCommonContext, C * Cette function ne doit JAMAIS appeler orderMachine, car c'est orderMachine qui se sert de cette fonction. * Cette function ne fait que vérifier si les étapes qu'on lui donne sont valides dans l'ordre */ - public isEtapesOk(sortedEtapes: readonly Etape[], initialState: CaminoState<CaminoContext, CaminoEvent> | null = null): boolean { + public isEtapesOk(sortedEtapes: readonly Etape[]): boolean { if (sortedEtapes.length) { for (let i = 1; i < sortedEtapes.length; i++) { if (sortedEtapes[i - 1].date > sortedEtapes[i].date) { @@ -131,7 +109,7 @@ export abstract class CaminoMachine<CaminoContext extends CaminoCommonContext, C } } try { - const result = this.goTo(sortedEtapes, initialState) + const result = this.goTo(sortedEtapes) return result.valid } catch (e) { @@ -141,35 +119,38 @@ export abstract class CaminoMachine<CaminoContext extends CaminoCommonContext, C } } - private goTo( - etapes: readonly Etape[], - initialState: CaminoState<CaminoContext, CaminoEvent> | null = null - ): + private goTo(etapes: readonly Etape[]): | { valid: false; etapeIndex: number } | { valid: true state: CaminoState<CaminoContext, CaminoEvent> } { - const service = interpret(this.machine) + const service = createActor(this.machine, {}) - if (initialState === null) { - service.start() - } else { - service.start(initialState) - } - for (let i = 0; i < etapes.length; i++) { - const etapeAFaire = etapes[i] - const event = this.eventFrom(etapeAFaire) - if (!service.getSnapshot().can(event) || service.getSnapshot().done) { - service.stop() + service.subscribe({ + error(_err) { + // on catch les erreurs sinon on a des trucs bizarre dans la console, mais le `can` plus bas fait déjà le taf de savoir si un événement est valide ou non + }, + }) + + service.start() + try { + for (let i = 0; i < etapes.length; i++) { + const etapeAFaire = etapes[i] + const event = this.eventFrom(etapeAFaire) + + if (!service.getSnapshot().can(event) || service.getSnapshot().status === 'done') { + service.stop() - return { valid: false, etapeIndex: i } + return { valid: false, etapeIndex: i } + } + service.send(event) } - service.send(event) + } finally { + service.stop() } const state = service.getSnapshot() - service.stop() return { valid: true, state } } @@ -194,8 +175,8 @@ export abstract class CaminoMachine<CaminoContext extends CaminoCommonContext, C } } - private assertGoTo(etapes: readonly Etape[], initialState: CaminoState<CaminoContext, CaminoEvent> | null = null): CaminoState<CaminoContext, CaminoEvent> { - const value = this.goTo(etapes, initialState) + private assertGoTo(etapes: readonly Etape[]): CaminoState<CaminoContext, CaminoEvent> { + const value = this.goTo(etapes) if (!value.valid) { throw new Error(`Les étapes '${JSON.stringify(etapes)}' sont invalides à partir de l’étape ${value.etapeIndex}`) } else { @@ -206,7 +187,7 @@ export abstract class CaminoMachine<CaminoContext extends CaminoCommonContext, C public whoIsBlocking(etapes: readonly Etape[]): Intervenant[] { const state = this.assertGoTo(etapes) - const responsables: string[] = [...state.tags] + const responsables: string[] = [...(state?.tags ?? [])] return intervenants.filter(r => responsables.includes(tags.responsable[r])) } @@ -214,13 +195,25 @@ export abstract class CaminoMachine<CaminoContext extends CaminoCommonContext, C public possibleNextEtapes(etapes: readonly Etape[], date: CaminoDate): (Omit<Etape, 'date'> & { mainStep: boolean })[] { const state = this.assertGoTo(etapes) - return state.nextEvents - .filter((event: string) => this.isEvent(event)) - .flatMap(event => { - const events = this.toPotentialCaminoXStateEvent(event, date) + if (state !== undefined) { + return getNextEvents(state) + .filter((event: string) => this.isEvent(event)) + .flatMap(event => { + const events = this.toPotentialCaminoXStateEvent(event, date) + + return events + .filter(event => { + return state.can(event) && state.status !== 'done' + }) + .flatMap(event => this.caminoXStateEventToEtapes(event)) + }) + .filter(event => event !== undefined) + } - return events.filter(event => state.can(event) && !state.done).flatMap(event => this.caminoXStateEventToEtapes(event)) - }) - .filter(event => event !== undefined) + return [] } } + +export function getNextEvents(snapshot: AnyMachineSnapshot) { + return [...new Set([...snapshot._nodes.flatMap(sn => sn.ownEvents)])] +} diff --git a/packages/api/src/business/rules-demarches/machine-test-helper.ts b/packages/api/src/business/rules-demarches/machine-test-helper.ts index 2796e9814..c0e64e084 100644 --- a/packages/api/src/business/rules-demarches/machine-test-helper.ts +++ b/packages/api/src/business/rules-demarches/machine-test-helper.ts @@ -1,11 +1,10 @@ import { CaminoCommonContext, Etape } from './machine-common.js' -import { EventObject } from 'xstate/lib/types.js' -import { interpret } from 'xstate' -import { CaminoMachine } from './machine-helper.js' +import { Actor, EventObject, createActor } from 'xstate' +import { CaminoMachine, getNextEvents } from './machine-helper.js' import { expect } from 'vitest' import { CaminoDate } from 'camino-common/src/date.js' interface CustomMatchers<R = unknown> { - canOnlyTransitionTo<T extends EventObject>(context: { machine: CaminoMachine<any, T>; date: CaminoDate }, _events: T['type'][]): R + canOnlyTransitionTo<T extends EventObject, C extends CaminoCommonContext>(context: { machine: CaminoMachine<C, T>; date: CaminoDate }, _events: T['type'][]): R } declare global { @@ -19,15 +18,18 @@ declare global { } } expect.extend({ - canOnlyTransitionTo<T extends EventObject>(service: any, { machine, date }: { machine: CaminoMachine<any, T>; date: CaminoDate }, events: T['type'][]) { + canOnlyTransitionTo<T extends EventObject, C extends CaminoCommonContext>( + service: Actor<CaminoMachine<C, T>['machine']>, + { machine, date }: { machine: CaminoMachine<C, T>; date: CaminoDate }, + events: T['type'][] + ) { events.sort() - const passEvents: EventObject['type'][] = service - .getSnapshot() - .nextEvents.filter((event: string) => machine.isEvent(event)) - .filter((event: EventObject['type']) => { + const passEvents: (typeof events)[number][] = getNextEvents(service.getSnapshot()) + .filter((event: string) => machine.isEvent(event)) + .filter((event: (typeof events)[number]) => { const events = machine.toPotentialCaminoXStateEvent(event, date) - return events.some(event => service.getSnapshot().can(event) && !service.getSnapshot().done) + return events.some(event => service.getSnapshot().can(event) && service.getSnapshot().status !== 'done') }) passEvents.sort() @@ -46,7 +48,7 @@ expect.extend({ }) export const interpretMachine = <T extends EventObject, C extends CaminoCommonContext>(machine: CaminoMachine<C, T>, etapes: readonly Etape[]) => { - const service = interpret(machine.machine) + const service = createActor(machine.machine) service.start() @@ -54,18 +56,18 @@ export const interpretMachine = <T extends EventObject, C extends CaminoCommonCo const etapeAFaire = etapes[i] const event = machine.eventFrom(etapeAFaire) - if (!service.getSnapshot().can(event) || service.getSnapshot().done) { + if (!service.getSnapshot().can(event) || service.getSnapshot().status === 'done') { throw new Error( `Error: cannot execute step: '${JSON.stringify(etapeAFaire)}' after '${JSON.stringify( etapes.slice(0, i).map(etape => etape.etapeTypeId + '_' + etape.etapeStatutId) - )}'. The event ${JSON.stringify(event)} should be one of '${service - .getSnapshot() - .nextEvents.filter(event => machine.isEvent(event)) + )}'. The event ${JSON.stringify(event)} should be one of '${getNextEvents(service.getSnapshot()) + .filter(event => machine.isEvent(event)) .filter((event: EventObject['type']) => { const events = machine.toPotentialCaminoXStateEvent(event, etapeAFaire.date) - return events.some(event => service.getSnapshot().can(event) && !service.getSnapshot().done) - })}'` + return events.some(event => service.getSnapshot().can(event) && service.getSnapshot().status !== 'done') + }) + .sort()}'` ) } service.send(event) diff --git a/packages/api/src/business/rules-demarches/prm/oct.machine.test.ts b/packages/api/src/business/rules-demarches/prm/oct.machine.test.ts index b5a49fda1..1ba5dd52d 100644 --- a/packages/api/src/business/rules-demarches/prm/oct.machine.test.ts +++ b/packages/api/src/business/rules-demarches/prm/oct.machine.test.ts @@ -93,7 +93,7 @@ describe('vérifie l’arbre d’octroi de PRM', () => { { ...ETES.avisEtRapportDuDirecteurRegionalChargeDeLenvironnementDeLamenagementEtDuLogement.FAVORABLE, date: toCaminoDate('2022-04-20') }, ] expect(() => orderAndInterpretMachine(prmOctMachine, etapes)).toThrowErrorMatchingInlineSnapshot( - '"Error: cannot execute step: \'{\\"etapeTypeId\\":\\"apd\\",\\"etapeStatutId\\":\\"fav\\",\\"date\\":\\"2022-04-20\\"}\' after \'[\\"mfr_fai\\",\\"mdp_fai\\",\\"spp_fai\\",\\"mcr_fav\\",\\"anf_fai\\",\\"ssr_fai\\"]\'. The event {\\"type\\":\\"RENDRE_RAPPORT_DREAL\\",\\"date\\":\\"2022-04-20\\"} should be one of \'RENDRE_AVIS_SERVICE_ADMINISTRATIF_CIVIL_LOCAL,RENDRE_AVIS_AUTORITE_MILITAIRE,RENDRE_AVIS_DES_DTT,RENDRE_AVIS_PARC_NATUREL_REGIONAL,RENDRE_AVIS_PARC_NATIONAL,RENDRE_AVIS_AGENCE_REGIONALE_SANTE_ARS,RENDRE_AVIS_ONF,RENDRE_AVIS_INSTITUT_NATIONAL_ORIGINE_ET_QUALITE_INAO,RENDRE_AVIS_DIRECTION_REGIONALE_AFFAIRES_CULTURELLES,RENDRE_AVIS_DIRECTION_REGIONALE_FINANCES_PUBLIQUES,MODIFIER_DEMANDE,DEMANDER_INFORMATIONS,RECEVOIR_INFORMATIONS,DESISTER_PAR_LE_DEMANDEUR,CLASSER_SANS_SUITE,DEPOSER_DEMANDE_CONCURRENTE\'"' + '"Error: cannot execute step: \'{\\"etapeTypeId\\":\\"apd\\",\\"etapeStatutId\\":\\"fav\\",\\"date\\":\\"2022-04-20\\"}\' after \'[\\"mfr_fai\\",\\"mdp_fai\\",\\"spp_fai\\",\\"mcr_fav\\",\\"anf_fai\\",\\"ssr_fai\\"]\'. The event {\\"type\\":\\"RENDRE_RAPPORT_DREAL\\",\\"date\\":\\"2022-04-20\\"} should be one of \'CLASSER_SANS_SUITE,DEMANDER_INFORMATIONS,DEPOSER_DEMANDE_CONCURRENTE,DESISTER_PAR_LE_DEMANDEUR,MODIFIER_DEMANDE,RECEVOIR_INFORMATIONS,RENDRE_AVIS_AGENCE_REGIONALE_SANTE_ARS,RENDRE_AVIS_AUTORITE_MILITAIRE,RENDRE_AVIS_DES_DTT,RENDRE_AVIS_DIRECTION_REGIONALE_AFFAIRES_CULTURELLES,RENDRE_AVIS_DIRECTION_REGIONALE_FINANCES_PUBLIQUES,RENDRE_AVIS_INSTITUT_NATIONAL_ORIGINE_ET_QUALITE_INAO,RENDRE_AVIS_ONF,RENDRE_AVIS_PARC_NATIONAL,RENDRE_AVIS_PARC_NATUREL_REGIONAL,RENDRE_AVIS_SERVICE_ADMINISTRATIF_CIVIL_LOCAL\'"' ) }) @@ -108,7 +108,7 @@ describe('vérifie l’arbre d’octroi de PRM', () => { { ...ETES.saisineDesServices.FAIT, date: toCaminoDate('2022-04-19') }, ] expect(() => orderAndInterpretMachine(prmOctMachine, etapes)).toThrowErrorMatchingInlineSnapshot( - '"Error: cannot execute step: \'{\\"etapeTypeId\\":\\"ssr\\",\\"etapeStatutId\\":\\"fai\\",\\"date\\":\\"2022-04-19\\"}\' after \'[\\"mfr_fai\\",\\"mdp_fai\\",\\"spp_fai\\",\\"mcr_fav\\",\\"anf_fai\\",\\"ssr_fai\\"]\'. The event {\\"type\\":\\"FAIRE_SAISINE_DES_SERVICES\\",\\"date\\":\\"2022-04-19\\"} should be one of \'RENDRE_AVIS_SERVICE_ADMINISTRATIF_CIVIL_LOCAL,RENDRE_AVIS_AUTORITE_MILITAIRE,RENDRE_AVIS_DES_DTT,RENDRE_AVIS_PARC_NATUREL_REGIONAL,RENDRE_AVIS_PARC_NATIONAL,RENDRE_AVIS_AGENCE_REGIONALE_SANTE_ARS,RENDRE_AVIS_ONF,RENDRE_AVIS_INSTITUT_NATIONAL_ORIGINE_ET_QUALITE_INAO,RENDRE_AVIS_DIRECTION_REGIONALE_AFFAIRES_CULTURELLES,RENDRE_AVIS_DIRECTION_REGIONALE_FINANCES_PUBLIQUES,MODIFIER_DEMANDE,DEMANDER_INFORMATIONS,RECEVOIR_INFORMATIONS,DESISTER_PAR_LE_DEMANDEUR,CLASSER_SANS_SUITE,DEPOSER_DEMANDE_CONCURRENTE\'"' + '"Error: cannot execute step: \'{\\"etapeTypeId\\":\\"ssr\\",\\"etapeStatutId\\":\\"fai\\",\\"date\\":\\"2022-04-19\\"}\' after \'[\\"mfr_fai\\",\\"mdp_fai\\",\\"spp_fai\\",\\"mcr_fav\\",\\"anf_fai\\",\\"ssr_fai\\"]\'. The event {\\"type\\":\\"FAIRE_SAISINE_DES_SERVICES\\",\\"date\\":\\"2022-04-19\\"} should be one of \'CLASSER_SANS_SUITE,DEMANDER_INFORMATIONS,DEPOSER_DEMANDE_CONCURRENTE,DESISTER_PAR_LE_DEMANDEUR,MODIFIER_DEMANDE,RECEVOIR_INFORMATIONS,RENDRE_AVIS_AGENCE_REGIONALE_SANTE_ARS,RENDRE_AVIS_AUTORITE_MILITAIRE,RENDRE_AVIS_DES_DTT,RENDRE_AVIS_DIRECTION_REGIONALE_AFFAIRES_CULTURELLES,RENDRE_AVIS_DIRECTION_REGIONALE_FINANCES_PUBLIQUES,RENDRE_AVIS_INSTITUT_NATIONAL_ORIGINE_ET_QUALITE_INAO,RENDRE_AVIS_ONF,RENDRE_AVIS_PARC_NATIONAL,RENDRE_AVIS_PARC_NATUREL_REGIONAL,RENDRE_AVIS_SERVICE_ADMINISTRATIF_CIVIL_LOCAL\'"' ) }) @@ -122,7 +122,7 @@ describe('vérifie l’arbre d’octroi de PRM', () => { { ...ETES.ouvertureDeLaParticipationDuPublic.FAIT, date: toCaminoDate('2022-04-19') }, ] expect(() => orderAndInterpretMachine(prmOctMachine, etapes)).toThrowErrorMatchingInlineSnapshot( - '"Error: cannot execute step: \'{\\"etapeTypeId\\":\\"ppu\\",\\"etapeStatutId\\":\\"fai\\",\\"date\\":\\"2022-04-19\\"}\' after \'[\\"mfr_fai\\",\\"mdp_fai\\",\\"spp_fai\\",\\"mcr_fav\\",\\"anf_fai\\"]\'. The event {\\"type\\":\\"OUVRIR_PARTICIPATION_DU_PUBLIC\\",\\"date\\":\\"2022-04-19\\"} should be one of \'MODIFIER_DEMANDE,DEMANDER_INFORMATIONS,RECEVOIR_INFORMATIONS,DESISTER_PAR_LE_DEMANDEUR,CLASSER_SANS_SUITE,FAIRE_SAISINE_DES_SERVICES,DEPOSER_DEMANDE_CONCURRENTE\'"' + '"Error: cannot execute step: \'{\\"etapeTypeId\\":\\"ppu\\",\\"etapeStatutId\\":\\"fai\\",\\"date\\":\\"2022-04-19\\"}\' after \'[\\"mfr_fai\\",\\"mdp_fai\\",\\"spp_fai\\",\\"mcr_fav\\",\\"anf_fai\\"]\'. The event {\\"type\\":\\"OUVRIR_PARTICIPATION_DU_PUBLIC\\",\\"date\\":\\"2022-04-19\\"} should be one of \'CLASSER_SANS_SUITE,DEMANDER_INFORMATIONS,DEPOSER_DEMANDE_CONCURRENTE,DESISTER_PAR_LE_DEMANDEUR,FAIRE_SAISINE_DES_SERVICES,MODIFIER_DEMANDE,RECEVOIR_INFORMATIONS\'"' ) }) @@ -151,7 +151,7 @@ describe('vérifie l’arbre d’octroi de PRM', () => { ] expect(() => orderAndInterpretMachine(prmOctMachine, etapes)).toThrowErrorMatchingInlineSnapshot( - '"Error: cannot execute step: \'{\\"etapeTypeId\\":\\"rpu\\",\\"etapeStatutId\\":\\"fai\\",\\"date\\":\\"2022-06-01\\"}\' after \'[\\"mfr_fai\\",\\"mdp_fai\\",\\"spp_fai\\",\\"mcr_fav\\",\\"anf_fai\\",\\"ssr_fai\\",\\"aof_fav\\",\\"ppu_fai\\",\\"ppc_ter\\",\\"apd_fav\\",\\"app_fav\\",\\"cac_fai\\",\\"scg_fai\\",\\"rcg_fav\\",\\"acg_fav\\",\\"sas_fai\\",\\"dex_rej\\",\\"npp_fai\\",\\"mno_fai\\"]\'. The event {\\"type\\":\\"PUBLIER_DECISIONS_RECUEIL_ACTES_ADMINISTRATIFS\\"} should be one of \'RENDRE_DECISION_ANNULATION_PAR_JUGE_ADMINISTRATIF,RENDRE_DECISION_ABROGATION\'"' + '"Error: cannot execute step: \'{\\"etapeTypeId\\":\\"rpu\\",\\"etapeStatutId\\":\\"fai\\",\\"date\\":\\"2022-06-01\\"}\' after \'[\\"mfr_fai\\",\\"mdp_fai\\",\\"spp_fai\\",\\"mcr_fav\\",\\"anf_fai\\",\\"ssr_fai\\",\\"aof_fav\\",\\"ppu_fai\\",\\"ppc_ter\\",\\"apd_fav\\",\\"app_fav\\",\\"cac_fai\\",\\"scg_fai\\",\\"rcg_fav\\",\\"acg_fav\\",\\"sas_fai\\",\\"dex_rej\\",\\"npp_fai\\",\\"mno_fai\\"]\'. The event {\\"type\\":\\"PUBLIER_DECISIONS_RECUEIL_ACTES_ADMINISTRATIFS\\"} should be one of \'RENDRE_DECISION_ABROGATION,RENDRE_DECISION_ANNULATION_PAR_JUGE_ADMINISTRATIF\'"' ) }) diff --git a/packages/api/src/business/rules-demarches/prm/oct.machine.ts b/packages/api/src/business/rules-demarches/prm/oct.machine.ts index 1ac008b86..e1f5db99c 100644 --- a/packages/api/src/business/rules-demarches/prm/oct.machine.ts +++ b/packages/api/src/business/rules-demarches/prm/oct.machine.ts @@ -230,19 +230,19 @@ interface PrmOctContext extends CaminoCommonContext { surface: number | null } -const peutOuvrirParticipationDuPublic = (context: PrmOctContext, event: OuvrirParticipationDuPublic): boolean => { - return estExempteDeLaMiseEnConcurrence(context) || (context.dateAvisMiseEnConcurrentJorf !== null && daysBetween(dateAddMonths(context.dateAvisMiseEnConcurrentJorf, 1), event.date) >= 0) +const peutOuvrirParticipationDuPublic = ({ context, event }: { context: PrmOctContext; event: OuvrirParticipationDuPublic }): boolean => { + return estExempteDeLaMiseEnConcurrence({ context }) || (context.dateAvisMiseEnConcurrentJorf !== null && daysBetween(dateAddMonths(context.dateAvisMiseEnConcurrentJorf, 1), event.date) >= 0) } -const peutRendreRapportDREAL = (context: PrmOctContext, event: RendreRapportDREAL): boolean => { +const peutRendreRapportDREAL = ({ context, event }: { context: PrmOctContext; event: RendreRapportDREAL }): boolean => { return isMetropole(context.paysId) && !!context.dateSaisineDesServices && daysBetween(dateAddMonths(context.dateSaisineDesServices, 1), event.date) >= 0 } -const peutRendreAvisCDM = (context: PrmOctContext, event: RendreAvisCDM): boolean => { +const peutRendreAvisCDM = ({ context, event }: { context: PrmOctContext; event: RendreAvisCDM }): boolean => { return isOutreMer(context.paysId) && !!context.dateSaisineDesServices && daysBetween(dateAddMonths(context.dateSaisineDesServices, 1), event.date) >= 0 } -const estExempteDeLaMiseEnConcurrence = (context: PrmOctContext): boolean => { +const estExempteDeLaMiseEnConcurrence = ({ context }: { context: PrmOctContext }): boolean => { if (isGuyane(context.paysId)) { if (context.surface === null) { throw new Error('la surface est obligatoire quand on est en Guyane') @@ -254,8 +254,8 @@ const estExempteDeLaMiseEnConcurrence = (context: PrmOctContext): boolean => { return false } -const prmOctMachine = createMachine<PrmOctContext, XStateEvent>({ - predictableActionArguments: true, +const prmOctMachine = createMachine({ + types: {} as { context: PrmOctContext; events: XStateEvent }, id: 'oct', initial: 'demandeAFaire', context: { @@ -269,28 +269,28 @@ const prmOctMachine = createMachine<PrmOctContext, XStateEvent>({ on: { MODIFIER_DEMANDE: { actions: () => ({}), - cond: context => context.demarcheStatut === DemarchesStatutsIds.EnInstruction || context.demarcheStatut === DemarchesStatutsIds.Depose, + guard: ({ context }) => context.demarcheStatut === DemarchesStatutsIds.EnInstruction || context.demarcheStatut === DemarchesStatutsIds.Depose, }, DEMANDER_INFORMATIONS: { actions: () => ({}), - cond: context => context.demarcheStatut === DemarchesStatutsIds.EnInstruction || context.demarcheStatut === DemarchesStatutsIds.Depose, + guard: ({ context }) => context.demarcheStatut === DemarchesStatutsIds.EnInstruction || context.demarcheStatut === DemarchesStatutsIds.Depose, }, RECEVOIR_INFORMATIONS: { actions: () => ({}), - cond: context => context.demarcheStatut === DemarchesStatutsIds.EnInstruction || context.demarcheStatut === DemarchesStatutsIds.Depose, + guard: ({ context }) => context.demarcheStatut === DemarchesStatutsIds.EnInstruction || context.demarcheStatut === DemarchesStatutsIds.Depose, }, DESISTER_PAR_LE_DEMANDEUR: { - target: 'done', - cond: context => context.demarcheStatut === DemarchesStatutsIds.EnInstruction || context.demarcheStatut === DemarchesStatutsIds.Depose, - actions: assign<CaminoCommonContext, { type: 'DESISTER_PAR_LE_DEMANDEUR' }>({ + target: '.done', + guard: ({ context }) => context.demarcheStatut === DemarchesStatutsIds.EnInstruction || context.demarcheStatut === DemarchesStatutsIds.Depose, + actions: assign({ demarcheStatut: DemarchesStatutsIds.Desiste, visibilite: 'publique', }), }, CLASSER_SANS_SUITE: { - target: 'done', - cond: context => context.demarcheStatut === DemarchesStatutsIds.EnInstruction, - actions: assign<CaminoCommonContext, { type: 'CLASSER_SANS_SUITE' }>({ + target: '.done', + guard: ({ context }) => context.demarcheStatut === DemarchesStatutsIds.EnInstruction, + actions: assign({ demarcheStatut: DemarchesStatutsIds.ClasseSansSuite, visibilite: 'publique', }), @@ -301,11 +301,11 @@ const prmOctMachine = createMachine<PrmOctContext, XStateEvent>({ on: { FAIRE_DEMANDE: { target: 'depotDeLaDemandeAFaire', - actions: assign<PrmOctContext, FaireDemande>({ - paysId: (_context, event) => { + actions: assign({ + paysId: ({ event }) => { return event.paysId }, - surface: (_context, event) => { + surface: ({ event }) => { return event.surface }, }), @@ -316,7 +316,7 @@ const prmOctMachine = createMachine<PrmOctContext, XStateEvent>({ on: { DEPOSER_DEMANDE: { target: 'saisineDuPrefetAFaire', - actions: assign<CaminoCommonContext, { type: 'DEPOSER_DEMANDE' }>({ + actions: assign({ demarcheStatut: DemarchesStatutsIds.Depose, }), }, @@ -332,7 +332,7 @@ const prmOctMachine = createMachine<PrmOctContext, XStateEvent>({ DEMANDER_COMPLEMENTS_POUR_RECEVABILITE: 'complementsPourRecevabiliteAFaire', FAIRE_RECEVABILITE_DEMANDE_FAVORABLE: { target: 'avisDeMiseEnConcurrenceAuJORFAFaire', - actions: assign<CaminoCommonContext, { type: 'FAIRE_RECEVABILITE_DEMANDE_FAVORABLE' }>({ + actions: assign({ demarcheStatut: DemarchesStatutsIds.EnInstruction, visibilite: 'publique', }), @@ -345,7 +345,7 @@ const prmOctMachine = createMachine<PrmOctContext, XStateEvent>({ RECEVOIR_COMPLEMENTS_POUR_RECEVABILITE: 'recevabiliteDeLaDemandeAFaire', FAIRE_RECEVABILITE_DEMANDE_FAVORABLE: { target: 'avisDeMiseEnConcurrenceAuJORFAFaire', - actions: assign<CaminoCommonContext, { type: 'FAIRE_RECEVABILITE_DEMANDE_FAVORABLE' }>({ + actions: assign({ demarcheStatut: DemarchesStatutsIds.EnInstruction, visibilite: 'publique', }), @@ -355,14 +355,14 @@ const prmOctMachine = createMachine<PrmOctContext, XStateEvent>({ }, avisDeMiseEnConcurrenceAuJORFAFaire: { always: { - cond: estExempteDeLaMiseEnConcurrence, + guard: estExempteDeLaMiseEnConcurrence, target: 'saisinesEtMiseEnConcurrence', }, on: { RENDRE_AVIS_DE_MISE_EN_CONCURRENCE_AU_JORF: { target: 'saisinesEtMiseEnConcurrence', - actions: assign<PrmOctContext, RendreAvisMiseEnConcurrentJORF>({ - dateAvisMiseEnConcurrentJorf: (_context, event) => { + actions: assign({ + dateAvisMiseEnConcurrentJorf: ({ event }) => { return event.date }, }), @@ -383,7 +383,7 @@ const prmOctMachine = createMachine<PrmOctContext, XStateEvent>({ states: { saisineDesCollectivitesLocalesAFaire: { always: { - cond: (context: PrmOctContext) => !isGuyane(context.paysId), + guard: ({ context }) => !isGuyane(context.paysId), target: 'done', }, on: { @@ -405,8 +405,8 @@ const prmOctMachine = createMachine<PrmOctContext, XStateEvent>({ on: { FAIRE_SAISINE_DES_SERVICES: { target: 'avisDesServicesARendre', - actions: assign<PrmOctContext, FaireSaisineDesServices>({ - dateSaisineDesServices: (_context, event) => event.date, + actions: assign({ + dateSaisineDesServices: ({ event }) => event.date, }), }, }, @@ -417,8 +417,8 @@ const prmOctMachine = createMachine<PrmOctContext, XStateEvent>({ states: { rendreAvisDrealAFaire: { on: { - RENDRE_AVIS_CDM: { target: '#rapportDREALAFaire', cond: peutRendreAvisCDM }, - RENDRE_RAPPORT_DREAL: { target: '#avisPrefetARendre', cond: peutRendreRapportDREAL }, + RENDRE_AVIS_CDM: { target: '#rapportDREALAFaire', guard: peutRendreAvisCDM }, + RENDRE_RAPPORT_DREAL: { target: '#avisPrefetARendre', guard: peutRendreRapportDREAL }, }, }, avisServiceAdministratifCivilLocal: { @@ -444,8 +444,8 @@ const prmOctMachine = createMachine<PrmOctContext, XStateEvent>({ states: { avisDesDDTARendre: { on: { - RENDRE_AVIS_DES_DTT: { target: 'avisDesDDTRendu', cond: (context: PrmOctContext) => !isGuyane(context.paysId) }, - RENDRE_AVIS_POLICE_EAU: { target: 'avisDesDDTRendu', cond: (context: PrmOctContext) => isGuyane(context.paysId) }, + RENDRE_AVIS_DES_DTT: { target: 'avisDesDDTRendu', guard: ({ context }) => !isGuyane(context.paysId) }, + RENDRE_AVIS_POLICE_EAU: { target: 'avisDesDDTRendu', guard: ({ context }) => isGuyane(context.paysId) }, }, }, avisDesDDTRendu: { type: 'final' }, @@ -527,11 +527,11 @@ const prmOctMachine = createMachine<PrmOctContext, XStateEvent>({ always: [ { target: 'avisCommissionDepartementaleDesMinesEnGuyaneEtOutreMerARendre', - cond: (context: PrmOctContext) => isOutreMer(context.paysId), + guard: ({ context }) => isOutreMer(context.paysId), }, { target: 'rapportDREALAFaire', - cond: (context: PrmOctContext) => isMetropole(context.paysId), + guard: ({ context }) => isMetropole(context.paysId), }, ], }, @@ -560,8 +560,8 @@ const prmOctMachine = createMachine<PrmOctContext, XStateEvent>({ states: { participationDuPublicPasEncorePossible: { on: { - DEPOSER_DEMANDE_CONCURRENTE: { target: 'participationDuPublicPasEncorePossible', cond: context => !estExempteDeLaMiseEnConcurrence(context) }, - OUVRIR_PARTICIPATION_DU_PUBLIC: { target: 'clotureParticipationDuPublicAFaire', cond: peutOuvrirParticipationDuPublic }, + DEPOSER_DEMANDE_CONCURRENTE: { target: 'participationDuPublicPasEncorePossible', guard: value => !estExempteDeLaMiseEnConcurrence(value) }, + OUVRIR_PARTICIPATION_DU_PUBLIC: { target: 'clotureParticipationDuPublicAFaire', guard: peutOuvrirParticipationDuPublic }, }, }, clotureParticipationDuPublicAFaire: { @@ -605,7 +605,7 @@ const prmOctMachine = createMachine<PrmOctContext, XStateEvent>({ RENDRE_DECISION_ADMINISTRATION_ACCEPTE: 'publicationAuJORFAFaire', RENDRE_DECISION_ADMINISTRATION_REJETE: { target: 'decisionsEtNotificationsRejetAFaire', - actions: assign<PrmOctContext, { type: 'RENDRE_DECISION_ADMINISTRATION_REJETE' }>({ + actions: assign({ demarcheStatut: DemarchesStatutsIds.Rejete, }), }, @@ -615,7 +615,7 @@ const prmOctMachine = createMachine<PrmOctContext, XStateEvent>({ on: { FAIRE_PUBLICATION_AU_JORF: { target: 'notificationsAFaire', - actions: assign<PrmOctContext, { type: 'FAIRE_PUBLICATION_AU_JORF' }>({ + actions: assign({ demarcheStatut: DemarchesStatutsIds.Accepte, }), }, diff --git a/packages/api/src/business/rules-demarches/pxg/oct.cas.json b/packages/api/src/business/rules-demarches/pxg/oct.cas.json index fd79d6739..09f4d57a4 100644 --- a/packages/api/src/business/rules-demarches/pxg/oct.cas.json +++ b/packages/api/src/business/rules-demarches/pxg/oct.cas.json @@ -1 +1 @@ -[{"id":0,"demarcheStatutId":"eco","demarchePublique":false,"etapes":[{"date":"2022-06-14","etapeTypeId":"mfr","etapeStatutId":"aco"}]}] \ No newline at end of file +[{"id":0,"demarcheStatutId":"eco","demarchePublique":false,"etapes":[{"date":"2022-03-26","etapeTypeId":"mfr","etapeStatutId":"fai","paysId":"FR","surface":0.15}]},{"id":1,"demarcheStatutId":"acc","demarchePublique":true,"etapes":[{"date":"2020-07-03","etapeTypeId":"mfr","etapeStatutId":"fai","paysId":"FR"},{"date":"2020-07-12","etapeTypeId":"mdp","etapeStatutId":"fai"},{"date":"2020-09-08","etapeTypeId":"mca","etapeStatutId":"fai"},{"date":"2020-10-05","etapeTypeId":"rca","etapeStatutId":"fai","paysId":"FR"},{"date":"2020-11-16","etapeTypeId":"mcr","etapeStatutId":"fav"},{"date":"2021-07-15","etapeTypeId":"ssr","etapeStatutId":"fai"},{"date":"2021-07-21","etapeTypeId":"scl","etapeStatutId":"fai"},{"date":"2021-07-26","etapeTypeId":"wse","etapeStatutId":"fai"},{"date":"2021-09-26","etapeTypeId":"wae","etapeStatutId":"fav"},{"date":"2021-12-05","etapeTypeId":"epu","etapeStatutId":"fai"},{"date":"2022-01-03","etapeTypeId":"epc","etapeStatutId":"ter"},{"date":"2022-04-11","etapeTypeId":"apd","etapeStatutId":"fav"},{"date":"2022-05-02","etapeTypeId":"wtp","etapeStatutId":"fai"},{"date":"2022-05-13","etapeTypeId":"wau","etapeStatutId":"fav"},{"date":"2022-05-13","etapeTypeId":"acd","etapeStatutId":"fav"},{"date":"2022-05-23","etapeTypeId":"dex","etapeStatutId":"acc","paysId":"FR"}]},{"id":2,"demarcheStatutId":"acc","demarchePublique":true,"etapes":[{"date":"2019-06-23","etapeTypeId":"mfr","etapeStatutId":"fai","paysId":"FR"},{"date":"2019-06-23","etapeTypeId":"mdp","etapeStatutId":"fai"},{"date":"2019-09-12","etapeTypeId":"mca","etapeStatutId":"fai"},{"date":"2020-02-24","etapeTypeId":"rca","etapeStatutId":"fai","paysId":"FR"},{"date":"2020-04-03","etapeTypeId":"mcr","etapeStatutId":"fav"},{"date":"2020-04-03","etapeTypeId":"ssr","etapeStatutId":"fai"},{"date":"2020-04-11","etapeTypeId":"scl","etapeStatutId":"fai"},{"date":"2020-05-12","etapeTypeId":"wse","etapeStatutId":"fai"},{"date":"2020-07-12","etapeTypeId":"wae","etapeStatutId":"fav"},{"date":"2022-05-09","etapeTypeId":"apd","etapeStatutId":"fav"},{"date":"2022-06-05","etapeTypeId":"wtp","etapeStatutId":"fai"},{"date":"2022-06-05","etapeTypeId":"wau","etapeStatutId":"fav"},{"date":"2022-06-05","etapeTypeId":"acd","etapeStatutId":"fav"},{"date":"2022-07-25","etapeTypeId":"dex","etapeStatutId":"acc","paysId":"FR"}]},{"id":3,"demarcheStatutId":"acc","demarchePublique":true,"etapes":[{"date":"2020-11-17","etapeTypeId":"mfr","etapeStatutId":"fai","paysId":"FR"},{"date":"2020-11-17","etapeTypeId":"mdp","etapeStatutId":"fai"},{"date":"2021-04-11","etapeTypeId":"mca","etapeStatutId":"fai"},{"date":"2021-06-13","etapeTypeId":"rca","etapeStatutId":"fai","paysId":"FR"},{"date":"2021-06-28","etapeTypeId":"mcr","etapeStatutId":"fav"},{"date":"2021-06-28","etapeTypeId":"wse","etapeStatutId":"fai"},{"date":"2021-06-29","etapeTypeId":"scl","etapeStatutId":"fai"},{"date":"2021-08-29","etapeTypeId":"wae","etapeStatutId":"fav"},{"date":"2021-08-30","etapeTypeId":"ama","etapeStatutId":"fav"},{"date":"2021-09-13","etapeTypeId":"ssr","etapeStatutId":"fai"},{"date":"2022-01-26","etapeTypeId":"apd","etapeStatutId":"fav"},{"date":"2022-02-01","etapeTypeId":"wtp","etapeStatutId":"fai"},{"date":"2022-02-12","etapeTypeId":"wau","etapeStatutId":"fav"},{"date":"2022-02-13","etapeTypeId":"acd","etapeStatutId":"fav"},{"date":"2022-02-21","etapeTypeId":"dex","etapeStatutId":"acc","paysId":"FR"}]},{"id":4,"demarcheStatutId":"acc","demarchePublique":true,"etapes":[{"date":"2019-12-19","etapeTypeId":"mfr","etapeStatutId":"fai","paysId":"FR","surface":0.03},{"date":"2019-12-19","etapeTypeId":"mdp","etapeStatutId":"fai"},{"date":"2020-04-06","etapeTypeId":"mca","etapeStatutId":"fai"},{"date":"2020-05-28","etapeTypeId":"rca","etapeStatutId":"fai","paysId":"FR","surface":0.03},{"date":"2020-07-08","etapeTypeId":"mcr","etapeStatutId":"fav"},{"date":"2020-07-08","etapeTypeId":"scl","etapeStatutId":"fai"},{"date":"2020-07-08","etapeTypeId":"wse","etapeStatutId":"fai"},{"date":"2020-07-08","etapeTypeId":"wae","etapeStatutId":"fav"},{"date":"2020-07-08","etapeTypeId":"epu","etapeStatutId":"fai"},{"date":"2020-07-08","etapeTypeId":"epc","etapeStatutId":"ter"},{"date":"2020-07-08","etapeTypeId":"ssr","etapeStatutId":"fai"},{"date":"2020-08-05","etapeTypeId":"apd","etapeStatutId":"fav"},{"date":"2020-08-05","etapeTypeId":"wtp","etapeStatutId":"fai"},{"date":"2020-08-05","etapeTypeId":"wau","etapeStatutId":"fav"},{"date":"2020-09-30","etapeTypeId":"dex","etapeStatutId":"acc","paysId":"FR","surface":0.03}]},{"id":5,"demarcheStatutId":"acc","demarchePublique":true,"etapes":[{"date":"2017-10-31","etapeTypeId":"mfr","etapeStatutId":"fai","paysId":"FR"},{"date":"2017-10-31","etapeTypeId":"mdp","etapeStatutId":"fai"},{"date":"2018-01-10","etapeTypeId":"mca","etapeStatutId":"fai"},{"date":"2018-12-15","etapeTypeId":"rca","etapeStatutId":"fai","paysId":"FR"},{"date":"2019-01-17","etapeTypeId":"mcr","etapeStatutId":"fav"},{"date":"2019-01-17","etapeTypeId":"scl","etapeStatutId":"fai"},{"date":"2019-01-17","etapeTypeId":"ssr","etapeStatutId":"fai"},{"date":"2019-01-18","etapeTypeId":"wse","etapeStatutId":"fai"},{"date":"2019-03-18","etapeTypeId":"wae","etapeStatutId":"fav"},{"date":"2019-08-23","etapeTypeId":"apd","etapeStatutId":"fav"},{"date":"2019-09-13","etapeTypeId":"wtp","etapeStatutId":"fai"},{"date":"2019-09-13","etapeTypeId":"wau","etapeStatutId":"fav"},{"date":"2019-09-13","etapeTypeId":"acd","etapeStatutId":"fav"},{"date":"2019-10-10","etapeTypeId":"dex","etapeStatutId":"acc","paysId":"FR"}]},{"id":6,"demarcheStatutId":"acc","demarchePublique":true,"etapes":[{"date":"2022-06-27","etapeTypeId":"mfr","etapeStatutId":"fai","paysId":"FR","surface":0.19},{"date":"2022-06-27","etapeTypeId":"mdp","etapeStatutId":"fai"},{"date":"2022-07-02","etapeTypeId":"mcr","etapeStatutId":"fav"},{"date":"2022-09-04","etapeTypeId":"wse","etapeStatutId":"fai"},{"date":"2022-11-04","etapeTypeId":"wae","etapeStatutId":"fav"},{"date":"2022-12-15","etapeTypeId":"epu","etapeStatutId":"fai"},{"date":"2023-01-16","etapeTypeId":"ssr","etapeStatutId":"fai"},{"date":"2023-01-16","etapeTypeId":"scl","etapeStatutId":"fai"},{"date":"2023-01-16","etapeTypeId":"epc","etapeStatutId":"ter"},{"date":"2023-04-21","etapeTypeId":"apd","etapeStatutId":"fav"},{"date":"2023-06-03","etapeTypeId":"wtp","etapeStatutId":"fai"},{"date":"2023-06-17","etapeTypeId":"wau","etapeStatutId":"fav"},{"date":"2023-06-23","etapeTypeId":"dex","etapeStatutId":"acc","paysId":"FR","surface":0.19}]},{"id":7,"demarcheStatutId":"eco","demarchePublique":false,"etapes":[{"date":"2022-06-05","etapeTypeId":"mfr","etapeStatutId":"aco"}]}] \ No newline at end of file diff --git a/packages/api/src/business/rules-demarches/pxg/oct.machine.test.ts b/packages/api/src/business/rules-demarches/pxg/oct.machine.test.ts index 683a339f8..3dbedcd3f 100644 --- a/packages/api/src/business/rules-demarches/pxg/oct.machine.test.ts +++ b/packages/api/src/business/rules-demarches/pxg/oct.machine.test.ts @@ -12,7 +12,7 @@ describe('vérifie l’arbre d’octroi des PXG', () => { test('peut créer une "mfr"', () => { const etapes = [{ ...ETES.demande.FAIT, date: toCaminoDate('2022-04-14') }] const service = orderAndInterpretMachine(pxgOctMachine, etapes) - expect(service).canOnlyTransitionTo({ machine: pxgOctMachine, date: toCaminoDate('2022-04-14') }, ['DEPOSER_DEMANDE']) + expect(service).canOnlyTransitionTo({ machine: pxgOctMachine, date: toCaminoDate('2022-04-14') }, ['DEPOSER_DEMANDE', 'OUVRIR_ENQUETE_PUBLIQUE', 'RENDRE_DECISION_ADMINISTRATION_FAVORABLE']) }) test('peut avoir une démarche en instruction', () => { @@ -144,6 +144,86 @@ describe('vérifie l’arbre d’octroi des PXG', () => { expect(service).canOnlyTransitionTo({ machine: pxgOctMachine, date: toCaminoDate('2022-04-26') }, []) }) + describe('démarches simplifiées', () => { + test('la plus simple possible', () => { + const etapes = [ + { ...ETES.decisionDeLadministration.ACCEPTE, date: toCaminoDate('2022-04-14') }, + { ...ETES.publicationDeDecisionAuRecueilDesActesAdministratifs.FAIT, date: toCaminoDate('2022-04-15') }, + ] + const service = orderAndInterpretMachine(pxgOctMachine, etapes) + expect(service.getSnapshot().context.demarcheStatut).toBe(DemarchesStatutsIds.Accepte) + }) + + test('ne peut pas faire une rpu seule', () => { + const etapes = [{ ...ETES.publicationDeDecisionAuRecueilDesActesAdministratifs.FAIT, date: toCaminoDate('2022-04-15') }] + expect(() => orderAndInterpretMachine(pxgOctMachine, etapes)).toThrowErrorMatchingInlineSnapshot( + '"Error: cannot execute step: \'{\\"etapeTypeId\\":\\"rpu\\",\\"etapeStatutId\\":\\"fai\\",\\"date\\":\\"2022-04-15\\"}\' after \'[]\'. The event {\\"type\\":\\"PUBLIER_DECISION_RECUEIL_DES_ACTES_ADMINISTRATIFS\\"} should be one of \'FAIRE_DEMANDE,OUVRIR_ENQUETE_PUBLIQUE,RENDRE_DECISION_ADMINISTRATION_FAVORABLE\'"' + ) + }) + + test('peut créer avec une demande', () => { + const etapes = [ + { ...ETES.demande.FAIT, date: toCaminoDate('2022-04-14') }, + { ...ETES.decisionDeLadministration.ACCEPTE, date: toCaminoDate('2022-04-14') }, + { ...ETES.publicationDeDecisionAuRecueilDesActesAdministratifs.FAIT, date: toCaminoDate('2022-04-15') }, + ] + const service = orderAndInterpretMachine(pxgOctMachine, etapes) + expect(service.getSnapshot().context.demarcheStatut).toBe(DemarchesStatutsIds.Accepte) + }) + test('peut créer avec une enquête publique', () => { + const etapes = [ + { ...ETES.ouvertureDeLenquetePublique.FAIT, date: toCaminoDate('2022-04-13') }, + { ...ETES.clotureDeLenquetePublique.TERMINE, date: toCaminoDate('2022-04-14') }, + { ...ETES.decisionDeLadministration.ACCEPTE, date: toCaminoDate('2022-04-14') }, + { ...ETES.publicationDeDecisionAuRecueilDesActesAdministratifs.FAIT, date: toCaminoDate('2022-04-15') }, + ] + const service = orderAndInterpretMachine(pxgOctMachine, etapes) + expect(service.getSnapshot().context.demarcheStatut).toBe(DemarchesStatutsIds.Accepte) + }) + test('peut créer avec une demande et une enquête publique', () => { + const etapes = [ + { ...ETES.demande.FAIT, date: toCaminoDate('2022-04-12') }, + { ...ETES.ouvertureDeLenquetePublique.FAIT, date: toCaminoDate('2022-04-13') }, + { ...ETES.clotureDeLenquetePublique.TERMINE, date: toCaminoDate('2022-04-14') }, + { ...ETES.decisionDeLadministration.ACCEPTE, date: toCaminoDate('2022-04-14') }, + { ...ETES.publicationDeDecisionAuRecueilDesActesAdministratifs.FAIT, date: toCaminoDate('2022-04-15') }, + ] + const service = orderAndInterpretMachine(pxgOctMachine, etapes) + expect(service.getSnapshot().context.demarcheStatut).toBe(DemarchesStatutsIds.Accepte) + }) + + test('ne peut pas aller dans la démarche simplifiée une fois la demande déposée', () => { + const etapes = [ + { ...ETES.demande.FAIT, date: toCaminoDate('2022-04-12') }, + { ...ETES.depotDeLaDemande.FAIT, date: toCaminoDate('2022-04-12') }, + { ...ETES.ouvertureDeLenquetePublique.FAIT, date: toCaminoDate('2022-04-13') }, + ] + expect(() => orderAndInterpretMachine(pxgOctMachine, etapes)).toThrowErrorMatchingInlineSnapshot( + '"Error: cannot execute step: \'{\\"etapeTypeId\\":\\"epu\\",\\"etapeStatutId\\":\\"fai\\",\\"date\\":\\"2022-04-13\\"}\' after \'[\\"mfr_fai\\",\\"mdp_fai\\"]\'. The event {\\"type\\":\\"OUVRIR_ENQUETE_PUBLIQUE\\"} should be one of \'DEMANDER_COMPLEMENTS_POUR_RECEVABILITE,FAIRE_RECEVABILITE_DEMANDE_DEFAVORABLE,FAIRE_RECEVABILITE_DEMANDE_FAVORABLE\'"' + ) + }) + + test("ne peut pas refaire de décision de l'administration", () => { + const etapes = [ + { ...ETES.decisionDeLadministration.ACCEPTE, date: toCaminoDate('2022-04-14') }, + { ...ETES.decisionDeLadministration.ACCEPTE, date: toCaminoDate('2022-04-15') }, + ] + expect(() => orderAndInterpretMachine(pxgOctMachine, etapes)).toThrowErrorMatchingInlineSnapshot( + '"Error: cannot execute step: \'{\\"etapeTypeId\\":\\"dex\\",\\"etapeStatutId\\":\\"acc\\",\\"date\\":\\"2022-04-15\\"}\' after \'[\\"dex_acc\\"]\'. The event {\\"type\\":\\"RENDRE_DECISION_ADMINISTRATION_FAVORABLE\\"} should be one of \'PUBLIER_DECISION_RECUEIL_DES_ACTES_ADMINISTRATIFS\'"' + ) + }) + + test("ne peut pas faire une ouverture d'enquête publique après une décision de l'administration", () => { + const etapes = [ + { ...ETES.decisionDeLadministration.ACCEPTE, date: toCaminoDate('2022-04-14') }, + { ...ETES.ouvertureDeLenquetePublique.FAIT, date: toCaminoDate('2022-04-16') }, + ] + expect(() => orderAndInterpretMachine(pxgOctMachine, etapes)).toThrowErrorMatchingInlineSnapshot( + '"Error: cannot execute step: \'{\\"etapeTypeId\\":\\"epu\\",\\"etapeStatutId\\":\\"fai\\",\\"date\\":\\"2022-04-16\\"}\' after \'[\\"dex_acc\\"]\'. The event {\\"type\\":\\"OUVRIR_ENQUETE_PUBLIQUE\\"} should be one of \'PUBLIER_DECISION_RECUEIL_DES_ACTES_ADMINISTRATIFS\'"' + ) + }) + }) + // pour regénérer le oct.cas.json: `npm run test:generate-data -w packages/api` test.each(etapesProd as any[])('cas réel N°$id', demarche => { // ici les étapes sont déjà ordonnées diff --git a/packages/api/src/business/rules-demarches/pxg/oct.machine.ts b/packages/api/src/business/rules-demarches/pxg/oct.machine.ts index 50beea4f1..328f7b3b6 100644 --- a/packages/api/src/business/rules-demarches/pxg/oct.machine.ts +++ b/packages/api/src/business/rules-demarches/pxg/oct.machine.ts @@ -134,14 +134,16 @@ interface PxgContext extends CaminoCommonContext { saisineDesCollectivitesLocalesFaites: boolean saisineAutoriteEnvironnementaleFaite: boolean avisAutoriteEnvironnementaleFaite: boolean + depotFait: boolean + enquetePubliqueOuvertePourLaDemarcheSimplifiee: boolean } -const peutRendreAvisDREAL = (context: PxgContext): boolean => { +const peutRendreAvisDREAL = ({ context }: { context: PxgContext }): boolean => { return context.saisineDesServicesFaite && context.saisineDesCollectivitesLocalesFaites && context.avisAutoriteEnvironnementaleFaite } -const pxgOctMachine = createMachine<PxgContext, PXGOctXStateEvent>({ - predictableActionArguments: true, +const pxgOctMachine = createMachine({ + types: {} as { context: PxgContext; events: PXGOctXStateEvent }, id: 'PXGOct', initial: 'demandeAFaire', context: { @@ -151,15 +153,31 @@ const pxgOctMachine = createMachine<PxgContext, PXGOctXStateEvent>({ saisineDesCollectivitesLocalesFaites: false, saisineAutoriteEnvironnementaleFaite: false, avisAutoriteEnvironnementaleFaite: false, + depotFait: false, + enquetePubliqueOuvertePourLaDemarcheSimplifiee: false, }, on: { FAIRE_DESISTEMENT_DEMANDEUR: { - cond: context => [DemarchesStatutsIds.Depose, DemarchesStatutsIds.EnInstruction].includes(context.demarcheStatut), - target: 'desistementDuDemandeurRendu', + guard: ({ context, event: _event }) => [DemarchesStatutsIds.Depose, DemarchesStatutsIds.EnInstruction].includes(context.demarcheStatut), + target: '.desistementDuDemandeurRendu', }, FAIRE_CLASSEMENT_SANS_SUITE: { - cond: context => [DemarchesStatutsIds.Depose, DemarchesStatutsIds.EnInstruction].includes(context.demarcheStatut), - target: 'classementSansSuiteRendu', + guard: ({ context, event: _event }) => [DemarchesStatutsIds.Depose, DemarchesStatutsIds.EnInstruction].includes(context.demarcheStatut), + target: '.classementSansSuiteRendu', + }, + RENDRE_DECISION_ADMINISTRATION_FAVORABLE: { + target: '.publicationDeDecisionAuRecueilDesActesAdministratifsAFaire', + guard: ({ context, event: _event }) => !context.depotFait && context.demarcheStatut === DemarchesStatutsIds.EnConstruction, + actions: assign({ + demarcheStatut: DemarchesStatutsIds.Accepte, + }), + }, + OUVRIR_ENQUETE_PUBLIQUE: { + target: '.clotureEnquetePubliqueAFaire', + guard: ({ context, event: _event }) => !context.depotFait && !context.enquetePubliqueOuvertePourLaDemarcheSimplifiee && context.demarcheStatut !== DemarchesStatutsIds.Accepte, + actions: assign({ + enquetePubliqueOuvertePourLaDemarcheSimplifiee: true, + }), }, }, states: { @@ -170,7 +188,25 @@ const pxgOctMachine = createMachine<PxgContext, PXGOctXStateEvent>({ }, depotDeLaDemandeAFaire: { on: { - DEPOSER_DEMANDE: 'recevabiliteDeLaDemandeAFaire', + DEPOSER_DEMANDE: { + target: 'recevabiliteDeLaDemandeAFaire', + actions: assign({ + depotFait: true, + }), + }, + }, + }, + clotureEnquetePubliqueAFaire: { + on: { CLOTURER_ENQUETE_PUBLIQUE: 'clotureEnquetePubliqueFaite' }, + }, + clotureEnquetePubliqueFaite: { + on: { + RENDRE_DECISION_ADMINISTRATION_FAVORABLE: { + target: 'publicationDeDecisionAuRecueilDesActesAdministratifsAFaire', + actions: assign({ + demarcheStatut: DemarchesStatutsIds.Accepte, + }), + }, }, }, recevabiliteDeLaDemandeAFaire: { @@ -178,7 +214,7 @@ const pxgOctMachine = createMachine<PxgContext, PXGOctXStateEvent>({ DEMANDER_COMPLEMENTS_POUR_RECEVABILITE: 'complementsPourRecevabiliteAFaire', FAIRE_RECEVABILITE_DEMANDE_FAVORABLE: { target: 'saisinesAFaire', - actions: assign<PxgContext, { type: 'FAIRE_RECEVABILITE_DEMANDE_FAVORABLE' }>({ + actions: assign({ demarcheStatut: DemarchesStatutsIds.Depose, visibilite: 'publique', }), @@ -191,7 +227,7 @@ const pxgOctMachine = createMachine<PxgContext, PXGOctXStateEvent>({ RECEVOIR_COMPLEMENTS_POUR_RECEVABILITE: 'recevabiliteDeLaDemandeAFaire', FAIRE_RECEVABILITE_DEMANDE_FAVORABLE: { target: 'saisinesAFaire', - actions: assign<PxgContext, { type: 'FAIRE_RECEVABILITE_DEMANDE_FAVORABLE' }>({ + actions: assign({ demarcheStatut: DemarchesStatutsIds.Depose, visibilite: 'publique', }), @@ -213,13 +249,13 @@ const pxgOctMachine = createMachine<PxgContext, PXGOctXStateEvent>({ rendreAvisDrealPasEncorePossible: { always: { target: 'rendreAvisDrealAFaire', - cond: peutRendreAvisDREAL, + guard: peutRendreAvisDREAL, }, }, rendreAvisDrealAFaire: { on: { RENDRE_AVIS_DREAL: { - cond: peutRendreAvisDREAL, + guard: peutRendreAvisDREAL, target: '#transmissionDuProjetDePrescriptionsAuDemandeurAFaire', }, }, @@ -233,9 +269,9 @@ const pxgOctMachine = createMachine<PxgContext, PXGOctXStateEvent>({ on: { FAIRE_SAISINES_DES_SERVICES: { target: 'avisDesServicesARendre', - actions: assign<PxgContext, { type: 'FAIRE_SAISINES_DES_SERVICES' }>({ + actions: assign({ saisineDesServicesFaite: true, - demarcheStatut: (context, _event) => { + demarcheStatut: ({ context }) => { return context.saisineDesCollectivitesLocalesFaites && context.saisineAutoriteEnvironnementaleFaite ? DemarchesStatutsIds.EnInstruction : context.demarcheStatut }, }), @@ -346,9 +382,9 @@ const pxgOctMachine = createMachine<PxgContext, PXGOctXStateEvent>({ on: { FAIRE_SAISINE_DES_COLLECTIVITES_LOCALES: { target: 'consultationCLEEtConseilsMunicipauxAFaire', - actions: assign<PxgContext, { type: 'FAIRE_SAISINE_DES_COLLECTIVITES_LOCALES' }>({ + actions: assign({ saisineDesCollectivitesLocalesFaites: true, - demarcheStatut: (context, _event) => { + demarcheStatut: ({ context }) => { return context.saisineAutoriteEnvironnementaleFaite && context.saisineDesServicesFaite ? DemarchesStatutsIds.EnInstruction : context.demarcheStatut }, }), @@ -375,7 +411,7 @@ const pxgOctMachine = createMachine<PxgContext, PXGOctXStateEvent>({ consultationCLEDuSAGEAFaire: { on: { FAIRE_CONSULTATION_CLE_DU_SAGE: { - cond: context => context.saisineDesCollectivitesLocalesFaites && context.avisAutoriteEnvironnementaleFaite, + guard: ({ context, event: _event }) => context.saisineDesCollectivitesLocalesFaites && context.avisAutoriteEnvironnementaleFaite, target: 'consultationCLEDuSAGEAFait', }, }, @@ -394,9 +430,9 @@ const pxgOctMachine = createMachine<PxgContext, PXGOctXStateEvent>({ on: { FAIRE_SAISINE_AUTORITE_ENVIRONNEMENTALE: { target: 'avisAutoriteEnvironnementaleAFaire', - actions: assign<PxgContext, { type: 'FAIRE_SAISINE_AUTORITE_ENVIRONNEMENTALE' }>({ + actions: assign({ saisineAutoriteEnvironnementaleFaite: true, - demarcheStatut: (context, _event) => { + demarcheStatut: ({ context, event: _event }) => { return context.saisineDesCollectivitesLocalesFaites && context.saisineDesServicesFaite ? DemarchesStatutsIds.EnInstruction : context.demarcheStatut }, }), @@ -407,7 +443,7 @@ const pxgOctMachine = createMachine<PxgContext, PXGOctXStateEvent>({ on: { RENDRE_AVIS_AUTORITE_ENVIRONNEMENTALE: { target: 'ouvertureEnquetePubliqueAFaire', - actions: assign<PxgContext, { type: 'RENDRE_AVIS_AUTORITE_ENVIRONNEMENTALE' }>({ + actions: assign({ avisAutoriteEnvironnementaleFaite: true, }), }, @@ -429,7 +465,7 @@ const pxgOctMachine = createMachine<PxgContext, PXGOctXStateEvent>({ avisDREALARendre: { on: { RENDRE_AVIS_DREAL: { - cond: peutRendreAvisDREAL, + guard: peutRendreAvisDREAL, target: 'transmissionDuProjetDePrescriptionsAuDemandeurAFaire', }, }, @@ -450,13 +486,13 @@ const pxgOctMachine = createMachine<PxgContext, PXGOctXStateEvent>({ RENDRE_PASSAGE_CODERST: 'decisionDeLAdministrationARendre', RENDRE_DECISION_ADMINISTRATION_FAVORABLE: { target: 'notificationDuDemandeurEtPublicationDeDecisionAuRecueilDesActesAdministratifsAFaire', - actions: assign<PxgContext, { type: 'RENDRE_DECISION_ADMINISTRATION_FAVORABLE' }>({ + actions: assign({ demarcheStatut: DemarchesStatutsIds.Accepte, }), }, RENDRE_DECISION_ADMINISTRATION_DEFAVORABLE: { target: 'notificationDuDemandeurEtPublicationDeDecisionAuRecueilDesActesAdministratifsAFaire', - actions: assign<PxgContext, { type: 'RENDRE_DECISION_ADMINISTRATION_DEFAVORABLE' }>({ + actions: assign({ demarcheStatut: DemarchesStatutsIds.Rejete, }), }, @@ -466,13 +502,13 @@ const pxgOctMachine = createMachine<PxgContext, PXGOctXStateEvent>({ on: { RENDRE_DECISION_ADMINISTRATION_FAVORABLE: { target: 'notificationDuDemandeurEtPublicationDeDecisionAuRecueilDesActesAdministratifsAFaire', - actions: assign<PxgContext, { type: 'RENDRE_DECISION_ADMINISTRATION_FAVORABLE' }>({ + actions: assign({ demarcheStatut: DemarchesStatutsIds.Accepte, }), }, RENDRE_DECISION_ADMINISTRATION_DEFAVORABLE: { target: 'notificationDuDemandeurEtPublicationDeDecisionAuRecueilDesActesAdministratifsAFaire', - actions: assign<PxgContext, { type: 'RENDRE_DECISION_ADMINISTRATION_DEFAVORABLE' }>({ + actions: assign({ demarcheStatut: DemarchesStatutsIds.Rejete, }), }, @@ -507,16 +543,24 @@ const pxgOctMachine = createMachine<PxgContext, PXGOctXStateEvent>({ }, }, }, + publicationDeDecisionAuRecueilDesActesAdministratifsAFaire: { + on: { + PUBLIER_DECISION_RECUEIL_DES_ACTES_ADMINISTRATIFS: 'publicationDeDecisionAuRecueilDesActesAdministratifsFaite', + }, + }, + publicationDeDecisionAuRecueilDesActesAdministratifsFaite: { + type: 'final', + }, desistementDuDemandeurRendu: { type: 'final', - entry: assign<PxgContext>({ + entry: assign({ demarcheStatut: DemarchesStatutsIds.Desiste, visibilite: 'publique', }), }, classementSansSuiteRendu: { type: 'final', - entry: assign<PxgContext>({ + entry: assign({ demarcheStatut: DemarchesStatutsIds.ClasseSansSuite, visibilite: 'publique', }), -- GitLab