Skip to content
This repository was archived by the owner on Mar 5, 2025. It is now read-only.

feat(simplification): merge des enquêtes publiques #1459

Merged
merged 7 commits into from
Sep 16, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,7 @@ import { Pool } from 'pg'
import { EtapeId, getStatutId } from 'camino-common/src/etape'
import { getCurrent } from 'camino-common/src/date'
import { EtapeStatutId } from 'camino-common/src/static/etapesStatuts'
import { getParticipationEtapes, updateParticipationStatut } from '../../database/queries/titres-etapes.queries'
import { ETAPES_TYPES } from 'camino-common/src/static/etapesTypes'
import { getParticipationOrEnqueteEtapes, updateParticipationOrEnqueteStatut } from '../../database/queries/titres-etapes.queries'
import { simpleContenuToFlattenedContenu } from 'camino-common/src/sections'

export const titresEtapesStatutUpdate = async (pool: Pool): Promise<EtapeId[]> => {
Expand All @@ -13,21 +12,21 @@ export const titresEtapesStatutUpdate = async (pool: Pool): Promise<EtapeId[]> =
const titresEtapesIdsUpdated: EtapeId[] = []

const currentDate = getCurrent()
const participationEtapes = await getParticipationEtapes(pool)
const participationOrEnqueteEtapes = await getParticipationOrEnqueteEtapes(pool)

for (const etape of participationEtapes) {
for (const etape of participationOrEnqueteEtapes) {
const newStatut: EtapeStatutId = getStatutId(
{
...etape,
statutId: etape.etape_statut_id,
typeId: ETAPES_TYPES.participationDuPublic,
contenu: simpleContenuToFlattenedContenu(etape.titre_type_id, etape.demarche_type_id, ETAPES_TYPES.participationDuPublic, etape.contenu, etape.heritage_contenu),
typeId: etape.type_id,
contenu: simpleContenuToFlattenedContenu(etape.titre_type_id, etape.demarche_type_id, etape.type_id, etape.contenu, etape.heritage_contenu),
},
currentDate
)

if (newStatut !== etape.etape_statut_id) {
await updateParticipationStatut(pool, etape.id, newStatut)
await updateParticipationOrEnqueteStatut(pool, etape.id, newStatut)
console.info('titre / démarche / étape : statut (mise à jour) ->', `${etape.id} : ${etape.etape_statut_id} => ${newStatut}`)

titresEtapesIdsUpdated.push(etape.id)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -90,7 +90,7 @@ const trad: { [key in Event]: { db: DBEtat; mainStep: boolean } } = {
RENDRE_AVIS_COLLECTIVITES: { db: ETES.avisDesCollectivites, mainStep: true },
RENDRE_AVIS_SERVICES_COMMISSIONS: { db: ETES.avisDesServicesEtCommissionsConsultatives, mainStep: true },
RENDRE_AVIS_PREFET: { db: ETES.avisDuPrefet, mainStep: true },
OUVRIR_ENQUETE_PUBLIQUE: { db: ETES.ouvertureDeLenquetePublique, mainStep: true },
OUVRIR_ENQUETE_PUBLIQUE: { db: ETES.enquetePublique, mainStep: true },
OUVRIR_PARTICIPATION_DU_PUBLIC: { db: ETES.participationDuPublic, mainStep: true },
RENDRE_DECISION_ADMINISTRATION_ACCEPTEE: { db: { ACCEPTE: ETES.decisionDeLadministration.ACCEPTE }, mainStep: true },
// RECEVOIR_REPONSE_DEMANDEUR: {db: ETES.demandeur}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -741,7 +741,7 @@ describe("publicité d'une démarche", () => {
).toMatchObject({ publicLecture: true })
})

test.each<EtapeTypeId>(['anf', 'dex', 'dpu', 'rpu', 'ppu', 'epu', 'epc'])("une démarche d’un titre non énergétique dont l'étape la plus récente est %s est public", etapeTypeId => {
test.each<EtapeTypeId>(['anf', 'dex', 'dpu', 'rpu', 'ppu', 'epu'])("une démarche d’un titre non énergétique dont l'étape la plus récente est %s est public", etapeTypeId => {
expect(
titreDemarchePublicFind(
{
Expand Down
99 changes: 70 additions & 29 deletions packages/api/src/business/rules/titre-demarche-public-find.ts
Original file line number Diff line number Diff line change
@@ -1,13 +1,14 @@
import { DemarcheTypeId } from 'camino-common/src/static/demarchesTypes'
import { EtapeTypeId } from 'camino-common/src/static/etapesTypes'
import { getDomaineId, TitreTypeId } from 'camino-common/src/static/titresTypes'
import { DEMARCHES_TYPES_IDS, DemarcheTypeId } from 'camino-common/src/static/demarchesTypes'
import { ETAPES_TYPES, EtapeTypeId } from 'camino-common/src/static/etapesTypes'
import { getDomaineId, TITRES_TYPES_IDS, TitreTypeId } from 'camino-common/src/static/titresTypes'
import { getEtapesTDE } from 'camino-common/src/static/titresTypes_demarchesTypes_etapesTypes/index'
import { ITitreEtape, ITitreDemarche } from '../../types'
import { machineFind } from '../rules-demarches/definitions'
import { titreEtapeForMachineValidator, toMachineEtapes } from '../rules-demarches/machine-common'
import { titreEtapesSortAscByOrdre } from '../utils/titre-etapes-sort'
import { titreInModificationEnInstance } from './titre-statut-id-find'
import { isEtapeStatusRejete } from 'camino-common/src/static/etapesStatuts'
import { ETAPES_STATUTS, isEtapeStatusRejete } from 'camino-common/src/static/etapesStatuts'
import { DOMAINES_IDS } from 'camino-common/src/static/domaines'
const titreDemarchePublicLectureFind = (
publicLecture: boolean,
demarcheTypeId: DemarcheTypeId,
Expand All @@ -19,40 +20,47 @@ const titreDemarchePublicLectureFind = (
// si le type de démarche est retrait de la demande ou déchéance
// et que le type d'étape est saisine du préfet
// alors la démarche est publique
if (['ret', 'dec'].includes(demarcheTypeId) && titreEtape.typeId === 'spp') {
if ([DEMARCHES_TYPES_IDS.Retrait, DEMARCHES_TYPES_IDS.Decheance].includes(demarcheTypeId) && titreEtape.typeId === ETAPES_TYPES.saisineDuPrefet) {
return true
}

// si le type d'étape est un classement sans suite
// et le type de titre n'est ni ARM ni AXM
// alors la démarche n'est pas publique
if (titreEtape.typeId === 'css' && (!titreTypeId || !['arm', 'axm'].includes(titreTypeId))) {
if (
titreEtape.typeId === ETAPES_TYPES.classementSansSuite &&
(!titreTypeId || ![TITRES_TYPES_IDS.AUTORISATION_DE_RECHERCHE_METAUX, TITRES_TYPES_IDS.AUTORISATION_D_EXPLOITATION_METAUX].includes(titreTypeId))
) {
return false
}

// si le type d'étape est recevabilité de la demande
// et que le type de titre n'est pas ARM
// et que la démarche ne peut contenir de mise en concurrence au JORF ou JOUE
// alors la démarche est publique
if (titreEtape.typeId === 'mcr' && (!titreTypeId || titreTypeId !== 'arm') && !demarcheTypeEtapesTypes.find(et => ['anf', 'ane'].includes(et))) {
if (
titreEtape.typeId === ETAPES_TYPES.recevabiliteDeLaDemande &&
(!titreTypeId || titreTypeId !== TITRES_TYPES_IDS.AUTORISATION_DE_RECHERCHE_METAUX) &&
!demarcheTypeEtapesTypes.find(et => et === ETAPES_TYPES.avisDeMiseEnConcurrenceAuJORF)
) {
return true
}

// si le type d'étape est mise en concurrence au JORF ou JOUE
// si le type d'étape est mise en concurrence au JORF
// alors la démarche est publique
if (['anf', 'ane'].includes(titreEtape.typeId)) {
if (titreEtape.typeId === ETAPES_TYPES.avisDeMiseEnConcurrenceAuJORF) {
return true
}

// si le type d'étape est participation du public
// alors la démarche est publique
if (titreEtape.typeId === 'ppu') {
if (titreEtape.typeId === ETAPES_TYPES.participationDuPublic) {
return true
}

// si le type d'étape est publication de l'avis de décision implicite
// alors la démarche est publique
if (titreEtape.typeId === 'apu') {
if (titreEtape.typeId === ETAPES_TYPES.publicationDeLavisDeDecisionImplicite) {
return true
}

Expand All @@ -61,84 +69,117 @@ const titreDemarchePublicLectureFind = (
// ou avis de la commission des ARM (si pas de saisine)
// ou décision de l'ONF (étape historique)
// alors la démarche est publique
if (titreTypeId === 'arm' && ['sca', 'aca', 'def'].includes(titreEtape.typeId)) {
if (
titreTypeId === TITRES_TYPES_IDS.AUTORISATION_DE_RECHERCHE_METAUX &&
[
ETAPES_TYPES.saisineDeLaCommissionDesAutorisationsDeRecherchesMinieres_CARM_,
ETAPES_TYPES.avisDeLaCommissionDesAutorisationsDeRecherchesMinieres_CARM_,
ETAPES_TYPES.decisionDeLOfficeNationalDesForets,
].includes(titreEtape.typeId)
) {
return true
}

// si le type de titre est ARM ou AXM
// et que le type d'étape est désistement du demandeur
// alors la démarche est publique
if (titreTypeId && ['arm', 'axm'].includes(titreTypeId) && titreEtape.typeId === 'des') {
if (
titreTypeId &&
[TITRES_TYPES_IDS.AUTORISATION_DE_RECHERCHE_METAUX, TITRES_TYPES_IDS.AUTORISATION_D_EXPLOITATION_METAUX].includes(titreTypeId) &&
titreEtape.typeId === ETAPES_TYPES.desistementDuDemandeur
) {
return true
}

// si le type d'étape est décision implicite
// ou décision de l'administration
// et que le statut est rejeté
if (['dim', 'dex'].includes(titreEtape.typeId) && isEtapeStatusRejete(titreEtape.statutId)) {
if ([ETAPES_TYPES.decisionImplicite, ETAPES_TYPES.decisionDeLadministration].includes(titreEtape.typeId) && isEtapeStatusRejete(titreEtape.statutId)) {
// si le type de titre est AXM
// alors la démarche est publique
// sinon la démarche n'est pas (plus) publique
return titreTypeId === 'axm'
return titreTypeId === TITRES_TYPES_IDS.AUTORISATION_D_EXPLOITATION_METAUX
}

// si le type d'étape est décision implicite
// ou décision de l'administration
// ou publication au JORF
// et que le statut est accepté
// alors la démarche est publique
if (['dim', 'dex', 'dpu'].includes(titreEtape.typeId) && titreEtape.statutId === 'acc') {
if (
[ETAPES_TYPES.decisionImplicite, ETAPES_TYPES.decisionDeLadministration, ETAPES_TYPES.publicationDeDecisionAuJORF].includes(titreEtape.typeId) &&
titreEtape.statutId === ETAPES_STATUTS.ACCEPTE
) {
return true
}

// si le type d'étape est publication de décision unilatérale au JORF
// ou décision unilatérale de l'administration
// ou publication de décision au recueil des actes administratifs
// alors la démarche est publique
if (['dup', 'dux', 'rpu'].includes(titreEtape.typeId)) {
if ([ETAPES_TYPES.decisionAdministrative, ETAPES_TYPES.publicationDeDecisionAuRecueilDesActesAdministratifs].includes(titreEtape.typeId)) {
return true
}

// si le type de titre est ARM
// et que le type d'étape est signature de l'autorisation de recherche minière
// ou signature de l'avenant à l'autorisation de recherche minière
// alors la démarche est publique
if (titreTypeId === 'arm' && ['sco', 'aco'].includes(titreEtape.typeId)) {
if (
titreTypeId === TITRES_TYPES_IDS.AUTORISATION_DE_RECHERCHE_METAUX &&
[ETAPES_TYPES.signatureDeLautorisationDeRechercheMiniere, ETAPES_TYPES.avenantALautorisationDeRechercheMiniere].includes(titreEtape.typeId)
) {
return true
}

// - le type de l’étape est annulation de la décision (and)
// - et si le statut est favorable
if (['and'].includes(titreEtape.typeId) && titreEtape.statutId === 'fav') {
if (titreEtape.typeId === ETAPES_TYPES.decisionDuJugeAdministratif && titreEtape.statutId === ETAPES_STATUTS.FAVORABLE) {
// - alors, la démarche est publique
return true
}

// Si le type de titre est ARM et que le type de démarche est renonciation
// et que l’expertise de l’onf est cours (recevabilité de la demande faite), ou si la démarche a été désistée ou si classée sans suite
if (titreTypeId === 'arm' && ['ren', 'pro'].includes(demarcheTypeId) && ['mcr', 'css', 'des'].includes(titreEtape.typeId)) {
if (
titreTypeId === TITRES_TYPES_IDS.AUTORISATION_DE_RECHERCHE_METAUX &&
[DEMARCHES_TYPES_IDS.Renonciation, DEMARCHES_TYPES_IDS.Prolongation].includes(demarcheTypeId) &&
[ETAPES_TYPES.recevabiliteDeLaDemande, ETAPES_TYPES.classementSansSuite, ETAPES_TYPES.desistementDuDemandeur].includes(titreEtape.typeId)
) {
return true
}

// public pour tous des titres non énergétiques M, W, C avec une des étapes suivantes :
// avis de concurrence au JOUE (ane)
// avis de concurrence au JORF (anf)
// décision de l'administration (dex)
// publication de décision au JORF (dpu)
// publication de décision administrative au JORF (dup)
// publication de décision au recueil des actes administratifs (rpu)
// ouverture de la participation du public (ppu)
// clôture de la participation du public`(ppc)
// ouverture de l’enquête publique (epu)
// clôture de l’enquête publique (epc)
// participation du public (ppu)
// enquête publique (epu)

const domaineId = titreTypeId ? getDomaineId(titreTypeId) : null
if (domaineId && ['m', 'w', 'c'].includes(domaineId) && ['ane', 'anf', 'dex', 'dpu', 'dup', 'rpu', 'ppu', 'epu', 'epc'].includes(titreEtape.typeId)) {
if (
domaineId &&
[DOMAINES_IDS.METAUX, DOMAINES_IDS.GRANULATS_MARINS, DOMAINES_IDS.CARRIERES].includes(domaineId) &&
[
ETAPES_TYPES.avisDeMiseEnConcurrenceAuJORF,
ETAPES_TYPES.decisionDeLadministration,
ETAPES_TYPES.publicationDeDecisionAuJORF,
ETAPES_TYPES.publicationDeDecisionAuRecueilDesActesAdministratifs,
ETAPES_TYPES.participationDuPublic,
ETAPES_TYPES.enquetePublique,
].includes(titreEtape.typeId)
) {
return true
}

// Pour les PRM d’un titre en survie provisoire, les demandes de prolongations sont public
if (titreTypeId === 'prm' && titreEtape.typeId === 'mdp' && ['pr1', 'pr2'].includes(demarcheTypeId) && titreInModificationEnInstance([demarche])) {
if (
titreTypeId === TITRES_TYPES_IDS.PERMIS_EXCLUSIF_DE_RECHERCHES_METAUX &&
titreEtape.typeId === ETAPES_TYPES.depotDeLaDemande &&
[DEMARCHES_TYPES_IDS.Prolongation1, DEMARCHES_TYPES_IDS.Prolongation2].includes(demarcheTypeId) &&
titreInModificationEnInstance([demarche])
) {
return true
}

Expand Down Expand Up @@ -184,7 +225,7 @@ export const titreDemarchePublicFind = (
// les entreprises titulaires ou amodiataires peuvent voir la démarche
// si la démarche est visible au public
// ou si la démarche contient une demande ou une décision de l'administration unilatérale
const entreprisesLecture = publicLecture || titreDemarcheEtapes.some(te => ['mfr', 'dex', 'dux'].includes(te.typeId))
const entreprisesLecture = publicLecture || titreDemarcheEtapes.some(te => [ETAPES_TYPES.demande, ETAPES_TYPES.decisionDeLadministration, ETAPES_TYPES.decisionAdministrative].includes(te.typeId))

return { publicLecture, entreprisesLecture }
}
Original file line number Diff line number Diff line change
Expand Up @@ -239,9 +239,8 @@ const titreDemarcheTravauxStatutIdFind = (titreDemarcheEtapes: Pick<ITitreEtape,
[Travaux.AvisDesServicesEtCommissionsConsultatives]: DemarchesStatutsIds.EnInstruction,
[Travaux.AvisRapportDirecteurREAL]: DemarchesStatutsIds.EnInstruction,
[Travaux.TransPrescriptionsDemandeur]: DemarchesStatutsIds.EnInstruction,
[Travaux.OuvertureEnquetePublique]: DemarchesStatutsIds.EnInstruction,
[Travaux.EnquetePublique]: DemarchesStatutsIds.EnInstruction,
[Travaux.MemoireReponseExploitant]: DemarchesStatutsIds.EnInstruction,
[Travaux.ClotureEnquetePublique]: DemarchesStatutsIds.EnInstruction,
[Travaux.AvisPrescriptionsDemandeur]: DemarchesStatutsIds.EnInstruction,
[Travaux.ArretePrescriptionComplementaire]: DemarchesStatutsIds.EnInstruction,
[Travaux.ArretePrefectDonneActe1]: DemarchesStatutsIds.EnInstruction,
Expand Down
26 changes: 14 additions & 12 deletions packages/api/src/database/queries/titres-etapes.queries.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,8 +16,8 @@ import {
IUpdateEtapeAvisInfoDbQuery,
IUpdateEtapeAvisFileDbQuery,
IDeleteEtapeAvisDbQuery,
IGetParticipationEtapesDbQuery,
IUpdateParticipationStatutDbQuery,
IGetParticipationOrEnqueteEtapesDbQuery,
IUpdateParticipationOrEnqueteStatutDbQuery,
} from './titres-etapes.queries.types'
import {
ETAPE_IS_NOT_BROUILLON,
Expand Down Expand Up @@ -46,7 +46,7 @@ import { z } from 'zod'
import { entrepriseDocumentLargeObjectIdValidator } from '../../api/rest/entreprises.queries'
import { canReadDocument } from '../../api/rest/permissions/documents'
import { AdministrationId } from 'camino-common/src/static/administrations'
import { EtapeTypeId } from 'camino-common/src/static/etapesTypes'
import { EtapeTypeId, etapeTypeIdValidator } from 'camino-common/src/static/etapesTypes'
import { TitreTypeId, titreTypeIdValidator } from 'camino-common/src/static/titresTypes'
import { DeepReadonly, isNotNullNorUndefined, isNotNullNorUndefinedNorEmpty, SimplePromiseFn } from 'camino-common/src/typescript-tools'
import { CanReadDemarche } from '../../api/rest/permissions/demarches'
Expand Down Expand Up @@ -414,8 +414,9 @@ export const getDocumentsByEtapeId = async (
return z.array(etapeDocumentValidator).parse(result)
}

const getParticipationEtapesValidator = z.object({
const getParticipationOrEnqueteEtapesValidator = z.object({
id: etapeIdValidator,
type_id: etapeTypeIdValidator,
date: caminoDateValidator,
etape_statut_id: etapeStatutIdValidator,
contenu: contenuValidator,
Expand All @@ -424,14 +425,15 @@ const getParticipationEtapesValidator = z.object({
demarche_type_id: demarcheTypeIdValidator,
})

type GetParticipationEtapes = z.infer<typeof getParticipationEtapesValidator>
export const getParticipationEtapes = async (pool: Pool): Promise<GetParticipationEtapes[]> => {
return dbQueryAndValidate(getParticipationEtapesDb, undefined, pool, getParticipationEtapesValidator)
type GetParticipationOrEnqueteEtapesQuery = z.infer<typeof getParticipationOrEnqueteEtapesValidator>
export const getParticipationOrEnqueteEtapes = async (pool: Pool): Promise<GetParticipationOrEnqueteEtapesQuery[]> => {
return dbQueryAndValidate(getParticipationOrEnqueteEtapesDb, undefined, pool, getParticipationOrEnqueteEtapesValidator)
}

const getParticipationEtapesDb = sql<Redefine<IGetParticipationEtapesDbQuery, void, GetParticipationEtapes>>`
const getParticipationOrEnqueteEtapesDb = sql<Redefine<IGetParticipationOrEnqueteEtapesDbQuery, void, GetParticipationOrEnqueteEtapesQuery>>`
select
te.id,
te.type_id,
te.contenu,
te.heritage_contenu,
te.statut_id as etape_statut_id,
Expand All @@ -442,16 +444,16 @@ const getParticipationEtapesDb = sql<Redefine<IGetParticipationEtapesDbQuery, vo
join titres_demarches td on te.titre_demarche_id = td.id
join titres t on td.titre_id = t.id
where
te.type_id = 'ppu' and
te.type_id IN('ppu', 'epu') and
te.archive is false and
td.archive is false and
t.archive is false
`

export const updateParticipationStatut = async (pool: Pool, etapeId: EtapeId, newStatut: EtapeStatutId): Promise<void> => {
await dbQueryAndValidate(updateParticipationStatutDb, { etapeId, newStatut }, pool, z.void())
export const updateParticipationOrEnqueteStatut = async (pool: Pool, etapeId: EtapeId, newStatut: EtapeStatutId): Promise<void> => {
await dbQueryAndValidate(updateParticipationOrEnqueteStatutDb, { etapeId, newStatut }, pool, z.void())
}

const updateParticipationStatutDb = sql<Redefine<IUpdateParticipationStatutDbQuery, { newStatut: EtapeStatutId; etapeId: EtapeId }, void>>`
const updateParticipationOrEnqueteStatutDb = sql<Redefine<IUpdateParticipationOrEnqueteStatutDbQuery, { newStatut: EtapeStatutId; etapeId: EtapeId }, void>>`
UPDATE titres_etapes SET statut_id = $ newStatut ! where id = $ etapeId !
`
Loading
Loading